diff options
Diffstat (limited to 'crates')
115 files changed, 3253 insertions, 1824 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/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/attrs.rs b/crates/hir/src/attrs.rs index 4a11622fc..e8fa3c56e 100644 --- a/crates/hir/src/attrs.rs +++ b/crates/hir/src/attrs.rs | |||
@@ -112,7 +112,7 @@ fn resolve_doc_path( | |||
112 | AttrDefId::MacroDefId(_) => return None, | 112 | AttrDefId::MacroDefId(_) => return None, |
113 | }; | 113 | }; |
114 | let path = ast::Path::parse(link).ok()?; | 114 | let path = ast::Path::parse(link).ok()?; |
115 | let modpath = ModPath::from_src(path, &Hygiene::new_unhygienic()).unwrap(); | 115 | let modpath = ModPath::from_src(db.upcast(), path, &Hygiene::new_unhygienic()).unwrap(); |
116 | let resolved = resolver.resolve_module_path_in_items(db.upcast(), &modpath); | 116 | let resolved = resolver.resolve_module_path_in_items(db.upcast(), &modpath); |
117 | if resolved == PerNs::none() { | 117 | if resolved == PerNs::none() { |
118 | if let Some(trait_id) = resolver.resolve_module_path_in_trait_items(db.upcast(), &modpath) { | 118 | if let Some(trait_id) = resolver.resolve_module_path_in_trait_items(db.upcast(), &modpath) { |
diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs index d8ccfde0c..c9ef4b420 100644 --- a/crates/hir/src/lib.rs +++ b/crates/hir/src/lib.rs | |||
@@ -89,6 +89,7 @@ pub use crate::{ | |||
89 | // Generally, a refactoring which *removes* a name from this list is a good | 89 | // Generally, a refactoring which *removes* a name from this list is a good |
90 | // idea! | 90 | // idea! |
91 | pub use { | 91 | pub use { |
92 | cfg::{CfgAtom, CfgExpr, CfgOptions}, | ||
92 | hir_def::{ | 93 | hir_def::{ |
93 | adt::StructKind, | 94 | adt::StructKind, |
94 | attr::{Attr, Attrs, AttrsWithOwner, Documentation}, | 95 | attr::{Attr, Attrs, AttrsWithOwner, Documentation}, |
@@ -215,6 +216,10 @@ impl Crate { | |||
215 | 216 | ||
216 | doc_url.map(|s| s.trim_matches('"').trim_end_matches('/').to_owned() + "/") | 217 | doc_url.map(|s| s.trim_matches('"').trim_end_matches('/').to_owned() + "/") |
217 | } | 218 | } |
219 | |||
220 | pub fn cfg(&self, db: &dyn HirDatabase) -> CfgOptions { | ||
221 | db.crate_graph()[self.id].cfg_options.clone() | ||
222 | } | ||
218 | } | 223 | } |
219 | 224 | ||
220 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | 225 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] |
@@ -1666,7 +1671,7 @@ impl Impl { | |||
1666 | .value | 1671 | .value |
1667 | .attrs() | 1672 | .attrs() |
1668 | .filter_map(|it| { | 1673 | .filter_map(|it| { |
1669 | let path = ModPath::from_src(it.path()?, &hygenic)?; | 1674 | let path = ModPath::from_src(db.upcast(), it.path()?, &hygenic)?; |
1670 | if path.as_ident()?.to_string() == "derive" { | 1675 | if path.as_ident()?.to_string() == "derive" { |
1671 | Some(it) | 1676 | Some(it) |
1672 | } else { | 1677 | } else { |
@@ -1744,6 +1749,10 @@ impl Type { | |||
1744 | } | 1749 | } |
1745 | } | 1750 | } |
1746 | 1751 | ||
1752 | pub fn strip_references(&self) -> Type { | ||
1753 | self.derived(self.ty.strip_references().clone()) | ||
1754 | } | ||
1755 | |||
1747 | pub fn is_unknown(&self) -> bool { | 1756 | pub fn is_unknown(&self) -> bool { |
1748 | self.ty.is_unknown() | 1757 | self.ty.is_unknown() |
1749 | } | 1758 | } |
@@ -2062,6 +2071,10 @@ impl Type { | |||
2062 | Some(adt.into()) | 2071 | Some(adt.into()) |
2063 | } | 2072 | } |
2064 | 2073 | ||
2074 | pub fn as_builtin(&self) -> Option<BuiltinType> { | ||
2075 | self.ty.as_builtin().map(|inner| BuiltinType { inner }) | ||
2076 | } | ||
2077 | |||
2065 | pub fn as_dyn_trait(&self) -> Option<Trait> { | 2078 | pub fn as_dyn_trait(&self) -> Option<Trait> { |
2066 | self.ty.dyn_trait().map(Into::into) | 2079 | self.ty.dyn_trait().map(Into::into) |
2067 | } | 2080 | } |
diff --git a/crates/hir/src/semantics.rs b/crates/hir/src/semantics.rs index 62500602a..38bd376bc 100644 --- a/crates/hir/src/semantics.rs +++ b/crates/hir/src/semantics.rs | |||
@@ -196,6 +196,10 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> { | |||
196 | self.imp.resolve_label(lifetime) | 196 | self.imp.resolve_label(lifetime) |
197 | } | 197 | } |
198 | 198 | ||
199 | pub fn resolve_type(&self, ty: &ast::Type) -> Option<Type> { | ||
200 | self.imp.resolve_type(ty) | ||
201 | } | ||
202 | |||
199 | pub fn type_of_expr(&self, expr: &ast::Expr) -> Option<Type> { | 203 | pub fn type_of_expr(&self, expr: &ast::Expr) -> Option<Type> { |
200 | self.imp.type_of_expr(expr) | 204 | self.imp.type_of_expr(expr) |
201 | } | 205 | } |
@@ -476,6 +480,14 @@ impl<'db> SemanticsImpl<'db> { | |||
476 | ToDef::to_def(self, src) | 480 | ToDef::to_def(self, src) |
477 | } | 481 | } |
478 | 482 | ||
483 | fn resolve_type(&self, ty: &ast::Type) -> Option<Type> { | ||
484 | let scope = self.scope(ty.syntax()); | ||
485 | let ctx = body::LowerCtx::new(self.db.upcast(), scope.file_id); | ||
486 | let ty = hir_ty::TyLoweringContext::new(self.db, &scope.resolver) | ||
487 | .lower_ty(&crate::TypeRef::from_ast(&ctx, ty.clone())); | ||
488 | Type::new_with_resolver(self.db, &scope.resolver, ty) | ||
489 | } | ||
490 | |||
479 | fn type_of_expr(&self, expr: &ast::Expr) -> Option<Type> { | 491 | fn type_of_expr(&self, expr: &ast::Expr) -> Option<Type> { |
480 | self.analyze(expr.syntax()).type_of_expr(self.db, expr) | 492 | self.analyze(expr.syntax()).type_of_expr(self.db, expr) |
481 | } | 493 | } |
diff --git a/crates/hir/src/source_analyzer.rs b/crates/hir/src/source_analyzer.rs index 0895bd6f1..b5c65808e 100644 --- a/crates/hir/src/source_analyzer.rs +++ b/crates/hir/src/source_analyzer.rs | |||
@@ -283,7 +283,7 @@ impl SourceAnalyzer { | |||
283 | 283 | ||
284 | // This must be a normal source file rather than macro file. | 284 | // This must be a normal source file rather than macro file. |
285 | let hygiene = Hygiene::new(db.upcast(), self.file_id); | 285 | let hygiene = Hygiene::new(db.upcast(), self.file_id); |
286 | let ctx = body::LowerCtx::with_hygiene(&hygiene); | 286 | let ctx = body::LowerCtx::with_hygiene(db.upcast(), &hygiene); |
287 | let hir_path = Path::from_src(path.clone(), &ctx)?; | 287 | let hir_path = Path::from_src(path.clone(), &ctx)?; |
288 | 288 | ||
289 | // Case where path is a qualifier of another path, e.g. foo::bar::Baz where we | 289 | // Case where path is a qualifier of another path, e.g. foo::bar::Baz where we |
diff --git a/crates/hir_def/src/attr.rs b/crates/hir_def/src/attr.rs index d9294d93a..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; |
@@ -95,19 +95,19 @@ impl ops::Deref for AttrsWithOwner { | |||
95 | impl RawAttrs { | 95 | impl RawAttrs { |
96 | pub(crate) const EMPTY: Self = Self { entries: None }; | 96 | pub(crate) const EMPTY: Self = Self { entries: None }; |
97 | 97 | ||
98 | pub(crate) fn new(owner: &dyn ast::AttrsOwner, hygiene: &Hygiene) -> Self { | 98 | pub(crate) fn new( |
99 | db: &dyn DefDatabase, | ||
100 | owner: &dyn ast::AttrsOwner, | ||
101 | hygiene: &Hygiene, | ||
102 | ) -> Self { | ||
99 | let entries = collect_attrs(owner) | 103 | let entries = collect_attrs(owner) |
100 | .enumerate() | 104 | .flat_map(|(id, attr)| match attr { |
101 | .flat_map(|(i, attr)| { | 105 | Either::Left(attr) => Attr::from_src(db, attr, hygiene, id), |
102 | let index = AttrId(i as u32); | 106 | Either::Right(comment) => comment.doc_comment().map(|doc| Attr { |
103 | match attr { | 107 | id, |
104 | Either::Left(attr) => Attr::from_src(attr, hygiene, index), | 108 | input: Some(AttrInput::Literal(SmolStr::new(doc))), |
105 | Either::Right(comment) => comment.doc_comment().map(|doc| Attr { | 109 | path: Interned::new(ModPath::from(hir_expand::name!(doc))), |
106 | id: index, | 110 | }), |
107 | input: Some(AttrInput::Literal(SmolStr::new(doc))), | ||
108 | path: Interned::new(ModPath::from(hir_expand::name!(doc))), | ||
109 | }), | ||
110 | } | ||
111 | }) | 111 | }) |
112 | .collect::<Arc<_>>(); | 112 | .collect::<Arc<_>>(); |
113 | 113 | ||
@@ -116,10 +116,11 @@ impl RawAttrs { | |||
116 | 116 | ||
117 | fn from_attrs_owner(db: &dyn DefDatabase, owner: InFile<&dyn ast::AttrsOwner>) -> Self { | 117 | fn from_attrs_owner(db: &dyn DefDatabase, owner: InFile<&dyn ast::AttrsOwner>) -> Self { |
118 | let hygiene = Hygiene::new(db.upcast(), owner.file_id); | 118 | let hygiene = Hygiene::new(db.upcast(), owner.file_id); |
119 | Self::new(owner.value, &hygiene) | 119 | Self::new(db, owner.value, &hygiene) |
120 | } | 120 | } |
121 | 121 | ||
122 | 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 | ||
123 | match (&self.entries, &other.entries) { | 124 | match (&self.entries, &other.entries) { |
124 | (None, None) => Self::EMPTY, | 125 | (None, None) => Self::EMPTY, |
125 | (Some(entries), None) | (None, Some(entries)) => { | 126 | (Some(entries), None) | (None, Some(entries)) => { |
@@ -170,7 +171,7 @@ impl RawAttrs { | |||
170 | let attr = ast::Attr::parse(&format!("#[{}]", tree)).ok()?; | 171 | let attr = ast::Attr::parse(&format!("#[{}]", tree)).ok()?; |
171 | // FIXME hygiene | 172 | // FIXME hygiene |
172 | let hygiene = Hygiene::new_unhygienic(); | 173 | let hygiene = Hygiene::new_unhygienic(); |
173 | Attr::from_src(attr, &hygiene, index) | 174 | Attr::from_src(db, attr, &hygiene, index) |
174 | }); | 175 | }); |
175 | 176 | ||
176 | let cfg_options = &crate_graph[krate].cfg_options; | 177 | let cfg_options = &crate_graph[krate].cfg_options; |
@@ -371,39 +372,26 @@ impl AttrsWithOwner { | |||
371 | 372 | ||
372 | let def_map = module.def_map(db); | 373 | let def_map = module.def_map(db); |
373 | let mod_data = &def_map[module.local_id]; | 374 | let mod_data = &def_map[module.local_id]; |
374 | let attrs = match mod_data.declaration_source(db) { | 375 | match mod_data.declaration_source(db) { |
375 | Some(it) => { | 376 | Some(it) => { |
376 | 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)); |
377 | .map(|attr| InFile::new(it.file_id, attr)) | ||
378 | .collect(); | ||
379 | if let InFile { file_id, value: ModuleSource::SourceFile(file) } = | 378 | if let InFile { file_id, value: ModuleSource::SourceFile(file) } = |
380 | mod_data.definition_source(db) | 379 | mod_data.definition_source(db) |
381 | { | 380 | { |
382 | attrs.extend( | 381 | map.merge(AttrSourceMap::new(InFile::new(file_id, &file))); |
383 | collect_attrs(&file as &dyn ast::AttrsOwner) | ||
384 | .map(|attr| InFile::new(file_id, attr)), | ||
385 | ) | ||
386 | } | 382 | } |
387 | attrs | 383 | return map; |
388 | } | 384 | } |
389 | None => { | 385 | None => { |
390 | let InFile { file_id, value } = mod_data.definition_source(db); | 386 | let InFile { file_id, value } = mod_data.definition_source(db); |
391 | match &value { | 387 | let attrs_owner = match &value { |
392 | ModuleSource::SourceFile(file) => { | 388 | ModuleSource::SourceFile(file) => file as &dyn ast::AttrsOwner, |
393 | collect_attrs(file as &dyn ast::AttrsOwner) | 389 | ModuleSource::Module(module) => module as &dyn ast::AttrsOwner, |
394 | } | 390 | ModuleSource::BlockExpr(block) => block as &dyn ast::AttrsOwner, |
395 | ModuleSource::Module(module) => { | 391 | }; |
396 | collect_attrs(module as &dyn ast::AttrsOwner) | 392 | return AttrSourceMap::new(InFile::new(file_id, attrs_owner)); |
397 | } | ||
398 | ModuleSource::BlockExpr(block) => { | ||
399 | collect_attrs(block as &dyn ast::AttrsOwner) | ||
400 | } | ||
401 | } | ||
402 | .map(|attr| InFile::new(file_id, attr)) | ||
403 | .collect() | ||
404 | } | 393 | } |
405 | }; | 394 | } |
406 | return AttrSourceMap { attrs }; | ||
407 | } | 395 | } |
408 | AttrDefId::FieldId(id) => { | 396 | AttrDefId::FieldId(id) => { |
409 | let map = db.fields_attrs_source_map(id.parent); | 397 | let map = db.fields_attrs_source_map(id.parent); |
@@ -458,11 +446,7 @@ impl AttrsWithOwner { | |||
458 | }, | 446 | }, |
459 | }; | 447 | }; |
460 | 448 | ||
461 | AttrSourceMap { | 449 | AttrSourceMap::new(owner.as_ref().map(|node| node as &dyn AttrsOwner)) |
462 | attrs: collect_attrs(&owner.value) | ||
463 | .map(|attr| InFile::new(owner.file_id, attr)) | ||
464 | .collect(), | ||
465 | } | ||
466 | } | 450 | } |
467 | 451 | ||
468 | pub fn docs_with_rangemap( | 452 | pub fn docs_with_rangemap( |
@@ -484,10 +468,10 @@ impl AttrsWithOwner { | |||
484 | let mut buf = String::new(); | 468 | let mut buf = String::new(); |
485 | let mut mapping = Vec::new(); | 469 | let mut mapping = Vec::new(); |
486 | for (doc, idx) in docs { | 470 | for (doc, idx) in docs { |
487 | // str::lines doesn't yield anything for the empty string | ||
488 | if !doc.is_empty() { | 471 | if !doc.is_empty() { |
489 | for line in doc.split('\n') { | 472 | let mut base_offset = 0; |
490 | let line = line.trim_end(); | 473 | for raw_line in doc.split('\n') { |
474 | let line = raw_line.trim_end(); | ||
491 | let line_len = line.len(); | 475 | let line_len = line.len(); |
492 | let (offset, line) = match line.char_indices().nth(indent) { | 476 | let (offset, line) = match line.char_indices().nth(indent) { |
493 | Some((offset, _)) => (offset, &line[offset..]), | 477 | Some((offset, _)) => (offset, &line[offset..]), |
@@ -498,9 +482,13 @@ impl AttrsWithOwner { | |||
498 | mapping.push(( | 482 | mapping.push(( |
499 | TextRange::new(buf_offset.try_into().ok()?, buf.len().try_into().ok()?), | 483 | TextRange::new(buf_offset.try_into().ok()?, buf.len().try_into().ok()?), |
500 | idx, | 484 | idx, |
501 | TextRange::new(offset.try_into().ok()?, line_len.try_into().ok()?), | 485 | TextRange::at( |
486 | (base_offset + offset).try_into().ok()?, | ||
487 | line_len.try_into().ok()?, | ||
488 | ), | ||
502 | )); | 489 | )); |
503 | buf.push('\n'); | 490 | buf.push('\n'); |
491 | base_offset += raw_line.len() + 1; | ||
504 | } | 492 | } |
505 | } else { | 493 | } else { |
506 | buf.push('\n'); | 494 | buf.push('\n'); |
@@ -510,7 +498,7 @@ impl AttrsWithOwner { | |||
510 | if buf.is_empty() { | 498 | if buf.is_empty() { |
511 | None | 499 | None |
512 | } else { | 500 | } else { |
513 | Some((Documentation(buf), DocsRangeMap { mapping, source: self.source_map(db).attrs })) | 501 | Some((Documentation(buf), DocsRangeMap { mapping, source_map: self.source_map(db) })) |
514 | } | 502 | } |
515 | } | 503 | } |
516 | } | 504 | } |
@@ -551,27 +539,59 @@ fn inner_attributes( | |||
551 | } | 539 | } |
552 | 540 | ||
553 | pub struct AttrSourceMap { | 541 | pub struct AttrSourceMap { |
554 | attrs: Vec<InFile<Either<ast::Attr, ast::Comment>>>, | 542 | attrs: Vec<InFile<ast::Attr>>, |
543 | doc_comments: Vec<InFile<ast::Comment>>, | ||
555 | } | 544 | } |
556 | 545 | ||
557 | 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 | |||
558 | /// Maps the lowered `Attr` back to its original syntax node. | 565 | /// Maps the lowered `Attr` back to its original syntax node. |
559 | /// | 566 | /// |
560 | /// `attr` must come from the `owner` used for AttrSourceMap | 567 | /// `attr` must come from the `owner` used for AttrSourceMap |
561 | /// | 568 | /// |
562 | /// 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 |
563 | /// the attribute represented by `Attr`. | 570 | /// the attribute represented by `Attr`. |
564 | 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>> { |
565 | self.attrs | 572 | self.source_of_id(attr.id) |
566 | .get(attr.id.0 as usize) | 573 | } |
567 | .unwrap_or_else(|| panic!("cannot find `Attr` at index {:?}", attr.id)) | 574 | |
568 | .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 | } | ||
569 | } | 589 | } |
570 | } | 590 | } |
571 | 591 | ||
572 | /// 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. |
573 | pub struct DocsRangeMap { | 593 | pub struct DocsRangeMap { |
574 | source: Vec<InFile<Either<ast::Attr, ast::Comment>>>, | 594 | source_map: AttrSourceMap, |
575 | // (docstring-line-range, attr_index, attr-string-range) | 595 | // (docstring-line-range, attr_index, attr-string-range) |
576 | // 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 |
577 | // the original (untrimmed) syntax doc line | 597 | // the original (untrimmed) syntax doc line |
@@ -588,7 +608,7 @@ impl DocsRangeMap { | |||
588 | 608 | ||
589 | let relative_range = range - line_docs_range.start(); | 609 | let relative_range = range - line_docs_range.start(); |
590 | 610 | ||
591 | 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); |
592 | match source { | 612 | match source { |
593 | 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 |
594 | // as well as for whats done in syntax highlight doc injection | 614 | // as well as for whats done in syntax highlight doc injection |
@@ -607,6 +627,12 @@ impl DocsRangeMap { | |||
607 | } | 627 | } |
608 | } | 628 | } |
609 | 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 | |||
610 | #[derive(Debug, Clone, PartialEq, Eq)] | 636 | #[derive(Debug, Clone, PartialEq, Eq)] |
611 | pub struct Attr { | 637 | pub struct Attr { |
612 | pub(crate) id: AttrId, | 638 | pub(crate) id: AttrId, |
@@ -623,8 +649,13 @@ pub enum AttrInput { | |||
623 | } | 649 | } |
624 | 650 | ||
625 | impl Attr { | 651 | impl Attr { |
626 | fn from_src(ast: ast::Attr, hygiene: &Hygiene, id: AttrId) -> Option<Attr> { | 652 | fn from_src( |
627 | let path = Interned::new(ModPath::from_src(ast.path()?, hygiene)?); | 653 | db: &dyn DefDatabase, |
654 | ast: ast::Attr, | ||
655 | hygiene: &Hygiene, | ||
656 | id: AttrId, | ||
657 | ) -> Option<Attr> { | ||
658 | let path = Interned::new(ModPath::from_src(db, ast.path()?, hygiene)?); | ||
628 | let input = if let Some(ast::Expr::Literal(lit)) = ast.expr() { | 659 | let input = if let Some(ast::Expr::Literal(lit)) = ast.expr() { |
629 | let value = match lit.kind() { | 660 | let value = match lit.kind() { |
630 | ast::LiteralKind::String(string) => string.value()?.into(), | 661 | ast::LiteralKind::String(string) => string.value()?.into(), |
@@ -736,22 +767,32 @@ fn attrs_from_item_tree<N: ItemTreeNode>(id: ItemTreeId<N>, db: &dyn DefDatabase | |||
736 | 767 | ||
737 | fn collect_attrs( | 768 | fn collect_attrs( |
738 | owner: &dyn ast::AttrsOwner, | 769 | owner: &dyn ast::AttrsOwner, |
739 | ) -> impl Iterator<Item = Either<ast::Attr, ast::Comment>> { | 770 | ) -> impl Iterator<Item = (AttrId, Either<ast::Attr, ast::Comment>)> { |
740 | let (inner_attrs, inner_docs) = inner_attributes(owner.syntax()) | 771 | let (inner_attrs, inner_docs) = inner_attributes(owner.syntax()) |
741 | .map_or((None, None), |(attrs, docs)| (Some(attrs), Some(docs))); | 772 | .map_or((None, None), |(attrs, docs)| (Some(attrs), Some(docs))); |
742 | 773 | ||
743 | let outer_attrs = owner.attrs().filter(|attr| attr.kind().is_outer()); | 774 | let outer_attrs = owner.attrs().filter(|attr| attr.kind().is_outer()); |
744 | let attrs = outer_attrs | 775 | let attrs = |
745 | .chain(inner_attrs.into_iter().flatten()) | 776 | outer_attrs.chain(inner_attrs.into_iter().flatten()).enumerate().map(|(idx, attr)| { |
746 | .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 | }); | ||
747 | 783 | ||
748 | let outer_docs = | 784 | let outer_docs = |
749 | 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); |
750 | let docs = outer_docs | 786 | let docs = |
751 | .chain(inner_docs.into_iter().flatten()) | 787 | outer_docs.chain(inner_docs.into_iter().flatten()).enumerate().map(|(idx, docs_text)| { |
752 | .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 | }); | ||
753 | // 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 |
754 | 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)) |
755 | } | 796 | } |
756 | 797 | ||
757 | 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 131f424cc..8360426f1 100644 --- a/crates/hir_def/src/body.rs +++ b/crates/hir_def/src/body.rs | |||
@@ -72,7 +72,7 @@ impl CfgExpander { | |||
72 | } | 72 | } |
73 | 73 | ||
74 | pub(crate) fn parse_attrs(&self, db: &dyn DefDatabase, owner: &dyn ast::AttrsOwner) -> Attrs { | 74 | pub(crate) fn parse_attrs(&self, db: &dyn DefDatabase, owner: &dyn ast::AttrsOwner) -> Attrs { |
75 | RawAttrs::new(owner, &self.hygiene).filter(db, self.krate) | 75 | RawAttrs::new(db, owner, &self.hygiene).filter(db, self.krate) |
76 | } | 76 | } |
77 | 77 | ||
78 | pub(crate) fn is_cfg_enabled(&self, db: &dyn DefDatabase, owner: &dyn ast::AttrsOwner) -> bool { | 78 | pub(crate) fn is_cfg_enabled(&self, db: &dyn DefDatabase, owner: &dyn ast::AttrsOwner) -> bool { |
@@ -192,8 +192,8 @@ impl Expander { | |||
192 | self.current_file_id | 192 | self.current_file_id |
193 | } | 193 | } |
194 | 194 | ||
195 | fn parse_path(&mut self, path: ast::Path) -> Option<Path> { | 195 | fn parse_path(&mut self, db: &dyn DefDatabase, path: ast::Path) -> Option<Path> { |
196 | let ctx = LowerCtx::with_hygiene(&self.cfg_expander.hygiene); | 196 | let ctx = LowerCtx::with_hygiene(db, &self.cfg_expander.hygiene); |
197 | Path::from_src(path, &ctx) | 197 | Path::from_src(path, &ctx) |
198 | } | 198 | } |
199 | 199 | ||
diff --git a/crates/hir_def/src/body/lower.rs b/crates/hir_def/src/body/lower.rs index 820d5c17e..9f278d35b 100644 --- a/crates/hir_def/src/body/lower.rs +++ b/crates/hir_def/src/body/lower.rs | |||
@@ -40,23 +40,25 @@ use crate::{ | |||
40 | 40 | ||
41 | use super::{diagnostics::BodyDiagnostic, ExprSource, PatSource}; | 41 | use super::{diagnostics::BodyDiagnostic, ExprSource, PatSource}; |
42 | 42 | ||
43 | pub struct LowerCtx { | 43 | pub struct LowerCtx<'a> { |
44 | pub db: &'a dyn DefDatabase, | ||
44 | hygiene: Hygiene, | 45 | hygiene: Hygiene, |
45 | file_id: Option<HirFileId>, | 46 | file_id: Option<HirFileId>, |
46 | source_ast_id_map: Option<Arc<AstIdMap>>, | 47 | source_ast_id_map: Option<Arc<AstIdMap>>, |
47 | } | 48 | } |
48 | 49 | ||
49 | impl LowerCtx { | 50 | impl<'a> LowerCtx<'a> { |
50 | pub fn new(db: &dyn DefDatabase, file_id: HirFileId) -> Self { | 51 | pub fn new(db: &'a dyn DefDatabase, file_id: HirFileId) -> Self { |
51 | LowerCtx { | 52 | LowerCtx { |
53 | db, | ||
52 | hygiene: Hygiene::new(db.upcast(), file_id), | 54 | hygiene: Hygiene::new(db.upcast(), file_id), |
53 | file_id: Some(file_id), | 55 | file_id: Some(file_id), |
54 | source_ast_id_map: Some(db.ast_id_map(file_id)), | 56 | source_ast_id_map: Some(db.ast_id_map(file_id)), |
55 | } | 57 | } |
56 | } | 58 | } |
57 | 59 | ||
58 | pub fn with_hygiene(hygiene: &Hygiene) -> Self { | 60 | pub fn with_hygiene(db: &'a dyn DefDatabase, hygiene: &Hygiene) -> Self { |
59 | LowerCtx { hygiene: hygiene.clone(), file_id: None, source_ast_id_map: None } | 61 | LowerCtx { db, hygiene: hygiene.clone(), file_id: None, source_ast_id_map: None } |
60 | } | 62 | } |
61 | 63 | ||
62 | pub(crate) fn hygiene(&self) -> &Hygiene { | 64 | pub(crate) fn hygiene(&self) -> &Hygiene { |
@@ -145,7 +147,7 @@ impl ExprCollector<'_> { | |||
145 | (self.body, self.source_map) | 147 | (self.body, self.source_map) |
146 | } | 148 | } |
147 | 149 | ||
148 | fn ctx(&self) -> LowerCtx { | 150 | fn ctx(&self) -> LowerCtx<'_> { |
149 | LowerCtx::new(self.db, self.expander.current_file_id) | 151 | LowerCtx::new(self.db, self.expander.current_file_id) |
150 | } | 152 | } |
151 | 153 | ||
@@ -376,7 +378,7 @@ impl ExprCollector<'_> { | |||
376 | ast::Expr::PathExpr(e) => { | 378 | ast::Expr::PathExpr(e) => { |
377 | let path = e | 379 | let path = e |
378 | .path() | 380 | .path() |
379 | .and_then(|path| self.expander.parse_path(path)) | 381 | .and_then(|path| self.expander.parse_path(self.db, path)) |
380 | .map(Expr::Path) | 382 | .map(Expr::Path) |
381 | .unwrap_or(Expr::Missing); | 383 | .unwrap_or(Expr::Missing); |
382 | self.alloc_expr(path, syntax_ptr) | 384 | self.alloc_expr(path, syntax_ptr) |
@@ -408,7 +410,8 @@ impl ExprCollector<'_> { | |||
408 | self.alloc_expr(Expr::Yield { expr }, syntax_ptr) | 410 | self.alloc_expr(Expr::Yield { expr }, syntax_ptr) |
409 | } | 411 | } |
410 | ast::Expr::RecordExpr(e) => { | 412 | ast::Expr::RecordExpr(e) => { |
411 | let path = e.path().and_then(|path| self.expander.parse_path(path)).map(Box::new); | 413 | let path = |
414 | e.path().and_then(|path| self.expander.parse_path(self.db, path)).map(Box::new); | ||
412 | let record_lit = if let Some(nfl) = e.record_expr_field_list() { | 415 | let record_lit = if let Some(nfl) = e.record_expr_field_list() { |
413 | let fields = nfl | 416 | let fields = nfl |
414 | .fields() | 417 | .fields() |
@@ -801,7 +804,8 @@ impl ExprCollector<'_> { | |||
801 | } | 804 | } |
802 | } | 805 | } |
803 | ast::Pat::TupleStructPat(p) => { | 806 | ast::Pat::TupleStructPat(p) => { |
804 | let path = p.path().and_then(|path| self.expander.parse_path(path)).map(Box::new); | 807 | let path = |
808 | p.path().and_then(|path| self.expander.parse_path(self.db, path)).map(Box::new); | ||
805 | let (args, ellipsis) = self.collect_tuple_pat(p.fields()); | 809 | let (args, ellipsis) = self.collect_tuple_pat(p.fields()); |
806 | Pat::TupleStruct { path, args, ellipsis } | 810 | Pat::TupleStruct { path, args, ellipsis } |
807 | } | 811 | } |
@@ -811,7 +815,8 @@ impl ExprCollector<'_> { | |||
811 | Pat::Ref { pat, mutability } | 815 | Pat::Ref { pat, mutability } |
812 | } | 816 | } |
813 | ast::Pat::PathPat(p) => { | 817 | ast::Pat::PathPat(p) => { |
814 | let path = p.path().and_then(|path| self.expander.parse_path(path)).map(Box::new); | 818 | let path = |
819 | p.path().and_then(|path| self.expander.parse_path(self.db, path)).map(Box::new); | ||
815 | path.map(Pat::Path).unwrap_or(Pat::Missing) | 820 | path.map(Pat::Path).unwrap_or(Pat::Missing) |
816 | } | 821 | } |
817 | ast::Pat::OrPat(p) => { | 822 | ast::Pat::OrPat(p) => { |
@@ -825,7 +830,8 @@ impl ExprCollector<'_> { | |||
825 | } | 830 | } |
826 | ast::Pat::WildcardPat(_) => Pat::Wild, | 831 | ast::Pat::WildcardPat(_) => Pat::Wild, |
827 | ast::Pat::RecordPat(p) => { | 832 | ast::Pat::RecordPat(p) => { |
828 | let path = p.path().and_then(|path| self.expander.parse_path(path)).map(Box::new); | 833 | let path = |
834 | p.path().and_then(|path| self.expander.parse_path(self.db, path)).map(Box::new); | ||
829 | let args: Vec<_> = p | 835 | let args: Vec<_> = p |
830 | .record_pat_field_list() | 836 | .record_pat_field_list() |
831 | .expect("every struct should have a field list") | 837 | .expect("every struct should have a field list") |
diff --git a/crates/hir_def/src/find_path.rs b/crates/hir_def/src/find_path.rs index c06a37294..858e88038 100644 --- a/crates/hir_def/src/find_path.rs +++ b/crates/hir_def/src/find_path.rs | |||
@@ -386,7 +386,7 @@ mod tests { | |||
386 | let parsed_path_file = syntax::SourceFile::parse(&format!("use {};", path)); | 386 | let parsed_path_file = syntax::SourceFile::parse(&format!("use {};", path)); |
387 | let ast_path = | 387 | let ast_path = |
388 | parsed_path_file.syntax_node().descendants().find_map(syntax::ast::Path::cast).unwrap(); | 388 | parsed_path_file.syntax_node().descendants().find_map(syntax::ast::Path::cast).unwrap(); |
389 | let mod_path = ModPath::from_src(ast_path, &Hygiene::new_unhygienic()).unwrap(); | 389 | let mod_path = ModPath::from_src(&db, ast_path, &Hygiene::new_unhygienic()).unwrap(); |
390 | 390 | ||
391 | let def_map = module.def_map(&db); | 391 | let def_map = module.def_map(&db); |
392 | let resolved = def_map | 392 | let resolved = def_map |
diff --git a/crates/hir_def/src/item_tree.rs b/crates/hir_def/src/item_tree.rs index eaeca01bd..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; |
@@ -88,7 +88,7 @@ impl ItemTree { | |||
88 | let mut item_tree = match_ast! { | 88 | let mut item_tree = match_ast! { |
89 | match syntax { | 89 | match syntax { |
90 | ast::SourceFile(file) => { | 90 | ast::SourceFile(file) => { |
91 | top_attrs = Some(RawAttrs::new(&file, &hygiene)); | 91 | top_attrs = Some(RawAttrs::new(db, &file, &hygiene)); |
92 | ctx.lower_module_items(&file) | 92 | ctx.lower_module_items(&file) |
93 | }, | 93 | }, |
94 | ast::MacroItems(items) => { | 94 | ast::MacroItems(items) => { |
@@ -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 45b099cf3..fe348091d 100644 --- a/crates/hir_def/src/item_tree/lower.rs +++ b/crates/hir_def/src/item_tree/lower.rs | |||
@@ -31,18 +31,20 @@ where | |||
31 | } | 31 | } |
32 | } | 32 | } |
33 | 33 | ||
34 | pub(super) struct Ctx { | 34 | pub(super) struct Ctx<'a> { |
35 | db: &'a dyn DefDatabase, | ||
35 | tree: ItemTree, | 36 | tree: ItemTree, |
36 | hygiene: Hygiene, | 37 | hygiene: Hygiene, |
37 | file: HirFileId, | 38 | file: HirFileId, |
38 | source_ast_id_map: Arc<AstIdMap>, | 39 | source_ast_id_map: Arc<AstIdMap>, |
39 | body_ctx: crate::body::LowerCtx, | 40 | body_ctx: crate::body::LowerCtx<'a>, |
40 | forced_visibility: Option<RawVisibilityId>, | 41 | forced_visibility: Option<RawVisibilityId>, |
41 | } | 42 | } |
42 | 43 | ||
43 | impl Ctx { | 44 | impl<'a> Ctx<'a> { |
44 | pub(super) fn new(db: &dyn DefDatabase, hygiene: Hygiene, file: HirFileId) -> Self { | 45 | pub(super) fn new(db: &'a dyn DefDatabase, hygiene: Hygiene, file: HirFileId) -> Self { |
45 | Self { | 46 | Self { |
47 | db, | ||
46 | tree: ItemTree::default(), | 48 | tree: ItemTree::default(), |
47 | hygiene, | 49 | hygiene, |
48 | file, | 50 | file, |
@@ -126,7 +128,7 @@ impl Ctx { | |||
126 | | ast::Item::MacroDef(_) => {} | 128 | | ast::Item::MacroDef(_) => {} |
127 | }; | 129 | }; |
128 | 130 | ||
129 | let attrs = RawAttrs::new(item, &self.hygiene); | 131 | let attrs = RawAttrs::new(self.db, item, &self.hygiene); |
130 | let items = match item { | 132 | let items = match item { |
131 | ast::Item::Struct(ast) => self.lower_struct(ast).map(Into::into), | 133 | ast::Item::Struct(ast) => self.lower_struct(ast).map(Into::into), |
132 | ast::Item::Union(ast) => self.lower_union(ast).map(Into::into), | 134 | ast::Item::Union(ast) => self.lower_union(ast).map(Into::into), |
@@ -256,7 +258,7 @@ impl Ctx { | |||
256 | for field in fields.fields() { | 258 | for field in fields.fields() { |
257 | if let Some(data) = self.lower_record_field(&field) { | 259 | if let Some(data) = self.lower_record_field(&field) { |
258 | let idx = self.data().fields.alloc(data); | 260 | let idx = self.data().fields.alloc(data); |
259 | self.add_attrs(idx.into(), RawAttrs::new(&field, &self.hygiene)); | 261 | self.add_attrs(idx.into(), RawAttrs::new(self.db, &field, &self.hygiene)); |
260 | } | 262 | } |
261 | } | 263 | } |
262 | let end = self.next_field_idx(); | 264 | let end = self.next_field_idx(); |
@@ -276,7 +278,7 @@ impl Ctx { | |||
276 | for (i, field) in fields.fields().enumerate() { | 278 | for (i, field) in fields.fields().enumerate() { |
277 | let data = self.lower_tuple_field(i, &field); | 279 | let data = self.lower_tuple_field(i, &field); |
278 | let idx = self.data().fields.alloc(data); | 280 | let idx = self.data().fields.alloc(data); |
279 | self.add_attrs(idx.into(), RawAttrs::new(&field, &self.hygiene)); | 281 | self.add_attrs(idx.into(), RawAttrs::new(self.db, &field, &self.hygiene)); |
280 | } | 282 | } |
281 | let end = self.next_field_idx(); | 283 | let end = self.next_field_idx(); |
282 | IdRange::new(start..end) | 284 | IdRange::new(start..end) |
@@ -321,7 +323,7 @@ impl Ctx { | |||
321 | for variant in variants.variants() { | 323 | for variant in variants.variants() { |
322 | if let Some(data) = self.lower_variant(&variant) { | 324 | if let Some(data) = self.lower_variant(&variant) { |
323 | let idx = self.data().variants.alloc(data); | 325 | let idx = self.data().variants.alloc(data); |
324 | self.add_attrs(idx.into(), RawAttrs::new(&variant, &self.hygiene)); | 326 | self.add_attrs(idx.into(), RawAttrs::new(self.db, &variant, &self.hygiene)); |
325 | } | 327 | } |
326 | } | 328 | } |
327 | let end = self.next_variant_idx(); | 329 | let end = self.next_variant_idx(); |
@@ -364,7 +366,7 @@ impl Ctx { | |||
364 | }; | 366 | }; |
365 | let ty = Interned::new(self_type); | 367 | let ty = Interned::new(self_type); |
366 | let idx = self.data().params.alloc(Param::Normal(ty)); | 368 | let idx = self.data().params.alloc(Param::Normal(ty)); |
367 | self.add_attrs(idx.into(), RawAttrs::new(&self_param, &self.hygiene)); | 369 | self.add_attrs(idx.into(), RawAttrs::new(self.db, &self_param, &self.hygiene)); |
368 | has_self_param = true; | 370 | has_self_param = true; |
369 | } | 371 | } |
370 | for param in param_list.params() { | 372 | for param in param_list.params() { |
@@ -376,7 +378,7 @@ impl Ctx { | |||
376 | self.data().params.alloc(Param::Normal(ty)) | 378 | self.data().params.alloc(Param::Normal(ty)) |
377 | } | 379 | } |
378 | }; | 380 | }; |
379 | self.add_attrs(idx.into(), RawAttrs::new(¶m, &self.hygiene)); | 381 | self.add_attrs(idx.into(), RawAttrs::new(self.db, ¶m, &self.hygiene)); |
380 | } | 382 | } |
381 | } | 383 | } |
382 | let end_param = self.next_param_idx(); | 384 | let end_param = self.next_param_idx(); |
@@ -522,10 +524,11 @@ impl Ctx { | |||
522 | let is_unsafe = trait_def.unsafe_token().is_some(); | 524 | let is_unsafe = trait_def.unsafe_token().is_some(); |
523 | let bounds = self.lower_type_bounds(trait_def); | 525 | let bounds = self.lower_type_bounds(trait_def); |
524 | let items = trait_def.assoc_item_list().map(|list| { | 526 | let items = trait_def.assoc_item_list().map(|list| { |
527 | let db = self.db; | ||
525 | self.with_inherited_visibility(visibility, |this| { | 528 | self.with_inherited_visibility(visibility, |this| { |
526 | list.assoc_items() | 529 | list.assoc_items() |
527 | .filter_map(|item| { | 530 | .filter_map(|item| { |
528 | let attrs = RawAttrs::new(&item, &this.hygiene); | 531 | let attrs = RawAttrs::new(db, &item, &this.hygiene); |
529 | this.collect_inner_items(item.syntax()); | 532 | this.collect_inner_items(item.syntax()); |
530 | this.lower_assoc_item(&item).map(|item| { | 533 | this.lower_assoc_item(&item).map(|item| { |
531 | this.add_attrs(ModItem::from(item).into(), attrs); | 534 | this.add_attrs(ModItem::from(item).into(), attrs); |
@@ -567,7 +570,7 @@ impl Ctx { | |||
567 | .filter_map(|item| { | 570 | .filter_map(|item| { |
568 | self.collect_inner_items(item.syntax()); | 571 | self.collect_inner_items(item.syntax()); |
569 | let assoc = self.lower_assoc_item(&item)?; | 572 | let assoc = self.lower_assoc_item(&item)?; |
570 | let attrs = RawAttrs::new(&item, &self.hygiene); | 573 | let attrs = RawAttrs::new(self.db, &item, &self.hygiene); |
571 | self.add_attrs(ModItem::from(assoc).into(), attrs); | 574 | self.add_attrs(ModItem::from(assoc).into(), attrs); |
572 | Some(assoc) | 575 | Some(assoc) |
573 | }) | 576 | }) |
@@ -585,6 +588,7 @@ impl Ctx { | |||
585 | let mut imports = Vec::new(); | 588 | let mut imports = Vec::new(); |
586 | let tree = self.tree.data_mut(); | 589 | let tree = self.tree.data_mut(); |
587 | ModPath::expand_use_item( | 590 | ModPath::expand_use_item( |
591 | self.db, | ||
588 | InFile::new(self.file, use_item.clone()), | 592 | InFile::new(self.file, use_item.clone()), |
589 | &self.hygiene, | 593 | &self.hygiene, |
590 | |path, _use_tree, is_glob, alias| { | 594 | |path, _use_tree, is_glob, alias| { |
@@ -618,9 +622,10 @@ impl Ctx { | |||
618 | } | 622 | } |
619 | 623 | ||
620 | 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>> { |
621 | let path = Interned::new(ModPath::from_src(m.path()?, &self.hygiene)?); | 625 | let path = Interned::new(ModPath::from_src(self.db, m.path()?, &self.hygiene)?); |
622 | let ast_id = self.source_ast_id_map.ast_id(m); | 626 | let ast_id = self.source_ast_id_map.ast_id(m); |
623 | let res = MacroCall { path, ast_id }; | 627 | let fragment = hir_expand::to_fragment_kind(m); |
628 | let res = MacroCall { path, ast_id, fragment }; | ||
624 | Some(id(self.data().macro_calls.alloc(res))) | 629 | Some(id(self.data().macro_calls.alloc(res))) |
625 | } | 630 | } |
626 | 631 | ||
@@ -647,7 +652,7 @@ impl Ctx { | |||
647 | list.extern_items() | 652 | list.extern_items() |
648 | .filter_map(|item| { | 653 | .filter_map(|item| { |
649 | self.collect_inner_items(item.syntax()); | 654 | self.collect_inner_items(item.syntax()); |
650 | let attrs = RawAttrs::new(&item, &self.hygiene); | 655 | let attrs = RawAttrs::new(self.db, &item, &self.hygiene); |
651 | let id: ModItem = match item { | 656 | let id: ModItem = match item { |
652 | ast::ExternItem::Fn(ast) => { | 657 | ast::ExternItem::Fn(ast) => { |
653 | let func_id = self.lower_function(&ast)?; | 658 | let func_id = self.lower_function(&ast)?; |
@@ -755,7 +760,7 @@ impl Ctx { | |||
755 | fn lower_visibility(&mut self, item: &impl ast::VisibilityOwner) -> RawVisibilityId { | 760 | fn lower_visibility(&mut self, item: &impl ast::VisibilityOwner) -> RawVisibilityId { |
756 | let vis = match self.forced_visibility { | 761 | let vis = match self.forced_visibility { |
757 | Some(vis) => return vis, | 762 | Some(vis) => return vis, |
758 | None => RawVisibility::from_ast_with_hygiene(item.visibility(), &self.hygiene), | 763 | None => RawVisibility::from_ast_with_hygiene(self.db, item.visibility(), &self.hygiene), |
759 | }; | 764 | }; |
760 | 765 | ||
761 | self.data().vis.alloc(vis) | 766 | self.data().vis.alloc(vis) |
diff --git a/crates/hir_def/src/lib.rs b/crates/hir_def/src/lib.rs index 25694f037..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,9 +653,10 @@ 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(path, &h)); | 659 | let path = self.value.path().and_then(|path| path::ModPath::from_src(db, path, &h)); |
658 | 660 | ||
659 | let path = match error_sink | 661 | let path = match error_sink |
660 | .option(path, || mbe::ExpandError::Other("malformed macro invocation".into())) | 662 | .option(path, || mbe::ExpandError::Other("malformed macro invocation".into())) |
@@ -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>, |
@@ -712,13 +716,17 @@ fn macro_call_as_call_id( | |||
712 | krate, | 716 | krate, |
713 | macro_call, | 717 | macro_call, |
714 | def, | 718 | def, |
715 | &|path: ast::Path| resolver(path::ModPath::from_src(path, &hygiene)?), | 719 | &|path: ast::Path| resolver(path::ModPath::from_src(db, path, &hygiene)?), |
716 | error_sink, | 720 | error_sink, |
717 | ) | 721 | ) |
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 ba027c44a..249af6fc8 100644 --- a/crates/hir_def/src/nameres.rs +++ b/crates/hir_def/src/nameres.rs | |||
@@ -599,6 +599,7 @@ mod diagnostics { | |||
599 | let mut cur = 0; | 599 | let mut cur = 0; |
600 | let mut tree = None; | 600 | let mut tree = None; |
601 | ModPath::expand_use_item( | 601 | ModPath::expand_use_item( |
602 | db, | ||
602 | InFile::new(ast.file_id, use_item), | 603 | InFile::new(ast.file_id, use_item), |
603 | &hygiene, | 604 | &hygiene, |
604 | |_mod_path, use_tree, _is_glob, _alias| { | 605 | |_mod_path, use_tree, _is_glob, _alias| { |
@@ -628,7 +629,7 @@ mod diagnostics { | |||
628 | DiagnosticKind::UnresolvedProcMacro { ast } => { | 629 | DiagnosticKind::UnresolvedProcMacro { ast } => { |
629 | let mut precise_location = None; | 630 | let mut precise_location = None; |
630 | let (file, ast, name) = match ast { | 631 | let (file, ast, name) = match ast { |
631 | MacroCallKind::FnLike { ast_id } => { | 632 | MacroCallKind::FnLike { ast_id, .. } => { |
632 | let node = ast_id.to_node(db.upcast()); | 633 | let node = ast_id.to_node(db.upcast()); |
633 | (ast_id.file_id, SyntaxNodePtr::from(AstPtr::new(&node)), None) | 634 | (ast_id.file_id, SyntaxNodePtr::from(AstPtr::new(&node)), None) |
634 | } | 635 | } |
diff --git a/crates/hir_def/src/nameres/collector.rs b/crates/hir_def/src/nameres/collector.rs index 05ceb1efb..adfb78c94 100644 --- a/crates/hir_def/src/nameres/collector.rs +++ b/crates/hir_def/src/nameres/collector.rs | |||
@@ -13,14 +13,14 @@ use hir_expand::{ | |||
13 | builtin_macro::find_builtin_macro, | 13 | builtin_macro::find_builtin_macro, |
14 | name::{AsName, Name}, | 14 | name::{AsName, Name}, |
15 | proc_macro::ProcMacroExpander, | 15 | proc_macro::ProcMacroExpander, |
16 | AttrId, 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 | ||
@@ -807,9 +807,10 @@ impl DefCollector<'_> { | |||
807 | let mut res = ReachedFixedPoint::Yes; | 807 | let mut res = ReachedFixedPoint::Yes; |
808 | macros.retain(|directive| { | 808 | macros.retain(|directive| { |
809 | match &directive.kind { | 809 | match &directive.kind { |
810 | MacroDirectiveKind::FnLike { ast_id } => { | 810 | MacroDirectiveKind::FnLike { ast_id, fragment } => { |
811 | match macro_call_as_call_id( | 811 | match macro_call_as_call_id( |
812 | ast_id, | 812 | ast_id, |
813 | *fragment, | ||
813 | self.db, | 814 | self.db, |
814 | self.def_map.krate, | 815 | self.def_map.krate, |
815 | |path| { | 816 | |path| { |
@@ -926,8 +927,9 @@ impl DefCollector<'_> { | |||
926 | 927 | ||
927 | for directive in &self.unexpanded_macros { | 928 | for directive in &self.unexpanded_macros { |
928 | match &directive.kind { | 929 | match &directive.kind { |
929 | MacroDirectiveKind::FnLike { ast_id, .. } => match macro_call_as_call_id( | 930 | MacroDirectiveKind::FnLike { ast_id, fragment } => match macro_call_as_call_id( |
930 | ast_id, | 931 | ast_id, |
932 | *fragment, | ||
931 | self.db, | 933 | self.db, |
932 | self.def_map.krate, | 934 | self.def_map.krate, |
933 | |path| { | 935 | |path| { |
@@ -1496,6 +1498,7 @@ impl ModCollector<'_, '_> { | |||
1496 | let mut error = None; | 1498 | let mut error = None; |
1497 | match macro_call_as_call_id( | 1499 | match macro_call_as_call_id( |
1498 | &ast_id, | 1500 | &ast_id, |
1501 | mac.fragment, | ||
1499 | self.def_collector.db, | 1502 | self.def_collector.db, |
1500 | self.def_collector.def_map.krate, | 1503 | self.def_collector.def_map.krate, |
1501 | |path| { | 1504 | |path| { |
@@ -1524,9 +1527,14 @@ impl ModCollector<'_, '_> { | |||
1524 | } | 1527 | } |
1525 | Ok(Err(_)) => { | 1528 | Ok(Err(_)) => { |
1526 | // Built-in macro failed eager expansion. | 1529 | // Built-in macro failed eager expansion. |
1530 | |||
1531 | // FIXME: don't parse the file here | ||
1532 | let fragment = hir_expand::to_fragment_kind( | ||
1533 | &ast_id.ast_id.to_node(self.def_collector.db.upcast()), | ||
1534 | ); | ||
1527 | self.def_collector.def_map.diagnostics.push(DefDiagnostic::macro_error( | 1535 | self.def_collector.def_map.diagnostics.push(DefDiagnostic::macro_error( |
1528 | self.module_id, | 1536 | self.module_id, |
1529 | MacroCallKind::FnLike { ast_id: ast_id.ast_id }, | 1537 | MacroCallKind::FnLike { ast_id: ast_id.ast_id, fragment }, |
1530 | error.unwrap().to_string(), | 1538 | error.unwrap().to_string(), |
1531 | )); | 1539 | )); |
1532 | return; | 1540 | return; |
@@ -1543,7 +1551,7 @@ impl ModCollector<'_, '_> { | |||
1543 | self.def_collector.unexpanded_macros.push(MacroDirective { | 1551 | self.def_collector.unexpanded_macros.push(MacroDirective { |
1544 | module_id: self.module_id, | 1552 | module_id: self.module_id, |
1545 | depth: self.macro_depth + 1, | 1553 | depth: self.macro_depth + 1, |
1546 | kind: MacroDirectiveKind::FnLike { ast_id }, | 1554 | kind: MacroDirectiveKind::FnLike { ast_id, fragment: mac.fragment }, |
1547 | }); | 1555 | }); |
1548 | } | 1556 | } |
1549 | 1557 | ||
diff --git a/crates/hir_def/src/nameres/tests/incremental.rs b/crates/hir_def/src/nameres/tests/incremental.rs index 509e1bbbc..d884a6eb4 100644 --- a/crates/hir_def/src/nameres/tests/incremental.rs +++ b/crates/hir_def/src/nameres/tests/incremental.rs | |||
@@ -105,3 +105,61 @@ fn typing_inside_a_macro_should_not_invalidate_def_map() { | |||
105 | assert!(!format!("{:?}", events).contains("crate_def_map"), "{:#?}", events) | 105 | assert!(!format!("{:?}", events).contains("crate_def_map"), "{:#?}", events) |
106 | } | 106 | } |
107 | } | 107 | } |
108 | |||
109 | #[test] | ||
110 | fn typing_inside_a_function_should_not_invalidate_expansions() { | ||
111 | let (mut db, pos) = TestDB::with_position( | ||
112 | r#" | ||
113 | //- /lib.rs | ||
114 | macro_rules! m { | ||
115 | ($ident:ident) => { | ||
116 | fn $ident() { }; | ||
117 | } | ||
118 | } | ||
119 | mod foo; | ||
120 | |||
121 | //- /foo/mod.rs | ||
122 | pub mod bar; | ||
123 | |||
124 | //- /foo/bar.rs | ||
125 | m!(X); | ||
126 | fn quux() { 1$0 } | ||
127 | m!(Y); | ||
128 | m!(Z); | ||
129 | "#, | ||
130 | ); | ||
131 | let krate = db.test_crate(); | ||
132 | { | ||
133 | let events = db.log_executed(|| { | ||
134 | let crate_def_map = db.crate_def_map(krate); | ||
135 | let (_, module_data) = crate_def_map.modules.iter().last().unwrap(); | ||
136 | assert_eq!(module_data.scope.resolutions().count(), 4); | ||
137 | }); | ||
138 | let n_recalculated_item_trees = events.iter().filter(|it| it.contains("item_tree")).count(); | ||
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); | ||
143 | } | ||
144 | |||
145 | let new_text = r#" | ||
146 | m!(X); | ||
147 | fn quux() { 92 } | ||
148 | m!(Y); | ||
149 | m!(Z); | ||
150 | "#; | ||
151 | db.set_file_text(pos.file_id, Arc::new(new_text.to_string())); | ||
152 | |||
153 | { | ||
154 | let events = db.log_executed(|| { | ||
155 | let crate_def_map = db.crate_def_map(krate); | ||
156 | let (_, module_data) = crate_def_map.modules.iter().last().unwrap(); | ||
157 | assert_eq!(module_data.scope.resolutions().count(), 4); | ||
158 | }); | ||
159 | let n_recalculated_item_trees = events.iter().filter(|it| it.contains("item_tree")).count(); | ||
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); | ||
164 | } | ||
165 | } | ||
diff --git a/crates/hir_def/src/path.rs b/crates/hir_def/src/path.rs index 509f77850..a43441b1c 100644 --- a/crates/hir_def/src/path.rs +++ b/crates/hir_def/src/path.rs | |||
@@ -7,7 +7,7 @@ use std::{ | |||
7 | sync::Arc, | 7 | sync::Arc, |
8 | }; | 8 | }; |
9 | 9 | ||
10 | use crate::{body::LowerCtx, intern::Interned, type_ref::LifetimeRef}; | 10 | use crate::{body::LowerCtx, db::DefDatabase, intern::Interned, type_ref::LifetimeRef}; |
11 | use base_db::CrateId; | 11 | use base_db::CrateId; |
12 | use hir_expand::{ | 12 | use hir_expand::{ |
13 | hygiene::Hygiene, | 13 | hygiene::Hygiene, |
@@ -47,8 +47,8 @@ pub enum ImportAlias { | |||
47 | } | 47 | } |
48 | 48 | ||
49 | impl ModPath { | 49 | impl ModPath { |
50 | pub fn from_src(path: ast::Path, hygiene: &Hygiene) -> Option<ModPath> { | 50 | pub fn from_src(db: &dyn DefDatabase, path: ast::Path, hygiene: &Hygiene) -> Option<ModPath> { |
51 | let ctx = LowerCtx::with_hygiene(hygiene); | 51 | let ctx = LowerCtx::with_hygiene(db, hygiene); |
52 | lower::lower_path(path, &ctx).map(|it| (*it.mod_path).clone()) | 52 | lower::lower_path(path, &ctx).map(|it| (*it.mod_path).clone()) |
53 | } | 53 | } |
54 | 54 | ||
@@ -64,12 +64,13 @@ impl ModPath { | |||
64 | 64 | ||
65 | /// Calls `cb` with all paths, represented by this use item. | 65 | /// Calls `cb` with all paths, represented by this use item. |
66 | pub(crate) fn expand_use_item( | 66 | pub(crate) fn expand_use_item( |
67 | db: &dyn DefDatabase, | ||
67 | item_src: InFile<ast::Use>, | 68 | item_src: InFile<ast::Use>, |
68 | hygiene: &Hygiene, | 69 | hygiene: &Hygiene, |
69 | mut cb: impl FnMut(ModPath, &ast::UseTree, /* is_glob */ bool, Option<ImportAlias>), | 70 | mut cb: impl FnMut(ModPath, &ast::UseTree, /* is_glob */ bool, Option<ImportAlias>), |
70 | ) { | 71 | ) { |
71 | if let Some(tree) = item_src.value.use_tree() { | 72 | if let Some(tree) = item_src.value.use_tree() { |
72 | lower::lower_use_tree(None, tree, hygiene, &mut cb); | 73 | lower::lower_use_tree(db, None, tree, hygiene, &mut cb); |
73 | } | 74 | } |
74 | } | 75 | } |
75 | 76 | ||
diff --git a/crates/hir_def/src/path/lower.rs b/crates/hir_def/src/path/lower.rs index 1df6db525..a873325b2 100644 --- a/crates/hir_def/src/path/lower.rs +++ b/crates/hir_def/src/path/lower.rs | |||
@@ -36,7 +36,7 @@ pub(super) fn lower_path(mut path: ast::Path, ctx: &LowerCtx) -> Option<Path> { | |||
36 | match segment.kind()? { | 36 | match segment.kind()? { |
37 | ast::PathSegmentKind::Name(name_ref) => { | 37 | ast::PathSegmentKind::Name(name_ref) => { |
38 | // FIXME: this should just return name | 38 | // FIXME: this should just return name |
39 | match hygiene.name_ref_to_name(name_ref) { | 39 | match hygiene.name_ref_to_name(ctx.db.upcast(), name_ref) { |
40 | Either::Left(name) => { | 40 | Either::Left(name) => { |
41 | let args = segment | 41 | let args = segment |
42 | .generic_arg_list() | 42 | .generic_arg_list() |
@@ -133,7 +133,7 @@ pub(super) fn lower_path(mut path: ast::Path, ctx: &LowerCtx) -> Option<Path> { | |||
133 | // We follow what it did anyway :) | 133 | // We follow what it did anyway :) |
134 | if segments.len() == 1 && kind == PathKind::Plain { | 134 | if segments.len() == 1 && kind == PathKind::Plain { |
135 | if let Some(_macro_call) = path.syntax().parent().and_then(ast::MacroCall::cast) { | 135 | if let Some(_macro_call) = path.syntax().parent().and_then(ast::MacroCall::cast) { |
136 | if let Some(crate_id) = hygiene.local_inner_macros(path) { | 136 | if let Some(crate_id) = hygiene.local_inner_macros(ctx.db.upcast(), path) { |
137 | kind = PathKind::DollarCrate(crate_id); | 137 | kind = PathKind::DollarCrate(crate_id); |
138 | } | 138 | } |
139 | } | 139 | } |
diff --git a/crates/hir_def/src/path/lower/lower_use.rs b/crates/hir_def/src/path/lower/lower_use.rs index e2965b033..ee80e3df3 100644 --- a/crates/hir_def/src/path/lower/lower_use.rs +++ b/crates/hir_def/src/path/lower/lower_use.rs | |||
@@ -7,9 +7,13 @@ use either::Either; | |||
7 | use hir_expand::{hygiene::Hygiene, name::AsName}; | 7 | use hir_expand::{hygiene::Hygiene, name::AsName}; |
8 | use syntax::ast::{self, NameOwner}; | 8 | use syntax::ast::{self, NameOwner}; |
9 | 9 | ||
10 | use crate::path::{ImportAlias, ModPath, PathKind}; | 10 | use crate::{ |
11 | db::DefDatabase, | ||
12 | path::{ImportAlias, ModPath, PathKind}, | ||
13 | }; | ||
11 | 14 | ||
12 | pub(crate) fn lower_use_tree( | 15 | pub(crate) fn lower_use_tree( |
16 | db: &dyn DefDatabase, | ||
13 | prefix: Option<ModPath>, | 17 | prefix: Option<ModPath>, |
14 | tree: ast::UseTree, | 18 | tree: ast::UseTree, |
15 | hygiene: &Hygiene, | 19 | hygiene: &Hygiene, |
@@ -21,13 +25,13 @@ pub(crate) fn lower_use_tree( | |||
21 | None => prefix, | 25 | None => prefix, |
22 | // E.g. `use something::{inner}` (prefix is `None`, path is `something`) | 26 | // E.g. `use something::{inner}` (prefix is `None`, path is `something`) |
23 | // or `use something::{path::{inner::{innerer}}}` (prefix is `something::path`, path is `inner`) | 27 | // or `use something::{path::{inner::{innerer}}}` (prefix is `something::path`, path is `inner`) |
24 | Some(path) => match convert_path(prefix, path, hygiene) { | 28 | Some(path) => match convert_path(db, prefix, path, hygiene) { |
25 | Some(it) => Some(it), | 29 | Some(it) => Some(it), |
26 | None => return, // FIXME: report errors somewhere | 30 | None => return, // FIXME: report errors somewhere |
27 | }, | 31 | }, |
28 | }; | 32 | }; |
29 | for child_tree in use_tree_list.use_trees() { | 33 | for child_tree in use_tree_list.use_trees() { |
30 | lower_use_tree(prefix.clone(), child_tree, hygiene, cb); | 34 | lower_use_tree(db, prefix.clone(), child_tree, hygiene, cb); |
31 | } | 35 | } |
32 | } else { | 36 | } else { |
33 | let alias = tree.rename().map(|a| { | 37 | let alias = tree.rename().map(|a| { |
@@ -47,7 +51,7 @@ pub(crate) fn lower_use_tree( | |||
47 | } | 51 | } |
48 | } | 52 | } |
49 | } | 53 | } |
50 | if let Some(path) = convert_path(prefix, ast_path, hygiene) { | 54 | if let Some(path) = convert_path(db, prefix, ast_path, hygiene) { |
51 | cb(path, &tree, is_glob, alias) | 55 | cb(path, &tree, is_glob, alias) |
52 | } | 56 | } |
53 | // FIXME: report errors somewhere | 57 | // FIXME: report errors somewhere |
@@ -61,9 +65,14 @@ pub(crate) fn lower_use_tree( | |||
61 | } | 65 | } |
62 | } | 66 | } |
63 | 67 | ||
64 | fn convert_path(prefix: Option<ModPath>, path: ast::Path, hygiene: &Hygiene) -> Option<ModPath> { | 68 | fn convert_path( |
69 | db: &dyn DefDatabase, | ||
70 | prefix: Option<ModPath>, | ||
71 | path: ast::Path, | ||
72 | hygiene: &Hygiene, | ||
73 | ) -> Option<ModPath> { | ||
65 | let prefix = if let Some(qual) = path.qualifier() { | 74 | let prefix = if let Some(qual) = path.qualifier() { |
66 | Some(convert_path(prefix, qual, hygiene)?) | 75 | Some(convert_path(db, prefix, qual, hygiene)?) |
67 | } else { | 76 | } else { |
68 | prefix | 77 | prefix |
69 | }; | 78 | }; |
@@ -71,7 +80,7 @@ fn convert_path(prefix: Option<ModPath>, path: ast::Path, hygiene: &Hygiene) -> | |||
71 | let segment = path.segment()?; | 80 | let segment = path.segment()?; |
72 | let res = match segment.kind()? { | 81 | let res = match segment.kind()? { |
73 | ast::PathSegmentKind::Name(name_ref) => { | 82 | ast::PathSegmentKind::Name(name_ref) => { |
74 | match hygiene.name_ref_to_name(name_ref) { | 83 | match hygiene.name_ref_to_name(db.upcast(), name_ref) { |
75 | Either::Left(name) => { | 84 | Either::Left(name) => { |
76 | // no type args in use | 85 | // no type args in use |
77 | let mut res = prefix.unwrap_or_else(|| { | 86 | let mut res = prefix.unwrap_or_else(|| { |
diff --git a/crates/hir_def/src/visibility.rs b/crates/hir_def/src/visibility.rs index d4b7c9970..83500f54e 100644 --- a/crates/hir_def/src/visibility.rs +++ b/crates/hir_def/src/visibility.rs | |||
@@ -33,17 +33,19 @@ impl RawVisibility { | |||
33 | db: &dyn DefDatabase, | 33 | db: &dyn DefDatabase, |
34 | node: InFile<Option<ast::Visibility>>, | 34 | node: InFile<Option<ast::Visibility>>, |
35 | ) -> RawVisibility { | 35 | ) -> RawVisibility { |
36 | Self::from_ast_with_hygiene(node.value, &Hygiene::new(db.upcast(), node.file_id)) | 36 | Self::from_ast_with_hygiene(db, node.value, &Hygiene::new(db.upcast(), node.file_id)) |
37 | } | 37 | } |
38 | 38 | ||
39 | pub(crate) fn from_ast_with_hygiene( | 39 | pub(crate) fn from_ast_with_hygiene( |
40 | db: &dyn DefDatabase, | ||
40 | node: Option<ast::Visibility>, | 41 | node: Option<ast::Visibility>, |
41 | hygiene: &Hygiene, | 42 | hygiene: &Hygiene, |
42 | ) -> RawVisibility { | 43 | ) -> RawVisibility { |
43 | Self::from_ast_with_hygiene_and_default(node, RawVisibility::private(), hygiene) | 44 | Self::from_ast_with_hygiene_and_default(db, node, RawVisibility::private(), hygiene) |
44 | } | 45 | } |
45 | 46 | ||
46 | pub(crate) fn from_ast_with_hygiene_and_default( | 47 | pub(crate) fn from_ast_with_hygiene_and_default( |
48 | db: &dyn DefDatabase, | ||
47 | node: Option<ast::Visibility>, | 49 | node: Option<ast::Visibility>, |
48 | default: RawVisibility, | 50 | default: RawVisibility, |
49 | hygiene: &Hygiene, | 51 | hygiene: &Hygiene, |
@@ -54,7 +56,7 @@ impl RawVisibility { | |||
54 | }; | 56 | }; |
55 | match node.kind() { | 57 | match node.kind() { |
56 | ast::VisibilityKind::In(path) => { | 58 | ast::VisibilityKind::In(path) => { |
57 | let path = ModPath::from_src(path, hygiene); | 59 | let path = ModPath::from_src(db, path, hygiene); |
58 | let path = match path { | 60 | let path = match path { |
59 | None => return RawVisibility::private(), | 61 | None => return RawVisibility::private(), |
60 | Some(path) => path, | 62 | Some(path) => path, |
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..af9802144 100644 --- a/crates/hir_expand/src/builtin_macro.rs +++ b/crates/hir_expand/src/builtin_macro.rs | |||
@@ -578,6 +578,7 @@ mod tests { | |||
578 | krate, | 578 | krate, |
579 | kind: MacroCallKind::FnLike { | 579 | kind: MacroCallKind::FnLike { |
580 | ast_id: AstId::new(file_id.into(), ast_id_map.ast_id(¯o_call)), | 580 | ast_id: AstId::new(file_id.into(), ast_id_map.ast_id(¯o_call)), |
581 | fragment: FragmentKind::Expr, | ||
581 | }, | 582 | }, |
582 | }; | 583 | }; |
583 | 584 | ||
@@ -788,9 +789,9 @@ mod tests { | |||
788 | r##" | 789 | r##" |
789 | #[rustc_builtin_macro] | 790 | #[rustc_builtin_macro] |
790 | macro_rules! concat {} | 791 | macro_rules! concat {} |
791 | concat!("foo", "r", 0, r#"bar"#, false); | 792 | concat!("foo", "r", 0, r#"bar"#, "\n", false); |
792 | "##, | 793 | "##, |
793 | expect![[r#""foor0barfalse""#]], | 794 | expect![[r#""foor0bar\nfalse""#]], |
794 | ); | 795 | ); |
795 | } | 796 | } |
796 | } | 797 | } |
diff --git a/crates/hir_expand/src/db.rs b/crates/hir_expand/src/db.rs index 1e4b0cc19..9fa419fcf 100644 --- a/crates/hir_expand/src/db.rs +++ b/crates/hir_expand/src/db.rs | |||
@@ -3,20 +3,18 @@ | |||
3 | use std::sync::Arc; | 3 | use std::sync::Arc; |
4 | 4 | ||
5 | use base_db::{salsa, SourceDatabase}; | 5 | use base_db::{salsa, SourceDatabase}; |
6 | use mbe::{ExpandError, ExpandResult, MacroDef, MacroRules}; | 6 | use mbe::{ExpandError, ExpandResult}; |
7 | use parser::FragmentKind; | 7 | use parser::FragmentKind; |
8 | use syntax::{ | 8 | use syntax::{ |
9 | algo::diff, | 9 | algo::diff, |
10 | ast::{MacroStmts, NameOwner}, | 10 | ast::{self, NameOwner}, |
11 | AstNode, GreenNode, Parse, | 11 | AstNode, GreenNode, Parse, SyntaxNode, SyntaxToken, |
12 | SyntaxKind::*, | ||
13 | SyntaxNode, | ||
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. |
@@ -27,23 +25,28 @@ const TOKEN_LIMIT: usize = 524288; | |||
27 | 25 | ||
28 | #[derive(Debug, Clone, Eq, PartialEq)] | 26 | #[derive(Debug, Clone, Eq, PartialEq)] |
29 | pub enum TokenExpander { | 27 | pub enum TokenExpander { |
30 | MacroRules(mbe::MacroRules), | 28 | /// Old-style `macro_rules`. |
31 | MacroDef(mbe::MacroDef), | 29 | MacroRules { mac: mbe::MacroRules, def_site_token_map: mbe::TokenMap }, |
30 | /// AKA macros 2.0. | ||
31 | MacroDef { mac: mbe::MacroDef, def_site_token_map: mbe::TokenMap }, | ||
32 | /// Stuff like `line!` and `file!`. | ||
32 | Builtin(BuiltinFnLikeExpander), | 33 | Builtin(BuiltinFnLikeExpander), |
34 | /// `derive(Copy)` and such. | ||
33 | BuiltinDerive(BuiltinDeriveExpander), | 35 | BuiltinDerive(BuiltinDeriveExpander), |
36 | /// The thing we love the most here in rust-analyzer -- procedural macros. | ||
34 | ProcMacro(ProcMacroExpander), | 37 | ProcMacro(ProcMacroExpander), |
35 | } | 38 | } |
36 | 39 | ||
37 | impl TokenExpander { | 40 | impl TokenExpander { |
38 | pub fn expand( | 41 | fn expand( |
39 | &self, | 42 | &self, |
40 | db: &dyn AstDatabase, | 43 | db: &dyn AstDatabase, |
41 | id: LazyMacroId, | 44 | id: LazyMacroId, |
42 | tt: &tt::Subtree, | 45 | tt: &tt::Subtree, |
43 | ) -> mbe::ExpandResult<tt::Subtree> { | 46 | ) -> mbe::ExpandResult<tt::Subtree> { |
44 | match self { | 47 | match self { |
45 | TokenExpander::MacroRules(it) => it.expand(tt), | 48 | TokenExpander::MacroRules { mac, .. } => mac.expand(tt), |
46 | TokenExpander::MacroDef(it) => it.expand(tt), | 49 | TokenExpander::MacroDef { mac, .. } => mac.expand(tt), |
47 | TokenExpander::Builtin(it) => it.expand(db, id, tt), | 50 | TokenExpander::Builtin(it) => it.expand(db, id, tt), |
48 | // FIXME switch these to ExpandResult as well | 51 | // FIXME switch these to ExpandResult as well |
49 | TokenExpander::BuiltinDerive(it) => it.expand(db, id, tt).into(), | 52 | TokenExpander::BuiltinDerive(it) => it.expand(db, id, tt).into(), |
@@ -56,23 +59,23 @@ impl TokenExpander { | |||
56 | } | 59 | } |
57 | } | 60 | } |
58 | 61 | ||
59 | pub fn map_id_down(&self, id: tt::TokenId) -> tt::TokenId { | 62 | pub(crate) fn map_id_down(&self, id: tt::TokenId) -> tt::TokenId { |
60 | match self { | 63 | match self { |
61 | TokenExpander::MacroRules(it) => it.map_id_down(id), | 64 | TokenExpander::MacroRules { mac, .. } => mac.map_id_down(id), |
62 | TokenExpander::MacroDef(it) => it.map_id_down(id), | 65 | TokenExpander::MacroDef { mac, .. } => mac.map_id_down(id), |
63 | TokenExpander::Builtin(..) => id, | 66 | TokenExpander::Builtin(..) |
64 | TokenExpander::BuiltinDerive(..) => id, | 67 | | TokenExpander::BuiltinDerive(..) |
65 | TokenExpander::ProcMacro(..) => id, | 68 | | TokenExpander::ProcMacro(..) => id, |
66 | } | 69 | } |
67 | } | 70 | } |
68 | 71 | ||
69 | pub fn map_id_up(&self, id: tt::TokenId) -> (tt::TokenId, mbe::Origin) { | 72 | pub(crate) fn map_id_up(&self, id: tt::TokenId) -> (tt::TokenId, mbe::Origin) { |
70 | match self { | 73 | match self { |
71 | TokenExpander::MacroRules(it) => it.map_id_up(id), | 74 | TokenExpander::MacroRules { mac, .. } => mac.map_id_up(id), |
72 | TokenExpander::MacroDef(it) => it.map_id_up(id), | 75 | TokenExpander::MacroDef { mac, .. } => mac.map_id_up(id), |
73 | TokenExpander::Builtin(..) => (id, mbe::Origin::Call), | 76 | TokenExpander::Builtin(..) |
74 | TokenExpander::BuiltinDerive(..) => (id, mbe::Origin::Call), | 77 | | TokenExpander::BuiltinDerive(..) |
75 | TokenExpander::ProcMacro(..) => (id, mbe::Origin::Call), | 78 | | TokenExpander::ProcMacro(..) => (id, mbe::Origin::Call), |
76 | } | 79 | } |
77 | } | 80 | } |
78 | } | 81 | } |
@@ -82,28 +85,48 @@ impl TokenExpander { | |||
82 | pub trait AstDatabase: SourceDatabase { | 85 | pub trait AstDatabase: SourceDatabase { |
83 | fn ast_id_map(&self, file_id: HirFileId) -> Arc<AstIdMap>; | 86 | fn ast_id_map(&self, file_id: HirFileId) -> Arc<AstIdMap>; |
84 | 87 | ||
88 | /// Main public API -- parsis a hir file, not caring whether it's a real | ||
89 | /// file or a macro expansion. | ||
85 | #[salsa::transparent] | 90 | #[salsa::transparent] |
86 | fn parse_or_expand(&self, file_id: HirFileId) -> Option<SyntaxNode>; | 91 | fn parse_or_expand(&self, file_id: HirFileId) -> Option<SyntaxNode>; |
87 | 92 | /// Implementation for the macro case. | |
88 | #[salsa::interned] | ||
89 | fn intern_macro(&self, macro_call: MacroCallLoc) -> LazyMacroId; | ||
90 | fn macro_arg_text(&self, id: MacroCallId) -> Option<GreenNode>; | ||
91 | #[salsa::transparent] | ||
92 | fn macro_arg(&self, id: MacroCallId) -> Option<Arc<(tt::Subtree, mbe::TokenMap)>>; | ||
93 | fn macro_def(&self, id: MacroDefId) -> Option<Arc<(TokenExpander, mbe::TokenMap)>>; | ||
94 | fn parse_macro_expansion( | 93 | fn parse_macro_expansion( |
95 | &self, | 94 | &self, |
96 | macro_file: MacroFile, | 95 | macro_file: MacroFile, |
97 | ) -> ExpandResult<Option<(Parse<SyntaxNode>, Arc<mbe::TokenMap>)>>; | 96 | ) -> ExpandResult<Option<(Parse<SyntaxNode>, Arc<mbe::TokenMap>)>>; |
98 | fn macro_expand(&self, macro_call: MacroCallId) -> ExpandResult<Option<Arc<tt::Subtree>>>; | ||
99 | |||
100 | /// Firewall query that returns the error from the `macro_expand` query. | ||
101 | fn macro_expand_error(&self, macro_call: MacroCallId) -> Option<ExpandError>; | ||
102 | 97 | ||
98 | /// Macro ids. That's probably the tricksiest bit in rust-analyzer, and the | ||
99 | /// reason why we use salsa at all. | ||
100 | /// | ||
101 | /// We encode macro definitions into ids of macro calls, this what allows us | ||
102 | /// to be incremental. | ||
103 | #[salsa::interned] | ||
104 | fn intern_macro(&self, macro_call: MacroCallLoc) -> LazyMacroId; | ||
105 | /// Certain built-in macros are eager (`format!(concat!("file: ", file!(), "{}"")), 92`). | ||
106 | /// For them, we actually want to encode the whole token tree as an argument. | ||
103 | #[salsa::interned] | 107 | #[salsa::interned] |
104 | fn intern_eager_expansion(&self, eager: EagerCallLoc) -> EagerMacroId; | 108 | fn intern_eager_expansion(&self, eager: EagerCallLoc) -> EagerMacroId; |
105 | 109 | ||
110 | /// Lowers syntactic macro call to a token tree representation. | ||
111 | #[salsa::transparent] | ||
112 | fn macro_arg(&self, id: MacroCallId) -> Option<Arc<(tt::Subtree, mbe::TokenMap)>>; | ||
113 | /// Extracts syntax node, corresponding to a macro call. That's a firewall | ||
114 | /// query, only typing in the macro call itself changes the returned | ||
115 | /// subtree. | ||
116 | fn macro_arg_text(&self, id: MacroCallId) -> Option<GreenNode>; | ||
117 | /// Gets the expander for this macro. This compiles declarative macros, and | ||
118 | /// just fetches procedural ones. | ||
119 | fn macro_def(&self, id: MacroDefId) -> Option<Arc<TokenExpander>>; | ||
120 | |||
121 | /// Expand macro call to a token tree. This query is LRUed (we keep 128 or so results in memory) | ||
122 | fn macro_expand(&self, macro_call: MacroCallId) -> ExpandResult<Option<Arc<tt::Subtree>>>; | ||
123 | /// Special case of the previous query for procedural macros. We can't LRU | ||
124 | /// proc macros, since they are not deterministic in general, and | ||
125 | /// non-determinism breaks salsa in a very, very, very bad way. @edwin0cheng | ||
126 | /// heroically debugged this once! | ||
106 | fn expand_proc_macro(&self, call: MacroCallId) -> Result<tt::Subtree, mbe::ExpandError>; | 127 | fn expand_proc_macro(&self, call: MacroCallId) -> Result<tt::Subtree, mbe::ExpandError>; |
128 | /// Firewall query that returns the error from the `macro_expand` query. | ||
129 | fn macro_expand_error(&self, macro_call: MacroCallId) -> Option<ExpandError>; | ||
107 | 130 | ||
108 | fn hygiene_frame(&self, file_id: HirFileId) -> Arc<HygieneFrame>; | 131 | fn hygiene_frame(&self, file_id: HirFileId) -> Arc<HygieneFrame>; |
109 | } | 132 | } |
@@ -115,36 +138,160 @@ pub trait AstDatabase: SourceDatabase { | |||
115 | pub fn expand_hypothetical( | 138 | pub fn expand_hypothetical( |
116 | db: &dyn AstDatabase, | 139 | db: &dyn AstDatabase, |
117 | actual_macro_call: MacroCallId, | 140 | actual_macro_call: MacroCallId, |
118 | hypothetical_args: &syntax::ast::TokenTree, | 141 | hypothetical_args: &ast::TokenTree, |
119 | token_to_map: syntax::SyntaxToken, | 142 | token_to_map: SyntaxToken, |
120 | ) -> Option<(SyntaxNode, syntax::SyntaxToken)> { | 143 | ) -> Option<(SyntaxNode, SyntaxToken)> { |
121 | let macro_file = MacroFile { macro_call_id: actual_macro_call }; | ||
122 | let (tt, tmap_1) = mbe::syntax_node_to_token_tree(hypothetical_args.syntax()); | 144 | let (tt, tmap_1) = mbe::syntax_node_to_token_tree(hypothetical_args.syntax()); |
123 | let range = | 145 | let range = |
124 | token_to_map.text_range().checked_sub(hypothetical_args.syntax().text_range().start())?; | 146 | token_to_map.text_range().checked_sub(hypothetical_args.syntax().text_range().start())?; |
125 | let token_id = tmap_1.token_by_range(range)?; | 147 | let token_id = tmap_1.token_by_range(range)?; |
126 | let macro_def = expander(db, actual_macro_call)?; | 148 | |
149 | let lazy_id = match actual_macro_call { | ||
150 | MacroCallId::LazyMacro(id) => id, | ||
151 | MacroCallId::EagerMacro(_) => return None, | ||
152 | }; | ||
153 | |||
154 | let macro_def = { | ||
155 | let loc = db.lookup_intern_macro(lazy_id); | ||
156 | db.macro_def(loc.def)? | ||
157 | }; | ||
158 | |||
159 | let hypothetical_expansion = macro_def.expand(db, lazy_id, &tt); | ||
160 | |||
161 | let fragment_kind = macro_fragment_kind(db, actual_macro_call); | ||
162 | |||
127 | let (node, tmap_2) = | 163 | let (node, tmap_2) = |
128 | parse_macro_with_arg(db, macro_file, Some(std::sync::Arc::new((tt, tmap_1)))).value?; | 164 | mbe::token_tree_to_syntax_node(&hypothetical_expansion.value, fragment_kind).ok()?; |
129 | let token_id = macro_def.0.map_id_down(token_id); | 165 | |
166 | let token_id = macro_def.map_id_down(token_id); | ||
130 | let range = tmap_2.range_by_token(token_id)?.by_kind(token_to_map.kind())?; | 167 | let range = tmap_2.range_by_token(token_id)?.by_kind(token_to_map.kind())?; |
131 | let token = node.syntax_node().covering_element(range).into_token()?; | 168 | let token = node.syntax_node().covering_element(range).into_token()?; |
132 | Some((node.syntax_node(), token)) | 169 | Some((node.syntax_node(), token)) |
133 | } | 170 | } |
134 | 171 | ||
135 | fn ast_id_map(db: &dyn AstDatabase, file_id: HirFileId) -> Arc<AstIdMap> { | 172 | fn ast_id_map(db: &dyn AstDatabase, file_id: HirFileId) -> Arc<AstIdMap> { |
136 | let map = | 173 | let map = db.parse_or_expand(file_id).map(|it| AstIdMap::from_source(&it)).unwrap_or_default(); |
137 | db.parse_or_expand(file_id).map_or_else(AstIdMap::default, |it| AstIdMap::from_source(&it)); | ||
138 | Arc::new(map) | 174 | Arc::new(map) |
139 | } | 175 | } |
140 | 176 | ||
141 | fn macro_def(db: &dyn AstDatabase, id: MacroDefId) -> Option<Arc<(TokenExpander, mbe::TokenMap)>> { | 177 | fn parse_or_expand(db: &dyn AstDatabase, file_id: HirFileId) -> Option<SyntaxNode> { |
178 | match file_id.0 { | ||
179 | HirFileIdRepr::FileId(file_id) => Some(db.parse(file_id).tree().syntax().clone()), | ||
180 | HirFileIdRepr::MacroFile(macro_file) => { | ||
181 | db.parse_macro_expansion(macro_file).value.map(|(it, _)| it.syntax_node()) | ||
182 | } | ||
183 | } | ||
184 | } | ||
185 | |||
186 | fn parse_macro_expansion( | ||
187 | db: &dyn AstDatabase, | ||
188 | macro_file: MacroFile, | ||
189 | ) -> ExpandResult<Option<(Parse<SyntaxNode>, Arc<mbe::TokenMap>)>> { | ||
190 | let _p = profile::span("parse_macro_expansion"); | ||
191 | let result = db.macro_expand(macro_file.macro_call_id); | ||
192 | |||
193 | if let Some(err) = &result.err { | ||
194 | // Note: | ||
195 | // The final goal we would like to make all parse_macro success, | ||
196 | // such that the following log will not call anyway. | ||
197 | match macro_file.macro_call_id { | ||
198 | MacroCallId::LazyMacro(id) => { | ||
199 | let loc: MacroCallLoc = db.lookup_intern_macro(id); | ||
200 | let node = loc.kind.node(db); | ||
201 | |||
202 | // collect parent information for warning log | ||
203 | let parents = std::iter::successors(loc.kind.file_id().call_node(db), |it| { | ||
204 | it.file_id.call_node(db) | ||
205 | }) | ||
206 | .map(|n| format!("{:#}", n.value)) | ||
207 | .collect::<Vec<_>>() | ||
208 | .join("\n"); | ||
209 | |||
210 | log::warn!( | ||
211 | "fail on macro_parse: (reason: {:?} macro_call: {:#}) parents: {}", | ||
212 | err, | ||
213 | node.value, | ||
214 | parents | ||
215 | ); | ||
216 | } | ||
217 | _ => { | ||
218 | log::warn!("fail on macro_parse: (reason: {:?})", err); | ||
219 | } | ||
220 | } | ||
221 | } | ||
222 | let tt = match result.value { | ||
223 | Some(tt) => tt, | ||
224 | None => return ExpandResult { value: None, err: result.err }, | ||
225 | }; | ||
226 | |||
227 | let fragment_kind = macro_fragment_kind(db, macro_file.macro_call_id); | ||
228 | |||
229 | log::debug!("expanded = {}", tt.as_debug_string()); | ||
230 | log::debug!("kind = {:?}", fragment_kind); | ||
231 | |||
232 | let (parse, rev_token_map) = match mbe::token_tree_to_syntax_node(&tt, fragment_kind) { | ||
233 | Ok(it) => it, | ||
234 | Err(err) => { | ||
235 | log::debug!( | ||
236 | "failed to parse expanstion to {:?} = {}", | ||
237 | fragment_kind, | ||
238 | tt.as_debug_string() | ||
239 | ); | ||
240 | return ExpandResult::only_err(err); | ||
241 | } | ||
242 | }; | ||
243 | |||
244 | match result.err { | ||
245 | Some(err) => { | ||
246 | // Safety check for recursive identity macro. | ||
247 | let node = parse.syntax_node(); | ||
248 | let file: HirFileId = macro_file.into(); | ||
249 | let call_node = match file.call_node(db) { | ||
250 | Some(it) => it, | ||
251 | None => { | ||
252 | return ExpandResult::only_err(err); | ||
253 | } | ||
254 | }; | ||
255 | if is_self_replicating(&node, &call_node.value) { | ||
256 | return ExpandResult::only_err(err); | ||
257 | } else { | ||
258 | ExpandResult { value: Some((parse, Arc::new(rev_token_map))), err: Some(err) } | ||
259 | } | ||
260 | } | ||
261 | None => { | ||
262 | log::debug!("parse = {:?}", parse.syntax_node().kind()); | ||
263 | ExpandResult { value: Some((parse, Arc::new(rev_token_map))), err: None } | ||
264 | } | ||
265 | } | ||
266 | } | ||
267 | |||
268 | fn macro_arg(db: &dyn AstDatabase, id: MacroCallId) -> Option<Arc<(tt::Subtree, mbe::TokenMap)>> { | ||
269 | let arg = db.macro_arg_text(id)?; | ||
270 | let (tt, tmap) = mbe::syntax_node_to_token_tree(&SyntaxNode::new_root(arg)); | ||
271 | Some(Arc::new((tt, tmap))) | ||
272 | } | ||
273 | |||
274 | fn macro_arg_text(db: &dyn AstDatabase, id: MacroCallId) -> Option<GreenNode> { | ||
275 | let id = match id { | ||
276 | MacroCallId::LazyMacro(id) => id, | ||
277 | MacroCallId::EagerMacro(_id) => { | ||
278 | // FIXME: support macro_arg for eager macro | ||
279 | return None; | ||
280 | } | ||
281 | }; | ||
282 | let loc = db.lookup_intern_macro(id); | ||
283 | let arg = loc.kind.arg(db)?; | ||
284 | let arg = process_macro_input(db, arg, id); | ||
285 | Some(arg.green().into()) | ||
286 | } | ||
287 | |||
288 | fn macro_def(db: &dyn AstDatabase, id: MacroDefId) -> Option<Arc<TokenExpander>> { | ||
142 | match id.kind { | 289 | match id.kind { |
143 | MacroDefKind::Declarative(ast_id) => match ast_id.to_node(db) { | 290 | MacroDefKind::Declarative(ast_id) => match ast_id.to_node(db) { |
144 | syntax::ast::Macro::MacroRules(macro_rules) => { | 291 | ast::Macro::MacroRules(macro_rules) => { |
145 | let arg = macro_rules.token_tree()?; | 292 | let arg = macro_rules.token_tree()?; |
146 | let (tt, tmap) = mbe::ast_to_token_tree(&arg); | 293 | let (tt, def_site_token_map) = mbe::ast_to_token_tree(&arg); |
147 | let rules = match MacroRules::parse(&tt) { | 294 | let mac = match mbe::MacroRules::parse(&tt) { |
148 | Ok(it) => it, | 295 | Ok(it) => it, |
149 | Err(err) => { | 296 | Err(err) => { |
150 | let name = macro_rules.name().map(|n| n.to_string()).unwrap_or_default(); | 297 | let name = macro_rules.name().map(|n| n.to_string()).unwrap_or_default(); |
@@ -152,12 +299,12 @@ fn macro_def(db: &dyn AstDatabase, id: MacroDefId) -> Option<Arc<(TokenExpander, | |||
152 | return None; | 299 | return None; |
153 | } | 300 | } |
154 | }; | 301 | }; |
155 | Some(Arc::new((TokenExpander::MacroRules(rules), tmap))) | 302 | Some(Arc::new(TokenExpander::MacroRules { mac, def_site_token_map })) |
156 | } | 303 | } |
157 | syntax::ast::Macro::MacroDef(macro_def) => { | 304 | ast::Macro::MacroDef(macro_def) => { |
158 | let arg = macro_def.body()?; | 305 | let arg = macro_def.body()?; |
159 | let (tt, tmap) = mbe::ast_to_token_tree(&arg); | 306 | let (tt, def_site_token_map) = mbe::ast_to_token_tree(&arg); |
160 | let rules = match MacroDef::parse(&tt) { | 307 | let mac = match mbe::MacroDef::parse(&tt) { |
161 | Ok(it) => it, | 308 | Ok(it) => it, |
162 | Err(err) => { | 309 | Err(err) => { |
163 | let name = macro_def.name().map(|n| n.to_string()).unwrap_or_default(); | 310 | let name = macro_def.name().map(|n| n.to_string()).unwrap_or_default(); |
@@ -165,41 +312,18 @@ fn macro_def(db: &dyn AstDatabase, id: MacroDefId) -> Option<Arc<(TokenExpander, | |||
165 | return None; | 312 | return None; |
166 | } | 313 | } |
167 | }; | 314 | }; |
168 | Some(Arc::new((TokenExpander::MacroDef(rules), tmap))) | 315 | Some(Arc::new(TokenExpander::MacroDef { mac, def_site_token_map })) |
169 | } | 316 | } |
170 | }, | 317 | }, |
171 | MacroDefKind::BuiltIn(expander, _) => { | 318 | MacroDefKind::BuiltIn(expander, _) => Some(Arc::new(TokenExpander::Builtin(expander))), |
172 | Some(Arc::new((TokenExpander::Builtin(expander), mbe::TokenMap::default()))) | ||
173 | } | ||
174 | MacroDefKind::BuiltInDerive(expander, _) => { | 319 | MacroDefKind::BuiltInDerive(expander, _) => { |
175 | Some(Arc::new((TokenExpander::BuiltinDerive(expander), mbe::TokenMap::default()))) | 320 | Some(Arc::new(TokenExpander::BuiltinDerive(expander))) |
176 | } | 321 | } |
177 | MacroDefKind::BuiltInEager(..) => None, | 322 | MacroDefKind::BuiltInEager(..) => None, |
178 | MacroDefKind::ProcMacro(expander, ..) => { | 323 | MacroDefKind::ProcMacro(expander, ..) => Some(Arc::new(TokenExpander::ProcMacro(expander))), |
179 | Some(Arc::new((TokenExpander::ProcMacro(expander), mbe::TokenMap::default()))) | ||
180 | } | ||
181 | } | 324 | } |
182 | } | 325 | } |
183 | 326 | ||
184 | fn macro_arg_text(db: &dyn AstDatabase, id: MacroCallId) -> Option<GreenNode> { | ||
185 | let id = match id { | ||
186 | MacroCallId::LazyMacro(id) => id, | ||
187 | MacroCallId::EagerMacro(_id) => { | ||
188 | // FIXME: support macro_arg for eager macro | ||
189 | return None; | ||
190 | } | ||
191 | }; | ||
192 | let loc = db.lookup_intern_macro(id); | ||
193 | let arg = loc.kind.arg(db)?; | ||
194 | Some(arg.green()) | ||
195 | } | ||
196 | |||
197 | fn macro_arg(db: &dyn AstDatabase, id: MacroCallId) -> Option<Arc<(tt::Subtree, mbe::TokenMap)>> { | ||
198 | let arg = db.macro_arg_text(id)?; | ||
199 | let (tt, tmap) = mbe::syntax_node_to_token_tree(&SyntaxNode::new_root(arg)); | ||
200 | Some(Arc::new((tt, tmap))) | ||
201 | } | ||
202 | |||
203 | fn macro_expand(db: &dyn AstDatabase, id: MacroCallId) -> ExpandResult<Option<Arc<tt::Subtree>>> { | 327 | fn macro_expand(db: &dyn AstDatabase, id: MacroCallId) -> ExpandResult<Option<Arc<tt::Subtree>>> { |
204 | macro_expand_with_arg(db, id, None) | 328 | macro_expand_with_arg(db, id, None) |
205 | } | 329 | } |
@@ -208,19 +332,6 @@ fn macro_expand_error(db: &dyn AstDatabase, macro_call: MacroCallId) -> Option<E | |||
208 | db.macro_expand(macro_call).err | 332 | db.macro_expand(macro_call).err |
209 | } | 333 | } |
210 | 334 | ||
211 | fn expander(db: &dyn AstDatabase, id: MacroCallId) -> Option<Arc<(TokenExpander, mbe::TokenMap)>> { | ||
212 | let lazy_id = match id { | ||
213 | MacroCallId::LazyMacro(id) => id, | ||
214 | MacroCallId::EagerMacro(_id) => { | ||
215 | return None; | ||
216 | } | ||
217 | }; | ||
218 | |||
219 | let loc = db.lookup_intern_macro(lazy_id); | ||
220 | let macro_rules = db.macro_def(loc.def)?; | ||
221 | Some(macro_rules) | ||
222 | } | ||
223 | |||
224 | fn macro_expand_with_arg( | 335 | fn macro_expand_with_arg( |
225 | db: &dyn AstDatabase, | 336 | db: &dyn AstDatabase, |
226 | id: MacroCallId, | 337 | id: MacroCallId, |
@@ -254,7 +365,7 @@ fn macro_expand_with_arg( | |||
254 | Some(it) => it, | 365 | Some(it) => it, |
255 | None => return ExpandResult::str_err("Fail to find macro definition".into()), | 366 | None => return ExpandResult::str_err("Fail to find macro definition".into()), |
256 | }; | 367 | }; |
257 | let ExpandResult { value: tt, err } = macro_rules.0.expand(db, lazy_id, ¯o_arg.0); | 368 | let ExpandResult { value: tt, err } = macro_rules.expand(db, lazy_id, ¯o_arg.0); |
258 | // Set a hard limit for the expanded tt | 369 | // Set a hard limit for the expanded tt |
259 | let count = tt.count(); | 370 | let count = tt.count(); |
260 | if count > TOKEN_LIMIT { | 371 | if count > TOKEN_LIMIT { |
@@ -294,116 +405,11 @@ fn expand_proc_macro( | |||
294 | expander.expand(db, loc.krate, ¯o_arg.0) | 405 | expander.expand(db, loc.krate, ¯o_arg.0) |
295 | } | 406 | } |
296 | 407 | ||
297 | fn parse_or_expand(db: &dyn AstDatabase, file_id: HirFileId) -> Option<SyntaxNode> { | ||
298 | match file_id.0 { | ||
299 | HirFileIdRepr::FileId(file_id) => Some(db.parse(file_id).tree().syntax().clone()), | ||
300 | HirFileIdRepr::MacroFile(macro_file) => { | ||
301 | db.parse_macro_expansion(macro_file).value.map(|(it, _)| it.syntax_node()) | ||
302 | } | ||
303 | } | ||
304 | } | ||
305 | |||
306 | fn parse_macro_expansion( | ||
307 | db: &dyn AstDatabase, | ||
308 | macro_file: MacroFile, | ||
309 | ) -> ExpandResult<Option<(Parse<SyntaxNode>, Arc<mbe::TokenMap>)>> { | ||
310 | parse_macro_with_arg(db, macro_file, None) | ||
311 | } | ||
312 | |||
313 | fn parse_macro_with_arg( | ||
314 | db: &dyn AstDatabase, | ||
315 | macro_file: MacroFile, | ||
316 | arg: Option<Arc<(tt::Subtree, mbe::TokenMap)>>, | ||
317 | ) -> ExpandResult<Option<(Parse<SyntaxNode>, Arc<mbe::TokenMap>)>> { | ||
318 | let macro_call_id = macro_file.macro_call_id; | ||
319 | let result = if let Some(arg) = arg { | ||
320 | macro_expand_with_arg(db, macro_call_id, Some(arg)) | ||
321 | } else { | ||
322 | db.macro_expand(macro_call_id) | ||
323 | }; | ||
324 | |||
325 | let _p = profile::span("parse_macro_expansion"); | ||
326 | |||
327 | if let Some(err) = &result.err { | ||
328 | // Note: | ||
329 | // The final goal we would like to make all parse_macro success, | ||
330 | // such that the following log will not call anyway. | ||
331 | match macro_call_id { | ||
332 | MacroCallId::LazyMacro(id) => { | ||
333 | let loc: MacroCallLoc = db.lookup_intern_macro(id); | ||
334 | let node = loc.kind.node(db); | ||
335 | |||
336 | // collect parent information for warning log | ||
337 | let parents = std::iter::successors(loc.kind.file_id().call_node(db), |it| { | ||
338 | it.file_id.call_node(db) | ||
339 | }) | ||
340 | .map(|n| format!("{:#}", n.value)) | ||
341 | .collect::<Vec<_>>() | ||
342 | .join("\n"); | ||
343 | |||
344 | log::warn!( | ||
345 | "fail on macro_parse: (reason: {:?} macro_call: {:#}) parents: {}", | ||
346 | err, | ||
347 | node.value, | ||
348 | parents | ||
349 | ); | ||
350 | } | ||
351 | _ => { | ||
352 | log::warn!("fail on macro_parse: (reason: {:?})", err); | ||
353 | } | ||
354 | } | ||
355 | } | ||
356 | let tt = match result.value { | ||
357 | Some(tt) => tt, | ||
358 | None => return ExpandResult { value: None, err: result.err }, | ||
359 | }; | ||
360 | |||
361 | let fragment_kind = to_fragment_kind(db, macro_call_id); | ||
362 | |||
363 | log::debug!("expanded = {}", tt.as_debug_string()); | ||
364 | log::debug!("kind = {:?}", fragment_kind); | ||
365 | |||
366 | let (parse, rev_token_map) = match mbe::token_tree_to_syntax_node(&tt, fragment_kind) { | ||
367 | Ok(it) => it, | ||
368 | Err(err) => { | ||
369 | log::debug!( | ||
370 | "failed to parse expanstion to {:?} = {}", | ||
371 | fragment_kind, | ||
372 | tt.as_debug_string() | ||
373 | ); | ||
374 | return ExpandResult::only_err(err); | ||
375 | } | ||
376 | }; | ||
377 | |||
378 | match result.err { | ||
379 | Some(err) => { | ||
380 | // Safety check for recursive identity macro. | ||
381 | let node = parse.syntax_node(); | ||
382 | let file: HirFileId = macro_file.into(); | ||
383 | let call_node = match file.call_node(db) { | ||
384 | Some(it) => it, | ||
385 | None => { | ||
386 | return ExpandResult::only_err(err); | ||
387 | } | ||
388 | }; | ||
389 | if is_self_replicating(&node, &call_node.value) { | ||
390 | return ExpandResult::only_err(err); | ||
391 | } else { | ||
392 | ExpandResult { value: Some((parse, Arc::new(rev_token_map))), err: Some(err) } | ||
393 | } | ||
394 | } | ||
395 | None => { | ||
396 | log::debug!("parse = {:?}", parse.syntax_node().kind()); | ||
397 | ExpandResult { value: Some((parse, Arc::new(rev_token_map))), err: None } | ||
398 | } | ||
399 | } | ||
400 | } | ||
401 | |||
402 | fn is_self_replicating(from: &SyntaxNode, to: &SyntaxNode) -> bool { | 408 | fn is_self_replicating(from: &SyntaxNode, to: &SyntaxNode) -> bool { |
403 | if diff(from, to).is_empty() { | 409 | if diff(from, to).is_empty() { |
404 | return true; | 410 | return true; |
405 | } | 411 | } |
406 | if let Some(stmts) = MacroStmts::cast(from.clone()) { | 412 | if let Some(stmts) = ast::MacroStmts::cast(from.clone()) { |
407 | if stmts.statements().any(|stmt| diff(stmt.syntax(), to).is_empty()) { | 413 | if stmts.statements().any(|stmt| diff(stmt.syntax(), to).is_empty()) { |
408 | return true; | 414 | return true; |
409 | } | 415 | } |
@@ -420,62 +426,15 @@ fn hygiene_frame(db: &dyn AstDatabase, file_id: HirFileId) -> Arc<HygieneFrame> | |||
420 | Arc::new(HygieneFrame::new(db, file_id)) | 426 | Arc::new(HygieneFrame::new(db, file_id)) |
421 | } | 427 | } |
422 | 428 | ||
423 | /// Given a `MacroCallId`, return what `FragmentKind` it belongs to. | 429 | fn macro_fragment_kind(db: &dyn AstDatabase, id: MacroCallId) -> FragmentKind { |
424 | /// FIXME: Not completed | 430 | match id { |
425 | fn to_fragment_kind(db: &dyn AstDatabase, id: MacroCallId) -> FragmentKind { | 431 | MacroCallId::LazyMacro(id) => { |
426 | let lazy_id = match id { | 432 | let loc: MacroCallLoc = db.lookup_intern_macro(id); |
427 | MacroCallId::LazyMacro(id) => id, | 433 | loc.kind.fragment_kind() |
428 | MacroCallId::EagerMacro(id) => { | ||
429 | return db.lookup_intern_eager_expansion(id).fragment; | ||
430 | } | ||
431 | }; | ||
432 | let syn = db.lookup_intern_macro(lazy_id).kind.node(db).value; | ||
433 | |||
434 | let parent = match syn.parent() { | ||
435 | Some(it) => it, | ||
436 | None => return FragmentKind::Statements, | ||
437 | }; | ||
438 | |||
439 | match parent.kind() { | ||
440 | MACRO_ITEMS | SOURCE_FILE => FragmentKind::Items, | ||
441 | MACRO_STMTS => FragmentKind::Statements, | ||
442 | MACRO_PAT => FragmentKind::Pattern, | ||
443 | MACRO_TYPE => FragmentKind::Type, | ||
444 | ITEM_LIST => FragmentKind::Items, | ||
445 | LET_STMT => { | ||
446 | // FIXME: Handle LHS Pattern | ||
447 | FragmentKind::Expr | ||
448 | } | 434 | } |
449 | EXPR_STMT => FragmentKind::Statements, | 435 | MacroCallId::EagerMacro(id) => { |
450 | BLOCK_EXPR => FragmentKind::Statements, | 436 | let loc: EagerCallLoc = db.lookup_intern_eager_expansion(id); |
451 | ARG_LIST => FragmentKind::Expr, | 437 | loc.fragment |
452 | TRY_EXPR => FragmentKind::Expr, | ||
453 | TUPLE_EXPR => FragmentKind::Expr, | ||
454 | PAREN_EXPR => FragmentKind::Expr, | ||
455 | ARRAY_EXPR => FragmentKind::Expr, | ||
456 | FOR_EXPR => FragmentKind::Expr, | ||
457 | PATH_EXPR => FragmentKind::Expr, | ||
458 | CLOSURE_EXPR => FragmentKind::Expr, | ||
459 | CONDITION => FragmentKind::Expr, | ||
460 | BREAK_EXPR => FragmentKind::Expr, | ||
461 | RETURN_EXPR => FragmentKind::Expr, | ||
462 | MATCH_EXPR => FragmentKind::Expr, | ||
463 | MATCH_ARM => FragmentKind::Expr, | ||
464 | MATCH_GUARD => FragmentKind::Expr, | ||
465 | RECORD_EXPR_FIELD => FragmentKind::Expr, | ||
466 | CALL_EXPR => FragmentKind::Expr, | ||
467 | INDEX_EXPR => FragmentKind::Expr, | ||
468 | METHOD_CALL_EXPR => FragmentKind::Expr, | ||
469 | FIELD_EXPR => FragmentKind::Expr, | ||
470 | AWAIT_EXPR => FragmentKind::Expr, | ||
471 | CAST_EXPR => FragmentKind::Expr, | ||
472 | REF_EXPR => FragmentKind::Expr, | ||
473 | PREFIX_EXPR => FragmentKind::Expr, | ||
474 | RANGE_EXPR => FragmentKind::Expr, | ||
475 | BIN_EXPR => FragmentKind::Expr, | ||
476 | _ => { | ||
477 | // Unknown , Just guess it is `Items` | ||
478 | FragmentKind::Items | ||
479 | } | 438 | } |
480 | } | 439 | } |
481 | } | 440 | } |
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/hygiene.rs b/crates/hir_expand/src/hygiene.rs index 779725629..aca69e35a 100644 --- a/crates/hir_expand/src/hygiene.rs +++ b/crates/hir_expand/src/hygiene.rs | |||
@@ -5,6 +5,7 @@ | |||
5 | use std::sync::Arc; | 5 | use std::sync::Arc; |
6 | 6 | ||
7 | use base_db::CrateId; | 7 | use base_db::CrateId; |
8 | use db::TokenExpander; | ||
8 | use either::Either; | 9 | use either::Either; |
9 | use mbe::Origin; | 10 | use mbe::Origin; |
10 | use parser::SyntaxKind; | 11 | use parser::SyntaxKind; |
@@ -31,10 +32,14 @@ impl Hygiene { | |||
31 | } | 32 | } |
32 | 33 | ||
33 | // FIXME: this should just return name | 34 | // FIXME: this should just return name |
34 | pub fn name_ref_to_name(&self, name_ref: ast::NameRef) -> Either<Name, CrateId> { | 35 | pub fn name_ref_to_name( |
36 | &self, | ||
37 | db: &dyn AstDatabase, | ||
38 | name_ref: ast::NameRef, | ||
39 | ) -> Either<Name, CrateId> { | ||
35 | if let Some(frames) = &self.frames { | 40 | if let Some(frames) = &self.frames { |
36 | if name_ref.text() == "$crate" { | 41 | if name_ref.text() == "$crate" { |
37 | if let Some(krate) = frames.root_crate(name_ref.syntax()) { | 42 | if let Some(krate) = frames.root_crate(db, name_ref.syntax()) { |
38 | return Either::Right(krate); | 43 | return Either::Right(krate); |
39 | } | 44 | } |
40 | } | 45 | } |
@@ -43,15 +48,19 @@ impl Hygiene { | |||
43 | Either::Left(name_ref.as_name()) | 48 | Either::Left(name_ref.as_name()) |
44 | } | 49 | } |
45 | 50 | ||
46 | pub fn local_inner_macros(&self, path: ast::Path) -> Option<CrateId> { | 51 | pub fn local_inner_macros(&self, db: &dyn AstDatabase, path: ast::Path) -> Option<CrateId> { |
47 | let mut token = path.syntax().first_token()?.text_range(); | 52 | let mut token = path.syntax().first_token()?.text_range(); |
48 | let frames = self.frames.as_ref()?; | 53 | let frames = self.frames.as_ref()?; |
49 | let mut current = frames.0.clone(); | 54 | let mut current = frames.0.clone(); |
50 | 55 | ||
51 | loop { | 56 | loop { |
52 | let (mapped, origin) = current.expansion.as_ref()?.map_ident_up(token)?; | 57 | let (mapped, origin) = current.expansion.as_ref()?.map_ident_up(db, token)?; |
53 | if origin == Origin::Def { | 58 | if origin == Origin::Def { |
54 | return if current.local_inner { frames.root_crate(path.syntax()) } else { None }; | 59 | return if current.local_inner { |
60 | frames.root_crate(db, path.syntax()) | ||
61 | } else { | ||
62 | None | ||
63 | }; | ||
55 | } | 64 | } |
56 | current = current.call_site.as_ref()?.clone(); | 65 | current = current.call_site.as_ref()?.clone(); |
57 | token = mapped.value; | 66 | token = mapped.value; |
@@ -81,13 +90,13 @@ impl HygieneFrames { | |||
81 | HygieneFrames(Arc::new(HygieneFrame::new(db, file_id))) | 90 | HygieneFrames(Arc::new(HygieneFrame::new(db, file_id))) |
82 | } | 91 | } |
83 | 92 | ||
84 | fn root_crate(&self, node: &SyntaxNode) -> Option<CrateId> { | 93 | fn root_crate(&self, db: &dyn AstDatabase, node: &SyntaxNode) -> Option<CrateId> { |
85 | let mut token = node.first_token()?.text_range(); | 94 | let mut token = node.first_token()?.text_range(); |
86 | let mut result = self.0.krate; | 95 | let mut result = self.0.krate; |
87 | let mut current = self.0.clone(); | 96 | let mut current = self.0.clone(); |
88 | 97 | ||
89 | while let Some((mapped, origin)) = | 98 | while let Some((mapped, origin)) = |
90 | current.expansion.as_ref().and_then(|it| it.map_ident_up(token)) | 99 | current.expansion.as_ref().and_then(|it| it.map_ident_up(db, token)) |
91 | { | 100 | { |
92 | result = current.krate; | 101 | result = current.krate; |
93 | 102 | ||
@@ -111,26 +120,41 @@ impl HygieneFrames { | |||
111 | 120 | ||
112 | #[derive(Debug, Clone, PartialEq, Eq)] | 121 | #[derive(Debug, Clone, PartialEq, Eq)] |
113 | struct HygieneInfo { | 122 | struct HygieneInfo { |
114 | arg_start: InFile<TextSize>, | 123 | file: MacroFile, |
115 | /// The `macro_rules!` arguments. | 124 | /// The `macro_rules!` arguments. |
116 | def_start: Option<InFile<TextSize>>, | 125 | def_start: Option<InFile<TextSize>>, |
117 | 126 | ||
118 | macro_def: Arc<(db::TokenExpander, mbe::TokenMap)>, | 127 | macro_def: Arc<TokenExpander>, |
119 | macro_arg: Arc<(tt::Subtree, mbe::TokenMap)>, | 128 | macro_arg: Arc<(tt::Subtree, mbe::TokenMap)>, |
120 | exp_map: Arc<mbe::TokenMap>, | 129 | exp_map: Arc<mbe::TokenMap>, |
121 | } | 130 | } |
122 | 131 | ||
123 | impl HygieneInfo { | 132 | impl HygieneInfo { |
124 | fn map_ident_up(&self, token: TextRange) -> Option<(InFile<TextRange>, Origin)> { | 133 | fn map_ident_up( |
134 | &self, | ||
135 | db: &dyn AstDatabase, | ||
136 | token: TextRange, | ||
137 | ) -> Option<(InFile<TextRange>, Origin)> { | ||
125 | let token_id = self.exp_map.token_by_range(token)?; | 138 | let token_id = self.exp_map.token_by_range(token)?; |
126 | 139 | ||
127 | let (token_id, origin) = self.macro_def.0.map_id_up(token_id); | 140 | let (token_id, origin) = self.macro_def.map_id_up(token_id); |
128 | let (token_map, tt) = match origin { | 141 | let (token_map, tt) = match origin { |
129 | mbe::Origin::Call => (&self.macro_arg.1, self.arg_start), | 142 | mbe::Origin::Call => { |
130 | mbe::Origin::Def => ( | 143 | let call_id = match self.file.macro_call_id { |
131 | &self.macro_def.1, | 144 | MacroCallId::LazyMacro(lazy) => lazy, |
132 | *self.def_start.as_ref().expect("`Origin::Def` used with non-`macro_rules!` macro"), | 145 | MacroCallId::EagerMacro(_) => unreachable!(), |
133 | ), | 146 | }; |
147 | let loc: MacroCallLoc = db.lookup_intern_macro(call_id); | ||
148 | let arg_start = loc.kind.arg(db)?.text_range().start(); | ||
149 | (&self.macro_arg.1, InFile::new(loc.kind.file_id(), arg_start)) | ||
150 | } | ||
151 | mbe::Origin::Def => match (&*self.macro_def, self.def_start) { | ||
152 | (TokenExpander::MacroDef { def_site_token_map, .. }, Some(tt)) | ||
153 | | (TokenExpander::MacroRules { def_site_token_map, .. }, Some(tt)) => { | ||
154 | (def_site_token_map, tt) | ||
155 | } | ||
156 | _ => panic!("`Origin::Def` used with non-`macro_rules!` macro"), | ||
157 | }, | ||
134 | }; | 158 | }; |
135 | 159 | ||
136 | let range = token_map.range_by_token(token_id)?.by_kind(SyntaxKind::IDENT)?; | 160 | let range = token_map.range_by_token(token_id)?.by_kind(SyntaxKind::IDENT)?; |
@@ -143,8 +167,6 @@ fn make_hygiene_info( | |||
143 | macro_file: MacroFile, | 167 | macro_file: MacroFile, |
144 | loc: &MacroCallLoc, | 168 | loc: &MacroCallLoc, |
145 | ) -> Option<HygieneInfo> { | 169 | ) -> Option<HygieneInfo> { |
146 | let arg_tt = loc.kind.arg(db)?; | ||
147 | |||
148 | let def_offset = loc.def.ast_id().left().and_then(|id| { | 170 | let def_offset = loc.def.ast_id().left().and_then(|id| { |
149 | let def_tt = match id.to_node(db) { | 171 | let def_tt = match id.to_node(db) { |
150 | ast::Macro::MacroRules(mac) => mac.token_tree()?.syntax().text_range().start(), | 172 | ast::Macro::MacroRules(mac) => mac.token_tree()?.syntax().text_range().start(), |
@@ -157,13 +179,7 @@ fn make_hygiene_info( | |||
157 | let (_, exp_map) = db.parse_macro_expansion(macro_file).value?; | 179 | let (_, exp_map) = db.parse_macro_expansion(macro_file).value?; |
158 | let macro_arg = db.macro_arg(macro_file.macro_call_id)?; | 180 | let macro_arg = db.macro_arg(macro_file.macro_call_id)?; |
159 | 181 | ||
160 | Some(HygieneInfo { | 182 | Some(HygieneInfo { file: macro_file, def_start: def_offset, macro_arg, macro_def, exp_map }) |
161 | arg_start: InFile::new(loc.kind.file_id(), arg_tt.text_range().start()), | ||
162 | def_start: def_offset, | ||
163 | macro_arg, | ||
164 | macro_def, | ||
165 | exp_map, | ||
166 | }) | ||
167 | } | 183 | } |
168 | 184 | ||
169 | impl HygieneFrame { | 185 | impl HygieneFrame { |
@@ -174,7 +190,8 @@ impl HygieneFrame { | |||
174 | MacroCallId::EagerMacro(_id) => (None, None, false), | 190 | MacroCallId::EagerMacro(_id) => (None, None, false), |
175 | MacroCallId::LazyMacro(id) => { | 191 | MacroCallId::LazyMacro(id) => { |
176 | let loc = db.lookup_intern_macro(id); | 192 | let loc = db.lookup_intern_macro(id); |
177 | let info = make_hygiene_info(db, macro_file, &loc); | 193 | let info = make_hygiene_info(db, macro_file, &loc) |
194 | .map(|info| (loc.kind.file_id(), info)); | ||
178 | match loc.def.kind { | 195 | match loc.def.kind { |
179 | MacroDefKind::Declarative(_) => { | 196 | MacroDefKind::Declarative(_) => { |
180 | (info, Some(loc.def.krate), loc.def.local_inner) | 197 | (info, Some(loc.def.krate), loc.def.local_inner) |
@@ -188,7 +205,7 @@ impl HygieneFrame { | |||
188 | }, | 205 | }, |
189 | }; | 206 | }; |
190 | 207 | ||
191 | let info = match info { | 208 | let (calling_file, info) = match info { |
192 | None => { | 209 | None => { |
193 | return HygieneFrame { | 210 | return HygieneFrame { |
194 | expansion: None, | 211 | expansion: None, |
@@ -202,7 +219,7 @@ impl HygieneFrame { | |||
202 | }; | 219 | }; |
203 | 220 | ||
204 | let def_site = info.def_start.map(|it| db.hygiene_frame(it.file_id)); | 221 | let def_site = info.def_start.map(|it| db.hygiene_frame(it.file_id)); |
205 | let call_site = Some(db.hygiene_frame(info.arg_start.file_id)); | 222 | let call_site = Some(db.hygiene_frame(calling_file)); |
206 | 223 | ||
207 | HygieneFrame { expansion: Some(info), local_inner, krate, call_site, def_site } | 224 | HygieneFrame { expansion: Some(info), local_inner, krate, call_site, def_site } |
208 | } | 225 | } |
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 a0e6aec62..5df11856e 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; |
@@ -290,13 +293,21 @@ pub struct MacroCallLoc { | |||
290 | 293 | ||
291 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] | 294 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] |
292 | pub enum MacroCallKind { | 295 | pub enum MacroCallKind { |
293 | FnLike { ast_id: AstId<ast::MacroCall> }, | 296 | FnLike { |
294 | Derive { ast_id: AstId<ast::Item>, derive_name: String, derive_attr: AttrId }, | 297 | ast_id: AstId<ast::MacroCall>, |
298 | fragment: FragmentKind, | ||
299 | }, | ||
300 | Derive { | ||
301 | ast_id: AstId<ast::Item>, | ||
302 | derive_name: String, | ||
303 | /// Syntactical index of the invoking `#[derive]` attribute. | ||
304 | /// | ||
305 | /// Outer attributes are counted first, then inner attributes. This does not support | ||
306 | /// out-of-line modules, which may have attributes spread across 2 files! | ||
307 | derive_attr_index: u32, | ||
308 | }, | ||
295 | } | 309 | } |
296 | 310 | ||
297 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | ||
298 | pub struct AttrId(pub u32); | ||
299 | |||
300 | impl MacroCallKind { | 311 | impl MacroCallKind { |
301 | fn file_id(&self) -> HirFileId { | 312 | fn file_id(&self) -> HirFileId { |
302 | match self { | 313 | match self { |
@@ -324,6 +335,13 @@ impl MacroCallKind { | |||
324 | MacroCallKind::Derive { ast_id, .. } => Some(ast_id.to_node(db).syntax().clone()), | 335 | MacroCallKind::Derive { ast_id, .. } => Some(ast_id.to_node(db).syntax().clone()), |
325 | } | 336 | } |
326 | } | 337 | } |
338 | |||
339 | fn fragment_kind(&self) -> FragmentKind { | ||
340 | match self { | ||
341 | MacroCallKind::FnLike { fragment, .. } => *fragment, | ||
342 | MacroCallKind::Derive { .. } => FragmentKind::Items, | ||
343 | } | ||
344 | } | ||
327 | } | 345 | } |
328 | 346 | ||
329 | impl MacroCallId { | 347 | impl MacroCallId { |
@@ -351,13 +369,12 @@ pub struct ExpansionInfo { | |||
351 | /// The `macro_rules!` arguments. | 369 | /// The `macro_rules!` arguments. |
352 | def: Option<InFile<ast::TokenTree>>, | 370 | def: Option<InFile<ast::TokenTree>>, |
353 | 371 | ||
354 | macro_def: Arc<(db::TokenExpander, mbe::TokenMap)>, | 372 | macro_def: Arc<db::TokenExpander>, |
355 | macro_arg: Arc<(tt::Subtree, mbe::TokenMap)>, | 373 | macro_arg: Arc<(tt::Subtree, mbe::TokenMap)>, |
356 | exp_map: Arc<mbe::TokenMap>, | 374 | exp_map: Arc<mbe::TokenMap>, |
357 | } | 375 | } |
358 | 376 | ||
359 | pub use mbe::Origin; | 377 | pub use mbe::Origin; |
360 | use parser::FragmentKind; | ||
361 | 378 | ||
362 | impl ExpansionInfo { | 379 | impl ExpansionInfo { |
363 | pub fn call_node(&self) -> Option<InFile<SyntaxNode>> { | 380 | pub fn call_node(&self) -> Option<InFile<SyntaxNode>> { |
@@ -368,7 +385,7 @@ impl ExpansionInfo { | |||
368 | assert_eq!(token.file_id, self.arg.file_id); | 385 | assert_eq!(token.file_id, self.arg.file_id); |
369 | let range = token.value.text_range().checked_sub(self.arg.value.text_range().start())?; | 386 | let range = token.value.text_range().checked_sub(self.arg.value.text_range().start())?; |
370 | let token_id = self.macro_arg.1.token_by_range(range)?; | 387 | let token_id = self.macro_arg.1.token_by_range(range)?; |
371 | let token_id = self.macro_def.0.map_id_down(token_id); | 388 | let token_id = self.macro_def.map_id_down(token_id); |
372 | 389 | ||
373 | let range = self.exp_map.range_by_token(token_id)?.by_kind(token.value.kind())?; | 390 | let range = self.exp_map.range_by_token(token_id)?.by_kind(token.value.kind())?; |
374 | 391 | ||
@@ -383,17 +400,16 @@ impl ExpansionInfo { | |||
383 | ) -> Option<(InFile<SyntaxToken>, Origin)> { | 400 | ) -> Option<(InFile<SyntaxToken>, Origin)> { |
384 | let token_id = self.exp_map.token_by_range(token.value.text_range())?; | 401 | let token_id = self.exp_map.token_by_range(token.value.text_range())?; |
385 | 402 | ||
386 | let (token_id, origin) = self.macro_def.0.map_id_up(token_id); | 403 | let (token_id, origin) = self.macro_def.map_id_up(token_id); |
387 | let (token_map, tt) = match origin { | 404 | let (token_map, tt) = match origin { |
388 | mbe::Origin::Call => (&self.macro_arg.1, self.arg.clone()), | 405 | mbe::Origin::Call => (&self.macro_arg.1, self.arg.clone()), |
389 | mbe::Origin::Def => ( | 406 | mbe::Origin::Def => match (&*self.macro_def, self.def.as_ref()) { |
390 | &self.macro_def.1, | 407 | (db::TokenExpander::MacroRules { def_site_token_map, .. }, Some(tt)) |
391 | self.def | 408 | | (db::TokenExpander::MacroDef { def_site_token_map, .. }, Some(tt)) => { |
392 | .as_ref() | 409 | (def_site_token_map, tt.as_ref().map(|tt| tt.syntax().clone())) |
393 | .expect("`Origin::Def` used with non-`macro_rules!` macro") | 410 | } |
394 | .as_ref() | 411 | _ => panic!("`Origin::Def` used with non-`macro_rules!` macro"), |
395 | .map(|tt| tt.syntax().clone()), | 412 | }, |
396 | ), | ||
397 | }; | 413 | }; |
398 | 414 | ||
399 | let range = token_map.range_by_token(token_id)?.by_kind(token.value.kind())?; | 415 | let range = token_map.range_by_token(token_id)?.by_kind(token.value.kind())?; |
@@ -563,3 +579,59 @@ impl<N: AstNode> InFile<N> { | |||
563 | self.with_value(self.value.syntax()) | 579 | self.with_value(self.value.syntax()) |
564 | } | 580 | } |
565 | } | 581 | } |
582 | |||
583 | /// Given a `MacroCallId`, return what `FragmentKind` it belongs to. | ||
584 | /// FIXME: Not completed | ||
585 | pub fn to_fragment_kind(call: &ast::MacroCall) -> FragmentKind { | ||
586 | use syntax::SyntaxKind::*; | ||
587 | |||
588 | let syn = call.syntax(); | ||
589 | |||
590 | let parent = match syn.parent() { | ||
591 | Some(it) => it, | ||
592 | None => return FragmentKind::Statements, | ||
593 | }; | ||
594 | |||
595 | match parent.kind() { | ||
596 | MACRO_ITEMS | SOURCE_FILE => FragmentKind::Items, | ||
597 | MACRO_STMTS => FragmentKind::Statements, | ||
598 | MACRO_PAT => FragmentKind::Pattern, | ||
599 | MACRO_TYPE => FragmentKind::Type, | ||
600 | ITEM_LIST => FragmentKind::Items, | ||
601 | LET_STMT => { | ||
602 | // FIXME: Handle LHS Pattern | ||
603 | FragmentKind::Expr | ||
604 | } | ||
605 | EXPR_STMT => FragmentKind::Statements, | ||
606 | BLOCK_EXPR => FragmentKind::Statements, | ||
607 | ARG_LIST => FragmentKind::Expr, | ||
608 | TRY_EXPR => FragmentKind::Expr, | ||
609 | TUPLE_EXPR => FragmentKind::Expr, | ||
610 | PAREN_EXPR => FragmentKind::Expr, | ||
611 | ARRAY_EXPR => FragmentKind::Expr, | ||
612 | FOR_EXPR => FragmentKind::Expr, | ||
613 | PATH_EXPR => FragmentKind::Expr, | ||
614 | CLOSURE_EXPR => FragmentKind::Expr, | ||
615 | CONDITION => FragmentKind::Expr, | ||
616 | BREAK_EXPR => FragmentKind::Expr, | ||
617 | RETURN_EXPR => FragmentKind::Expr, | ||
618 | MATCH_EXPR => FragmentKind::Expr, | ||
619 | MATCH_ARM => FragmentKind::Expr, | ||
620 | MATCH_GUARD => FragmentKind::Expr, | ||
621 | RECORD_EXPR_FIELD => FragmentKind::Expr, | ||
622 | CALL_EXPR => FragmentKind::Expr, | ||
623 | INDEX_EXPR => FragmentKind::Expr, | ||
624 | METHOD_CALL_EXPR => FragmentKind::Expr, | ||
625 | FIELD_EXPR => FragmentKind::Expr, | ||
626 | AWAIT_EXPR => FragmentKind::Expr, | ||
627 | CAST_EXPR => FragmentKind::Expr, | ||
628 | REF_EXPR => FragmentKind::Expr, | ||
629 | PREFIX_EXPR => FragmentKind::Expr, | ||
630 | RANGE_EXPR => FragmentKind::Expr, | ||
631 | BIN_EXPR => FragmentKind::Expr, | ||
632 | _ => { | ||
633 | // Unknown , Just guess it is `Items` | ||
634 | FragmentKind::Items | ||
635 | } | ||
636 | } | ||
637 | } | ||
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/display.rs b/crates/hir_ty/src/display.rs index 4fb7d9cf2..1f6edf7a2 100644 --- a/crates/hir_ty/src/display.rs +++ b/crates/hir_ty/src/display.rs | |||
@@ -1000,7 +1000,7 @@ impl HirDisplay for TypeRef { | |||
1000 | } | 1000 | } |
1001 | TypeRef::Macro(macro_call) => { | 1001 | TypeRef::Macro(macro_call) => { |
1002 | let macro_call = macro_call.to_node(f.db.upcast()); | 1002 | let macro_call = macro_call.to_node(f.db.upcast()); |
1003 | let ctx = body::LowerCtx::with_hygiene(&Hygiene::new_unhygienic()); | 1003 | let ctx = body::LowerCtx::with_hygiene(f.db.upcast(), &Hygiene::new_unhygienic()); |
1004 | match macro_call.path() { | 1004 | match macro_call.path() { |
1005 | Some(path) => match Path::from_src(path, &ctx) { | 1005 | Some(path) => match Path::from_src(path, &ctx) { |
1006 | Some(path) => path.hir_fmt(f)?, | 1006 | Some(path) => path.hir_fmt(f)?, |
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 1c911a8b2..273d8cfbb 100644 --- a/crates/ide/src/diagnostics.rs +++ b/crates/ide/src/diagnostics.rs | |||
@@ -15,6 +15,7 @@ use hir::{ | |||
15 | diagnostics::{Diagnostic as _, DiagnosticCode, DiagnosticSinkBuilder}, | 15 | diagnostics::{Diagnostic as _, DiagnosticCode, DiagnosticSinkBuilder}, |
16 | InFile, Semantics, | 16 | InFile, Semantics, |
17 | }; | 17 | }; |
18 | use ide_assists::AssistResolveStrategy; | ||
18 | use ide_db::{base_db::SourceDatabase, RootDatabase}; | 19 | use ide_db::{base_db::SourceDatabase, RootDatabase}; |
19 | use itertools::Itertools; | 20 | use itertools::Itertools; |
20 | use rustc_hash::FxHashSet; | 21 | use rustc_hash::FxHashSet; |
@@ -84,7 +85,7 @@ pub struct DiagnosticsConfig { | |||
84 | pub(crate) fn diagnostics( | 85 | pub(crate) fn diagnostics( |
85 | db: &RootDatabase, | 86 | db: &RootDatabase, |
86 | config: &DiagnosticsConfig, | 87 | config: &DiagnosticsConfig, |
87 | resolve: bool, | 88 | resolve: &AssistResolveStrategy, |
88 | file_id: FileId, | 89 | file_id: FileId, |
89 | ) -> Vec<Diagnostic> { | 90 | ) -> Vec<Diagnostic> { |
90 | let _p = profile::span("diagnostics"); | 91 | let _p = profile::span("diagnostics"); |
@@ -212,7 +213,7 @@ pub(crate) fn diagnostics( | |||
212 | fn diagnostic_with_fix<D: DiagnosticWithFix>( | 213 | fn diagnostic_with_fix<D: DiagnosticWithFix>( |
213 | d: &D, | 214 | d: &D, |
214 | sema: &Semantics<RootDatabase>, | 215 | sema: &Semantics<RootDatabase>, |
215 | resolve: bool, | 216 | resolve: &AssistResolveStrategy, |
216 | ) -> Diagnostic { | 217 | ) -> Diagnostic { |
217 | Diagnostic::error(sema.diagnostics_display_range(d.display_source()).range, d.message()) | 218 | Diagnostic::error(sema.diagnostics_display_range(d.display_source()).range, d.message()) |
218 | .with_fix(d.fix(&sema, resolve)) | 219 | .with_fix(d.fix(&sema, resolve)) |
@@ -222,7 +223,7 @@ fn diagnostic_with_fix<D: DiagnosticWithFix>( | |||
222 | fn warning_with_fix<D: DiagnosticWithFix>( | 223 | fn warning_with_fix<D: DiagnosticWithFix>( |
223 | d: &D, | 224 | d: &D, |
224 | sema: &Semantics<RootDatabase>, | 225 | sema: &Semantics<RootDatabase>, |
225 | resolve: bool, | 226 | resolve: &AssistResolveStrategy, |
226 | ) -> Diagnostic { | 227 | ) -> Diagnostic { |
227 | Diagnostic::hint(sema.diagnostics_display_range(d.display_source()).range, d.message()) | 228 | Diagnostic::hint(sema.diagnostics_display_range(d.display_source()).range, d.message()) |
228 | .with_fix(d.fix(&sema, resolve)) | 229 | .with_fix(d.fix(&sema, resolve)) |
@@ -299,6 +300,7 @@ fn unresolved_fix(id: &'static str, label: &str, target: TextRange) -> Assist { | |||
299 | #[cfg(test)] | 300 | #[cfg(test)] |
300 | mod tests { | 301 | mod tests { |
301 | use expect_test::{expect, Expect}; | 302 | use expect_test::{expect, Expect}; |
303 | use ide_assists::AssistResolveStrategy; | ||
302 | use stdx::trim_indent; | 304 | use stdx::trim_indent; |
303 | use test_utils::assert_eq_text; | 305 | use test_utils::assert_eq_text; |
304 | 306 | ||
@@ -314,7 +316,11 @@ mod tests { | |||
314 | 316 | ||
315 | let (analysis, file_position) = fixture::position(ra_fixture_before); | 317 | let (analysis, file_position) = fixture::position(ra_fixture_before); |
316 | let diagnostic = analysis | 318 | let diagnostic = analysis |
317 | .diagnostics(&DiagnosticsConfig::default(), true, file_position.file_id) | 319 | .diagnostics( |
320 | &DiagnosticsConfig::default(), | ||
321 | AssistResolveStrategy::All, | ||
322 | file_position.file_id, | ||
323 | ) | ||
318 | .unwrap() | 324 | .unwrap() |
319 | .pop() | 325 | .pop() |
320 | .unwrap(); | 326 | .unwrap(); |
@@ -343,7 +349,11 @@ mod tests { | |||
343 | fn check_no_fix(ra_fixture: &str) { | 349 | fn check_no_fix(ra_fixture: &str) { |
344 | let (analysis, file_position) = fixture::position(ra_fixture); | 350 | let (analysis, file_position) = fixture::position(ra_fixture); |
345 | let diagnostic = analysis | 351 | let diagnostic = analysis |
346 | .diagnostics(&DiagnosticsConfig::default(), true, file_position.file_id) | 352 | .diagnostics( |
353 | &DiagnosticsConfig::default(), | ||
354 | AssistResolveStrategy::All, | ||
355 | file_position.file_id, | ||
356 | ) | ||
347 | .unwrap() | 357 | .unwrap() |
348 | .pop() | 358 | .pop() |
349 | .unwrap(); | 359 | .unwrap(); |
@@ -357,7 +367,9 @@ mod tests { | |||
357 | let diagnostics = files | 367 | let diagnostics = files |
358 | .into_iter() | 368 | .into_iter() |
359 | .flat_map(|file_id| { | 369 | .flat_map(|file_id| { |
360 | analysis.diagnostics(&DiagnosticsConfig::default(), true, file_id).unwrap() | 370 | analysis |
371 | .diagnostics(&DiagnosticsConfig::default(), AssistResolveStrategy::All, file_id) | ||
372 | .unwrap() | ||
361 | }) | 373 | }) |
362 | .collect::<Vec<_>>(); | 374 | .collect::<Vec<_>>(); |
363 | assert_eq!(diagnostics.len(), 0, "unexpected diagnostics:\n{:#?}", diagnostics); | 375 | assert_eq!(diagnostics.len(), 0, "unexpected diagnostics:\n{:#?}", diagnostics); |
@@ -365,8 +377,9 @@ mod tests { | |||
365 | 377 | ||
366 | fn check_expect(ra_fixture: &str, expect: Expect) { | 378 | fn check_expect(ra_fixture: &str, expect: Expect) { |
367 | let (analysis, file_id) = fixture::file(ra_fixture); | 379 | let (analysis, file_id) = fixture::file(ra_fixture); |
368 | let diagnostics = | 380 | let diagnostics = analysis |
369 | analysis.diagnostics(&DiagnosticsConfig::default(), true, file_id).unwrap(); | 381 | .diagnostics(&DiagnosticsConfig::default(), AssistResolveStrategy::All, file_id) |
382 | .unwrap(); | ||
370 | expect.assert_debug_eq(&diagnostics) | 383 | expect.assert_debug_eq(&diagnostics) |
371 | } | 384 | } |
372 | 385 | ||
@@ -641,6 +654,26 @@ fn test_fn() { | |||
641 | } | 654 | } |
642 | 655 | ||
643 | #[test] | 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] | ||
644 | fn test_fill_struct_fields_no_diagnostic() { | 677 | fn test_fill_struct_fields_no_diagnostic() { |
645 | check_no_diagnostics( | 678 | check_no_diagnostics( |
646 | r" | 679 | r" |
@@ -911,11 +944,13 @@ struct Foo { | |||
911 | 944 | ||
912 | let (analysis, file_id) = fixture::file(r#"mod foo;"#); | 945 | let (analysis, file_id) = fixture::file(r#"mod foo;"#); |
913 | 946 | ||
914 | let diagnostics = analysis.diagnostics(&config, true, file_id).unwrap(); | 947 | let diagnostics = |
948 | analysis.diagnostics(&config, AssistResolveStrategy::All, file_id).unwrap(); | ||
915 | assert!(diagnostics.is_empty()); | 949 | assert!(diagnostics.is_empty()); |
916 | 950 | ||
917 | let diagnostics = | 951 | let diagnostics = analysis |
918 | analysis.diagnostics(&DiagnosticsConfig::default(), true, file_id).unwrap(); | 952 | .diagnostics(&DiagnosticsConfig::default(), AssistResolveStrategy::All, file_id) |
953 | .unwrap(); | ||
919 | assert!(!diagnostics.is_empty()); | 954 | assert!(!diagnostics.is_empty()); |
920 | } | 955 | } |
921 | 956 | ||
@@ -1022,7 +1057,11 @@ impl TestStruct { | |||
1022 | 1057 | ||
1023 | let (analysis, file_position) = fixture::position(input); | 1058 | let (analysis, file_position) = fixture::position(input); |
1024 | let diagnostics = analysis | 1059 | let diagnostics = analysis |
1025 | .diagnostics(&DiagnosticsConfig::default(), true, file_position.file_id) | 1060 | .diagnostics( |
1061 | &DiagnosticsConfig::default(), | ||
1062 | AssistResolveStrategy::All, | ||
1063 | file_position.file_id, | ||
1064 | ) | ||
1026 | .unwrap(); | 1065 | .unwrap(); |
1027 | assert_eq!(diagnostics.len(), 1); | 1066 | assert_eq!(diagnostics.len(), 1); |
1028 | 1067 | ||
diff --git a/crates/ide/src/diagnostics/fixes.rs b/crates/ide/src/diagnostics/fixes.rs index 7be8b3459..15821500f 100644 --- a/crates/ide/src/diagnostics/fixes.rs +++ b/crates/ide/src/diagnostics/fixes.rs | |||
@@ -8,6 +8,7 @@ use hir::{ | |||
8 | }, | 8 | }, |
9 | HasSource, HirDisplay, InFile, Semantics, VariantDef, | 9 | HasSource, HirDisplay, InFile, Semantics, VariantDef, |
10 | }; | 10 | }; |
11 | use ide_assists::AssistResolveStrategy; | ||
11 | use ide_db::{ | 12 | use ide_db::{ |
12 | base_db::{AnchoredPathBuf, FileId}, | 13 | base_db::{AnchoredPathBuf, FileId}, |
13 | source_change::{FileSystemEdit, SourceChange}, | 14 | source_change::{FileSystemEdit, SourceChange}, |
@@ -35,11 +36,19 @@ pub(crate) trait DiagnosticWithFix: Diagnostic { | |||
35 | /// | 36 | /// |
36 | /// If `resolve` is false, the edit will be computed later, on demand, and | 37 | /// If `resolve` is false, the edit will be computed later, on demand, and |
37 | /// can be omitted. | 38 | /// can be omitted. |
38 | fn fix(&self, sema: &Semantics<RootDatabase>, _resolve: bool) -> Option<Assist>; | 39 | fn fix( |
40 | &self, | ||
41 | sema: &Semantics<RootDatabase>, | ||
42 | _resolve: &AssistResolveStrategy, | ||
43 | ) -> Option<Assist>; | ||
39 | } | 44 | } |
40 | 45 | ||
41 | impl DiagnosticWithFix for UnresolvedModule { | 46 | impl DiagnosticWithFix for UnresolvedModule { |
42 | fn fix(&self, sema: &Semantics<RootDatabase>, _resolve: bool) -> Option<Assist> { | 47 | fn fix( |
48 | &self, | ||
49 | sema: &Semantics<RootDatabase>, | ||
50 | _resolve: &AssistResolveStrategy, | ||
51 | ) -> Option<Assist> { | ||
43 | let root = sema.db.parse_or_expand(self.file)?; | 52 | let root = sema.db.parse_or_expand(self.file)?; |
44 | let unresolved_module = self.decl.to_node(&root); | 53 | let unresolved_module = self.decl.to_node(&root); |
45 | Some(fix( | 54 | Some(fix( |
@@ -59,7 +68,11 @@ impl DiagnosticWithFix for UnresolvedModule { | |||
59 | } | 68 | } |
60 | 69 | ||
61 | impl DiagnosticWithFix for NoSuchField { | 70 | impl DiagnosticWithFix for NoSuchField { |
62 | fn fix(&self, sema: &Semantics<RootDatabase>, _resolve: bool) -> Option<Assist> { | 71 | fn fix( |
72 | &self, | ||
73 | sema: &Semantics<RootDatabase>, | ||
74 | _resolve: &AssistResolveStrategy, | ||
75 | ) -> Option<Assist> { | ||
63 | let root = sema.db.parse_or_expand(self.file)?; | 76 | let root = sema.db.parse_or_expand(self.file)?; |
64 | missing_record_expr_field_fix( | 77 | missing_record_expr_field_fix( |
65 | &sema, | 78 | &sema, |
@@ -70,7 +83,11 @@ impl DiagnosticWithFix for NoSuchField { | |||
70 | } | 83 | } |
71 | 84 | ||
72 | impl DiagnosticWithFix for MissingFields { | 85 | impl DiagnosticWithFix for MissingFields { |
73 | fn fix(&self, sema: &Semantics<RootDatabase>, _resolve: bool) -> Option<Assist> { | 86 | fn fix( |
87 | &self, | ||
88 | sema: &Semantics<RootDatabase>, | ||
89 | _resolve: &AssistResolveStrategy, | ||
90 | ) -> Option<Assist> { | ||
74 | // Note that although we could add a diagnostics to | 91 | // Note that although we could add a diagnostics to |
75 | // fill the missing tuple field, e.g : | 92 | // fill the missing tuple field, e.g : |
76 | // `struct A(usize);` | 93 | // `struct A(usize);` |
@@ -106,7 +123,11 @@ impl DiagnosticWithFix for MissingFields { | |||
106 | } | 123 | } |
107 | 124 | ||
108 | impl DiagnosticWithFix for MissingOkOrSomeInTailExpr { | 125 | impl DiagnosticWithFix for MissingOkOrSomeInTailExpr { |
109 | fn fix(&self, sema: &Semantics<RootDatabase>, _resolve: bool) -> Option<Assist> { | 126 | fn fix( |
127 | &self, | ||
128 | sema: &Semantics<RootDatabase>, | ||
129 | _resolve: &AssistResolveStrategy, | ||
130 | ) -> Option<Assist> { | ||
110 | let root = sema.db.parse_or_expand(self.file)?; | 131 | let root = sema.db.parse_or_expand(self.file)?; |
111 | let tail_expr = self.expr.to_node(&root); | 132 | let tail_expr = self.expr.to_node(&root); |
112 | let tail_expr_range = tail_expr.syntax().text_range(); | 133 | let tail_expr_range = tail_expr.syntax().text_range(); |
@@ -119,7 +140,11 @@ impl DiagnosticWithFix for MissingOkOrSomeInTailExpr { | |||
119 | } | 140 | } |
120 | 141 | ||
121 | impl DiagnosticWithFix for RemoveThisSemicolon { | 142 | impl DiagnosticWithFix for RemoveThisSemicolon { |
122 | fn fix(&self, sema: &Semantics<RootDatabase>, _resolve: bool) -> Option<Assist> { | 143 | fn fix( |
144 | &self, | ||
145 | sema: &Semantics<RootDatabase>, | ||
146 | _resolve: &AssistResolveStrategy, | ||
147 | ) -> Option<Assist> { | ||
123 | let root = sema.db.parse_or_expand(self.file)?; | 148 | let root = sema.db.parse_or_expand(self.file)?; |
124 | 149 | ||
125 | let semicolon = self | 150 | let semicolon = self |
@@ -139,7 +164,11 @@ impl DiagnosticWithFix for RemoveThisSemicolon { | |||
139 | } | 164 | } |
140 | 165 | ||
141 | impl DiagnosticWithFix for IncorrectCase { | 166 | impl DiagnosticWithFix for IncorrectCase { |
142 | fn fix(&self, sema: &Semantics<RootDatabase>, resolve: bool) -> Option<Assist> { | 167 | fn fix( |
168 | &self, | ||
169 | sema: &Semantics<RootDatabase>, | ||
170 | resolve: &AssistResolveStrategy, | ||
171 | ) -> Option<Assist> { | ||
143 | let root = sema.db.parse_or_expand(self.file)?; | 172 | let root = sema.db.parse_or_expand(self.file)?; |
144 | let name_node = self.ident.to_node(&root); | 173 | let name_node = self.ident.to_node(&root); |
145 | 174 | ||
@@ -149,7 +178,7 @@ impl DiagnosticWithFix for IncorrectCase { | |||
149 | 178 | ||
150 | let label = format!("Rename to {}", self.suggested_text); | 179 | let label = format!("Rename to {}", self.suggested_text); |
151 | let mut res = unresolved_fix("change_case", &label, frange.range); | 180 | let mut res = unresolved_fix("change_case", &label, frange.range); |
152 | if resolve { | 181 | if resolve.should_resolve(&res.id) { |
153 | let source_change = rename_with_semantics(sema, file_position, &self.suggested_text); | 182 | let source_change = rename_with_semantics(sema, file_position, &self.suggested_text); |
154 | res.source_change = Some(source_change.ok().unwrap_or_default()); | 183 | res.source_change = Some(source_change.ok().unwrap_or_default()); |
155 | } | 184 | } |
@@ -159,7 +188,11 @@ impl DiagnosticWithFix for IncorrectCase { | |||
159 | } | 188 | } |
160 | 189 | ||
161 | impl DiagnosticWithFix for ReplaceFilterMapNextWithFindMap { | 190 | impl DiagnosticWithFix for ReplaceFilterMapNextWithFindMap { |
162 | fn fix(&self, sema: &Semantics<RootDatabase>, _resolve: bool) -> Option<Assist> { | 191 | fn fix( |
192 | &self, | ||
193 | sema: &Semantics<RootDatabase>, | ||
194 | _resolve: &AssistResolveStrategy, | ||
195 | ) -> Option<Assist> { | ||
163 | let root = sema.db.parse_or_expand(self.file)?; | 196 | let root = sema.db.parse_or_expand(self.file)?; |
164 | let next_expr = self.next_expr.to_node(&root); | 197 | let next_expr = self.next_expr.to_node(&root); |
165 | let next_call = ast::MethodCallExpr::cast(next_expr.syntax().clone())?; | 198 | let next_call = ast::MethodCallExpr::cast(next_expr.syntax().clone())?; |
diff --git a/crates/ide/src/diagnostics/unlinked_file.rs b/crates/ide/src/diagnostics/unlinked_file.rs index 7d39f4fbe..93fd25dea 100644 --- a/crates/ide/src/diagnostics/unlinked_file.rs +++ b/crates/ide/src/diagnostics/unlinked_file.rs | |||
@@ -5,6 +5,7 @@ use hir::{ | |||
5 | diagnostics::{Diagnostic, DiagnosticCode}, | 5 | diagnostics::{Diagnostic, DiagnosticCode}, |
6 | InFile, | 6 | InFile, |
7 | }; | 7 | }; |
8 | use ide_assists::AssistResolveStrategy; | ||
8 | use ide_db::{ | 9 | use ide_db::{ |
9 | base_db::{FileId, FileLoader, SourceDatabase, SourceDatabaseExt}, | 10 | base_db::{FileId, FileLoader, SourceDatabase, SourceDatabaseExt}, |
10 | source_change::SourceChange, | 11 | source_change::SourceChange, |
@@ -50,7 +51,11 @@ impl Diagnostic for UnlinkedFile { | |||
50 | } | 51 | } |
51 | 52 | ||
52 | impl DiagnosticWithFix for UnlinkedFile { | 53 | impl DiagnosticWithFix for UnlinkedFile { |
53 | fn fix(&self, sema: &hir::Semantics<RootDatabase>, _resolve: bool) -> Option<Assist> { | 54 | fn fix( |
55 | &self, | ||
56 | sema: &hir::Semantics<RootDatabase>, | ||
57 | _resolve: &AssistResolveStrategy, | ||
58 | ) -> Option<Assist> { | ||
54 | // If there's an existing module that could add a `mod` item to include the unlinked file, | 59 | // If there's an existing module that could add a `mod` item to include the unlinked file, |
55 | // suggest that as a fix. | 60 | // suggest that as a fix. |
56 | 61 | ||
diff --git a/crates/ide/src/folding_ranges.rs b/crates/ide/src/folding_ranges.rs index 2b9ed123c..a03988778 100644..100755 --- a/crates/ide/src/folding_ranges.rs +++ b/crates/ide/src/folding_ranges.rs | |||
@@ -20,6 +20,7 @@ pub enum FoldKind { | |||
20 | Consts, | 20 | Consts, |
21 | Statics, | 21 | Statics, |
22 | Array, | 22 | Array, |
23 | WhereClause, | ||
23 | } | 24 | } |
24 | 25 | ||
25 | #[derive(Debug)] | 26 | #[derive(Debug)] |
@@ -109,6 +110,13 @@ pub(crate) fn folding_ranges(file: &SourceFile) -> Vec<Fold> { | |||
109 | res.push(Fold { range, kind: FoldKind::Statics }) | 110 | res.push(Fold { range, kind: FoldKind::Statics }) |
110 | } | 111 | } |
111 | } | 112 | } |
113 | |||
114 | // Fold where clause | ||
115 | if node.kind() == WHERE_CLAUSE { | ||
116 | if let Some(range) = fold_range_for_where_clause(&node) { | ||
117 | res.push(Fold { range, kind: FoldKind::WhereClause }) | ||
118 | } | ||
119 | } | ||
112 | } | 120 | } |
113 | } | 121 | } |
114 | } | 122 | } |
@@ -241,6 +249,23 @@ fn contiguous_range_for_comment( | |||
241 | } | 249 | } |
242 | } | 250 | } |
243 | 251 | ||
252 | fn fold_range_for_where_clause(node: &SyntaxNode) -> Option<TextRange> { | ||
253 | let first_where_pred = node.first_child(); | ||
254 | let last_where_pred = node.last_child(); | ||
255 | |||
256 | if first_where_pred != last_where_pred { | ||
257 | let mut it = node.descendants_with_tokens(); | ||
258 | if let (Some(_where_clause), Some(where_kw), Some(last_comma)) = | ||
259 | (it.next(), it.next(), it.last()) | ||
260 | { | ||
261 | let start = where_kw.text_range().end(); | ||
262 | let end = last_comma.text_range().end(); | ||
263 | return Some(TextRange::new(start, end)); | ||
264 | } | ||
265 | } | ||
266 | None | ||
267 | } | ||
268 | |||
244 | #[cfg(test)] | 269 | #[cfg(test)] |
245 | mod tests { | 270 | mod tests { |
246 | use test_utils::extract_tags; | 271 | use test_utils::extract_tags; |
@@ -272,6 +297,7 @@ mod tests { | |||
272 | FoldKind::Consts => "consts", | 297 | FoldKind::Consts => "consts", |
273 | FoldKind::Statics => "statics", | 298 | FoldKind::Statics => "statics", |
274 | FoldKind::Array => "array", | 299 | FoldKind::Array => "array", |
300 | FoldKind::WhereClause => "whereclause", | ||
275 | }; | 301 | }; |
276 | assert_eq!(kind, &attr.unwrap()); | 302 | assert_eq!(kind, &attr.unwrap()); |
277 | } | 303 | } |
@@ -513,4 +539,23 @@ static SECOND_STATIC: &str = "second";</fold> | |||
513 | "#, | 539 | "#, |
514 | ) | 540 | ) |
515 | } | 541 | } |
542 | |||
543 | #[test] | ||
544 | fn fold_where_clause() { | ||
545 | // fold multi-line and don't fold single line. | ||
546 | check( | ||
547 | r#" | ||
548 | fn foo() | ||
549 | where<fold whereclause> | ||
550 | A: Foo, | ||
551 | B: Foo, | ||
552 | C: Foo, | ||
553 | D: Foo,</fold> {} | ||
554 | |||
555 | fn bar() | ||
556 | where | ||
557 | A: Bar, {} | ||
558 | "#, | ||
559 | ) | ||
560 | } | ||
516 | } | 561 | } |
diff --git a/crates/ide/src/goto_type_definition.rs b/crates/ide/src/goto_type_definition.rs index 9d34b109b..f3284bb96 100644 --- a/crates/ide/src/goto_type_definition.rs +++ b/crates/ide/src/goto_type_definition.rs | |||
@@ -30,6 +30,7 @@ pub(crate) fn goto_type_definition( | |||
30 | ast::Expr(it) => sema.type_of_expr(&it)?, | 30 | ast::Expr(it) => sema.type_of_expr(&it)?, |
31 | ast::Pat(it) => sema.type_of_pat(&it)?, | 31 | ast::Pat(it) => sema.type_of_pat(&it)?, |
32 | ast::SelfParam(it) => sema.type_of_self(&it)?, | 32 | ast::SelfParam(it) => sema.type_of_self(&it)?, |
33 | ast::Type(it) => sema.resolve_type(&it)?, | ||
33 | _ => return None, | 34 | _ => return None, |
34 | } | 35 | } |
35 | }; | 36 | }; |
@@ -149,4 +150,15 @@ impl Foo { | |||
149 | "#, | 150 | "#, |
150 | ) | 151 | ) |
151 | } | 152 | } |
153 | |||
154 | #[test] | ||
155 | fn goto_def_for_type_fallback() { | ||
156 | check( | ||
157 | r#" | ||
158 | struct Foo; | ||
159 | //^^^ | ||
160 | impl Foo$0 {} | ||
161 | "#, | ||
162 | ) | ||
163 | } | ||
152 | } | 164 | } |
diff --git a/crates/ide/src/inlay_hints.rs b/crates/ide/src/inlay_hints.rs index d5ef054d8..e0bf660c4 100644 --- a/crates/ide/src/inlay_hints.rs +++ b/crates/ide/src/inlay_hints.rs | |||
@@ -218,9 +218,7 @@ fn hint_iterator( | |||
218 | ty: &hir::Type, | 218 | ty: &hir::Type, |
219 | ) -> Option<SmolStr> { | 219 | ) -> Option<SmolStr> { |
220 | let db = sema.db; | 220 | let db = sema.db; |
221 | let strukt = std::iter::successors(Some(ty.clone()), |ty| ty.remove_ref()) | 221 | let strukt = ty.strip_references().as_adt()?; |
222 | .last() | ||
223 | .and_then(|strukt| strukt.as_adt())?; | ||
224 | let krate = strukt.krate(db); | 222 | let krate = strukt.krate(db); |
225 | if krate != famous_defs.core()? { | 223 | if krate != famous_defs.core()? { |
226 | return None; | 224 | return None; |
diff --git a/crates/ide/src/join_lines.rs b/crates/ide/src/join_lines.rs index fe2a349e6..61dcbb399 100644 --- a/crates/ide/src/join_lines.rs +++ b/crates/ide/src/join_lines.rs | |||
@@ -1,3 +1,5 @@ | |||
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::{ |
@@ -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 99e45633e..34360501a 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 | ||
@@ -87,7 +88,9 @@ pub use crate::{ | |||
87 | }, | 88 | }, |
88 | }; | 89 | }; |
89 | pub use hir::{Documentation, Semantics}; | 90 | pub use hir::{Documentation, Semantics}; |
90 | pub use ide_assists::{Assist, AssistConfig, AssistId, AssistKind}; | 91 | pub use ide_assists::{ |
92 | Assist, AssistConfig, AssistId, AssistKind, AssistResolveStrategy, SingleResolve, | ||
93 | }; | ||
91 | pub use ide_completion::{ | 94 | pub use ide_completion::{ |
92 | CompletionConfig, CompletionItem, CompletionItemKind, CompletionRelevance, ImportEdit, | 95 | CompletionConfig, CompletionItem, CompletionItemKind, CompletionRelevance, ImportEdit, |
93 | InsertTextFormat, | 96 | InsertTextFormat, |
@@ -285,6 +288,10 @@ impl Analysis { | |||
285 | self.with_db(|db| view_hir::view_hir(&db, position)) | 288 | self.with_db(|db| view_hir::view_hir(&db, position)) |
286 | } | 289 | } |
287 | 290 | ||
291 | pub fn view_crate_graph(&self) -> Cancelable<Result<String, String>> { | ||
292 | self.with_db(|db| view_crate_graph::view_crate_graph(&db)) | ||
293 | } | ||
294 | |||
288 | pub fn expand_macro(&self, position: FilePosition) -> Cancelable<Option<ExpandedMacro>> { | 295 | pub fn expand_macro(&self, position: FilePosition) -> Cancelable<Option<ExpandedMacro>> { |
289 | self.with_db(|db| expand_macro::expand_macro(db, position)) | 296 | self.with_db(|db| expand_macro::expand_macro(db, position)) |
290 | } | 297 | } |
@@ -518,12 +525,13 @@ impl Analysis { | |||
518 | pub fn assists( | 525 | pub fn assists( |
519 | &self, | 526 | &self, |
520 | config: &AssistConfig, | 527 | config: &AssistConfig, |
521 | resolve: bool, | 528 | resolve: AssistResolveStrategy, |
522 | frange: FileRange, | 529 | frange: FileRange, |
523 | ) -> Cancelable<Vec<Assist>> { | 530 | ) -> Cancelable<Vec<Assist>> { |
524 | self.with_db(|db| { | 531 | self.with_db(|db| { |
532 | let ssr_assists = ssr::ssr_assists(db, &resolve, frange); | ||
525 | let mut acc = Assist::get(db, config, resolve, frange); | 533 | let mut acc = Assist::get(db, config, resolve, frange); |
526 | ssr::add_ssr_assist(db, &mut acc, resolve, frange); | 534 | acc.extend(ssr_assists.into_iter()); |
527 | acc | 535 | acc |
528 | }) | 536 | }) |
529 | } | 537 | } |
@@ -532,10 +540,10 @@ impl Analysis { | |||
532 | pub fn diagnostics( | 540 | pub fn diagnostics( |
533 | &self, | 541 | &self, |
534 | config: &DiagnosticsConfig, | 542 | config: &DiagnosticsConfig, |
535 | resolve: bool, | 543 | resolve: AssistResolveStrategy, |
536 | file_id: FileId, | 544 | file_id: FileId, |
537 | ) -> Cancelable<Vec<Diagnostic>> { | 545 | ) -> Cancelable<Vec<Diagnostic>> { |
538 | self.with_db(|db| diagnostics::diagnostics(db, config, resolve, file_id)) | 546 | self.with_db(|db| diagnostics::diagnostics(db, config, &resolve, file_id)) |
539 | } | 547 | } |
540 | 548 | ||
541 | /// Convenience function to return assists + quick fixes for diagnostics | 549 | /// Convenience function to return assists + quick fixes for diagnostics |
@@ -543,7 +551,7 @@ impl Analysis { | |||
543 | &self, | 551 | &self, |
544 | assist_config: &AssistConfig, | 552 | assist_config: &AssistConfig, |
545 | diagnostics_config: &DiagnosticsConfig, | 553 | diagnostics_config: &DiagnosticsConfig, |
546 | resolve: bool, | 554 | resolve: AssistResolveStrategy, |
547 | frange: FileRange, | 555 | frange: FileRange, |
548 | ) -> Cancelable<Vec<Assist>> { | 556 | ) -> Cancelable<Vec<Assist>> { |
549 | let include_fixes = match &assist_config.allowed { | 557 | let include_fixes = match &assist_config.allowed { |
@@ -552,17 +560,21 @@ impl Analysis { | |||
552 | }; | 560 | }; |
553 | 561 | ||
554 | self.with_db(|db| { | 562 | self.with_db(|db| { |
563 | let ssr_assists = ssr::ssr_assists(db, &resolve, frange); | ||
564 | let diagnostic_assists = if include_fixes { | ||
565 | diagnostics::diagnostics(db, diagnostics_config, &resolve, frange.file_id) | ||
566 | .into_iter() | ||
567 | .filter_map(|it| it.fix) | ||
568 | .filter(|it| it.target.intersect(frange.range).is_some()) | ||
569 | .collect() | ||
570 | } else { | ||
571 | Vec::new() | ||
572 | }; | ||
573 | |||
555 | let mut res = Assist::get(db, assist_config, resolve, frange); | 574 | let mut res = Assist::get(db, assist_config, resolve, frange); |
556 | ssr::add_ssr_assist(db, &mut res, resolve, frange); | 575 | res.extend(ssr_assists.into_iter()); |
557 | 576 | res.extend(diagnostic_assists.into_iter()); | |
558 | if include_fixes { | 577 | |
559 | res.extend( | ||
560 | diagnostics::diagnostics(db, diagnostics_config, resolve, frange.file_id) | ||
561 | .into_iter() | ||
562 | .filter_map(|it| it.fix) | ||
563 | .filter(|it| it.target.intersect(frange.range).is_some()), | ||
564 | ); | ||
565 | } | ||
566 | res | 578 | res |
567 | }) | 579 | }) |
568 | } | 580 | } |
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/ssr.rs b/crates/ide/src/ssr.rs index f3638d928..57ec80261 100644 --- a/crates/ide/src/ssr.rs +++ b/crates/ide/src/ssr.rs | |||
@@ -2,18 +2,23 @@ | |||
2 | //! assist in ide_assists because that would require the ide_assists crate | 2 | //! assist in ide_assists because that would require the ide_assists crate |
3 | //! depend on the ide_ssr crate. | 3 | //! depend on the ide_ssr crate. |
4 | 4 | ||
5 | use ide_assists::{Assist, AssistId, AssistKind, GroupLabel}; | 5 | use ide_assists::{Assist, AssistId, AssistKind, AssistResolveStrategy, GroupLabel}; |
6 | use ide_db::{base_db::FileRange, label::Label, source_change::SourceChange, RootDatabase}; | 6 | use ide_db::{base_db::FileRange, label::Label, source_change::SourceChange, RootDatabase}; |
7 | 7 | ||
8 | pub(crate) fn add_ssr_assist( | 8 | pub(crate) fn ssr_assists( |
9 | db: &RootDatabase, | 9 | db: &RootDatabase, |
10 | base: &mut Vec<Assist>, | 10 | resolve: &AssistResolveStrategy, |
11 | resolve: bool, | ||
12 | frange: FileRange, | 11 | frange: FileRange, |
13 | ) -> Option<()> { | 12 | ) -> Vec<Assist> { |
14 | let (match_finder, comment_range) = ide_ssr::ssr_from_comment(db, frange)?; | 13 | let mut ssr_assists = Vec::with_capacity(2); |
15 | 14 | ||
16 | let (source_change_for_file, source_change_for_workspace) = if resolve { | 15 | let (match_finder, comment_range) = match ide_ssr::ssr_from_comment(db, frange) { |
16 | Some(ssr_data) => ssr_data, | ||
17 | None => return ssr_assists, | ||
18 | }; | ||
19 | let id = AssistId("ssr", AssistKind::RefactorRewrite); | ||
20 | |||
21 | let (source_change_for_file, source_change_for_workspace) = if resolve.should_resolve(&id) { | ||
17 | let edits = match_finder.edits(); | 22 | let edits = match_finder.edits(); |
18 | 23 | ||
19 | let source_change_for_file = { | 24 | let source_change_for_file = { |
@@ -35,16 +40,17 @@ pub(crate) fn add_ssr_assist( | |||
35 | 40 | ||
36 | for (label, source_change) in assists.into_iter() { | 41 | for (label, source_change) in assists.into_iter() { |
37 | let assist = Assist { | 42 | let assist = Assist { |
38 | id: AssistId("ssr", AssistKind::RefactorRewrite), | 43 | id, |
39 | label: Label::new(label), | 44 | label: Label::new(label), |
40 | group: Some(GroupLabel("Apply SSR".into())), | 45 | group: Some(GroupLabel("Apply SSR".into())), |
41 | target: comment_range, | 46 | target: comment_range, |
42 | source_change, | 47 | source_change, |
43 | }; | 48 | }; |
44 | 49 | ||
45 | base.push(assist); | 50 | ssr_assists.push(assist); |
46 | } | 51 | } |
47 | Some(()) | 52 | |
53 | ssr_assists | ||
48 | } | 54 | } |
49 | 55 | ||
50 | #[cfg(test)] | 56 | #[cfg(test)] |
@@ -52,7 +58,7 @@ mod tests { | |||
52 | use std::sync::Arc; | 58 | use std::sync::Arc; |
53 | 59 | ||
54 | use expect_test::expect; | 60 | use expect_test::expect; |
55 | use ide_assists::Assist; | 61 | use ide_assists::{Assist, AssistResolveStrategy}; |
56 | use ide_db::{ | 62 | use ide_db::{ |
57 | base_db::{fixture::WithFixture, salsa::Durability, FileRange}, | 63 | base_db::{fixture::WithFixture, salsa::Durability, FileRange}, |
58 | symbol_index::SymbolsDatabase, | 64 | symbol_index::SymbolsDatabase, |
@@ -60,24 +66,14 @@ mod tests { | |||
60 | }; | 66 | }; |
61 | use rustc_hash::FxHashSet; | 67 | use rustc_hash::FxHashSet; |
62 | 68 | ||
63 | use super::add_ssr_assist; | 69 | use super::ssr_assists; |
64 | 70 | ||
65 | fn get_assists(ra_fixture: &str, resolve: bool) -> Vec<Assist> { | 71 | fn get_assists(ra_fixture: &str, resolve: AssistResolveStrategy) -> Vec<Assist> { |
66 | let (mut db, file_id, range_or_offset) = RootDatabase::with_range_or_offset(ra_fixture); | 72 | let (mut db, file_id, range_or_offset) = RootDatabase::with_range_or_offset(ra_fixture); |
67 | let mut local_roots = FxHashSet::default(); | 73 | let mut local_roots = FxHashSet::default(); |
68 | local_roots.insert(ide_db::base_db::fixture::WORKSPACE); | 74 | local_roots.insert(ide_db::base_db::fixture::WORKSPACE); |
69 | db.set_local_roots_with_durability(Arc::new(local_roots), Durability::HIGH); | 75 | db.set_local_roots_with_durability(Arc::new(local_roots), Durability::HIGH); |
70 | 76 | ssr_assists(&db, &resolve, FileRange { file_id, range: range_or_offset.into() }) | |
71 | let mut assists = vec![]; | ||
72 | |||
73 | add_ssr_assist( | ||
74 | &db, | ||
75 | &mut assists, | ||
76 | resolve, | ||
77 | FileRange { file_id, range: range_or_offset.into() }, | ||
78 | ); | ||
79 | |||
80 | assists | ||
81 | } | 77 | } |
82 | 78 | ||
83 | #[test] | 79 | #[test] |
@@ -88,16 +84,13 @@ mod tests { | |||
88 | // This is foo $0 | 84 | // This is foo $0 |
89 | fn foo() {} | 85 | fn foo() {} |
90 | "#; | 86 | "#; |
91 | let resolve = true; | 87 | let assists = get_assists(ra_fixture, AssistResolveStrategy::All); |
92 | |||
93 | let assists = get_assists(ra_fixture, resolve); | ||
94 | 88 | ||
95 | assert_eq!(0, assists.len()); | 89 | assert_eq!(0, assists.len()); |
96 | } | 90 | } |
97 | 91 | ||
98 | #[test] | 92 | #[test] |
99 | fn resolve_edits_true() { | 93 | fn resolve_edits_true() { |
100 | let resolve = true; | ||
101 | let assists = get_assists( | 94 | let assists = get_assists( |
102 | r#" | 95 | r#" |
103 | //- /lib.rs | 96 | //- /lib.rs |
@@ -109,7 +102,7 @@ mod tests { | |||
109 | //- /bar.rs | 102 | //- /bar.rs |
110 | fn bar() { 2 } | 103 | fn bar() { 2 } |
111 | "#, | 104 | "#, |
112 | resolve, | 105 | AssistResolveStrategy::All, |
113 | ); | 106 | ); |
114 | 107 | ||
115 | assert_eq!(2, assists.len()); | 108 | assert_eq!(2, assists.len()); |
@@ -200,7 +193,6 @@ mod tests { | |||
200 | 193 | ||
201 | #[test] | 194 | #[test] |
202 | fn resolve_edits_false() { | 195 | fn resolve_edits_false() { |
203 | let resolve = false; | ||
204 | let assists = get_assists( | 196 | let assists = get_assists( |
205 | r#" | 197 | r#" |
206 | //- /lib.rs | 198 | //- /lib.rs |
@@ -212,7 +204,7 @@ mod tests { | |||
212 | //- /bar.rs | 204 | //- /bar.rs |
213 | fn bar() { 2 } | 205 | fn bar() { 2 } |
214 | "#, | 206 | "#, |
215 | resolve, | 207 | AssistResolveStrategy::None, |
216 | ); | 208 | ); |
217 | 209 | ||
218 | assert_eq!(2, assists.len()); | 210 | assert_eq!(2, assists.len()); |
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 638f42c2f..8d83ba206 100644 --- a/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html +++ b/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html | |||
@@ -142,6 +142,7 @@ It is beyond me why you'd use these when you got /// | |||
142 | ```rust | 142 | ```rust |
143 | </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="function injected">example</span><span class="parenthesis injected">(</span><span class="operator injected">&</span><span class="bracket injected">[</span><span class="numeric_literal injected">1</span><span class="comma injected">,</span><span class="none injected"> </span><span class="numeric_literal injected">2</span><span class="comma injected">,</span><span class="none injected"> </span><span class="numeric_literal injected">3</span><span class="bracket injected">]</span><span class="parenthesis injected">)</span><span class="semicolon injected">;</span><span class="comment documentation"> | 143 | </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="function injected">example</span><span class="parenthesis injected">(</span><span class="operator injected">&</span><span class="bracket injected">[</span><span class="numeric_literal injected">1</span><span class="comma injected">,</span><span class="none injected"> </span><span class="numeric_literal injected">2</span><span class="comma injected">,</span><span class="none injected"> </span><span class="numeric_literal injected">3</span><span class="bracket injected">]</span><span class="parenthesis injected">)</span><span class="semicolon injected">;</span><span class="comment documentation"> |
144 | ``` | 144 | ``` |
145 | </span><span class="function documentation injected intra_doc_link">[`block_comments2`]</span><span class="comment documentation"> tests these with indentation | ||
145 | */</span> | 146 | */</span> |
146 | <span class="keyword">pub</span> <span class="keyword">fn</span> <span class="function declaration">block_comments</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span><span class="brace">}</span> | 147 | <span class="keyword">pub</span> <span class="keyword">fn</span> <span class="function declaration">block_comments</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span><span class="brace">}</span> |
147 | 148 | ||
@@ -150,5 +151,6 @@ It is beyond me why you'd use these when you got /// | |||
150 | ```rust | 151 | ```rust |
151 | </span><span class="comment documentation"> </span><span class="none injected"> </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="function injected">example</span><span class="parenthesis injected">(</span><span class="operator injected">&</span><span class="bracket injected">[</span><span class="numeric_literal injected">1</span><span class="comma injected">,</span><span class="none injected"> </span><span class="numeric_literal injected">2</span><span class="comma injected">,</span><span class="none injected"> </span><span class="numeric_literal injected">3</span><span class="bracket injected">]</span><span class="parenthesis injected">)</span><span class="semicolon injected">;</span><span class="comment documentation"> | 152 | </span><span class="comment documentation"> </span><span class="none injected"> </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="function injected">example</span><span class="parenthesis injected">(</span><span class="operator injected">&</span><span class="bracket injected">[</span><span class="numeric_literal injected">1</span><span class="comma injected">,</span><span class="none injected"> </span><span class="numeric_literal injected">2</span><span class="comma injected">,</span><span class="none injected"> </span><span class="numeric_literal injected">3</span><span class="bracket injected">]</span><span class="parenthesis injected">)</span><span class="semicolon injected">;</span><span class="comment documentation"> |
152 | ``` | 153 | ``` |
154 | </span><span class="function documentation injected intra_doc_link">[`block_comments`]</span><span class="comment documentation"> tests these without indentation | ||
153 | */</span> | 155 | */</span> |
154 | <span class="keyword">pub</span> <span class="keyword">fn</span> <span class="function declaration">block_comments2</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span><span class="brace">}</span></code></pre> \ No newline at end of file | 156 | <span class="keyword">pub</span> <span class="keyword">fn</span> <span class="function declaration">block_comments2</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span><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 17cc6334b..b6e952b08 100644 --- a/crates/ide/src/syntax_highlighting/tests.rs +++ b/crates/ide/src/syntax_highlighting/tests.rs | |||
@@ -618,6 +618,7 @@ It is beyond me why you'd use these when you got /// | |||
618 | ```rust | 618 | ```rust |
619 | let _ = example(&[1, 2, 3]); | 619 | let _ = example(&[1, 2, 3]); |
620 | ``` | 620 | ``` |
621 | [`block_comments2`] tests these with indentation | ||
621 | */ | 622 | */ |
622 | pub fn block_comments() {} | 623 | pub fn block_comments() {} |
623 | 624 | ||
@@ -626,6 +627,7 @@ pub fn block_comments() {} | |||
626 | ```rust | 627 | ```rust |
627 | let _ = example(&[1, 2, 3]); | 628 | let _ = example(&[1, 2, 3]); |
628 | ``` | 629 | ``` |
630 | [`block_comments`] tests these without indentation | ||
629 | */ | 631 | */ |
630 | pub fn block_comments2() {} | 632 | pub fn block_comments2() {} |
631 | "# | 633 | "# |
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..5e4ba881e --- /dev/null +++ b/crates/ide/src/view_crate_graph.rs | |||
@@ -0,0 +1,111 @@ | |||
1 | use std::{ | ||
2 | error::Error, | ||
3 | io::{Read, Write}, | ||
4 | process::{Command, Stdio}, | ||
5 | sync::Arc, | ||
6 | }; | ||
7 | |||
8 | use dot::{Id, LabelText}; | ||
9 | use ide_db::{ | ||
10 | base_db::{CrateGraph, CrateId, Dependency, SourceDatabase, SourceDatabaseExt}, | ||
11 | RootDatabase, | ||
12 | }; | ||
13 | use rustc_hash::FxHashSet; | ||
14 | |||
15 | // Feature: View Crate Graph | ||
16 | // | ||
17 | // Renders the currently loaded crate graph as an SVG graphic. Requires the `dot` tool, which | ||
18 | // is part of graphviz, to be installed. | ||
19 | // | ||
20 | // Only workspace crates are included, no crates.io dependencies or sysroot crates. | ||
21 | // | ||
22 | // |=== | ||
23 | // | Editor | Action Name | ||
24 | // | ||
25 | // | VS Code | **Rust Analyzer: View Crate Graph** | ||
26 | // |=== | ||
27 | pub(crate) fn view_crate_graph(db: &RootDatabase) -> Result<String, String> { | ||
28 | let crate_graph = db.crate_graph(); | ||
29 | let crates_to_render = crate_graph | ||
30 | .iter() | ||
31 | .filter(|krate| { | ||
32 | // Only render workspace crates | ||
33 | let root_id = db.file_source_root(crate_graph[*krate].root_file_id); | ||
34 | !db.source_root(root_id).is_library | ||
35 | }) | ||
36 | .collect(); | ||
37 | let graph = DotCrateGraph { graph: crate_graph, crates_to_render }; | ||
38 | |||
39 | let mut dot = Vec::new(); | ||
40 | dot::render(&graph, &mut dot).unwrap(); | ||
41 | |||
42 | render_svg(&dot).map_err(|e| e.to_string()) | ||
43 | } | ||
44 | |||
45 | fn render_svg(dot: &[u8]) -> Result<String, Box<dyn Error>> { | ||
46 | // We shell out to `dot` to render to SVG, as there does not seem to be a pure-Rust renderer. | ||
47 | let child = Command::new("dot") | ||
48 | .arg("-Tsvg") | ||
49 | .stdin(Stdio::piped()) | ||
50 | .stdout(Stdio::piped()) | ||
51 | .spawn() | ||
52 | .map_err(|err| format!("failed to spawn `dot`: {}", err))?; | ||
53 | child.stdin.unwrap().write_all(&dot)?; | ||
54 | |||
55 | let mut svg = String::new(); | ||
56 | child.stdout.unwrap().read_to_string(&mut svg)?; | ||
57 | Ok(svg) | ||
58 | } | ||
59 | |||
60 | struct DotCrateGraph { | ||
61 | graph: Arc<CrateGraph>, | ||
62 | crates_to_render: FxHashSet<CrateId>, | ||
63 | } | ||
64 | |||
65 | type Edge<'a> = (CrateId, &'a Dependency); | ||
66 | |||
67 | impl<'a> dot::GraphWalk<'a, CrateId, Edge<'a>> for DotCrateGraph { | ||
68 | fn nodes(&'a self) -> dot::Nodes<'a, CrateId> { | ||
69 | self.crates_to_render.iter().copied().collect() | ||
70 | } | ||
71 | |||
72 | fn edges(&'a self) -> dot::Edges<'a, Edge<'a>> { | ||
73 | self.crates_to_render | ||
74 | .iter() | ||
75 | .flat_map(|krate| { | ||
76 | self.graph[*krate] | ||
77 | .dependencies | ||
78 | .iter() | ||
79 | .filter(|dep| self.crates_to_render.contains(&dep.crate_id)) | ||
80 | .map(move |dep| (*krate, dep)) | ||
81 | }) | ||
82 | .collect() | ||
83 | } | ||
84 | |||
85 | fn source(&'a self, edge: &Edge<'a>) -> CrateId { | ||
86 | edge.0 | ||
87 | } | ||
88 | |||
89 | fn target(&'a self, edge: &Edge<'a>) -> CrateId { | ||
90 | edge.1.crate_id | ||
91 | } | ||
92 | } | ||
93 | |||
94 | impl<'a> dot::Labeller<'a, CrateId, Edge<'a>> for DotCrateGraph { | ||
95 | fn graph_id(&'a self) -> Id<'a> { | ||
96 | Id::new("rust_analyzer_crate_graph").unwrap() | ||
97 | } | ||
98 | |||
99 | fn node_id(&'a self, n: &CrateId) -> Id<'a> { | ||
100 | Id::new(format!("_{}", n.0)).unwrap() | ||
101 | } | ||
102 | |||
103 | fn node_shape(&'a self, _node: &CrateId) -> Option<LabelText<'a>> { | ||
104 | Some(LabelText::LabelStr("box".into())) | ||
105 | } | ||
106 | |||
107 | fn node_label(&'a self, n: &CrateId) -> LabelText<'a> { | ||
108 | let name = self.graph[*n].display_name.as_ref().map_or("(unnamed crate)", |name| &*name); | ||
109 | LabelText::LabelStr(name.into()) | ||
110 | } | ||
111 | } | ||
diff --git a/crates/ide_assists/src/assist_context.rs b/crates/ide_assists/src/assist_context.rs index 8714e4978..682f0ff5e 100644 --- a/crates/ide_assists/src/assist_context.rs +++ b/crates/ide_assists/src/assist_context.rs | |||
@@ -13,13 +13,15 @@ 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 | }; |
20 | use text_edit::{TextEdit, TextEditBuilder}; | 20 | use text_edit::{TextEdit, TextEditBuilder}; |
21 | 21 | ||
22 | use crate::{assist_config::AssistConfig, Assist, AssistId, AssistKind, GroupLabel}; | 22 | use crate::{ |
23 | assist_config::AssistConfig, Assist, AssistId, AssistKind, AssistResolveStrategy, GroupLabel, | ||
24 | }; | ||
23 | 25 | ||
24 | /// `AssistContext` allows to apply an assist or check if it could be applied. | 26 | /// `AssistContext` allows to apply an assist or check if it could be applied. |
25 | /// | 27 | /// |
@@ -105,14 +107,14 @@ impl<'a> AssistContext<'a> { | |||
105 | } | 107 | } |
106 | 108 | ||
107 | pub(crate) struct Assists { | 109 | pub(crate) struct Assists { |
108 | resolve: bool, | ||
109 | file: FileId, | 110 | file: FileId, |
111 | resolve: AssistResolveStrategy, | ||
110 | buf: Vec<Assist>, | 112 | buf: Vec<Assist>, |
111 | allowed: Option<Vec<AssistKind>>, | 113 | allowed: Option<Vec<AssistKind>>, |
112 | } | 114 | } |
113 | 115 | ||
114 | impl Assists { | 116 | impl Assists { |
115 | pub(crate) fn new(ctx: &AssistContext, resolve: bool) -> Assists { | 117 | pub(crate) fn new(ctx: &AssistContext, resolve: AssistResolveStrategy) -> Assists { |
116 | Assists { | 118 | Assists { |
117 | resolve, | 119 | resolve, |
118 | file: ctx.frange.file_id, | 120 | file: ctx.frange.file_id, |
@@ -158,7 +160,7 @@ impl Assists { | |||
158 | } | 160 | } |
159 | 161 | ||
160 | fn add_impl(&mut self, mut assist: Assist, f: impl FnOnce(&mut AssistBuilder)) -> Option<()> { | 162 | fn add_impl(&mut self, mut assist: Assist, f: impl FnOnce(&mut AssistBuilder)) -> Option<()> { |
161 | let source_change = if self.resolve { | 163 | let source_change = if self.resolve.should_resolve(&assist.id) { |
162 | let mut builder = AssistBuilder::new(self.file); | 164 | let mut builder = AssistBuilder::new(self.file); |
163 | f(&mut builder); | 165 | f(&mut builder); |
164 | Some(builder.finish()) | 166 | Some(builder.finish()) |
@@ -185,7 +187,29 @@ pub(crate) struct AssistBuilder { | |||
185 | source_change: SourceChange, | 187 | source_change: SourceChange, |
186 | 188 | ||
187 | /// Maps the original, immutable `SyntaxNode` to a `clone_for_update` twin. | 189 | /// Maps the original, immutable `SyntaxNode` to a `clone_for_update` twin. |
188 | mutated_tree: Option<(SyntaxNode, SyntaxNode)>, | 190 | mutated_tree: Option<TreeMutator>, |
191 | } | ||
192 | |||
193 | pub(crate) struct TreeMutator { | ||
194 | immutable: SyntaxNode, | ||
195 | mutable_clone: SyntaxNode, | ||
196 | } | ||
197 | |||
198 | impl TreeMutator { | ||
199 | pub(crate) fn new(immutable: &SyntaxNode) -> TreeMutator { | ||
200 | let immutable = immutable.ancestors().last().unwrap(); | ||
201 | let mutable_clone = immutable.clone_for_update(); | ||
202 | TreeMutator { immutable, mutable_clone } | ||
203 | } | ||
204 | |||
205 | pub(crate) fn make_mut<N: AstNode>(&self, node: &N) -> N { | ||
206 | N::cast(self.make_syntax_mut(node.syntax())).unwrap() | ||
207 | } | ||
208 | |||
209 | pub(crate) fn make_syntax_mut(&self, node: &SyntaxNode) -> SyntaxNode { | ||
210 | let ptr = SyntaxNodePtr::new(node); | ||
211 | ptr.to_node(&self.mutable_clone) | ||
212 | } | ||
189 | } | 213 | } |
190 | 214 | ||
191 | impl AssistBuilder { | 215 | impl AssistBuilder { |
@@ -204,8 +228,8 @@ impl AssistBuilder { | |||
204 | } | 228 | } |
205 | 229 | ||
206 | fn commit(&mut self) { | 230 | fn commit(&mut self) { |
207 | if let Some((old, new)) = self.mutated_tree.take() { | 231 | if let Some(tm) = self.mutated_tree.take() { |
208 | algo::diff(&old, &new).into_text_edit(&mut self.edit) | 232 | algo::diff(&tm.immutable, &tm.mutable_clone).into_text_edit(&mut self.edit) |
209 | } | 233 | } |
210 | 234 | ||
211 | let edit = mem::take(&mut self.edit).finish(); | 235 | let edit = mem::take(&mut self.edit).finish(); |
@@ -228,16 +252,7 @@ impl AssistBuilder { | |||
228 | /// 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 |
229 | /// mutable state. | 253 | /// mutable state. |
230 | pub(crate) fn make_mut(&mut self, node: SyntaxNode) -> SyntaxNode { | 254 | pub(crate) fn make_mut(&mut self, node: SyntaxNode) -> SyntaxNode { |
231 | let root = &self | 255 | self.mutated_tree.get_or_insert_with(|| TreeMutator::new(&node)).make_syntax_mut(&node) |
232 | .mutated_tree | ||
233 | .get_or_insert_with(|| { | ||
234 | let immutable = node.ancestors().last().unwrap(); | ||
235 | let mutable = immutable.clone_for_update(); | ||
236 | (immutable, mutable) | ||
237 | }) | ||
238 | .1; | ||
239 | let ptr = SyntaxNodePtr::new(&&node); | ||
240 | ptr.to_node(root) | ||
241 | } | 256 | } |
242 | 257 | ||
243 | /// Remove specified `range` of text. | 258 | /// Remove specified `range` of text. |
@@ -275,12 +290,6 @@ impl AssistBuilder { | |||
275 | 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) { |
276 | 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) |
277 | } | 292 | } |
278 | pub(crate) fn rewrite(&mut self, rewriter: SyntaxRewriter) { | ||
279 | if let Some(node) = rewriter.rewrite_root() { | ||
280 | let new = rewriter.rewrite(&node); | ||
281 | algo::diff(&node, &new).into_text_edit(&mut self.edit); | ||
282 | } | ||
283 | } | ||
284 | 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>) { |
285 | let file_system_edit = | 294 | let file_system_edit = |
286 | 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/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..da8d245e5 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_ast_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 b30652a9d..6311afc1f 100644 --- a/crates/ide_assists/src/handlers/extract_function.rs +++ b/crates/ide_assists/src/handlers/extract_function.rs | |||
@@ -10,18 +10,18 @@ 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}, |
17 | AstNode, | 16 | AstNode, |
18 | }, | 17 | }, |
18 | ted, | ||
19 | SyntaxKind::{self, BLOCK_EXPR, BREAK_EXPR, COMMENT, PATH_EXPR, RETURN_EXPR}, | 19 | SyntaxKind::{self, BLOCK_EXPR, BREAK_EXPR, COMMENT, PATH_EXPR, RETURN_EXPR}, |
20 | SyntaxNode, SyntaxToken, TextRange, TextSize, TokenAtOffset, WalkEvent, T, | 20 | SyntaxNode, SyntaxToken, TextRange, TextSize, TokenAtOffset, WalkEvent, T, |
21 | }; | 21 | }; |
22 | 22 | ||
23 | use crate::{ | 23 | use crate::{ |
24 | assist_context::{AssistContext, Assists}, | 24 | assist_context::{AssistContext, Assists, TreeMutator}, |
25 | AssistId, | 25 | AssistId, |
26 | }; | 26 | }; |
27 | 27 | ||
@@ -956,10 +956,10 @@ fn format_replacement(ctx: &AssistContext, fun: &Function, indent: IndentLevel) | |||
956 | let args = fun.params.iter().map(|param| param.to_arg(ctx)); | 956 | let args = fun.params.iter().map(|param| param.to_arg(ctx)); |
957 | let args = make::arg_list(args); | 957 | let args = make::arg_list(args); |
958 | let call_expr = if fun.self_param.is_some() { | 958 | let call_expr = if fun.self_param.is_some() { |
959 | let self_arg = make::expr_path(make_path_from_text("self")); | 959 | let self_arg = make::expr_path(make::ext::ident_path("self")); |
960 | make::expr_method_call(self_arg, &fun.name, args) | 960 | make::expr_method_call(self_arg, &fun.name, args) |
961 | } else { | 961 | } else { |
962 | let func = make::expr_path(make_path_from_text(&fun.name)); | 962 | let func = make::expr_path(make::ext::ident_path(&fun.name)); |
963 | make::expr_call(func, args) | 963 | make::expr_call(func, args) |
964 | }; | 964 | }; |
965 | 965 | ||
@@ -1054,11 +1054,11 @@ impl FlowHandler { | |||
1054 | make::expr_if(condition, block, None) | 1054 | make::expr_if(condition, block, None) |
1055 | } | 1055 | } |
1056 | FlowHandler::IfOption { action } => { | 1056 | FlowHandler::IfOption { action } => { |
1057 | let path = make_path_from_text("Some"); | 1057 | let path = make::ext::ident_path("Some"); |
1058 | let value_pat = make::ident_pat(make::name("value")); | 1058 | let value_pat = make::ident_pat(make::name("value")); |
1059 | 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())); |
1060 | let cond = make::condition(call_expr, Some(pattern.into())); | 1060 | let cond = make::condition(call_expr, Some(pattern.into())); |
1061 | let value = make::expr_path(make_path_from_text("value")); | 1061 | let value = make::expr_path(make::ext::ident_path("value")); |
1062 | let action_expr = action.make_result_handler(Some(value)); | 1062 | let action_expr = action.make_result_handler(Some(value)); |
1063 | let action_stmt = make::expr_stmt(action_expr); | 1063 | let action_stmt = make::expr_stmt(action_expr); |
1064 | let then = make::block_expr(iter::once(action_stmt.into()), None); | 1064 | let then = make::block_expr(iter::once(action_stmt.into()), None); |
@@ -1068,14 +1068,14 @@ impl FlowHandler { | |||
1068 | let some_name = "value"; | 1068 | let some_name = "value"; |
1069 | 1069 | ||
1070 | let some_arm = { | 1070 | let some_arm = { |
1071 | let path = make_path_from_text("Some"); | 1071 | let path = make::ext::ident_path("Some"); |
1072 | let value_pat = make::ident_pat(make::name(some_name)); | 1072 | let value_pat = make::ident_pat(make::name(some_name)); |
1073 | 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())); |
1074 | let value = make::expr_path(make_path_from_text(some_name)); | 1074 | let value = make::expr_path(make::ext::ident_path(some_name)); |
1075 | make::match_arm(iter::once(pat.into()), value) | 1075 | make::match_arm(iter::once(pat.into()), value) |
1076 | }; | 1076 | }; |
1077 | let none_arm = { | 1077 | let none_arm = { |
1078 | let path = make_path_from_text("None"); | 1078 | let path = make::ext::ident_path("None"); |
1079 | let pat = make::path_pat(path); | 1079 | let pat = make::path_pat(path); |
1080 | make::match_arm(iter::once(pat), none.make_result_handler(None)) | 1080 | make::match_arm(iter::once(pat), none.make_result_handler(None)) |
1081 | }; | 1081 | }; |
@@ -1087,17 +1087,17 @@ impl FlowHandler { | |||
1087 | let err_name = "value"; | 1087 | let err_name = "value"; |
1088 | 1088 | ||
1089 | let ok_arm = { | 1089 | let ok_arm = { |
1090 | let path = make_path_from_text("Ok"); | 1090 | let path = make::ext::ident_path("Ok"); |
1091 | let value_pat = make::ident_pat(make::name(ok_name)); | 1091 | let value_pat = make::ident_pat(make::name(ok_name)); |
1092 | 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())); |
1093 | let value = make::expr_path(make_path_from_text(ok_name)); | 1093 | let value = make::expr_path(make::ext::ident_path(ok_name)); |
1094 | make::match_arm(iter::once(pat.into()), value) | 1094 | make::match_arm(iter::once(pat.into()), value) |
1095 | }; | 1095 | }; |
1096 | let err_arm = { | 1096 | let err_arm = { |
1097 | let path = make_path_from_text("Err"); | 1097 | let path = make::ext::ident_path("Err"); |
1098 | let value_pat = make::ident_pat(make::name(err_name)); | 1098 | let value_pat = make::ident_pat(make::name(err_name)); |
1099 | 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())); |
1100 | let value = make::expr_path(make_path_from_text(err_name)); | 1100 | let value = make::expr_path(make::ext::ident_path(err_name)); |
1101 | 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))) |
1102 | }; | 1102 | }; |
1103 | let arms = make::match_arm_list(vec![ok_arm, err_arm]); | 1103 | let arms = make::match_arm_list(vec![ok_arm, err_arm]); |
@@ -1107,13 +1107,9 @@ impl FlowHandler { | |||
1107 | } | 1107 | } |
1108 | } | 1108 | } |
1109 | 1109 | ||
1110 | fn make_path_from_text(text: &str) -> ast::Path { | ||
1111 | make::path_unqualified(make::path_segment(make::name_ref(text))) | ||
1112 | } | ||
1113 | |||
1114 | fn path_expr_from_local(ctx: &AssistContext, var: Local) -> ast::Expr { | 1110 | fn path_expr_from_local(ctx: &AssistContext, var: Local) -> ast::Expr { |
1115 | let name = var.name(ctx.db()).unwrap().to_string(); | 1111 | let name = var.name(ctx.db()).unwrap().to_string(); |
1116 | make::expr_path(make_path_from_text(&name)) | 1112 | make::expr_path(make::ext::ident_path(&name)) |
1117 | } | 1113 | } |
1118 | 1114 | ||
1119 | fn format_function( | 1115 | fn format_function( |
@@ -1179,7 +1175,7 @@ fn make_ret_ty(ctx: &AssistContext, module: hir::Module, fun: &Function) -> Opti | |||
1179 | fun_ty.make_ty(ctx, module) | 1175 | fun_ty.make_ty(ctx, module) |
1180 | } | 1176 | } |
1181 | FlowHandler::Try { kind: TryKind::Option } => { | 1177 | FlowHandler::Try { kind: TryKind::Option } => { |
1182 | 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)) |
1183 | } | 1179 | } |
1184 | FlowHandler::Try { kind: TryKind::Result { ty: parent_ret_ty } } => { | 1180 | FlowHandler::Try { kind: TryKind::Result { ty: parent_ret_ty } } => { |
1185 | let handler_ty = parent_ret_ty | 1181 | let handler_ty = parent_ret_ty |
@@ -1187,29 +1183,21 @@ fn make_ret_ty(ctx: &AssistContext, module: hir::Module, fun: &Function) -> Opti | |||
1187 | .nth(1) | 1183 | .nth(1) |
1188 | .map(|ty| make_ty(&ty, ctx, module)) | 1184 | .map(|ty| make_ty(&ty, ctx, module)) |
1189 | .unwrap_or_else(make::ty_unit); | 1185 | .unwrap_or_else(make::ty_unit); |
1190 | make::ty_generic( | 1186 | make::ext::ty_result(fun_ty.make_ty(ctx, module), handler_ty) |
1191 | make::name_ref("Result"), | ||
1192 | vec![fun_ty.make_ty(ctx, module), handler_ty], | ||
1193 | ) | ||
1194 | } | 1187 | } |
1195 | FlowHandler::If { .. } => make::ty("bool"), | 1188 | FlowHandler::If { .. } => make::ext::ty_bool(), |
1196 | FlowHandler::IfOption { action } => { | 1189 | FlowHandler::IfOption { action } => { |
1197 | let handler_ty = action | 1190 | let handler_ty = action |
1198 | .expr_ty(ctx) | 1191 | .expr_ty(ctx) |
1199 | .map(|ty| make_ty(&ty, ctx, module)) | 1192 | .map(|ty| make_ty(&ty, ctx, module)) |
1200 | .unwrap_or_else(make::ty_unit); | 1193 | .unwrap_or_else(make::ty_unit); |
1201 | make::ty_generic(make::name_ref("Option"), iter::once(handler_ty)) | 1194 | make::ext::ty_option(handler_ty) |
1202 | } | ||
1203 | FlowHandler::MatchOption { .. } => { | ||
1204 | make::ty_generic(make::name_ref("Option"), iter::once(fun_ty.make_ty(ctx, module))) | ||
1205 | } | 1195 | } |
1196 | FlowHandler::MatchOption { .. } => make::ext::ty_option(fun_ty.make_ty(ctx, module)), | ||
1206 | FlowHandler::MatchResult { err } => { | 1197 | FlowHandler::MatchResult { err } => { |
1207 | let handler_ty = | 1198 | let handler_ty = |
1208 | 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); |
1209 | make::ty_generic( | 1200 | make::ext::ty_result(fun_ty.make_ty(ctx, module), handler_ty) |
1210 | make::name_ref("Result"), | ||
1211 | vec![fun_ty.make_ty(ctx, module), handler_ty], | ||
1212 | ) | ||
1213 | } | 1201 | } |
1214 | }; | 1202 | }; |
1215 | Some(make::ret_type(ret_ty)) | 1203 | Some(make::ret_type(ret_ty)) |
@@ -1296,7 +1284,7 @@ fn make_body( | |||
1296 | TryKind::Option => "Some", | 1284 | TryKind::Option => "Some", |
1297 | TryKind::Result { .. } => "Ok", | 1285 | TryKind::Result { .. } => "Ok", |
1298 | }; | 1286 | }; |
1299 | let func = make::expr_path(make_path_from_text(constructor)); | 1287 | let func = make::expr_path(make::ext::ident_path(constructor)); |
1300 | let args = make::arg_list(iter::once(tail_expr)); | 1288 | let args = make::arg_list(iter::once(tail_expr)); |
1301 | make::expr_call(func, args) | 1289 | make::expr_call(func, args) |
1302 | }) | 1290 | }) |
@@ -1306,16 +1294,16 @@ fn make_body( | |||
1306 | with_tail_expr(block, lit_false.into()) | 1294 | with_tail_expr(block, lit_false.into()) |
1307 | } | 1295 | } |
1308 | FlowHandler::IfOption { .. } => { | 1296 | FlowHandler::IfOption { .. } => { |
1309 | let none = make::expr_path(make_path_from_text("None")); | 1297 | let none = make::expr_path(make::ext::ident_path("None")); |
1310 | with_tail_expr(block, none) | 1298 | with_tail_expr(block, none) |
1311 | } | 1299 | } |
1312 | FlowHandler::MatchOption { .. } => map_tail_expr(block, |tail_expr| { | 1300 | FlowHandler::MatchOption { .. } => map_tail_expr(block, |tail_expr| { |
1313 | let some = make::expr_path(make_path_from_text("Some")); | 1301 | let some = make::expr_path(make::ext::ident_path("Some")); |
1314 | let args = make::arg_list(iter::once(tail_expr)); | 1302 | let args = make::arg_list(iter::once(tail_expr)); |
1315 | make::expr_call(some, args) | 1303 | make::expr_call(some, args) |
1316 | }), | 1304 | }), |
1317 | FlowHandler::MatchResult { .. } => map_tail_expr(block, |tail_expr| { | 1305 | FlowHandler::MatchResult { .. } => map_tail_expr(block, |tail_expr| { |
1318 | let ok = make::expr_path(make_path_from_text("Ok")); | 1306 | let ok = make::expr_path(make::ext::ident_path("Ok")); |
1319 | let args = make::arg_list(iter::once(tail_expr)); | 1307 | let args = make::arg_list(iter::once(tail_expr)); |
1320 | make::expr_call(ok, args) | 1308 | make::expr_call(ok, args) |
1321 | }), | 1309 | }), |
@@ -1361,12 +1349,16 @@ fn rewrite_body_segment( | |||
1361 | syntax: &SyntaxNode, | 1349 | syntax: &SyntaxNode, |
1362 | ) -> SyntaxNode { | 1350 | ) -> SyntaxNode { |
1363 | let syntax = fix_param_usages(ctx, params, syntax); | 1351 | let syntax = fix_param_usages(ctx, params, syntax); |
1364 | update_external_control_flow(handler, &syntax) | 1352 | update_external_control_flow(handler, &syntax); |
1353 | syntax | ||
1365 | } | 1354 | } |
1366 | 1355 | ||
1367 | /// change all usages to account for added `&`/`&mut` for some params | 1356 | /// change all usages to account for added `&`/`&mut` for some params |
1368 | fn fix_param_usages(ctx: &AssistContext, params: &[Param], syntax: &SyntaxNode) -> SyntaxNode { | 1357 | fn fix_param_usages(ctx: &AssistContext, params: &[Param], syntax: &SyntaxNode) -> SyntaxNode { |
1369 | let mut rewriter = SyntaxRewriter::default(); | 1358 | let mut usages_for_param: Vec<(&Param, Vec<ast::Expr>)> = Vec::new(); |
1359 | |||
1360 | let tm = TreeMutator::new(syntax); | ||
1361 | |||
1370 | for param in params { | 1362 | for param in params { |
1371 | if !param.kind().is_ref() { | 1363 | if !param.kind().is_ref() { |
1372 | continue; | 1364 | continue; |
@@ -1376,101 +1368,100 @@ fn fix_param_usages(ctx: &AssistContext, params: &[Param], syntax: &SyntaxNode) | |||
1376 | let usages = usages | 1368 | let usages = usages |
1377 | .iter() | 1369 | .iter() |
1378 | .filter(|reference| syntax.text_range().contains_range(reference.range)) | 1370 | .filter(|reference| syntax.text_range().contains_range(reference.range)) |
1379 | .filter_map(|reference| path_element_of_reference(syntax, reference)); | 1371 | .filter_map(|reference| path_element_of_reference(syntax, reference)) |
1380 | for path in usages { | 1372 | .map(|expr| tm.make_mut(&expr)); |
1381 | match path.syntax().ancestors().skip(1).find_map(ast::Expr::cast) { | 1373 | |
1374 | usages_for_param.push((param, usages.collect())); | ||
1375 | } | ||
1376 | |||
1377 | let res = tm.make_syntax_mut(syntax); | ||
1378 | |||
1379 | for (param, usages) in usages_for_param { | ||
1380 | for usage in usages { | ||
1381 | match usage.syntax().ancestors().skip(1).find_map(ast::Expr::cast) { | ||
1382 | Some(ast::Expr::MethodCallExpr(_)) | Some(ast::Expr::FieldExpr(_)) => { | 1382 | Some(ast::Expr::MethodCallExpr(_)) | Some(ast::Expr::FieldExpr(_)) => { |
1383 | // do nothing | 1383 | // do nothing |
1384 | } | 1384 | } |
1385 | Some(ast::Expr::RefExpr(node)) | 1385 | Some(ast::Expr::RefExpr(node)) |
1386 | if param.kind() == ParamKind::MutRef && node.mut_token().is_some() => | 1386 | if param.kind() == ParamKind::MutRef && node.mut_token().is_some() => |
1387 | { | 1387 | { |
1388 | rewriter.replace_ast(&node.clone().into(), &node.expr().unwrap()); | 1388 | ted::replace(node.syntax(), node.expr().unwrap().syntax()); |
1389 | } | 1389 | } |
1390 | Some(ast::Expr::RefExpr(node)) | 1390 | Some(ast::Expr::RefExpr(node)) |
1391 | if param.kind() == ParamKind::SharedRef && node.mut_token().is_none() => | 1391 | if param.kind() == ParamKind::SharedRef && node.mut_token().is_none() => |
1392 | { | 1392 | { |
1393 | rewriter.replace_ast(&node.clone().into(), &node.expr().unwrap()); | 1393 | ted::replace(node.syntax(), node.expr().unwrap().syntax()); |
1394 | } | 1394 | } |
1395 | Some(_) | None => { | 1395 | Some(_) | None => { |
1396 | rewriter.replace_ast(&path, &make::expr_prefix(T![*], path.clone())); | 1396 | let p = &make::expr_prefix(T![*], usage.clone()).clone_for_update(); |
1397 | ted::replace(usage.syntax(), p.syntax()) | ||
1397 | } | 1398 | } |
1398 | }; | 1399 | } |
1399 | } | 1400 | } |
1400 | } | 1401 | } |
1401 | 1402 | ||
1402 | rewriter.rewrite(syntax) | 1403 | res |
1403 | } | 1404 | } |
1404 | 1405 | ||
1405 | fn update_external_control_flow(handler: &FlowHandler, syntax: &SyntaxNode) -> SyntaxNode { | 1406 | fn update_external_control_flow(handler: &FlowHandler, syntax: &SyntaxNode) { |
1406 | let mut rewriter = SyntaxRewriter::default(); | ||
1407 | |||
1408 | let mut nested_loop = None; | 1407 | let mut nested_loop = None; |
1409 | let mut nested_scope = None; | 1408 | let mut nested_scope = None; |
1410 | for event in syntax.preorder() { | 1409 | for event in syntax.preorder() { |
1411 | let node = match event { | 1410 | match event { |
1412 | WalkEvent::Enter(e) => { | 1411 | WalkEvent::Enter(e) => match e.kind() { |
1413 | match e.kind() { | 1412 | SyntaxKind::LOOP_EXPR | SyntaxKind::WHILE_EXPR | SyntaxKind::FOR_EXPR => { |
1414 | SyntaxKind::LOOP_EXPR | SyntaxKind::WHILE_EXPR | SyntaxKind::FOR_EXPR => { | 1413 | if nested_loop.is_none() { |
1415 | if nested_loop.is_none() { | 1414 | nested_loop = Some(e.clone()); |
1416 | nested_loop = Some(e.clone()); | ||
1417 | } | ||
1418 | } | 1415 | } |
1419 | SyntaxKind::FN | 1416 | } |
1420 | | SyntaxKind::CONST | 1417 | SyntaxKind::FN |
1421 | | SyntaxKind::STATIC | 1418 | | SyntaxKind::CONST |
1422 | | SyntaxKind::IMPL | 1419 | | SyntaxKind::STATIC |
1423 | | SyntaxKind::MODULE => { | 1420 | | SyntaxKind::IMPL |
1424 | if nested_scope.is_none() { | 1421 | | SyntaxKind::MODULE => { |
1425 | nested_scope = Some(e.clone()); | 1422 | if nested_scope.is_none() { |
1426 | } | 1423 | nested_scope = Some(e.clone()); |
1427 | } | 1424 | } |
1428 | _ => {} | ||
1429 | } | 1425 | } |
1430 | e | 1426 | _ => {} |
1431 | } | 1427 | }, |
1432 | 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 | |||
1433 | if nested_loop.as_ref() == Some(&e) { | 1456 | if nested_loop.as_ref() == Some(&e) { |
1434 | nested_loop = None; | 1457 | nested_loop = None; |
1435 | } | 1458 | } |
1436 | if nested_scope.as_ref() == Some(&e) { | 1459 | if nested_scope.as_ref() == Some(&e) { |
1437 | nested_scope = None; | 1460 | nested_scope = None; |
1438 | } | 1461 | } |
1439 | continue; | ||
1440 | } | 1462 | } |
1441 | }; | 1463 | }; |
1442 | if nested_scope.is_some() { | ||
1443 | continue; | ||
1444 | } | ||
1445 | let expr = match ast::Expr::cast(node) { | ||
1446 | Some(e) => e, | ||
1447 | None => continue, | ||
1448 | }; | ||
1449 | match expr { | ||
1450 | ast::Expr::ReturnExpr(return_expr) if nested_scope.is_none() => { | ||
1451 | let expr = return_expr.expr(); | ||
1452 | if let Some(replacement) = make_rewritten_flow(handler, expr) { | ||
1453 | rewriter.replace_ast(&return_expr.into(), &replacement); | ||
1454 | } | ||
1455 | } | ||
1456 | ast::Expr::BreakExpr(break_expr) if nested_loop.is_none() => { | ||
1457 | let expr = break_expr.expr(); | ||
1458 | if let Some(replacement) = make_rewritten_flow(handler, expr) { | ||
1459 | rewriter.replace_ast(&break_expr.into(), &replacement); | ||
1460 | } | ||
1461 | } | ||
1462 | ast::Expr::ContinueExpr(continue_expr) if nested_loop.is_none() => { | ||
1463 | if let Some(replacement) = make_rewritten_flow(handler, None) { | ||
1464 | rewriter.replace_ast(&continue_expr.into(), &replacement); | ||
1465 | } | ||
1466 | } | ||
1467 | _ => { | ||
1468 | // do nothing | ||
1469 | } | ||
1470 | } | ||
1471 | } | 1464 | } |
1472 | |||
1473 | rewriter.rewrite(syntax) | ||
1474 | } | 1465 | } |
1475 | 1466 | ||
1476 | 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> { |
@@ -1480,16 +1471,16 @@ fn make_rewritten_flow(handler: &FlowHandler, arg_expr: Option<ast::Expr>) -> Op | |||
1480 | FlowHandler::IfOption { .. } => { | 1471 | FlowHandler::IfOption { .. } => { |
1481 | 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())); |
1482 | let args = make::arg_list(iter::once(expr)); | 1473 | let args = make::arg_list(iter::once(expr)); |
1483 | 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) |
1484 | } | 1475 | } |
1485 | FlowHandler::MatchOption { .. } => make::expr_path(make_path_from_text("None")), | 1476 | FlowHandler::MatchOption { .. } => make::expr_path(make::ext::ident_path("None")), |
1486 | FlowHandler::MatchResult { .. } => { | 1477 | FlowHandler::MatchResult { .. } => { |
1487 | 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())); |
1488 | let args = make::arg_list(iter::once(expr)); | 1479 | let args = make::arg_list(iter::once(expr)); |
1489 | 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) |
1490 | } | 1481 | } |
1491 | }; | 1482 | }; |
1492 | Some(make::expr_return(Some(value))) | 1483 | Some(make::expr_return(Some(value)).clone_for_update()) |
1493 | } | 1484 | } |
1494 | 1485 | ||
1495 | #[cfg(test)] | 1486 | #[cfg(test)] |
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/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/introduce_named_lifetime.rs b/crates/ide_assists/src/handlers/introduce_named_lifetime.rs index 9f4f71d6c..68bc15120 100644 --- a/crates/ide_assists/src/handlers/introduce_named_lifetime.rs +++ b/crates/ide_assists/src/handlers/introduce_named_lifetime.rs | |||
@@ -89,14 +89,12 @@ fn generate_fn_def_assist( | |||
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 | ||
@@ -112,11 +110,10 @@ fn generate_impl_def_assist( | |||
112 | let impl_def = builder.make_ast_mut(impl_def); | 110 | let impl_def = builder.make_ast_mut(impl_def); |
113 | let lifetime = builder.make_ast_mut(lifetime); | 111 | let lifetime = builder.make_ast_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 { |
diff --git a/crates/ide_assists/src/handlers/merge_imports.rs b/crates/ide_assists/src/handlers/merge_imports.rs index add7b8e37..3cd090737 100644 --- a/crates/ide_assists/src/handlers/merge_imports.rs +++ b/crates/ide_assists/src/handlers/merge_imports.rs | |||
@@ -27,14 +27,14 @@ pub(crate) fn merge_imports(acc: &mut Assists, ctx: &AssistContext) -> Option<() | |||
27 | if let Some(use_item) = tree.syntax().parent().and_then(ast::Use::cast) { | 27 | if let Some(use_item) = tree.syntax().parent().and_then(ast::Use::cast) { |
28 | let (merged, to_remove) = | 28 | let (merged, to_remove) = |
29 | next_prev().filter_map(|dir| neighbor(&use_item, dir)).find_map(|use_item2| { | 29 | next_prev().filter_map(|dir| neighbor(&use_item, dir)).find_map(|use_item2| { |
30 | try_merge_imports(&use_item, &use_item2, MergeBehavior::Full).zip(Some(use_item2)) | 30 | try_merge_imports(&use_item, &use_item2, MergeBehavior::Crate).zip(Some(use_item2)) |
31 | })?; | 31 | })?; |
32 | 32 | ||
33 | imports = Some((use_item, merged, to_remove)); | 33 | imports = Some((use_item, merged, to_remove)); |
34 | } else { | 34 | } else { |
35 | let (merged, to_remove) = | 35 | let (merged, to_remove) = |
36 | next_prev().filter_map(|dir| neighbor(&tree, dir)).find_map(|use_tree| { | 36 | next_prev().filter_map(|dir| neighbor(&tree, dir)).find_map(|use_tree| { |
37 | try_merge_trees(&tree, &use_tree, MergeBehavior::Full).zip(Some(use_tree)) | 37 | try_merge_trees(&tree, &use_tree, MergeBehavior::Crate).zip(Some(use_tree)) |
38 | })?; | 38 | })?; |
39 | 39 | ||
40 | uses = Some((tree.clone(), merged, to_remove)) | 40 | uses = Some((tree.clone(), merged, to_remove)) |
diff --git a/crates/ide_assists/src/handlers/move_bounds.rs b/crates/ide_assists/src/handlers/move_bounds.rs index 011a28d44..fa3f76609 100644 --- a/crates/ide_assists/src/handlers/move_bounds.rs +++ b/crates/ide_assists/src/handlers/move_bounds.rs | |||
@@ -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/pull_assignment_up.rs b/crates/ide_assists/src/handlers/pull_assignment_up.rs index 04bae4e58..3128faa68 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_ast_mut(stmt), rhs.clone_for_update())) | ||
78 | .collect(); | ||
79 | |||
80 | let tgt = edit.make_ast_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/reorder_impl.rs b/crates/ide_assists/src/handlers/reorder_impl.rs index 72d889248..54a9a468e 100644 --- a/crates/ide_assists/src/handlers/reorder_impl.rs +++ b/crates/ide_assists/src/handlers/reorder_impl.rs | |||
@@ -79,9 +79,12 @@ 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 | methods.into_iter().zip(sorted).for_each(|(old, new)| { | 82 | let methods = |
83 | ted::replace(builder.make_ast_mut(old).syntax(), new.clone_for_update().syntax()) | 83 | methods.into_iter().map(|fn_| builder.make_ast_mut(fn_)).collect::<Vec<_>>(); |
84 | }); | 84 | methods |
85 | .into_iter() | ||
86 | .zip(sorted) | ||
87 | .for_each(|(old, new)| ted::replace(old.syntax(), new.clone_for_update().syntax())); | ||
85 | }, | 88 | }, |
86 | ) | 89 | ) |
87 | } | 90 | } |
@@ -160,7 +163,7 @@ $0impl Bar for Foo {} | |||
160 | } | 163 | } |
161 | 164 | ||
162 | #[test] | 165 | #[test] |
163 | fn reorder_impl_trait_methods() { | 166 | fn reorder_impl_trait_functions() { |
164 | check_assist( | 167 | check_assist( |
165 | reorder_impl, | 168 | reorder_impl, |
166 | r#" | 169 | r#" |
@@ -197,4 +200,33 @@ impl Bar for Foo { | |||
197 | "#, | 200 | "#, |
198 | ) | 201 | ) |
199 | } | 202 | } |
203 | |||
204 | #[test] | ||
205 | fn reorder_impl_trait_methods_uneven_ident_lengths() { | ||
206 | check_assist( | ||
207 | reorder_impl, | ||
208 | r#" | ||
209 | trait Bar { | ||
210 | fn foo(&mut self) {} | ||
211 | fn fooo(&mut self) {} | ||
212 | } | ||
213 | |||
214 | struct Foo; | ||
215 | impl Bar for Foo { | ||
216 | fn fooo(&mut self) {} | ||
217 | fn foo(&mut self) {$0} | ||
218 | }"#, | ||
219 | r#" | ||
220 | trait Bar { | ||
221 | fn foo(&mut self) {} | ||
222 | fn fooo(&mut self) {} | ||
223 | } | ||
224 | |||
225 | struct Foo; | ||
226 | impl Bar for Foo { | ||
227 | fn foo(&mut self) {} | ||
228 | fn fooo(&mut self) {} | ||
229 | }"#, | ||
230 | ) | ||
231 | } | ||
200 | } | 232 | } |
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..15420aedf 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_ast_mut(impl_trait_type); |
36 | let fn_ = edit.make_ast_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 |