aboutsummaryrefslogtreecommitdiff
path: root/crates
diff options
context:
space:
mode:
Diffstat (limited to 'crates')
-rw-r--r--crates/hir/src/semantics.rs7
-rw-r--r--crates/hir/src/source_analyzer.rs9
-rw-r--r--crates/hir_def/src/body.rs26
-rw-r--r--crates/hir_def/src/body/lower.rs34
-rw-r--r--crates/hir_def/src/item_tree.rs5
-rw-r--r--crates/hir_def/src/lib.rs1
-rw-r--r--crates/hir_def/src/path.rs7
-rw-r--r--crates/hir_def/src/path/lower.rs17
-rw-r--r--crates/hir_def/src/type_ref.rs18
-rw-r--r--crates/hir_expand/src/db.rs1
-rw-r--r--crates/hir_expand/src/eager.rs1
-rw-r--r--crates/hir_ty/src/display.rs15
-rw-r--r--crates/hir_ty/src/lower.rs69
-rw-r--r--crates/hir_ty/src/tests/macros.rs199
-rw-r--r--crates/ide_assists/src/handlers/extract_function.rs64
-rw-r--r--crates/ide_assists/src/handlers/introduce_named_lifetime.rs112
-rw-r--r--crates/parser/src/grammar/types.rs10
-rw-r--r--crates/syntax/src/ast/edit_in_place.rs153
-rw-r--r--crates/syntax/src/ted.rs7
-rw-r--r--crates/syntax/test_data/parser/inline/ok/0117_macro_call_type.rast48
20 files changed, 689 insertions, 114 deletions
diff --git a/crates/hir/src/semantics.rs b/crates/hir/src/semantics.rs
index 7955bf0b5..62500602a 100644
--- a/crates/hir/src/semantics.rs
+++ b/crates/hir/src/semantics.rs
@@ -6,10 +6,11 @@ use std::{cell::RefCell, fmt, iter::successors};
6 6
7use base_db::{FileId, FileRange}; 7use base_db::{FileId, FileRange};
8use hir_def::{ 8use hir_def::{
9 body,
9 resolver::{self, HasResolver, Resolver, TypeNs}, 10 resolver::{self, HasResolver, Resolver, TypeNs},
10 AsMacroCall, FunctionId, TraitId, VariantId, 11 AsMacroCall, FunctionId, TraitId, VariantId,
11}; 12};
12use hir_expand::{hygiene::Hygiene, name::AsName, ExpansionInfo}; 13use hir_expand::{name::AsName, ExpansionInfo};
13use hir_ty::associated_type_shorthand_candidates; 14use hir_ty::associated_type_shorthand_candidates;
14use itertools::Itertools; 15use itertools::Itertools;
15use rustc_hash::{FxHashMap, FxHashSet}; 16use rustc_hash::{FxHashMap, FxHashSet};
@@ -853,8 +854,8 @@ impl<'a> SemanticsScope<'a> {
853 /// Resolve a path as-if it was written at the given scope. This is 854 /// Resolve a path as-if it was written at the given scope. This is
854 /// necessary a heuristic, as it doesn't take hygiene into account. 855 /// necessary a heuristic, as it doesn't take hygiene into account.
855 pub fn speculative_resolve(&self, path: &ast::Path) -> Option<PathResolution> { 856 pub fn speculative_resolve(&self, path: &ast::Path) -> Option<PathResolution> {
856 let hygiene = Hygiene::new(self.db.upcast(), self.file_id); 857 let ctx = body::LowerCtx::new(self.db.upcast(), self.file_id);
857 let path = Path::from_src(path.clone(), &hygiene)?; 858 let path = Path::from_src(path.clone(), &ctx)?;
858 resolve_hir_path(self.db, &self.resolver, &path) 859 resolve_hir_path(self.db, &self.resolver, &path)
859 } 860 }
860} 861}
diff --git a/crates/hir/src/source_analyzer.rs b/crates/hir/src/source_analyzer.rs
index 847d2537d..0895bd6f1 100644
--- a/crates/hir/src/source_analyzer.rs
+++ b/crates/hir/src/source_analyzer.rs
@@ -9,6 +9,7 @@ use std::{iter::once, sync::Arc};
9 9
10use hir_def::{ 10use hir_def::{
11 body::{ 11 body::{
12 self,
12 scope::{ExprScopes, ScopeId}, 13 scope::{ExprScopes, ScopeId},
13 Body, BodySourceMap, 14 Body, BodySourceMap,
14 }, 15 },
@@ -202,8 +203,8 @@ impl SourceAnalyzer {
202 db: &dyn HirDatabase, 203 db: &dyn HirDatabase,
203 macro_call: InFile<&ast::MacroCall>, 204 macro_call: InFile<&ast::MacroCall>,
204 ) -> Option<MacroDef> { 205 ) -> Option<MacroDef> {
205 let hygiene = Hygiene::new(db.upcast(), macro_call.file_id); 206 let ctx = body::LowerCtx::new(db.upcast(), macro_call.file_id);
206 let path = macro_call.value.path().and_then(|ast| Path::from_src(ast, &hygiene))?; 207 let path = macro_call.value.path().and_then(|ast| Path::from_src(ast, &ctx))?;
207 self.resolver.resolve_path_as_macro(db.upcast(), path.mod_path()).map(|it| it.into()) 208 self.resolver.resolve_path_as_macro(db.upcast(), path.mod_path()).map(|it| it.into())
208 } 209 }
209 210
@@ -281,7 +282,9 @@ impl SourceAnalyzer {
281 } 282 }
282 283
283 // This must be a normal source file rather than macro file. 284 // This must be a normal source file rather than macro file.
284 let hir_path = Path::from_src(path.clone(), &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);
287 let hir_path = Path::from_src(path.clone(), &ctx)?;
285 288
286 // 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
287 // trying to resolve foo::bar. 290 // trying to resolve foo::bar.
diff --git a/crates/hir_def/src/body.rs b/crates/hir_def/src/body.rs
index 96b959967..131f424cc 100644
--- a/crates/hir_def/src/body.rs
+++ b/crates/hir_def/src/body.rs
@@ -21,7 +21,7 @@ use profile::Count;
21use rustc_hash::FxHashMap; 21use rustc_hash::FxHashMap;
22use syntax::{ast, AstNode, AstPtr}; 22use syntax::{ast, AstNode, AstPtr};
23 23
24pub(crate) use lower::LowerCtx; 24pub use lower::LowerCtx;
25 25
26use crate::{ 26use crate::{
27 attr::{Attrs, RawAttrs}, 27 attr::{Attrs, RawAttrs},
@@ -37,13 +37,15 @@ use crate::{
37 37
38/// A subset of Expander that only deals with cfg attributes. We only need it to 38/// A subset of Expander that only deals with cfg attributes. We only need it to
39/// avoid cyclic queries in crate def map during enum processing. 39/// avoid cyclic queries in crate def map during enum processing.
40#[derive(Debug)]
40pub(crate) struct CfgExpander { 41pub(crate) struct CfgExpander {
41 cfg_options: CfgOptions, 42 cfg_options: CfgOptions,
42 hygiene: Hygiene, 43 hygiene: Hygiene,
43 krate: CrateId, 44 krate: CrateId,
44} 45}
45 46
46pub(crate) struct Expander { 47#[derive(Debug)]
48pub struct Expander {
47 cfg_expander: CfgExpander, 49 cfg_expander: CfgExpander,
48 def_map: Arc<DefMap>, 50 def_map: Arc<DefMap>,
49 current_file_id: HirFileId, 51 current_file_id: HirFileId,
@@ -80,11 +82,7 @@ impl CfgExpander {
80} 82}
81 83
82impl Expander { 84impl Expander {
83 pub(crate) fn new( 85 pub fn new(db: &dyn DefDatabase, current_file_id: HirFileId, module: ModuleId) -> Expander {
84 db: &dyn DefDatabase,
85 current_file_id: HirFileId,
86 module: ModuleId,
87 ) -> Expander {
88 let cfg_expander = CfgExpander::new(db, current_file_id, module.krate); 86 let cfg_expander = CfgExpander::new(db, current_file_id, module.krate);
89 let def_map = module.def_map(db); 87 let def_map = module.def_map(db);
90 let ast_id_map = db.ast_id_map(current_file_id); 88 let ast_id_map = db.ast_id_map(current_file_id);
@@ -98,7 +96,7 @@ impl Expander {
98 } 96 }
99 } 97 }
100 98
101 pub(crate) fn enter_expand<T: ast::AstNode>( 99 pub fn enter_expand<T: ast::AstNode>(
102 &mut self, 100 &mut self,
103 db: &dyn DefDatabase, 101 db: &dyn DefDatabase,
104 macro_call: ast::MacroCall, 102 macro_call: ast::MacroCall,
@@ -170,7 +168,7 @@ impl Expander {
170 Ok(ExpandResult { value: Some((mark, node)), err }) 168 Ok(ExpandResult { value: Some((mark, node)), err })
171 } 169 }
172 170
173 pub(crate) fn exit(&mut self, db: &dyn DefDatabase, mut mark: Mark) { 171 pub fn exit(&mut self, db: &dyn DefDatabase, mut mark: Mark) {
174 self.cfg_expander.hygiene = Hygiene::new(db.upcast(), mark.file_id); 172 self.cfg_expander.hygiene = Hygiene::new(db.upcast(), mark.file_id);
175 self.current_file_id = mark.file_id; 173 self.current_file_id = mark.file_id;
176 self.ast_id_map = mem::take(&mut mark.ast_id_map); 174 self.ast_id_map = mem::take(&mut mark.ast_id_map);
@@ -190,8 +188,13 @@ impl Expander {
190 &self.cfg_expander.cfg_options 188 &self.cfg_expander.cfg_options
191 } 189 }
192 190
191 pub fn current_file_id(&self) -> HirFileId {
192 self.current_file_id
193 }
194
193 fn parse_path(&mut self, path: ast::Path) -> Option<Path> { 195 fn parse_path(&mut self, path: ast::Path) -> Option<Path> {
194 Path::from_src(path, &self.cfg_expander.hygiene) 196 let ctx = LowerCtx::with_hygiene(&self.cfg_expander.hygiene);
197 Path::from_src(path, &ctx)
195 } 198 }
196 199
197 fn resolve_path_as_macro(&self, db: &dyn DefDatabase, path: &ModPath) -> Option<MacroDefId> { 200 fn resolve_path_as_macro(&self, db: &dyn DefDatabase, path: &ModPath) -> Option<MacroDefId> {
@@ -204,7 +207,8 @@ impl Expander {
204 } 207 }
205} 208}
206 209
207pub(crate) struct Mark { 210#[derive(Debug)]
211pub struct Mark {
208 file_id: HirFileId, 212 file_id: HirFileId,
209 ast_id_map: Arc<AstIdMap>, 213 ast_id_map: Arc<AstIdMap>,
210 bomb: DropBomb, 214 bomb: DropBomb,
diff --git a/crates/hir_def/src/body/lower.rs b/crates/hir_def/src/body/lower.rs
index c0b0b7841..c11da30d2 100644
--- a/crates/hir_def/src/body/lower.rs
+++ b/crates/hir_def/src/body/lower.rs
@@ -1,10 +1,11 @@
1//! Transforms `ast::Expr` into an equivalent `hir_def::expr::Expr` 1//! Transforms `ast::Expr` into an equivalent `hir_def::expr::Expr`
2//! representation. 2//! representation.
3 3
4use std::mem; 4use std::{mem, sync::Arc};
5 5
6use either::Either; 6use either::Either;
7use hir_expand::{ 7use hir_expand::{
8 ast_id_map::{AstIdMap, FileAstId},
8 hygiene::Hygiene, 9 hygiene::Hygiene,
9 name::{name, AsName, Name}, 10 name::{name, AsName, Name},
10 ExpandError, HirFileId, 11 ExpandError, HirFileId,
@@ -39,20 +40,39 @@ use crate::{
39 40
40use super::{diagnostics::BodyDiagnostic, ExprSource, PatSource}; 41use super::{diagnostics::BodyDiagnostic, ExprSource, PatSource};
41 42
42pub(crate) struct LowerCtx { 43pub struct LowerCtx {
43 hygiene: Hygiene, 44 hygiene: Hygiene,
45 file_id: Option<HirFileId>,
46 source_ast_id_map: Option<Arc<AstIdMap>>,
44} 47}
45 48
46impl LowerCtx { 49impl LowerCtx {
47 pub(crate) fn new(db: &dyn DefDatabase, file_id: HirFileId) -> Self { 50 pub fn new(db: &dyn DefDatabase, file_id: HirFileId) -> Self {
48 LowerCtx { hygiene: Hygiene::new(db.upcast(), file_id) } 51 LowerCtx {
52 hygiene: Hygiene::new(db.upcast(), file_id),
53 file_id: Some(file_id),
54 source_ast_id_map: Some(db.ast_id_map(file_id)),
55 }
56 }
57
58 pub fn with_hygiene(hygiene: &Hygiene) -> Self {
59 LowerCtx { hygiene: hygiene.clone(), file_id: None, source_ast_id_map: None }
60 }
61
62 pub(crate) fn hygiene(&self) -> &Hygiene {
63 &self.hygiene
49 } 64 }
50 pub(crate) fn with_hygiene(hygiene: &Hygiene) -> Self { 65
51 LowerCtx { hygiene: hygiene.clone() } 66 pub(crate) fn file_id(&self) -> HirFileId {
67 self.file_id.unwrap()
52 } 68 }
53 69
54 pub(crate) fn lower_path(&self, ast: ast::Path) -> Option<Path> { 70 pub(crate) fn lower_path(&self, ast: ast::Path) -> Option<Path> {
55 Path::from_src(ast, &self.hygiene) 71 Path::from_src(ast, self)
72 }
73
74 pub(crate) fn ast_id<N: AstNode>(&self, item: &N) -> Option<FileAstId<N>> {
75 self.source_ast_id_map.as_ref().map(|ast_id_map| ast_id_map.ast_id(item))
56 } 76 }
57} 77}
58 78
diff --git a/crates/hir_def/src/item_tree.rs b/crates/hir_def/src/item_tree.rs
index 94e08f835..16a94a058 100644
--- a/crates/hir_def/src/item_tree.rs
+++ b/crates/hir_def/src/item_tree.rs
@@ -104,6 +104,11 @@ impl ItemTree {
104 // items and expanded during block DefMap computation 104 // items and expanded during block DefMap computation
105 return Default::default(); 105 return Default::default();
106 }, 106 },
107 ast::Type(ty) => {
108 // Types can contain inner items. We return an empty item tree in this case, but
109 // still need to collect inner items.
110 ctx.lower_inner_items(ty.syntax())
111 },
107 ast::Expr(e) => { 112 ast::Expr(e) => {
108 // Macros can expand to expressions. We return an empty item tree in this case, but 113 // Macros can expand to expressions. We return an empty item tree in this case, but
109 // still need to collect inner items. 114 // still need to collect inner items.
diff --git a/crates/hir_def/src/lib.rs b/crates/hir_def/src/lib.rs
index 5ac1670b5..25694f037 100644
--- a/crates/hir_def/src/lib.rs
+++ b/crates/hir_def/src/lib.rs
@@ -688,6 +688,7 @@ impl<T: ast::AstNode> AstIdWithPath<T> {
688 } 688 }
689} 689}
690 690
691#[derive(Debug)]
691pub struct UnresolvedMacro { 692pub struct UnresolvedMacro {
692 pub path: ModPath, 693 pub path: ModPath,
693} 694}
diff --git a/crates/hir_def/src/path.rs b/crates/hir_def/src/path.rs
index b528ff8ba..509f77850 100644
--- a/crates/hir_def/src/path.rs
+++ b/crates/hir_def/src/path.rs
@@ -48,7 +48,8 @@ pub enum ImportAlias {
48 48
49impl ModPath { 49impl ModPath {
50 pub fn from_src(path: ast::Path, hygiene: &Hygiene) -> Option<ModPath> { 50 pub fn from_src(path: ast::Path, hygiene: &Hygiene) -> Option<ModPath> {
51 lower::lower_path(path, hygiene).map(|it| (*it.mod_path).clone()) 51 let ctx = LowerCtx::with_hygiene(hygiene);
52 lower::lower_path(path, &ctx).map(|it| (*it.mod_path).clone())
52 } 53 }
53 54
54 pub fn from_segments(kind: PathKind, segments: impl IntoIterator<Item = Name>) -> ModPath { 55 pub fn from_segments(kind: PathKind, segments: impl IntoIterator<Item = Name>) -> ModPath {
@@ -167,8 +168,8 @@ pub enum GenericArg {
167impl Path { 168impl Path {
168 /// Converts an `ast::Path` to `Path`. Works with use trees. 169 /// Converts an `ast::Path` to `Path`. Works with use trees.
169 /// It correctly handles `$crate` based path from macro call. 170 /// It correctly handles `$crate` based path from macro call.
170 pub fn from_src(path: ast::Path, hygiene: &Hygiene) -> Option<Path> { 171 pub fn from_src(path: ast::Path, ctx: &LowerCtx) -> Option<Path> {
171 lower::lower_path(path, hygiene) 172 lower::lower_path(path, ctx)
172 } 173 }
173 174
174 /// Converts a known mod path to `Path`. 175 /// Converts a known mod path to `Path`.
diff --git a/crates/hir_def/src/path/lower.rs b/crates/hir_def/src/path/lower.rs
index 7b29d9d4f..1df6db525 100644
--- a/crates/hir_def/src/path/lower.rs
+++ b/crates/hir_def/src/path/lower.rs
@@ -6,10 +6,7 @@ use crate::intern::Interned;
6use std::sync::Arc; 6use std::sync::Arc;
7 7
8use either::Either; 8use either::Either;
9use hir_expand::{ 9use hir_expand::name::{name, AsName};
10 hygiene::Hygiene,
11 name::{name, AsName},
12};
13use syntax::ast::{self, AstNode, TypeBoundsOwner}; 10use syntax::ast::{self, AstNode, TypeBoundsOwner};
14 11
15use super::AssociatedTypeBinding; 12use super::AssociatedTypeBinding;
@@ -23,12 +20,12 @@ pub(super) use lower_use::lower_use_tree;
23 20
24/// Converts an `ast::Path` to `Path`. Works with use trees. 21/// Converts an `ast::Path` to `Path`. Works with use trees.
25/// It correctly handles `$crate` based path from macro call. 22/// It correctly handles `$crate` based path from macro call.
26pub(super) fn lower_path(mut path: ast::Path, hygiene: &Hygiene) -> Option<Path> { 23pub(super) fn lower_path(mut path: ast::Path, ctx: &LowerCtx) -> Option<Path> {
27 let mut kind = PathKind::Plain; 24 let mut kind = PathKind::Plain;
28 let mut type_anchor = None; 25 let mut type_anchor = None;
29 let mut segments = Vec::new(); 26 let mut segments = Vec::new();
30 let mut generic_args = Vec::new(); 27 let mut generic_args = Vec::new();
31 let ctx = LowerCtx::with_hygiene(hygiene); 28 let hygiene = ctx.hygiene();
32 loop { 29 loop {
33 let segment = path.segment()?; 30 let segment = path.segment()?;
34 31
@@ -43,10 +40,10 @@ pub(super) fn lower_path(mut path: ast::Path, hygiene: &Hygiene) -> Option<Path>
43 Either::Left(name) => { 40 Either::Left(name) => {
44 let args = segment 41 let args = segment
45 .generic_arg_list() 42 .generic_arg_list()
46 .and_then(|it| lower_generic_args(&ctx, it)) 43 .and_then(|it| lower_generic_args(ctx, it))
47 .or_else(|| { 44 .or_else(|| {
48 lower_generic_args_from_fn_path( 45 lower_generic_args_from_fn_path(
49 &ctx, 46 ctx,
50 segment.param_list(), 47 segment.param_list(),
51 segment.ret_type(), 48 segment.ret_type(),
52 ) 49 )
@@ -64,7 +61,7 @@ pub(super) fn lower_path(mut path: ast::Path, hygiene: &Hygiene) -> Option<Path>
64 ast::PathSegmentKind::Type { type_ref, trait_ref } => { 61 ast::PathSegmentKind::Type { type_ref, trait_ref } => {
65 assert!(path.qualifier().is_none()); // this can only occur at the first segment 62 assert!(path.qualifier().is_none()); // this can only occur at the first segment
66 63
67 let self_type = TypeRef::from_ast(&ctx, type_ref?); 64 let self_type = TypeRef::from_ast(ctx, type_ref?);
68 65
69 match trait_ref { 66 match trait_ref {
70 // <T>::foo 67 // <T>::foo
@@ -74,7 +71,7 @@ pub(super) fn lower_path(mut path: ast::Path, hygiene: &Hygiene) -> Option<Path>
74 } 71 }
75 // <T as Trait<A>>::Foo desugars to Trait<Self=T, A>::Foo 72 // <T as Trait<A>>::Foo desugars to Trait<Self=T, A>::Foo
76 Some(trait_ref) => { 73 Some(trait_ref) => {
77 let path = Path::from_src(trait_ref.path()?, hygiene)?; 74 let path = Path::from_src(trait_ref.path()?, ctx)?;
78 let mod_path = (*path.mod_path).clone(); 75 let mod_path = (*path.mod_path).clone();
79 let num_segments = path.mod_path.segments.len(); 76 let num_segments = path.mod_path.segments.len();
80 kind = mod_path.kind; 77 kind = mod_path.kind;
diff --git a/crates/hir_def/src/type_ref.rs b/crates/hir_def/src/type_ref.rs
index 4c24aae94..ea29da5da 100644
--- a/crates/hir_def/src/type_ref.rs
+++ b/crates/hir_def/src/type_ref.rs
@@ -1,6 +1,7 @@
1//! HIR for references to types. Paths in these are not yet resolved. They can 1//! HIR for references to types. Paths in these are not yet resolved. They can
2//! be directly created from an ast::TypeRef, without further queries. 2//! be directly created from an ast::TypeRef, without further queries.
3use hir_expand::name::Name; 3
4use hir_expand::{name::Name, AstId, InFile};
4use syntax::ast; 5use syntax::ast;
5 6
6use crate::{body::LowerCtx, path::Path}; 7use crate::{body::LowerCtx, path::Path};
@@ -68,6 +69,7 @@ impl TraitRef {
68 } 69 }
69 } 70 }
70} 71}
72
71/// Compare ty::Ty 73/// Compare ty::Ty
72#[derive(Clone, PartialEq, Eq, Hash, Debug)] 74#[derive(Clone, PartialEq, Eq, Hash, Debug)]
73pub enum TypeRef { 75pub enum TypeRef {
@@ -84,6 +86,7 @@ pub enum TypeRef {
84 // For 86 // For
85 ImplTrait(Vec<TypeBound>), 87 ImplTrait(Vec<TypeBound>),
86 DynTrait(Vec<TypeBound>), 88 DynTrait(Vec<TypeBound>),
89 Macro(AstId<ast::MacroCall>),
87 Error, 90 Error,
88} 91}
89 92
@@ -116,7 +119,7 @@ pub enum TypeBound {
116 119
117impl TypeRef { 120impl TypeRef {
118 /// Converts an `ast::TypeRef` to a `hir::TypeRef`. 121 /// Converts an `ast::TypeRef` to a `hir::TypeRef`.
119 pub(crate) fn from_ast(ctx: &LowerCtx, node: ast::Type) -> Self { 122 pub fn from_ast(ctx: &LowerCtx, node: ast::Type) -> Self {
120 match node { 123 match node {
121 ast::Type::ParenType(inner) => TypeRef::from_ast_opt(&ctx, inner.ty()), 124 ast::Type::ParenType(inner) => TypeRef::from_ast_opt(&ctx, inner.ty()),
122 ast::Type::TupleType(inner) => { 125 ast::Type::TupleType(inner) => {
@@ -176,8 +179,13 @@ impl TypeRef {
176 ast::Type::DynTraitType(inner) => { 179 ast::Type::DynTraitType(inner) => {
177 TypeRef::DynTrait(type_bounds_from_ast(ctx, inner.type_bound_list())) 180 TypeRef::DynTrait(type_bounds_from_ast(ctx, inner.type_bound_list()))
178 } 181 }
179 // FIXME: Macros in type position are not yet supported. 182 ast::Type::MacroType(mt) => match mt.macro_call() {
180 ast::Type::MacroType(_) => TypeRef::Error, 183 Some(mc) => ctx
184 .ast_id(&mc)
185 .map(|mc| TypeRef::Macro(InFile::new(ctx.file_id(), mc)))
186 .unwrap_or(TypeRef::Error),
187 None => TypeRef::Error,
188 },
181 } 189 }
182 } 190 }
183 191
@@ -215,7 +223,7 @@ impl TypeRef {
215 } 223 }
216 } 224 }
217 TypeRef::Path(path) => go_path(path, f), 225 TypeRef::Path(path) => go_path(path, f),
218 TypeRef::Never | TypeRef::Placeholder | TypeRef::Error => {} 226 TypeRef::Never | TypeRef::Placeholder | TypeRef::Macro(_) | TypeRef::Error => {}
219 }; 227 };
220 } 228 }
221 229
diff --git a/crates/hir_expand/src/db.rs b/crates/hir_expand/src/db.rs
index ca705ee9d..1e4b0cc19 100644
--- a/crates/hir_expand/src/db.rs
+++ b/crates/hir_expand/src/db.rs
@@ -440,6 +440,7 @@ fn to_fragment_kind(db: &dyn AstDatabase, id: MacroCallId) -> FragmentKind {
440 MACRO_ITEMS | SOURCE_FILE => FragmentKind::Items, 440 MACRO_ITEMS | SOURCE_FILE => FragmentKind::Items,
441 MACRO_STMTS => FragmentKind::Statements, 441 MACRO_STMTS => FragmentKind::Statements,
442 MACRO_PAT => FragmentKind::Pattern, 442 MACRO_PAT => FragmentKind::Pattern,
443 MACRO_TYPE => FragmentKind::Type,
443 ITEM_LIST => FragmentKind::Items, 444 ITEM_LIST => FragmentKind::Items,
444 LET_STMT => { 445 LET_STMT => {
445 // FIXME: Handle LHS Pattern 446 // FIXME: Handle LHS Pattern
diff --git a/crates/hir_expand/src/eager.rs b/crates/hir_expand/src/eager.rs
index ef126e4ad..a5ac32d3c 100644
--- a/crates/hir_expand/src/eager.rs
+++ b/crates/hir_expand/src/eager.rs
@@ -31,6 +31,7 @@ use parser::FragmentKind;
31use std::sync::Arc; 31use std::sync::Arc;
32use syntax::{algo::SyntaxRewriter, SyntaxNode}; 32use syntax::{algo::SyntaxRewriter, SyntaxNode};
33 33
34#[derive(Debug)]
34pub struct ErrorEmitted { 35pub struct ErrorEmitted {
35 _private: (), 36 _private: (),
36} 37}
diff --git a/crates/hir_ty/src/display.rs b/crates/hir_ty/src/display.rs
index e7c9dabc2..4fb7d9cf2 100644
--- a/crates/hir_ty/src/display.rs
+++ b/crates/hir_ty/src/display.rs
@@ -9,6 +9,7 @@ use std::{
9 9
10use chalk_ir::BoundVar; 10use chalk_ir::BoundVar;
11use hir_def::{ 11use hir_def::{
12 body,
12 db::DefDatabase, 13 db::DefDatabase,
13 find_path, 14 find_path,
14 generics::TypeParamProvenance, 15 generics::TypeParamProvenance,
@@ -18,7 +19,7 @@ use hir_def::{
18 visibility::Visibility, 19 visibility::Visibility,
19 AssocContainerId, Lookup, ModuleId, TraitId, 20 AssocContainerId, Lookup, ModuleId, TraitId,
20}; 21};
21use hir_expand::name::Name; 22use hir_expand::{hygiene::Hygiene, name::Name};
22 23
23use crate::{ 24use crate::{
24 const_from_placeholder_idx, db::HirDatabase, from_assoc_type_id, from_foreign_def_id, 25 const_from_placeholder_idx, db::HirDatabase, from_assoc_type_id, from_foreign_def_id,
@@ -997,6 +998,18 @@ impl HirDisplay for TypeRef {
997 write!(f, "dyn ")?; 998 write!(f, "dyn ")?;
998 f.write_joined(bounds, " + ")?; 999 f.write_joined(bounds, " + ")?;
999 } 1000 }
1001 TypeRef::Macro(macro_call) => {
1002 let macro_call = macro_call.to_node(f.db.upcast());
1003 let ctx = body::LowerCtx::with_hygiene(&Hygiene::new_unhygienic());
1004 match macro_call.path() {
1005 Some(path) => match Path::from_src(path, &ctx) {
1006 Some(path) => path.hir_fmt(f)?,
1007 None => write!(f, "{{macro}}")?,
1008 },
1009 None => write!(f, "{{macro}}")?,
1010 }
1011 write!(f, "!(..)")?;
1012 }
1000 TypeRef::Error => write!(f, "{{error}}")?, 1013 TypeRef::Error => write!(f, "{{error}}")?,
1001 } 1014 }
1002 Ok(()) 1015 Ok(())
diff --git a/crates/hir_ty/src/lower.rs b/crates/hir_ty/src/lower.rs
index a035686bc..7fd46becd 100644
--- a/crates/hir_ty/src/lower.rs
+++ b/crates/hir_ty/src/lower.rs
@@ -5,12 +5,14 @@
5//! - Building the type for an item: This happens through the `type_for_def` query. 5//! - Building the type for an item: This happens through the `type_for_def` query.
6//! 6//!
7//! This usually involves resolving names, collecting generic arguments etc. 7//! This usually involves resolving names, collecting generic arguments etc.
8use std::cell::{Cell, RefCell};
8use std::{iter, sync::Arc}; 9use std::{iter, sync::Arc};
9 10
10use base_db::CrateId; 11use base_db::CrateId;
11use chalk_ir::{cast::Cast, fold::Shift, interner::HasInterner, Mutability, Safety}; 12use chalk_ir::{cast::Cast, fold::Shift, interner::HasInterner, Mutability, Safety};
12use hir_def::{ 13use hir_def::{
13 adt::StructKind, 14 adt::StructKind,
15 body::{Expander, LowerCtx},
14 builtin_type::BuiltinType, 16 builtin_type::BuiltinType,
15 generics::{TypeParamProvenance, WherePredicate, WherePredicateTypeTarget}, 17 generics::{TypeParamProvenance, WherePredicate, WherePredicateTypeTarget},
16 path::{GenericArg, Path, PathSegment, PathSegments}, 18 path::{GenericArg, Path, PathSegment, PathSegments},
@@ -20,10 +22,11 @@ use hir_def::{
20 GenericDefId, HasModule, ImplId, LocalFieldId, Lookup, StaticId, StructId, TraitId, 22 GenericDefId, HasModule, ImplId, LocalFieldId, Lookup, StaticId, StructId, TraitId,
21 TypeAliasId, TypeParamId, UnionId, VariantId, 23 TypeAliasId, TypeParamId, UnionId, VariantId,
22}; 24};
23use hir_expand::name::Name; 25use hir_expand::{name::Name, ExpandResult};
24use la_arena::ArenaMap; 26use la_arena::ArenaMap;
25use smallvec::SmallVec; 27use smallvec::SmallVec;
26use stdx::impl_from; 28use stdx::impl_from;
29use syntax::ast;
27 30
28use crate::{ 31use crate::{
29 db::HirDatabase, 32 db::HirDatabase,
@@ -50,7 +53,7 @@ pub struct TyLoweringContext<'a> {
50 /// possible currently, so this should be fine for now. 53 /// possible currently, so this should be fine for now.
51 pub type_param_mode: TypeParamLoweringMode, 54 pub type_param_mode: TypeParamLoweringMode,
52 pub impl_trait_mode: ImplTraitLoweringMode, 55 pub impl_trait_mode: ImplTraitLoweringMode,
53 impl_trait_counter: std::cell::Cell<u16>, 56 impl_trait_counter: Cell<u16>,
54 /// When turning `impl Trait` into opaque types, we have to collect the 57 /// When turning `impl Trait` into opaque types, we have to collect the
55 /// bounds at the same time to get the IDs correct (without becoming too 58 /// bounds at the same time to get the IDs correct (without becoming too
56 /// complicated). I don't like using interior mutability (as for the 59 /// complicated). I don't like using interior mutability (as for the
@@ -59,16 +62,17 @@ pub struct TyLoweringContext<'a> {
59 /// we're grouping the mutable data (the counter and this field) together 62 /// we're grouping the mutable data (the counter and this field) together
60 /// with the immutable context (the references to the DB and resolver). 63 /// with the immutable context (the references to the DB and resolver).
61 /// Splitting this up would be a possible fix. 64 /// Splitting this up would be a possible fix.
62 opaque_type_data: std::cell::RefCell<Vec<ReturnTypeImplTrait>>, 65 opaque_type_data: RefCell<Vec<ReturnTypeImplTrait>>,
66 expander: RefCell<Option<Expander>>,
63} 67}
64 68
65impl<'a> TyLoweringContext<'a> { 69impl<'a> TyLoweringContext<'a> {
66 pub fn new(db: &'a dyn HirDatabase, resolver: &'a Resolver) -> Self { 70 pub fn new(db: &'a dyn HirDatabase, resolver: &'a Resolver) -> Self {
67 let impl_trait_counter = std::cell::Cell::new(0); 71 let impl_trait_counter = Cell::new(0);
68 let impl_trait_mode = ImplTraitLoweringMode::Disallowed; 72 let impl_trait_mode = ImplTraitLoweringMode::Disallowed;
69 let type_param_mode = TypeParamLoweringMode::Placeholder; 73 let type_param_mode = TypeParamLoweringMode::Placeholder;
70 let in_binders = DebruijnIndex::INNERMOST; 74 let in_binders = DebruijnIndex::INNERMOST;
71 let opaque_type_data = std::cell::RefCell::new(Vec::new()); 75 let opaque_type_data = RefCell::new(Vec::new());
72 Self { 76 Self {
73 db, 77 db,
74 resolver, 78 resolver,
@@ -77,6 +81,7 @@ impl<'a> TyLoweringContext<'a> {
77 impl_trait_counter, 81 impl_trait_counter,
78 type_param_mode, 82 type_param_mode,
79 opaque_type_data, 83 opaque_type_data,
84 expander: RefCell::new(None),
80 } 85 }
81 } 86 }
82 87
@@ -86,15 +91,18 @@ impl<'a> TyLoweringContext<'a> {
86 f: impl FnOnce(&TyLoweringContext) -> T, 91 f: impl FnOnce(&TyLoweringContext) -> T,
87 ) -> T { 92 ) -> T {
88 let opaque_ty_data_vec = self.opaque_type_data.replace(Vec::new()); 93 let opaque_ty_data_vec = self.opaque_type_data.replace(Vec::new());
94 let expander = self.expander.replace(None);
89 let new_ctx = Self { 95 let new_ctx = Self {
90 in_binders: debruijn, 96 in_binders: debruijn,
91 impl_trait_counter: std::cell::Cell::new(self.impl_trait_counter.get()), 97 impl_trait_counter: Cell::new(self.impl_trait_counter.get()),
92 opaque_type_data: std::cell::RefCell::new(opaque_ty_data_vec), 98 opaque_type_data: RefCell::new(opaque_ty_data_vec),
99 expander: RefCell::new(expander),
93 ..*self 100 ..*self
94 }; 101 };
95 let result = f(&new_ctx); 102 let result = f(&new_ctx);
96 self.impl_trait_counter.set(new_ctx.impl_trait_counter.get()); 103 self.impl_trait_counter.set(new_ctx.impl_trait_counter.get());
97 self.opaque_type_data.replace(new_ctx.opaque_type_data.into_inner()); 104 self.opaque_type_data.replace(new_ctx.opaque_type_data.into_inner());
105 self.expander.replace(new_ctx.expander.into_inner());
98 result 106 result
99 } 107 }
100 108
@@ -287,6 +295,53 @@ impl<'a> TyLoweringContext<'a> {
287 } 295 }
288 } 296 }
289 } 297 }
298 TypeRef::Macro(macro_call) => {
299 let (expander, recursion_start) = {
300 let mut expander = self.expander.borrow_mut();
301 if expander.is_some() {
302 (Some(expander), false)
303 } else {
304 if let Some(module_id) = self.resolver.module() {
305 *expander = Some(Expander::new(
306 self.db.upcast(),
307 macro_call.file_id,
308 module_id,
309 ));
310 (Some(expander), true)
311 } else {
312 (None, false)
313 }
314 }
315 };
316 let ty = if let Some(mut expander) = expander {
317 let expander_mut = expander.as_mut().unwrap();
318 let macro_call = macro_call.to_node(self.db.upcast());
319 match expander_mut.enter_expand::<ast::Type>(self.db.upcast(), macro_call) {
320 Ok(ExpandResult { value: Some((mark, expanded)), .. }) => {
321 let ctx =
322 LowerCtx::new(self.db.upcast(), expander_mut.current_file_id());
323 let type_ref = TypeRef::from_ast(&ctx, expanded);
324
325 drop(expander);
326 let ty = self.lower_ty(&type_ref);
327
328 self.expander
329 .borrow_mut()
330 .as_mut()
331 .unwrap()
332 .exit(self.db.upcast(), mark);
333 Some(ty)
334 }
335 _ => None,
336 }
337 } else {
338 None
339 };
340 if recursion_start {
341 *self.expander.borrow_mut() = None;
342 }
343 ty.unwrap_or_else(|| TyKind::Error.intern(&Interner))
344 }
290 TypeRef::Error => TyKind::Error.intern(&Interner), 345 TypeRef::Error => TyKind::Error.intern(&Interner),
291 }; 346 };
292 (ty, res) 347 (ty, res)
diff --git a/crates/hir_ty/src/tests/macros.rs b/crates/hir_ty/src/tests/macros.rs
index b8e373ed8..6588aa46c 100644
--- a/crates/hir_ty/src/tests/macros.rs
+++ b/crates/hir_ty/src/tests/macros.rs
@@ -1074,3 +1074,202 @@ fn macro_in_arm() {
1074 "#]], 1074 "#]],
1075 ); 1075 );
1076} 1076}
1077
1078#[test]
1079fn macro_in_type_alias_position() {
1080 check_infer(
1081 r#"
1082 macro_rules! U32 {
1083 () => { u32 };
1084 }
1085
1086 trait Foo {
1087 type Ty;
1088 }
1089
1090 impl<T> Foo for T {
1091 type Ty = U32!();
1092 }
1093
1094 type TayTo = U32!();
1095
1096 fn testy() {
1097 let a: <() as Foo>::Ty;
1098 let b: TayTo;
1099 }
1100 "#,
1101 expect![[r#"
1102 147..196 '{ ...yTo; }': ()
1103 157..158 'a': u32
1104 185..186 'b': u32
1105 "#]],
1106 );
1107}
1108
1109#[test]
1110fn nested_macro_in_type_alias_position() {
1111 check_infer(
1112 r#"
1113 macro_rules! U32Inner2 {
1114 () => { u32 };
1115 }
1116
1117 macro_rules! U32Inner1 {
1118 () => { U32Inner2!() };
1119 }
1120
1121 macro_rules! U32 {
1122 () => { U32Inner1!() };
1123 }
1124
1125 trait Foo {
1126 type Ty;
1127 }
1128
1129 impl<T> Foo for T {
1130 type Ty = U32!();
1131 }
1132
1133 type TayTo = U32!();
1134
1135 fn testy() {
1136 let a: <() as Foo>::Ty;
1137 let b: TayTo;
1138 }
1139 "#,
1140 expect![[r#"
1141 259..308 '{ ...yTo; }': ()
1142 269..270 'a': u32
1143 297..298 'b': u32
1144 "#]],
1145 );
1146}
1147
1148#[test]
1149fn macros_in_type_alias_position_generics() {
1150 check_infer(
1151 r#"
1152 struct Foo<A, B>(A, B);
1153
1154 macro_rules! U32 {
1155 () => { u32 };
1156 }
1157
1158 macro_rules! Bar {
1159 () => { Foo<U32!(), U32!()> };
1160 }
1161
1162 trait Moo {
1163 type Ty;
1164 }
1165
1166 impl<T> Moo for T {
1167 type Ty = Bar!();
1168 }
1169
1170 type TayTo = Bar!();
1171
1172 fn main() {
1173 let a: <() as Moo>::Ty;
1174 let b: TayTo;
1175 }
1176 "#,
1177 expect![[r#"
1178 228..277 '{ ...yTo; }': ()
1179 238..239 'a': Foo<u32, u32>
1180 266..267 'b': Foo<u32, u32>
1181 "#]],
1182 );
1183}
1184
1185#[test]
1186fn macros_in_type_position() {
1187 check_infer(
1188 r#"
1189 struct Foo<A, B>(A, B);
1190
1191 macro_rules! U32 {
1192 () => { u32 };
1193 }
1194
1195 macro_rules! Bar {
1196 () => { Foo<U32!(), U32!()> };
1197 }
1198
1199 fn main() {
1200 let a: Bar!();
1201 }
1202 "#,
1203 expect![[r#"
1204 133..155 '{ ...!(); }': ()
1205 143..144 'a': Foo<u32, u32>
1206 "#]],
1207 );
1208}
1209
1210#[test]
1211fn macros_in_type_generics() {
1212 check_infer(
1213 r#"
1214 struct Foo<A, B>(A, B);
1215
1216 macro_rules! U32 {
1217 () => { u32 };
1218 }
1219
1220 macro_rules! Bar {
1221 () => { Foo<U32!(), U32!()> };
1222 }
1223
1224 trait Moo {
1225 type Ty;
1226 }
1227
1228 impl<T> Moo for T {
1229 type Ty = Foo<Bar!(), Bar!()>;
1230 }
1231
1232 type TayTo = Foo<Bar!(), U32!()>;
1233
1234 fn main() {
1235 let a: <() as Moo>::Ty;
1236 let b: TayTo;
1237 }
1238 "#,
1239 expect![[r#"
1240 254..303 '{ ...yTo; }': ()
1241 264..265 'a': Foo<Foo<u32, u32>, Foo<u32, u32>>
1242 292..293 'b': Foo<Foo<u32, u32>, u32>
1243 "#]],
1244 );
1245}
1246
1247#[test]
1248fn infinitely_recursive_macro_type() {
1249 check_infer(
1250 r#"
1251 struct Bar<T, X>(T, X);
1252
1253 macro_rules! Foo {
1254 () => { Foo!() }
1255 }
1256
1257 macro_rules! U32 {
1258 () => { u32 }
1259 }
1260
1261 type A = Foo!();
1262 type B = Bar<Foo!(), U32!()>;
1263
1264 fn main() {
1265 let a: A;
1266 let b: B;
1267 }
1268 "#,
1269 expect![[r#"
1270 166..197 '{ ...: B; }': ()
1271 176..177 'a': {unknown}
1272 190..191 'b': Bar<{unknown}, u32>
1273 "#]],
1274 );
1275}
diff --git a/crates/ide_assists/src/handlers/extract_function.rs b/crates/ide_assists/src/handlers/extract_function.rs
index 059414274..78a57fbdc 100644
--- a/crates/ide_assists/src/handlers/extract_function.rs
+++ b/crates/ide_assists/src/handlers/extract_function.rs
@@ -599,7 +599,12 @@ fn extraction_target(node: &SyntaxNode, selection_range: TextRange) -> Option<Fu
599 // we have selected a few statements in a block 599 // we have selected a few statements in a block
600 // so covering_element returns the whole block 600 // so covering_element returns the whole block
601 if node.kind() == BLOCK_EXPR { 601 if node.kind() == BLOCK_EXPR {
602 let body = FunctionBody::from_range(node.clone(), selection_range); 602 // Extract the full statements.
603 let statements_range = node
604 .children()
605 .filter(|c| selection_range.intersect(c.text_range()).is_some())
606 .fold(selection_range, |acc, c| acc.cover(c.text_range()));
607 let body = FunctionBody::from_range(node.clone(), statements_range);
603 if body.is_some() { 608 if body.is_some() {
604 return body; 609 return body;
605 } 610 }
@@ -610,7 +615,8 @@ fn extraction_target(node: &SyntaxNode, selection_range: TextRange) -> Option<Fu
610 // so we try to expand covering_element to parent and repeat the previous 615 // so we try to expand covering_element to parent and repeat the previous
611 if let Some(parent) = node.parent() { 616 if let Some(parent) = node.parent() {
612 if parent.kind() == BLOCK_EXPR { 617 if parent.kind() == BLOCK_EXPR {
613 let body = FunctionBody::from_range(parent, selection_range); 618 // Extract the full statement.
619 let body = FunctionBody::from_range(parent, node.text_range());
614 if body.is_some() { 620 if body.is_some() {
615 return body; 621 return body;
616 } 622 }
@@ -1785,6 +1791,60 @@ fn $0fun_name() -> i32 {
1785 } 1791 }
1786 1792
1787 #[test] 1793 #[test]
1794 fn extract_partial_block_single_line() {
1795 check_assist(
1796 extract_function,
1797 r#"
1798fn foo() {
1799 let n = 1;
1800 let mut v = $0n * n;$0
1801 v += 1;
1802}"#,
1803 r#"
1804fn foo() {
1805 let n = 1;
1806 let mut v = fun_name(n);
1807 v += 1;
1808}
1809
1810fn $0fun_name(n: i32) -> i32 {
1811 let mut v = n * n;
1812 v
1813}"#,
1814 );
1815 }
1816
1817 #[test]
1818 fn extract_partial_block() {
1819 check_assist(
1820 extract_function,
1821 r#"
1822fn foo() {
1823 let m = 2;
1824 let n = 1;
1825 let mut v = m $0* n;
1826 let mut w = 3;$0
1827 v += 1;
1828 w += 1;
1829}"#,
1830 r#"
1831fn foo() {
1832 let m = 2;
1833 let n = 1;
1834 let (mut v, mut w) = fun_name(m, n);
1835 v += 1;
1836 w += 1;
1837}
1838
1839fn $0fun_name(m: i32, n: i32) -> (i32, i32) {
1840 let mut v = m * n;
1841 let mut w = 3;
1842 (v, w)
1843}"#,
1844 );
1845 }
1846
1847 #[test]
1788 fn argument_form_expr() { 1848 fn argument_form_expr() {
1789 check_assist( 1849 check_assist(
1790 extract_function, 1850 extract_function,
diff --git a/crates/ide_assists/src/handlers/introduce_named_lifetime.rs b/crates/ide_assists/src/handlers/introduce_named_lifetime.rs
index 02782eb6d..9f4f71d6c 100644
--- a/crates/ide_assists/src/handlers/introduce_named_lifetime.rs
+++ b/crates/ide_assists/src/handlers/introduce_named_lifetime.rs
@@ -1,7 +1,8 @@
1use rustc_hash::FxHashSet; 1use rustc_hash::FxHashSet;
2use syntax::{ 2use syntax::{
3 ast::{self, GenericParamsOwner, NameOwner}, 3 ast::{self, edit_in_place::GenericParamsOwnerEdit, make, GenericParamsOwner},
4 AstNode, TextRange, TextSize, 4 ted::{self, Position},
5 AstNode, TextRange,
5}; 6};
6 7
7use crate::{assist_context::AssistBuilder, AssistContext, AssistId, AssistKind, Assists}; 8use crate::{assist_context::AssistBuilder, AssistContext, AssistId, AssistKind, Assists};
@@ -37,10 +38,12 @@ static ASSIST_LABEL: &str = "Introduce named lifetime";
37pub(crate) fn introduce_named_lifetime(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { 38pub(crate) fn introduce_named_lifetime(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
38 let lifetime = 39 let lifetime =
39 ctx.find_node_at_offset::<ast::Lifetime>().filter(|lifetime| lifetime.text() == "'_")?; 40 ctx.find_node_at_offset::<ast::Lifetime>().filter(|lifetime| lifetime.text() == "'_")?;
41 let lifetime_loc = lifetime.lifetime_ident_token()?.text_range();
42
40 if let Some(fn_def) = lifetime.syntax().ancestors().find_map(ast::Fn::cast) { 43 if let Some(fn_def) = lifetime.syntax().ancestors().find_map(ast::Fn::cast) {
41 generate_fn_def_assist(acc, &fn_def, lifetime.lifetime_ident_token()?.text_range()) 44 generate_fn_def_assist(acc, fn_def, lifetime_loc, lifetime)
42 } else if let Some(impl_def) = lifetime.syntax().ancestors().find_map(ast::Impl::cast) { 45 } else if let Some(impl_def) = lifetime.syntax().ancestors().find_map(ast::Impl::cast) {
43 generate_impl_def_assist(acc, &impl_def, lifetime.lifetime_ident_token()?.text_range()) 46 generate_impl_def_assist(acc, impl_def, lifetime_loc, lifetime)
44 } else { 47 } else {
45 None 48 None
46 } 49 }
@@ -49,26 +52,26 @@ pub(crate) fn introduce_named_lifetime(acc: &mut Assists, ctx: &AssistContext) -
49/// Generate the assist for the fn def case 52/// Generate the assist for the fn def case
50fn generate_fn_def_assist( 53fn generate_fn_def_assist(
51 acc: &mut Assists, 54 acc: &mut Assists,
52 fn_def: &ast::Fn, 55 fn_def: ast::Fn,
53 lifetime_loc: TextRange, 56 lifetime_loc: TextRange,
57 lifetime: ast::Lifetime,
54) -> Option<()> { 58) -> Option<()> {
55 let param_list: ast::ParamList = fn_def.param_list()?; 59 let param_list: ast::ParamList = fn_def.param_list()?;
56 let new_lifetime_param = generate_unique_lifetime_param_name(&fn_def.generic_param_list())?; 60 let new_lifetime_param = generate_unique_lifetime_param_name(fn_def.generic_param_list())?;
57 let end_of_fn_ident = fn_def.name()?.ident_token()?.text_range().end();
58 let self_param = 61 let self_param =
59 // use the self if it's a reference and has no explicit lifetime 62 // use the self if it's a reference and has no explicit lifetime
60 param_list.self_param().filter(|p| p.lifetime().is_none() && p.amp_token().is_some()); 63 param_list.self_param().filter(|p| p.lifetime().is_none() && p.amp_token().is_some());
61 // compute the location which implicitly has the same lifetime as the anonymous lifetime 64 // compute the location which implicitly has the same lifetime as the anonymous lifetime
62 let loc_needing_lifetime = if let Some(self_param) = self_param { 65 let loc_needing_lifetime = if let Some(self_param) = self_param {
63 // if we have a self reference, use that 66 // if we have a self reference, use that
64 Some(self_param.name()?.syntax().text_range().start()) 67 Some(NeedsLifetime::SelfParam(self_param))
65 } else { 68 } else {
66 // otherwise, if there's a single reference parameter without a named liftime, use that 69 // otherwise, if there's a single reference parameter without a named liftime, use that
67 let fn_params_without_lifetime: Vec<_> = param_list 70 let fn_params_without_lifetime: Vec<_> = param_list
68 .params() 71 .params()
69 .filter_map(|param| match param.ty() { 72 .filter_map(|param| match param.ty() {
70 Some(ast::Type::RefType(ascribed_type)) if ascribed_type.lifetime().is_none() => { 73 Some(ast::Type::RefType(ascribed_type)) if ascribed_type.lifetime().is_none() => {
71 Some(ascribed_type.amp_token()?.text_range().end()) 74 Some(NeedsLifetime::RefType(ascribed_type))
72 } 75 }
73 _ => None, 76 _ => None,
74 }) 77 })
@@ -81,30 +84,46 @@ fn generate_fn_def_assist(
81 } 84 }
82 }; 85 };
83 acc.add(AssistId(ASSIST_NAME, AssistKind::Refactor), ASSIST_LABEL, lifetime_loc, |builder| { 86 acc.add(AssistId(ASSIST_NAME, AssistKind::Refactor), ASSIST_LABEL, lifetime_loc, |builder| {
84 add_lifetime_param(fn_def, builder, end_of_fn_ident, new_lifetime_param); 87 let fn_def = builder.make_ast_mut(fn_def);
85 builder.replace(lifetime_loc, format!("'{}", new_lifetime_param)); 88 let lifetime = builder.make_ast_mut(lifetime);
86 loc_needing_lifetime.map(|loc| builder.insert(loc, format!("'{} ", new_lifetime_param))); 89 let loc_needing_lifetime =
90 loc_needing_lifetime.and_then(|it| it.make_mut(builder).to_position());
91
92 add_lifetime_param(fn_def.get_or_create_generic_param_list(), new_lifetime_param);
93 ted::replace(
94 lifetime.syntax(),
95 make_ast_lifetime(new_lifetime_param).clone_for_update().syntax(),
96 );
97 loc_needing_lifetime.map(|position| {
98 ted::insert(position, make_ast_lifetime(new_lifetime_param).clone_for_update().syntax())
99 });
87 }) 100 })
88} 101}
89 102
90/// Generate the assist for the impl def case 103/// Generate the assist for the impl def case
91fn generate_impl_def_assist( 104fn generate_impl_def_assist(
92 acc: &mut Assists, 105 acc: &mut Assists,
93 impl_def: &ast::Impl, 106 impl_def: ast::Impl,
94 lifetime_loc: TextRange, 107 lifetime_loc: TextRange,
108 lifetime: ast::Lifetime,
95) -> Option<()> { 109) -> Option<()> {
96 let new_lifetime_param = generate_unique_lifetime_param_name(&impl_def.generic_param_list())?; 110 let new_lifetime_param = generate_unique_lifetime_param_name(impl_def.generic_param_list())?;
97 let end_of_impl_kw = impl_def.impl_token()?.text_range().end();
98 acc.add(AssistId(ASSIST_NAME, AssistKind::Refactor), ASSIST_LABEL, lifetime_loc, |builder| { 111 acc.add(AssistId(ASSIST_NAME, AssistKind::Refactor), ASSIST_LABEL, lifetime_loc, |builder| {
99 add_lifetime_param(impl_def, builder, end_of_impl_kw, new_lifetime_param); 112 let impl_def = builder.make_ast_mut(impl_def);
100 builder.replace(lifetime_loc, format!("'{}", new_lifetime_param)); 113 let lifetime = builder.make_ast_mut(lifetime);
114
115 add_lifetime_param(impl_def.get_or_create_generic_param_list(), new_lifetime_param);
116 ted::replace(
117 lifetime.syntax(),
118 make_ast_lifetime(new_lifetime_param).clone_for_update().syntax(),
119 );
101 }) 120 })
102} 121}
103 122
104/// Given a type parameter list, generate a unique lifetime parameter name 123/// Given a type parameter list, generate a unique lifetime parameter name
105/// which is not in the list 124/// which is not in the list
106fn generate_unique_lifetime_param_name( 125fn generate_unique_lifetime_param_name(
107 existing_type_param_list: &Option<ast::GenericParamList>, 126 existing_type_param_list: Option<ast::GenericParamList>,
108) -> Option<char> { 127) -> Option<char> {
109 match existing_type_param_list { 128 match existing_type_param_list {
110 Some(type_params) => { 129 Some(type_params) => {
@@ -118,25 +137,37 @@ fn generate_unique_lifetime_param_name(
118 } 137 }
119} 138}
120 139
121/// Add the lifetime param to `builder`. If there are type parameters in `type_params_owner`, add it to the end. Otherwise 140fn add_lifetime_param(type_params: ast::GenericParamList, new_lifetime_param: char) {
122/// add new type params brackets with the lifetime parameter at `new_type_params_loc`. 141 let generic_param =
123fn add_lifetime_param<TypeParamsOwner: ast::GenericParamsOwner>( 142 make::generic_param(format!("'{}", new_lifetime_param), None).clone_for_update();
124 type_params_owner: &TypeParamsOwner, 143 type_params.add_generic_param(generic_param);
125 builder: &mut AssistBuilder, 144}
126 new_type_params_loc: TextSize, 145
127 new_lifetime_param: char, 146fn make_ast_lifetime(new_lifetime_param: char) -> ast::Lifetime {
128) { 147 make::generic_param(format!("'{}", new_lifetime_param), None)
129 match type_params_owner.generic_param_list() { 148 .syntax()
130 // add the new lifetime parameter to an existing type param list 149 .descendants()
131 Some(type_params) => { 150 .find_map(ast::Lifetime::cast)
132 builder.insert( 151 .unwrap()
133 (u32::from(type_params.syntax().text_range().end()) - 1).into(), 152}
134 format!(", '{}", new_lifetime_param), 153
135 ); 154enum NeedsLifetime {
155 SelfParam(ast::SelfParam),
156 RefType(ast::RefType),
157}
158
159impl NeedsLifetime {
160 fn make_mut(self, builder: &mut AssistBuilder) -> Self {
161 match self {
162 Self::SelfParam(it) => Self::SelfParam(builder.make_ast_mut(it)),
163 Self::RefType(it) => Self::RefType(builder.make_ast_mut(it)),
136 } 164 }
137 // create a new type param list containing only the new lifetime parameter 165 }
138 None => { 166
139 builder.insert(new_type_params_loc, format!("<'{}>", new_lifetime_param)); 167 fn to_position(self) -> Option<Position> {
168 match self {
169 Self::SelfParam(it) => Some(Position::after(it.amp_token()?)),
170 Self::RefType(it) => Some(Position::after(it.amp_token()?)),
140 } 171 }
141 } 172 }
142} 173}
@@ -312,4 +343,13 @@ mod tests {
312 r#"fn my_fun<'other, 'a>(self, f: &'a Foo, b: &'other Bar) -> X<'a>"#, 343 r#"fn my_fun<'other, 'a>(self, f: &'a Foo, b: &'other Bar) -> X<'a>"#,
313 ); 344 );
314 } 345 }
346
347 #[test]
348 fn test_function_add_lifetime_to_self_ref_mut() {
349 check_assist(
350 introduce_named_lifetime,
351 r#"fn foo(&mut self) -> &'_$0 ()"#,
352 r#"fn foo<'a>(&'a mut self) -> &'a ()"#,
353 );
354 }
315} 355}
diff --git a/crates/parser/src/grammar/types.rs b/crates/parser/src/grammar/types.rs
index 94cbf7d85..6ae3e734f 100644
--- a/crates/parser/src/grammar/types.rs
+++ b/crates/parser/src/grammar/types.rs
@@ -283,17 +283,21 @@ pub(super) fn path_type(p: &mut Parser) {
283// type B = crate::foo!(); 283// type B = crate::foo!();
284fn path_or_macro_type_(p: &mut Parser, allow_bounds: bool) { 284fn path_or_macro_type_(p: &mut Parser, allow_bounds: bool) {
285 assert!(paths::is_path_start(p)); 285 assert!(paths::is_path_start(p));
286 let r = p.start();
286 let m = p.start(); 287 let m = p.start();
288
287 paths::type_path(p); 289 paths::type_path(p);
288 290
289 let kind = if p.at(T![!]) && !p.at(T![!=]) { 291 let kind = if p.at(T![!]) && !p.at(T![!=]) {
290 items::macro_call_after_excl(p); 292 items::macro_call_after_excl(p);
291 MACRO_CALL 293 m.complete(p, MACRO_CALL);
294 MACRO_TYPE
292 } else { 295 } else {
296 m.abandon(p);
293 PATH_TYPE 297 PATH_TYPE
294 }; 298 };
295 299
296 let path = m.complete(p, kind); 300 let path = r.complete(p, kind);
297 301
298 if allow_bounds { 302 if allow_bounds {
299 opt_type_bounds_as_dyn_trait_type(p, path); 303 opt_type_bounds_as_dyn_trait_type(p, path);
@@ -319,7 +323,7 @@ pub(super) fn path_type_(p: &mut Parser, allow_bounds: bool) {
319fn opt_type_bounds_as_dyn_trait_type(p: &mut Parser, type_marker: CompletedMarker) { 323fn opt_type_bounds_as_dyn_trait_type(p: &mut Parser, type_marker: CompletedMarker) {
320 assert!(matches!( 324 assert!(matches!(
321 type_marker.kind(), 325 type_marker.kind(),
322 SyntaxKind::PATH_TYPE | SyntaxKind::FOR_TYPE | SyntaxKind::MACRO_CALL 326 SyntaxKind::PATH_TYPE | SyntaxKind::FOR_TYPE | SyntaxKind::MACRO_TYPE
323 )); 327 ));
324 if !p.at(T![+]) { 328 if !p.at(T![+]) {
325 return; 329 return;
diff --git a/crates/syntax/src/ast/edit_in_place.rs b/crates/syntax/src/ast/edit_in_place.rs
index 529bd0eb1..04f97f368 100644
--- a/crates/syntax/src/ast/edit_in_place.rs
+++ b/crates/syntax/src/ast/edit_in_place.rs
@@ -14,10 +14,29 @@ use crate::{
14use super::NameOwner; 14use super::NameOwner;
15 15
16pub trait GenericParamsOwnerEdit: ast::GenericParamsOwner + AstNodeEdit { 16pub trait GenericParamsOwnerEdit: ast::GenericParamsOwner + AstNodeEdit {
17 fn get_or_create_generic_param_list(&self) -> ast::GenericParamList;
17 fn get_or_create_where_clause(&self) -> ast::WhereClause; 18 fn get_or_create_where_clause(&self) -> ast::WhereClause;
18} 19}
19 20
20impl GenericParamsOwnerEdit for ast::Fn { 21impl GenericParamsOwnerEdit for ast::Fn {
22 fn get_or_create_generic_param_list(&self) -> ast::GenericParamList {
23 match self.generic_param_list() {
24 Some(it) => it,
25 None => {
26 let position = if let Some(name) = self.name() {
27 Position::after(name.syntax)
28 } else if let Some(fn_token) = self.fn_token() {
29 Position::after(fn_token)
30 } else if let Some(param_list) = self.param_list() {
31 Position::before(param_list.syntax)
32 } else {
33 Position::last_child_of(self.syntax())
34 };
35 create_generic_param_list(position)
36 }
37 }
38 }
39
21 fn get_or_create_where_clause(&self) -> WhereClause { 40 fn get_or_create_where_clause(&self) -> WhereClause {
22 if self.where_clause().is_none() { 41 if self.where_clause().is_none() {
23 let position = if let Some(ty) = self.ret_type() { 42 let position = if let Some(ty) = self.ret_type() {
@@ -34,6 +53,20 @@ impl GenericParamsOwnerEdit for ast::Fn {
34} 53}
35 54
36impl GenericParamsOwnerEdit for ast::Impl { 55impl GenericParamsOwnerEdit for ast::Impl {
56 fn get_or_create_generic_param_list(&self) -> ast::GenericParamList {
57 match self.generic_param_list() {
58 Some(it) => it,
59 None => {
60 let position = if let Some(imp_token) = self.impl_token() {
61 Position::after(imp_token)
62 } else {
63 Position::last_child_of(self.syntax())
64 };
65 create_generic_param_list(position)
66 }
67 }
68 }
69
37 fn get_or_create_where_clause(&self) -> WhereClause { 70 fn get_or_create_where_clause(&self) -> WhereClause {
38 if self.where_clause().is_none() { 71 if self.where_clause().is_none() {
39 let position = if let Some(items) = self.assoc_item_list() { 72 let position = if let Some(items) = self.assoc_item_list() {
@@ -48,6 +81,22 @@ impl GenericParamsOwnerEdit for ast::Impl {
48} 81}
49 82
50impl GenericParamsOwnerEdit for ast::Trait { 83impl GenericParamsOwnerEdit for ast::Trait {
84 fn get_or_create_generic_param_list(&self) -> ast::GenericParamList {
85 match self.generic_param_list() {
86 Some(it) => it,
87 None => {
88 let position = if let Some(name) = self.name() {
89 Position::after(name.syntax)
90 } else if let Some(trait_token) = self.trait_token() {
91 Position::after(trait_token)
92 } else {
93 Position::last_child_of(self.syntax())
94 };
95 create_generic_param_list(position)
96 }
97 }
98 }
99
51 fn get_or_create_where_clause(&self) -> WhereClause { 100 fn get_or_create_where_clause(&self) -> WhereClause {
52 if self.where_clause().is_none() { 101 if self.where_clause().is_none() {
53 let position = if let Some(items) = self.assoc_item_list() { 102 let position = if let Some(items) = self.assoc_item_list() {
@@ -62,6 +111,22 @@ impl GenericParamsOwnerEdit for ast::Trait {
62} 111}
63 112
64impl GenericParamsOwnerEdit for ast::Struct { 113impl GenericParamsOwnerEdit for ast::Struct {
114 fn get_or_create_generic_param_list(&self) -> ast::GenericParamList {
115 match self.generic_param_list() {
116 Some(it) => it,
117 None => {
118 let position = if let Some(name) = self.name() {
119 Position::after(name.syntax)
120 } else if let Some(struct_token) = self.struct_token() {
121 Position::after(struct_token)
122 } else {
123 Position::last_child_of(self.syntax())
124 };
125 create_generic_param_list(position)
126 }
127 }
128 }
129
65 fn get_or_create_where_clause(&self) -> WhereClause { 130 fn get_or_create_where_clause(&self) -> WhereClause {
66 if self.where_clause().is_none() { 131 if self.where_clause().is_none() {
67 let tfl = self.field_list().and_then(|fl| match fl { 132 let tfl = self.field_list().and_then(|fl| match fl {
@@ -84,6 +149,22 @@ impl GenericParamsOwnerEdit for ast::Struct {
84} 149}
85 150
86impl GenericParamsOwnerEdit for ast::Enum { 151impl GenericParamsOwnerEdit for ast::Enum {
152 fn get_or_create_generic_param_list(&self) -> ast::GenericParamList {
153 match self.generic_param_list() {
154 Some(it) => it,
155 None => {
156 let position = if let Some(name) = self.name() {
157 Position::after(name.syntax)
158 } else if let Some(enum_token) = self.enum_token() {
159 Position::after(enum_token)
160 } else {
161 Position::last_child_of(self.syntax())
162 };
163 create_generic_param_list(position)
164 }
165 }
166 }
167
87 fn get_or_create_where_clause(&self) -> WhereClause { 168 fn get_or_create_where_clause(&self) -> WhereClause {
88 if self.where_clause().is_none() { 169 if self.where_clause().is_none() {
89 let position = if let Some(gpl) = self.generic_param_list() { 170 let position = if let Some(gpl) = self.generic_param_list() {
@@ -104,6 +185,37 @@ fn create_where_clause(position: Position) {
104 ted::insert(position, where_clause.syntax()); 185 ted::insert(position, where_clause.syntax());
105} 186}
106 187
188fn create_generic_param_list(position: Position) -> ast::GenericParamList {
189 let gpl = make::generic_param_list(empty()).clone_for_update();
190 ted::insert_raw(position, gpl.syntax());
191 gpl
192}
193
194impl ast::GenericParamList {
195 pub fn add_generic_param(&self, generic_param: ast::GenericParam) {
196 match self.generic_params().last() {
197 Some(last_param) => {
198 let mut elems = Vec::new();
199 if !last_param
200 .syntax()
201 .siblings_with_tokens(Direction::Next)
202 .any(|it| it.kind() == T![,])
203 {
204 elems.push(make::token(T![,]).into());
205 elems.push(make::tokens::single_space().into());
206 };
207 elems.push(generic_param.syntax().clone().into());
208 let after_last_param = Position::after(last_param.syntax());
209 ted::insert_all(after_last_param, elems);
210 }
211 None => {
212 let after_l_angle = Position::after(self.l_angle_token().unwrap());
213 ted::insert(after_l_angle, generic_param.syntax())
214 }
215 }
216 }
217}
218
107impl ast::WhereClause { 219impl ast::WhereClause {
108 pub fn add_predicate(&self, predicate: ast::WherePred) { 220 pub fn add_predicate(&self, predicate: ast::WherePred) {
109 if let Some(pred) = self.predicates().last() { 221 if let Some(pred) = self.predicates().last() {
@@ -164,3 +276,44 @@ impl ast::Use {
164 ted::remove(self.syntax()) 276 ted::remove(self.syntax())
165 } 277 }
166} 278}
279
280#[cfg(test)]
281mod tests {
282 use std::fmt;
283
284 use crate::SourceFile;
285
286 use super::*;
287
288 fn ast_mut_from_text<N: AstNode>(text: &str) -> N {
289 let parse = SourceFile::parse(text);
290 parse.tree().syntax().descendants().find_map(N::cast).unwrap().clone_for_update()
291 }
292
293 #[test]
294 fn test_create_generic_param_list() {
295 fn check_create_gpl<N: GenericParamsOwnerEdit + fmt::Display>(before: &str, after: &str) {
296 let gpl_owner = ast_mut_from_text::<N>(before);
297 gpl_owner.get_or_create_generic_param_list();
298 assert_eq!(gpl_owner.to_string(), after);
299 }
300
301 check_create_gpl::<ast::Fn>("fn foo", "fn foo<>");
302 check_create_gpl::<ast::Fn>("fn foo() {}", "fn foo<>() {}");
303
304 check_create_gpl::<ast::Impl>("impl", "impl<>");
305 check_create_gpl::<ast::Impl>("impl Struct {}", "impl<> Struct {}");
306 check_create_gpl::<ast::Impl>("impl Trait for Struct {}", "impl<> Trait for Struct {}");
307
308 check_create_gpl::<ast::Trait>("trait Trait<>", "trait Trait<>");
309 check_create_gpl::<ast::Trait>("trait Trait<> {}", "trait Trait<> {}");
310
311 check_create_gpl::<ast::Struct>("struct A", "struct A<>");
312 check_create_gpl::<ast::Struct>("struct A;", "struct A<>;");
313 check_create_gpl::<ast::Struct>("struct A();", "struct A<>();");
314 check_create_gpl::<ast::Struct>("struct A {}", "struct A<> {}");
315
316 check_create_gpl::<ast::Enum>("enum E", "enum E<>");
317 check_create_gpl::<ast::Enum>("enum E {", "enum E<> {");
318 }
319}
diff --git a/crates/syntax/src/ted.rs b/crates/syntax/src/ted.rs
index 177d4ff67..450f2e447 100644
--- a/crates/syntax/src/ted.rs
+++ b/crates/syntax/src/ted.rs
@@ -165,6 +165,13 @@ fn ws_between(left: &SyntaxElement, right: &SyntaxElement) -> Option<SyntaxToken
165 if right.kind() == T![;] || right.kind() == T![,] { 165 if right.kind() == T![;] || right.kind() == T![,] {
166 return None; 166 return None;
167 } 167 }
168 if left.kind() == T![<] || right.kind() == T![>] {
169 return None;
170 }
171 if left.kind() == T![&] && right.kind() == SyntaxKind::LIFETIME {
172 return None;
173 }
174
168 if right.kind() == SyntaxKind::USE { 175 if right.kind() == SyntaxKind::USE {
169 let indent = IndentLevel::from_element(left); 176 let indent = IndentLevel::from_element(left);
170 return Some(make::tokens::whitespace(&format!("\n{}", indent))); 177 return Some(make::tokens::whitespace(&format!("\n{}", indent)));
diff --git a/crates/syntax/test_data/parser/inline/ok/0117_macro_call_type.rast b/crates/syntax/test_data/parser/inline/ok/0117_macro_call_type.rast
index 3016a6574..1ff3f7656 100644
--- a/crates/syntax/test_data/parser/inline/ok/0117_macro_call_type.rast
+++ b/crates/syntax/test_data/parser/inline/ok/0117_macro_call_type.rast
@@ -7,15 +7,16 @@ [email protected]
7 [email protected] " " 7 [email protected] " "
8 [email protected] "=" 8 [email protected] "="
9 [email protected] " " 9 [email protected] " "
10 [email protected] 10 [email protected]
11 [email protected] 11 [email protected]
12 [email protected] 12 [email protected]
13 [email protected] 13 [email protected]
14 [email protected] "foo" 14 [email protected]
15 [email protected] "!" 15 [email protected] "foo"
16 [email protected] 16 [email protected] "!"
17 [email protected] "(" 17 [email protected]
18 [email protected] ")" 18 [email protected] "("
19 [email protected] ")"
19 [email protected] ";" 20 [email protected] ";"
20 [email protected] "\n" 21 [email protected] "\n"
21 [email protected] 22 [email protected]
@@ -26,19 +27,20 @@ [email protected]
26 [email protected] " " 27 [email protected] " "
27 [email protected] "=" 28 [email protected] "="
28 [email protected] " " 29 [email protected] " "
29 [email protected] 30 [email protected]
30 [email protected] 31 [email protected]
31 [email protected] 32 [email protected]
32 [email protected] 33 [email protected]
33 [email protected] 34 [email protected]
34 [email protected] "crate" 35 [email protected]
35 [email protected] "::" 36 [email protected] "crate"
36 [email protected] 37 [email protected] "::"
37 [email protected] 38 [email protected]
38 [email protected] "foo" 39 [email protected]
39 [email protected] "!" 40 [email protected] "foo"
40 [email protected] 41 [email protected] "!"
41 [email protected] "(" 42 [email protected]
42 [email protected] ")" 43 [email protected] "("
44 [email protected] ")"
43 [email protected] ";" 45 [email protected] ";"
44 [email protected] "\n" 46 [email protected] "\n"