aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-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
-rw-r--r--docs/dev/README.md3
21 files changed, 692 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"
diff --git a/docs/dev/README.md b/docs/dev/README.md
index d0e6d29d8..7e4488a41 100644
--- a/docs/dev/README.md
+++ b/docs/dev/README.md
@@ -242,6 +242,9 @@ There are three sets of people with extra permissions:
242 They also have direct commit access, but all changes should via bors queue. 242 They also have direct commit access, but all changes should via bors queue.
243 It's ok to self-approve if you think you know what you are doing! 243 It's ok to self-approve if you think you know what you are doing!
244 bors should automatically sync the permissions. 244 bors should automatically sync the permissions.
245 Feel free to request a review or assign any PR to a reviewer with the relevant expertise to bring the work to their attention.
246 Don't feel pressured to review assigned PRs though.
247 If you don't feel like reviewing for whatever reason, someone else will pick the review up!
245* [**triage**](https://github.com/orgs/rust-analyzer/teams/triage) team in the organization. 248* [**triage**](https://github.com/orgs/rust-analyzer/teams/triage) team in the organization.
246 This team can label and close issues. 249 This team can label and close issues.
247 250