aboutsummaryrefslogtreecommitdiff
path: root/crates
diff options
context:
space:
mode:
Diffstat (limited to 'crates')
-rw-r--r--crates/base_db/src/fixture.rs8
-rw-r--r--crates/hir/src/lib.rs12
-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.rs42
-rw-r--r--crates/hir_def/src/body/tests.rs2
-rw-r--r--crates/hir_def/src/diagnostics.rs5
-rw-r--r--crates/hir_def/src/find_path.rs604
-rw-r--r--crates/hir_def/src/item_tree.rs5
-rw-r--r--crates/hir_def/src/lib.rs30
-rw-r--r--crates/hir_def/src/nameres.rs13
-rw-r--r--crates/hir_def/src/nameres/collector.rs9
-rw-r--r--crates/hir_def/src/nameres/path_resolution.rs8
-rw-r--r--crates/hir_def/src/nameres/tests/diagnostics.rs4
-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/test_db.rs51
-rw-r--r--crates/hir_def/src/type_ref.rs18
-rw-r--r--crates/hir_def/src/visibility.rs10
-rw-r--r--crates/hir_expand/src/builtin_macro.rs10
-rw-r--r--crates/hir_expand/src/db.rs1
-rw-r--r--crates/hir_expand/src/eager.rs1
-rw-r--r--crates/hir_expand/src/name.rs1
-rw-r--r--crates/hir_ty/src/autoderef.rs2
-rw-r--r--crates/hir_ty/src/db.rs19
-rw-r--r--crates/hir_ty/src/display.rs15
-rw-r--r--crates/hir_ty/src/lib.rs1
-rw-r--r--crates/hir_ty/src/lower.rs69
-rw-r--r--crates/hir_ty/src/method_resolution.rs42
-rw-r--r--crates/hir_ty/src/tests/macros.rs199
-rw-r--r--crates/hir_ty/src/tests/method_resolution.rs37
-rw-r--r--crates/hir_ty/src/tests/simple.rs18
-rw-r--r--crates/hir_ty/src/utils.rs2
-rw-r--r--crates/ide/src/diagnostics.rs2
-rw-r--r--crates/ide/src/lib.rs6
-rw-r--r--crates/ide/src/move_item.rs100
-rw-r--r--crates/ide/src/syntax_highlighting/inject.rs16
-rw-r--r--crates/ide/src/syntax_highlighting/tests.rs2
-rw-r--r--crates/ide_assists/src/handlers/fill_match_arms.rs150
-rw-r--r--crates/ide_assists/src/handlers/generate_deref.rs227
-rw-r--r--crates/ide_assists/src/handlers/introduce_named_lifetime.rs112
-rw-r--r--crates/ide_assists/src/lib.rs2
-rw-r--r--crates/ide_assists/src/tests.rs7
-rw-r--r--crates/ide_assists/src/tests/generated.rs27
-rw-r--r--crates/ide_completion/src/completions/flyimport.rs73
-rw-r--r--crates/ide_db/src/apply_change.rs2
-rw-r--r--crates/ide_db/src/call_info.rs10
-rw-r--r--crates/ide_db/src/call_info/tests.rs27
-rw-r--r--crates/ide_db/src/helpers.rs5
-rw-r--r--crates/ide_db/src/helpers/famous_defs_fixture.rs8
-rw-r--r--crates/ide_db/src/helpers/import_assets.rs22
-rw-r--r--crates/ide_db/src/helpers/rust_doc.rs34
-rw-r--r--crates/mbe/src/syntax_bridge.rs2
-rw-r--r--crates/mbe/src/tests/expand.rs42
-rw-r--r--crates/parser/src/grammar.rs37
-rw-r--r--crates/parser/src/grammar/attributes.rs28
-rw-r--r--crates/parser/src/grammar/types.rs10
-rw-r--r--crates/rust-analyzer/src/config.rs11
-rw-r--r--crates/rust-analyzer/src/diagnostics/test_data/clippy_pass_by_ref.txt1
-rw-r--r--crates/rust-analyzer/src/diagnostics/test_data/rustc_unused_variable.txt1
-rw-r--r--crates/rust-analyzer/src/diagnostics/test_data/rustc_unused_variable_as_hint.txt1
-rw-r--r--crates/rust-analyzer/src/diagnostics/test_data/rustc_unused_variable_as_info.txt1
-rw-r--r--crates/rust-analyzer/src/diagnostics/test_data/snap_multi_line_fix.txt1
-rw-r--r--crates/rust-analyzer/src/diagnostics/to_proto.rs1
-rw-r--r--crates/rust-analyzer/src/handlers.rs9
-rw-r--r--crates/rust-analyzer/src/lsp_ext.rs8
-rw-r--r--crates/rust-analyzer/src/main_loop.rs8
-rw-r--r--crates/rust-analyzer/src/markdown.rs22
-rw-r--r--crates/rust-analyzer/src/to_proto.rs109
-rw-r--r--crates/syntax/Cargo.toml2
-rw-r--r--crates/syntax/src/ast/edit_in_place.rs153
-rw-r--r--crates/syntax/src/ast/make.rs18
-rw-r--r--crates/syntax/src/ted.rs7
-rw-r--r--crates/syntax/test_data/parser/inline/ok/0117_macro_call_type.rast48
75 files changed, 2023 insertions, 633 deletions
diff --git a/crates/base_db/src/fixture.rs b/crates/base_db/src/fixture.rs
index 04e2be390..0132565e4 100644
--- a/crates/base_db/src/fixture.rs
+++ b/crates/base_db/src/fixture.rs
@@ -35,7 +35,7 @@ pub trait WithFixture: Default + SourceDatabaseExt + 'static {
35 fn with_position(ra_fixture: &str) -> (Self, FilePosition) { 35 fn with_position(ra_fixture: &str) -> (Self, FilePosition) {
36 let (db, file_id, range_or_offset) = Self::with_range_or_offset(ra_fixture); 36 let (db, file_id, range_or_offset) = Self::with_range_or_offset(ra_fixture);
37 let offset = match range_or_offset { 37 let offset = match range_or_offset {
38 RangeOrOffset::Range(_) => panic!(), 38 RangeOrOffset::Range(_) => panic!("Expected a cursor position, got a range instead"),
39 RangeOrOffset::Offset(it) => it, 39 RangeOrOffset::Offset(it) => it,
40 }; 40 };
41 (db, FilePosition { file_id, offset }) 41 (db, FilePosition { file_id, offset })
@@ -45,7 +45,7 @@ pub trait WithFixture: Default + SourceDatabaseExt + 'static {
45 let (db, file_id, range_or_offset) = Self::with_range_or_offset(ra_fixture); 45 let (db, file_id, range_or_offset) = Self::with_range_or_offset(ra_fixture);
46 let range = match range_or_offset { 46 let range = match range_or_offset {
47 RangeOrOffset::Range(it) => it, 47 RangeOrOffset::Range(it) => it,
48 RangeOrOffset::Offset(_) => panic!(), 48 RangeOrOffset::Offset(_) => panic!("Expected a cursor range, got a position instead"),
49 }; 49 };
50 (db, FileRange { file_id, range }) 50 (db, FileRange { file_id, range })
51 } 51 }
@@ -54,7 +54,9 @@ pub trait WithFixture: Default + SourceDatabaseExt + 'static {
54 let fixture = ChangeFixture::parse(ra_fixture); 54 let fixture = ChangeFixture::parse(ra_fixture);
55 let mut db = Self::default(); 55 let mut db = Self::default();
56 fixture.change.apply(&mut db); 56 fixture.change.apply(&mut db);
57 let (file_id, range_or_offset) = fixture.file_position.unwrap(); 57 let (file_id, range_or_offset) = fixture
58 .file_position
59 .expect("Could not find file position in fixture. Did you forget to add an `$0`?");
58 (db, file_id, range_or_offset) 60 (db, file_id, range_or_offset)
59 } 61 }
60 62
diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs
index eba46a056..0acfa582a 100644
--- a/crates/hir/src/lib.rs
+++ b/crates/hir/src/lib.rs
@@ -2066,6 +2066,18 @@ impl Type {
2066 self.ty.dyn_trait().map(Into::into) 2066 self.ty.dyn_trait().map(Into::into)
2067 } 2067 }
2068 2068
2069 /// If a type can be represented as `dyn Trait`, returns all traits accessible via this type,
2070 /// or an empty iterator otherwise.
2071 pub fn applicable_inherent_traits<'a>(
2072 &'a self,
2073 db: &'a dyn HirDatabase,
2074 ) -> impl Iterator<Item = Trait> + 'a {
2075 self.autoderef(db)
2076 .filter_map(|derefed_type| derefed_type.ty.dyn_trait())
2077 .flat_map(move |dyn_trait_id| hir_ty::all_super_traits(db.upcast(), dyn_trait_id))
2078 .map(Trait::from)
2079 }
2080
2069 pub fn as_impl_traits(&self, db: &dyn HirDatabase) -> Option<Vec<Trait>> { 2081 pub fn as_impl_traits(&self, db: &dyn HirDatabase) -> Option<Vec<Trait>> {
2070 self.ty.impl_trait_bounds(db).map(|it| { 2082 self.ty.impl_trait_bounds(db).map(|it| {
2071 it.into_iter() 2083 it.into_iter()
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 ed07d6928..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
@@ -568,9 +588,13 @@ impl ExprCollector<'_> {
568 588
569 let res = match res { 589 let res = match res {
570 Ok(res) => res, 590 Ok(res) => res,
571 Err(UnresolvedMacro) => { 591 Err(UnresolvedMacro { path }) => {
572 self.source_map.diagnostics.push(BodyDiagnostic::UnresolvedMacroCall( 592 self.source_map.diagnostics.push(BodyDiagnostic::UnresolvedMacroCall(
573 UnresolvedMacroCall { file: outer_file, node: syntax_ptr.cast().unwrap() }, 593 UnresolvedMacroCall {
594 file: outer_file,
595 node: syntax_ptr.cast().unwrap(),
596 path,
597 },
574 )); 598 ));
575 collector(self, None); 599 collector(self, None);
576 return; 600 return;
diff --git a/crates/hir_def/src/body/tests.rs b/crates/hir_def/src/body/tests.rs
index c1d3e998f..63f5fe88d 100644
--- a/crates/hir_def/src/body/tests.rs
+++ b/crates/hir_def/src/body/tests.rs
@@ -180,7 +180,7 @@ fn unresolved_macro_diag() {
180 r#" 180 r#"
181fn f() { 181fn f() {
182 m!(); 182 m!();
183 //^^^^ unresolved macro call 183 //^^^^ unresolved macro `m!`
184} 184}
185 "#, 185 "#,
186 ); 186 );
diff --git a/crates/hir_def/src/diagnostics.rs b/crates/hir_def/src/diagnostics.rs
index 97abf8653..a71ae2668 100644
--- a/crates/hir_def/src/diagnostics.rs
+++ b/crates/hir_def/src/diagnostics.rs
@@ -8,7 +8,7 @@ use hir_expand::diagnostics::{Diagnostic, DiagnosticCode, DiagnosticSink};
8use hir_expand::{HirFileId, InFile}; 8use hir_expand::{HirFileId, InFile};
9use syntax::{ast, AstPtr, SyntaxNodePtr, TextRange}; 9use syntax::{ast, AstPtr, SyntaxNodePtr, TextRange};
10 10
11use crate::{db::DefDatabase, DefWithBodyId}; 11use crate::{db::DefDatabase, path::ModPath, DefWithBodyId};
12 12
13pub fn validate_body(db: &dyn DefDatabase, owner: DefWithBodyId, sink: &mut DiagnosticSink<'_>) { 13pub fn validate_body(db: &dyn DefDatabase, owner: DefWithBodyId, sink: &mut DiagnosticSink<'_>) {
14 let source_map = db.body_with_source_map(owner).1; 14 let source_map = db.body_with_source_map(owner).1;
@@ -103,6 +103,7 @@ impl Diagnostic for UnresolvedImport {
103pub struct UnresolvedMacroCall { 103pub struct UnresolvedMacroCall {
104 pub file: HirFileId, 104 pub file: HirFileId,
105 pub node: AstPtr<ast::MacroCall>, 105 pub node: AstPtr<ast::MacroCall>,
106 pub path: ModPath,
106} 107}
107 108
108impl Diagnostic for UnresolvedMacroCall { 109impl Diagnostic for UnresolvedMacroCall {
@@ -110,7 +111,7 @@ impl Diagnostic for UnresolvedMacroCall {
110 DiagnosticCode("unresolved-macro-call") 111 DiagnosticCode("unresolved-macro-call")
111 } 112 }
112 fn message(&self) -> String { 113 fn message(&self) -> String {
113 "unresolved macro call".to_string() 114 format!("unresolved macro `{}!`", self.path)
114 } 115 }
115 fn display_source(&self) -> InFile<SyntaxNodePtr> { 116 fn display_source(&self) -> InFile<SyntaxNodePtr> {
116 InFile::new(self.file, self.node.clone().into()) 117 InFile::new(self.file, self.node.clone().into())
diff --git a/crates/hir_def/src/find_path.rs b/crates/hir_def/src/find_path.rs
index 109d3552f..2c4bbe585 100644
--- a/crates/hir_def/src/find_path.rs
+++ b/crates/hir_def/src/find_path.rs
@@ -425,106 +425,142 @@ mod tests {
425 425
426 #[test] 426 #[test]
427 fn same_module() { 427 fn same_module() {
428 let code = r#" 428 check_found_path(
429 //- /main.rs 429 r#"
430 struct S; 430struct S;
431 $0 431$0
432 "#; 432 "#,
433 check_found_path(code, "S", "S", "crate::S", "self::S"); 433 "S",
434 "S",
435 "crate::S",
436 "self::S",
437 );
434 } 438 }
435 439
436 #[test] 440 #[test]
437 fn enum_variant() { 441 fn enum_variant() {
438 let code = r#" 442 check_found_path(
439 //- /main.rs 443 r#"
440 enum E { A } 444enum E { A }
441 $0 445$0
442 "#; 446 "#,
443 check_found_path(code, "E::A", "E::A", "E::A", "E::A"); 447 "E::A",
448 "E::A",
449 "E::A",
450 "E::A",
451 );
444 } 452 }
445 453
446 #[test] 454 #[test]
447 fn sub_module() { 455 fn sub_module() {
448 let code = r#" 456 check_found_path(
449 //- /main.rs 457 r#"
450 mod foo { 458mod foo {
451 pub struct S; 459 pub struct S;
452 } 460}
453 $0 461$0
454 "#; 462 "#,
455 check_found_path(code, "foo::S", "foo::S", "crate::foo::S", "self::foo::S"); 463 "foo::S",
464 "foo::S",
465 "crate::foo::S",
466 "self::foo::S",
467 );
456 } 468 }
457 469
458 #[test] 470 #[test]
459 fn super_module() { 471 fn super_module() {
460 let code = r#" 472 check_found_path(
461 //- /main.rs 473 r#"
462 mod foo; 474//- /main.rs
463 //- /foo.rs 475mod foo;
464 mod bar; 476//- /foo.rs
465 struct S; 477mod bar;
466 //- /foo/bar.rs 478struct S;
467 $0 479//- /foo/bar.rs
468 "#; 480$0
469 check_found_path(code, "super::S", "super::S", "crate::foo::S", "super::S"); 481 "#,
482 "super::S",
483 "super::S",
484 "crate::foo::S",
485 "super::S",
486 );
470 } 487 }
471 488
472 #[test] 489 #[test]
473 fn self_module() { 490 fn self_module() {
474 let code = r#" 491 check_found_path(
475 //- /main.rs 492 r#"
476 mod foo; 493//- /main.rs
477 //- /foo.rs 494mod foo;
478 $0 495//- /foo.rs
479 "#; 496$0
480 check_found_path(code, "self", "self", "crate::foo", "self"); 497 "#,
498 "self",
499 "self",
500 "crate::foo",
501 "self",
502 );
481 } 503 }
482 504
483 #[test] 505 #[test]
484 fn crate_root() { 506 fn crate_root() {
485 let code = r#" 507 check_found_path(
486 //- /main.rs 508 r#"
487 mod foo; 509//- /main.rs
488 //- /foo.rs 510mod foo;
489 $0 511//- /foo.rs
490 "#; 512$0
491 check_found_path(code, "crate", "crate", "crate", "crate"); 513 "#,
514 "crate",
515 "crate",
516 "crate",
517 "crate",
518 );
492 } 519 }
493 520
494 #[test] 521 #[test]
495 fn same_crate() { 522 fn same_crate() {
496 let code = r#" 523 check_found_path(
497 //- /main.rs 524 r#"
498 mod foo; 525//- /main.rs
499 struct S; 526mod foo;
500 //- /foo.rs 527struct S;
501 $0 528//- /foo.rs
502 "#; 529$0
503 check_found_path(code, "crate::S", "crate::S", "crate::S", "crate::S"); 530 "#,
531 "crate::S",
532 "crate::S",
533 "crate::S",
534 "crate::S",
535 );
504 } 536 }
505 537
506 #[test] 538 #[test]
507 fn different_crate() { 539 fn different_crate() {
508 let code = r#" 540 check_found_path(
509 //- /main.rs crate:main deps:std 541 r#"
510 $0 542//- /main.rs crate:main deps:std
511 //- /std.rs crate:std 543$0
512 pub struct S; 544//- /std.rs crate:std
513 "#; 545pub struct S;
514 check_found_path(code, "std::S", "std::S", "std::S", "std::S"); 546 "#,
547 "std::S",
548 "std::S",
549 "std::S",
550 "std::S",
551 );
515 } 552 }
516 553
517 #[test] 554 #[test]
518 fn different_crate_renamed() { 555 fn different_crate_renamed() {
519 let code = r#"
520 //- /main.rs crate:main deps:std
521 extern crate std as std_renamed;
522 $0
523 //- /std.rs crate:std
524 pub struct S;
525 "#;
526 check_found_path( 556 check_found_path(
527 code, 557 r#"
558//- /main.rs crate:main deps:std
559extern crate std as std_renamed;
560$0
561//- /std.rs crate:std
562pub struct S;
563 "#,
528 "std_renamed::S", 564 "std_renamed::S",
529 "std_renamed::S", 565 "std_renamed::S",
530 "std_renamed::S", 566 "std_renamed::S",
@@ -537,41 +573,38 @@ mod tests {
537 cov_mark::check!(partially_imported); 573 cov_mark::check!(partially_imported);
538 // Tests that short paths are used even for external items, when parts of the path are 574 // Tests that short paths are used even for external items, when parts of the path are
539 // already in scope. 575 // already in scope.
540 let code = r#" 576 check_found_path(
541 //- /main.rs crate:main deps:syntax 577 r#"
578//- /main.rs crate:main deps:syntax
542 579
543 use syntax::ast; 580use syntax::ast;
544 $0 581$0
545 582
546 //- /lib.rs crate:syntax 583//- /lib.rs crate:syntax
547 pub mod ast { 584pub mod ast {
548 pub enum ModuleItem { 585 pub enum ModuleItem {
549 A, B, C, 586 A, B, C,
550 } 587 }
551 } 588}
552 "#; 589 "#,
553 check_found_path(
554 code,
555 "ast::ModuleItem", 590 "ast::ModuleItem",
556 "syntax::ast::ModuleItem", 591 "syntax::ast::ModuleItem",
557 "syntax::ast::ModuleItem", 592 "syntax::ast::ModuleItem",
558 "syntax::ast::ModuleItem", 593 "syntax::ast::ModuleItem",
559 ); 594 );
560 595
561 let code = r#"
562 //- /main.rs crate:main deps:syntax
563
564 $0
565
566 //- /lib.rs crate:syntax
567 pub mod ast {
568 pub enum ModuleItem {
569 A, B, C,
570 }
571 }
572 "#;
573 check_found_path( 596 check_found_path(
574 code, 597 r#"
598//- /main.rs crate:main deps:syntax
599$0
600
601//- /lib.rs crate:syntax
602pub mod ast {
603 pub enum ModuleItem {
604 A, B, C,
605 }
606}
607 "#,
575 "syntax::ast::ModuleItem", 608 "syntax::ast::ModuleItem",
576 "syntax::ast::ModuleItem", 609 "syntax::ast::ModuleItem",
577 "syntax::ast::ModuleItem", 610 "syntax::ast::ModuleItem",
@@ -581,68 +614,86 @@ mod tests {
581 614
582 #[test] 615 #[test]
583 fn same_crate_reexport() { 616 fn same_crate_reexport() {
584 let code = r#" 617 check_found_path(
585 //- /main.rs 618 r#"
586 mod bar { 619mod bar {
587 mod foo { pub(super) struct S; } 620 mod foo { pub(super) struct S; }
588 pub(crate) use foo::*; 621 pub(crate) use foo::*;
589 } 622}
590 $0 623$0
591 "#; 624 "#,
592 check_found_path(code, "bar::S", "bar::S", "crate::bar::S", "self::bar::S"); 625 "bar::S",
626 "bar::S",
627 "crate::bar::S",
628 "self::bar::S",
629 );
593 } 630 }
594 631
595 #[test] 632 #[test]
596 fn same_crate_reexport_rename() { 633 fn same_crate_reexport_rename() {
597 let code = r#" 634 check_found_path(
598 //- /main.rs 635 r#"
599 mod bar { 636mod bar {
600 mod foo { pub(super) struct S; } 637 mod foo { pub(super) struct S; }
601 pub(crate) use foo::S as U; 638 pub(crate) use foo::S as U;
602 } 639}
603 $0 640$0
604 "#; 641 "#,
605 check_found_path(code, "bar::U", "bar::U", "crate::bar::U", "self::bar::U"); 642 "bar::U",
643 "bar::U",
644 "crate::bar::U",
645 "self::bar::U",
646 );
606 } 647 }
607 648
608 #[test] 649 #[test]
609 fn different_crate_reexport() { 650 fn different_crate_reexport() {
610 let code = r#" 651 check_found_path(
611 //- /main.rs crate:main deps:std 652 r#"
612 $0 653//- /main.rs crate:main deps:std
613 //- /std.rs crate:std deps:core 654$0
614 pub use core::S; 655//- /std.rs crate:std deps:core
615 //- /core.rs crate:core 656pub use core::S;
616 pub struct S; 657//- /core.rs crate:core
617 "#; 658pub struct S;
618 check_found_path(code, "std::S", "std::S", "std::S", "std::S"); 659 "#,
660 "std::S",
661 "std::S",
662 "std::S",
663 "std::S",
664 );
619 } 665 }
620 666
621 #[test] 667 #[test]
622 fn prelude() { 668 fn prelude() {
623 let code = r#" 669 check_found_path(
624 //- /main.rs crate:main deps:std 670 r#"
625 $0 671//- /main.rs crate:main deps:std
626 //- /std.rs crate:std 672$0
627 pub mod prelude { pub struct S; } 673//- /std.rs crate:std
628 #[prelude_import] 674pub mod prelude { pub struct S; }
629 pub use prelude::*; 675#[prelude_import]
630 "#; 676pub use prelude::*;
631 check_found_path(code, "S", "S", "S", "S"); 677 "#,
678 "S",
679 "S",
680 "S",
681 "S",
682 );
632 } 683 }
633 684
634 #[test] 685 #[test]
635 fn enum_variant_from_prelude() { 686 fn enum_variant_from_prelude() {
636 let code = r#" 687 let code = r#"
637 //- /main.rs crate:main deps:std 688//- /main.rs crate:main deps:std
638 $0 689$0
639 //- /std.rs crate:std 690//- /std.rs crate:std
640 pub mod prelude { 691pub mod prelude {
641 pub enum Option<T> { Some(T), None } 692 pub enum Option<T> { Some(T), None }
642 pub use Option::*; 693 pub use Option::*;
643 } 694}
644 #[prelude_import] 695#[prelude_import]
645 pub use prelude::*; 696pub use prelude::*;
646 "#; 697 "#;
647 check_found_path(code, "None", "None", "None", "None"); 698 check_found_path(code, "None", "None", "None", "None");
648 check_found_path(code, "Some", "Some", "Some", "Some"); 699 check_found_path(code, "Some", "Some", "Some", "Some");
@@ -650,71 +701,85 @@ mod tests {
650 701
651 #[test] 702 #[test]
652 fn shortest_path() { 703 fn shortest_path() {
653 let code = r#" 704 check_found_path(
654 //- /main.rs 705 r#"
655 pub mod foo; 706//- /main.rs
656 pub mod baz; 707pub mod foo;
657 struct S; 708pub mod baz;
658 $0 709struct S;
659 //- /foo.rs 710$0
660 pub mod bar { pub struct S; } 711//- /foo.rs
661 //- /baz.rs 712pub mod bar { pub struct S; }
662 pub use crate::foo::bar::S; 713//- /baz.rs
663 "#; 714pub use crate::foo::bar::S;
664 check_found_path(code, "baz::S", "baz::S", "crate::baz::S", "self::baz::S"); 715 "#,
716 "baz::S",
717 "baz::S",
718 "crate::baz::S",
719 "self::baz::S",
720 );
665 } 721 }
666 722
667 #[test] 723 #[test]
668 fn discount_private_imports() { 724 fn discount_private_imports() {
669 let code = r#" 725 check_found_path(
670 //- /main.rs 726 r#"
671 mod foo; 727//- /main.rs
672 pub mod bar { pub struct S; } 728mod foo;
673 use bar::S; 729pub mod bar { pub struct S; }
674 //- /foo.rs 730use bar::S;
675 $0 731//- /foo.rs
676 "#; 732$0
677 // crate::S would be shorter, but using private imports seems wrong 733 "#,
678 check_found_path(code, "crate::bar::S", "crate::bar::S", "crate::bar::S", "crate::bar::S"); 734 // crate::S would be shorter, but using private imports seems wrong
735 "crate::bar::S",
736 "crate::bar::S",
737 "crate::bar::S",
738 "crate::bar::S",
739 );
679 } 740 }
680 741
681 #[test] 742 #[test]
682 fn import_cycle() { 743 fn import_cycle() {
683 let code = r#" 744 check_found_path(
684 //- /main.rs 745 r#"
685 pub mod foo; 746//- /main.rs
686 pub mod bar; 747pub mod foo;
687 pub mod baz; 748pub mod bar;
688 //- /bar.rs 749pub mod baz;
689 $0 750//- /bar.rs
690 //- /foo.rs 751$0
691 pub use super::baz; 752//- /foo.rs
692 pub struct S; 753pub use super::baz;
693 //- /baz.rs 754pub struct S;
694 pub use super::foo; 755//- /baz.rs
695 "#; 756pub use super::foo;
696 check_found_path(code, "crate::foo::S", "crate::foo::S", "crate::foo::S", "crate::foo::S"); 757 "#,
758 "crate::foo::S",
759 "crate::foo::S",
760 "crate::foo::S",
761 "crate::foo::S",
762 );
697 } 763 }
698 764
699 #[test] 765 #[test]
700 fn prefer_std_paths_over_alloc() { 766 fn prefer_std_paths_over_alloc() {
701 cov_mark::check!(prefer_std_paths); 767 cov_mark::check!(prefer_std_paths);
702 let code = r#" 768 check_found_path(
703 //- /main.rs crate:main deps:alloc,std 769 r#"
704 $0 770//- /main.rs crate:main deps:alloc,std
771$0
705 772
706 //- /std.rs crate:std deps:alloc 773//- /std.rs crate:std deps:alloc
707 pub mod sync { 774pub mod sync {
708 pub use alloc::sync::Arc; 775 pub use alloc::sync::Arc;
709 } 776}
710 777
711 //- /zzz.rs crate:alloc 778//- /zzz.rs crate:alloc
712 pub mod sync { 779pub mod sync {
713 pub struct Arc; 780 pub struct Arc;
714 } 781}
715 "#; 782 "#,
716 check_found_path(
717 code,
718 "std::sync::Arc", 783 "std::sync::Arc",
719 "std::sync::Arc", 784 "std::sync::Arc",
720 "std::sync::Arc", 785 "std::sync::Arc",
@@ -725,26 +790,25 @@ mod tests {
725 #[test] 790 #[test]
726 fn prefer_core_paths_over_std() { 791 fn prefer_core_paths_over_std() {
727 cov_mark::check!(prefer_no_std_paths); 792 cov_mark::check!(prefer_no_std_paths);
728 let code = r#" 793 check_found_path(
729 //- /main.rs crate:main deps:core,std 794 r#"
730 #![no_std] 795//- /main.rs crate:main deps:core,std
796#![no_std]
731 797
732 $0 798$0
733 799
734 //- /std.rs crate:std deps:core 800//- /std.rs crate:std deps:core
735 801
736 pub mod fmt { 802pub mod fmt {
737 pub use core::fmt::Error; 803 pub use core::fmt::Error;
738 } 804}
739 805
740 //- /zzz.rs crate:core 806//- /zzz.rs crate:core
741 807
742 pub mod fmt { 808pub mod fmt {
743 pub struct Error; 809 pub struct Error;
744 } 810}
745 "#; 811 "#,
746 check_found_path(
747 code,
748 "core::fmt::Error", 812 "core::fmt::Error",
749 "core::fmt::Error", 813 "core::fmt::Error",
750 "core::fmt::Error", 814 "core::fmt::Error",
@@ -754,26 +818,25 @@ mod tests {
754 818
755 #[test] 819 #[test]
756 fn prefer_alloc_paths_over_std() { 820 fn prefer_alloc_paths_over_std() {
757 let code = r#" 821 check_found_path(
758 //- /main.rs crate:main deps:alloc,std 822 r#"
759 #![no_std] 823//- /main.rs crate:main deps:alloc,std
824#![no_std]
760 825
761 $0 826$0
762 827
763 //- /std.rs crate:std deps:alloc 828//- /std.rs crate:std deps:alloc
764 829
765 pub mod sync { 830pub mod sync {
766 pub use alloc::sync::Arc; 831 pub use alloc::sync::Arc;
767 } 832}
768 833
769 //- /zzz.rs crate:alloc 834//- /zzz.rs crate:alloc
770 835
771 pub mod sync { 836pub mod sync {
772 pub struct Arc; 837 pub struct Arc;
773 } 838}
774 "#; 839 "#,
775 check_found_path(
776 code,
777 "alloc::sync::Arc", 840 "alloc::sync::Arc",
778 "alloc::sync::Arc", 841 "alloc::sync::Arc",
779 "alloc::sync::Arc", 842 "alloc::sync::Arc",
@@ -783,20 +846,19 @@ mod tests {
783 846
784 #[test] 847 #[test]
785 fn prefer_shorter_paths_if_not_alloc() { 848 fn prefer_shorter_paths_if_not_alloc() {
786 let code = r#" 849 check_found_path(
787 //- /main.rs crate:main deps:megaalloc,std 850 r#"
788 $0 851//- /main.rs crate:main deps:megaalloc,std
852$0
789 853
790 //- /std.rs crate:std deps:megaalloc 854//- /std.rs crate:std deps:megaalloc
791 pub mod sync { 855pub mod sync {
792 pub use megaalloc::sync::Arc; 856 pub use megaalloc::sync::Arc;
793 } 857}
794 858
795 //- /zzz.rs crate:megaalloc 859//- /zzz.rs crate:megaalloc
796 pub struct Arc; 860pub struct Arc;
797 "#; 861 "#,
798 check_found_path(
799 code,
800 "megaalloc::Arc", 862 "megaalloc::Arc",
801 "megaalloc::Arc", 863 "megaalloc::Arc",
802 "megaalloc::Arc", 864 "megaalloc::Arc",
@@ -807,12 +869,11 @@ mod tests {
807 #[test] 869 #[test]
808 fn builtins_are_in_scope() { 870 fn builtins_are_in_scope() {
809 let code = r#" 871 let code = r#"
810 //- /main.rs 872$0
811 $0
812 873
813 pub mod primitive { 874pub mod primitive {
814 pub use u8; 875 pub use u8;
815 } 876}
816 "#; 877 "#;
817 check_found_path(code, "u8", "u8", "u8", "u8"); 878 check_found_path(code, "u8", "u8", "u8", "u8");
818 check_found_path(code, "u16", "u16", "u16", "u16"); 879 check_found_path(code, "u16", "u16", "u16", "u16");
@@ -822,10 +883,10 @@ mod tests {
822 fn inner_items() { 883 fn inner_items() {
823 check_found_path( 884 check_found_path(
824 r#" 885 r#"
825 fn main() { 886fn main() {
826 struct Inner {} 887 struct Inner {}
827 $0 888 $0
828 } 889}
829 "#, 890 "#,
830 "Inner", 891 "Inner",
831 "Inner", 892 "Inner",
@@ -838,12 +899,12 @@ mod tests {
838 fn inner_items_from_outer_scope() { 899 fn inner_items_from_outer_scope() {
839 check_found_path( 900 check_found_path(
840 r#" 901 r#"
841 fn main() { 902fn main() {
842 struct Struct {} 903 struct Struct {}
843 { 904 {
844 $0 905 $0
845 } 906 }
846 } 907}
847 "#, 908 "#,
848 "Struct", 909 "Struct",
849 "Struct", 910 "Struct",
@@ -857,14 +918,14 @@ mod tests {
857 cov_mark::check!(prefixed_in_block_expression); 918 cov_mark::check!(prefixed_in_block_expression);
858 check_found_path( 919 check_found_path(
859 r#" 920 r#"
860 fn main() { 921fn main() {
861 mod module { 922 mod module {
862 struct Struct {} 923 struct Struct {}
863 } 924 }
864 { 925 {
865 $0 926 $0
866 } 927 }
867 } 928}
868 "#, 929 "#,
869 "module::Struct", 930 "module::Struct",
870 "module::Struct", 931 "module::Struct",
@@ -877,14 +938,14 @@ mod tests {
877 fn outer_items_with_inner_items_present() { 938 fn outer_items_with_inner_items_present() {
878 check_found_path( 939 check_found_path(
879 r#" 940 r#"
880 mod module { 941mod module {
881 pub struct CompleteMe; 942 pub struct CompleteMe;
882 } 943}
883 944
884 fn main() { 945fn main() {
885 fn inner() {} 946 fn inner() {}
886 $0 947 $0
887 } 948}
888 "#, 949 "#,
889 "module::CompleteMe", 950 "module::CompleteMe",
890 "module::CompleteMe", 951 "module::CompleteMe",
@@ -894,6 +955,29 @@ mod tests {
894 } 955 }
895 956
896 #[test] 957 #[test]
958 fn from_inside_module() {
959 // This worked correctly, but the test suite logic was broken.
960 cov_mark::check!(submodule_in_testdb);
961 check_found_path(
962 r#"
963mod baz {
964 pub struct Foo {}
965}
966
967mod bar {
968 fn bar() {
969 $0
970 }
971}
972 "#,
973 "crate::baz::Foo",
974 "crate::baz::Foo",
975 "crate::baz::Foo",
976 "crate::baz::Foo",
977 )
978 }
979
980 #[test]
897 fn recursive_pub_mod_reexport() { 981 fn recursive_pub_mod_reexport() {
898 cov_mark::check!(recursive_imports); 982 cov_mark::check!(recursive_imports);
899 check_found_path( 983 check_found_path(
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 ffee05500..25694f037 100644
--- a/crates/hir_def/src/lib.rs
+++ b/crates/hir_def/src/lib.rs
@@ -66,6 +66,7 @@ use hir_expand::{
66}; 66};
67use la_arena::Idx; 67use la_arena::Idx;
68use nameres::DefMap; 68use nameres::DefMap;
69use path::ModPath;
69use syntax::ast; 70use syntax::ast;
70 71
71use crate::builtin_type::BuiltinType; 72use crate::builtin_type::BuiltinType;
@@ -107,6 +108,18 @@ impl ModuleId {
107 pub fn containing_module(&self, db: &dyn db::DefDatabase) -> Option<ModuleId> { 108 pub fn containing_module(&self, db: &dyn db::DefDatabase) -> Option<ModuleId> {
108 self.def_map(db).containing_module(self.local_id) 109 self.def_map(db).containing_module(self.local_id)
109 } 110 }
111
112 /// Returns `true` if this module represents a block expression.
113 ///
114 /// Returns `false` if this module is a submodule *inside* a block expression
115 /// (eg. `m` in `{ mod m {} }`).
116 pub fn is_block_root(&self, db: &dyn db::DefDatabase) -> bool {
117 if self.block.is_none() {
118 return false;
119 }
120
121 self.def_map(db)[self.local_id].parent.is_none()
122 }
110} 123}
111 124
112/// An ID of a module, **local** to a specific crate 125/// An ID of a module, **local** to a specific crate
@@ -675,7 +688,10 @@ impl<T: ast::AstNode> AstIdWithPath<T> {
675 } 688 }
676} 689}
677 690
678pub struct UnresolvedMacro; 691#[derive(Debug)]
692pub struct UnresolvedMacro {
693 pub path: ModPath,
694}
679 695
680fn macro_call_as_call_id( 696fn macro_call_as_call_id(
681 call: &AstIdWithPath<ast::MacroCall>, 697 call: &AstIdWithPath<ast::MacroCall>,
@@ -684,7 +700,8 @@ fn macro_call_as_call_id(
684 resolver: impl Fn(path::ModPath) -> Option<MacroDefId>, 700 resolver: impl Fn(path::ModPath) -> Option<MacroDefId>,
685 error_sink: &mut dyn FnMut(mbe::ExpandError), 701 error_sink: &mut dyn FnMut(mbe::ExpandError),
686) -> Result<Result<MacroCallId, ErrorEmitted>, UnresolvedMacro> { 702) -> Result<Result<MacroCallId, ErrorEmitted>, UnresolvedMacro> {
687 let def: MacroDefId = resolver(call.path.clone()).ok_or(UnresolvedMacro)?; 703 let def: MacroDefId =
704 resolver(call.path.clone()).ok_or_else(|| UnresolvedMacro { path: call.path.clone() })?;
688 705
689 let res = if let MacroDefKind::BuiltInEager(..) = def.kind { 706 let res = if let MacroDefKind::BuiltInEager(..) = def.kind {
690 let macro_call = InFile::new(call.ast_id.file_id, call.ast_id.to_node(db.upcast())); 707 let macro_call = InFile::new(call.ast_id.file_id, call.ast_id.to_node(db.upcast()));
@@ -714,8 +731,13 @@ fn derive_macro_as_call_id(
714 krate: CrateId, 731 krate: CrateId,
715 resolver: impl Fn(path::ModPath) -> Option<MacroDefId>, 732 resolver: impl Fn(path::ModPath) -> Option<MacroDefId>,
716) -> Result<MacroCallId, UnresolvedMacro> { 733) -> Result<MacroCallId, UnresolvedMacro> {
717 let def: MacroDefId = resolver(item_attr.path.clone()).ok_or(UnresolvedMacro)?; 734 let def: MacroDefId = resolver(item_attr.path.clone())
718 let last_segment = item_attr.path.segments().last().ok_or(UnresolvedMacro)?; 735 .ok_or_else(|| UnresolvedMacro { path: item_attr.path.clone() })?;
736 let last_segment = item_attr
737 .path
738 .segments()
739 .last()
740 .ok_or_else(|| UnresolvedMacro { path: item_attr.path.clone() })?;
719 let res = def 741 let res = def
720 .as_lazy_macro( 742 .as_lazy_macro(
721 db.upcast(), 743 db.upcast(),
diff --git a/crates/hir_def/src/nameres.rs b/crates/hir_def/src/nameres.rs
index 9e181751c..542f190a1 100644
--- a/crates/hir_def/src/nameres.rs
+++ b/crates/hir_def/src/nameres.rs
@@ -481,7 +481,7 @@ mod diagnostics {
481 481
482 UnresolvedProcMacro { ast: MacroCallKind }, 482 UnresolvedProcMacro { ast: MacroCallKind },
483 483
484 UnresolvedMacroCall { ast: AstId<ast::MacroCall> }, 484 UnresolvedMacroCall { ast: AstId<ast::MacroCall>, path: ModPath },
485 485
486 MacroError { ast: MacroCallKind, message: String }, 486 MacroError { ast: MacroCallKind, message: String },
487 } 487 }
@@ -546,8 +546,9 @@ mod diagnostics {
546 pub(super) fn unresolved_macro_call( 546 pub(super) fn unresolved_macro_call(
547 container: LocalModuleId, 547 container: LocalModuleId,
548 ast: AstId<ast::MacroCall>, 548 ast: AstId<ast::MacroCall>,
549 path: ModPath,
549 ) -> Self { 550 ) -> Self {
550 Self { in_module: container, kind: DiagnosticKind::UnresolvedMacroCall { ast } } 551 Self { in_module: container, kind: DiagnosticKind::UnresolvedMacroCall { ast, path } }
551 } 552 }
552 553
553 pub(super) fn add_to( 554 pub(super) fn add_to(
@@ -662,9 +663,13 @@ mod diagnostics {
662 }); 663 });
663 } 664 }
664 665
665 DiagnosticKind::UnresolvedMacroCall { ast } => { 666 DiagnosticKind::UnresolvedMacroCall { ast, path } => {
666 let node = ast.to_node(db.upcast()); 667 let node = ast.to_node(db.upcast());
667 sink.push(UnresolvedMacroCall { file: ast.file_id, node: AstPtr::new(&node) }); 668 sink.push(UnresolvedMacroCall {
669 file: ast.file_id,
670 node: AstPtr::new(&node),
671 path: path.clone(),
672 });
668 } 673 }
669 674
670 DiagnosticKind::MacroError { ast, message } => { 675 DiagnosticKind::MacroError { ast, message } => {
diff --git a/crates/hir_def/src/nameres/collector.rs b/crates/hir_def/src/nameres/collector.rs
index fb4ddff5e..05ceb1efb 100644
--- a/crates/hir_def/src/nameres/collector.rs
+++ b/crates/hir_def/src/nameres/collector.rs
@@ -829,7 +829,7 @@ impl DefCollector<'_> {
829 res = ReachedFixedPoint::No; 829 res = ReachedFixedPoint::No;
830 return false; 830 return false;
831 } 831 }
832 Err(UnresolvedMacro) | Ok(Err(_)) => {} 832 Err(UnresolvedMacro { .. }) | Ok(Err(_)) => {}
833 } 833 }
834 } 834 }
835 MacroDirectiveKind::Derive { ast_id, derive_attr } => { 835 MacroDirectiveKind::Derive { ast_id, derive_attr } => {
@@ -845,7 +845,7 @@ impl DefCollector<'_> {
845 res = ReachedFixedPoint::No; 845 res = ReachedFixedPoint::No;
846 return false; 846 return false;
847 } 847 }
848 Err(UnresolvedMacro) => (), 848 Err(UnresolvedMacro { .. }) => (),
849 } 849 }
850 } 850 }
851 } 851 }
@@ -943,10 +943,11 @@ impl DefCollector<'_> {
943 &mut |_| (), 943 &mut |_| (),
944 ) { 944 ) {
945 Ok(_) => (), 945 Ok(_) => (),
946 Err(UnresolvedMacro) => { 946 Err(UnresolvedMacro { path }) => {
947 self.def_map.diagnostics.push(DefDiagnostic::unresolved_macro_call( 947 self.def_map.diagnostics.push(DefDiagnostic::unresolved_macro_call(
948 directive.module_id, 948 directive.module_id,
949 ast_id.ast_id, 949 ast_id.ast_id,
950 path,
950 )); 951 ));
951 } 952 }
952 }, 953 },
@@ -1530,7 +1531,7 @@ impl ModCollector<'_, '_> {
1530 )); 1531 ));
1531 return; 1532 return;
1532 } 1533 }
1533 Err(UnresolvedMacro) => (), 1534 Err(UnresolvedMacro { .. }) => (),
1534 } 1535 }
1535 1536
1536 // Case 2: resolve in module scope, expand during name resolution. 1537 // Case 2: resolve in module scope, expand during name resolution.
diff --git a/crates/hir_def/src/nameres/path_resolution.rs b/crates/hir_def/src/nameres/path_resolution.rs
index ccc9f22eb..c984148c3 100644
--- a/crates/hir_def/src/nameres/path_resolution.rs
+++ b/crates/hir_def/src/nameres/path_resolution.rs
@@ -387,7 +387,13 @@ impl DefMap {
387 .get_legacy_macro(name) 387 .get_legacy_macro(name)
388 .map_or_else(PerNs::none, |m| PerNs::macros(m, Visibility::Public)); 388 .map_or_else(PerNs::none, |m| PerNs::macros(m, Visibility::Public));
389 let from_scope = self[module].scope.get(name); 389 let from_scope = self[module].scope.get(name);
390 let from_builtin = BUILTIN_SCOPE.get(name).copied().unwrap_or_else(PerNs::none); 390 let from_builtin = match self.block {
391 Some(_) => {
392 // Only resolve to builtins in the root `DefMap`.
393 PerNs::none()
394 }
395 None => BUILTIN_SCOPE.get(name).copied().unwrap_or_else(PerNs::none),
396 };
391 let from_scope_or_builtin = match shadow { 397 let from_scope_or_builtin = match shadow {
392 BuiltinShadowMode::Module => from_scope.or(from_builtin), 398 BuiltinShadowMode::Module => from_scope.or(from_builtin),
393 BuiltinShadowMode::Other => { 399 BuiltinShadowMode::Other => {
diff --git a/crates/hir_def/src/nameres/tests/diagnostics.rs b/crates/hir_def/src/nameres/tests/diagnostics.rs
index 1ac88fc89..543975e07 100644
--- a/crates/hir_def/src/nameres/tests/diagnostics.rs
+++ b/crates/hir_def/src/nameres/tests/diagnostics.rs
@@ -170,7 +170,7 @@ fn unresolved_legacy_scope_macro() {
170 170
171 m!(); 171 m!();
172 m2!(); 172 m2!();
173 //^^^^^^ unresolved macro call 173 //^^^^^^ unresolved macro `self::m2!`
174 "#, 174 "#,
175 ); 175 );
176} 176}
@@ -187,7 +187,7 @@ fn unresolved_module_scope_macro() {
187 187
188 self::m!(); 188 self::m!();
189 self::m2!(); 189 self::m2!();
190 //^^^^^^^^^^^^ unresolved macro call 190 //^^^^^^^^^^^^ unresolved macro `self::m2!`
191 "#, 191 "#,
192 ); 192 );
193} 193}
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/test_db.rs b/crates/hir_def/src/test_db.rs
index dd36106f8..8fa703a57 100644
--- a/crates/hir_def/src/test_db.rs
+++ b/crates/hir_def/src/test_db.rs
@@ -15,7 +15,12 @@ use rustc_hash::FxHashSet;
15use syntax::{algo, ast, AstNode, TextRange, TextSize}; 15use syntax::{algo, ast, AstNode, TextRange, TextSize};
16use test_utils::extract_annotations; 16use test_utils::extract_annotations;
17 17
18use crate::{db::DefDatabase, nameres::DefMap, src::HasSource, Lookup, ModuleDefId, ModuleId}; 18use crate::{
19 db::DefDatabase,
20 nameres::{DefMap, ModuleSource},
21 src::HasSource,
22 LocalModuleId, Lookup, ModuleDefId, ModuleId,
23};
19 24
20#[salsa::database( 25#[salsa::database(
21 base_db::SourceDatabaseExtStorage, 26 base_db::SourceDatabaseExtStorage,
@@ -87,10 +92,11 @@ impl TestDB {
87 pub(crate) fn module_at_position(&self, position: FilePosition) -> ModuleId { 92 pub(crate) fn module_at_position(&self, position: FilePosition) -> ModuleId {
88 let file_module = self.module_for_file(position.file_id); 93 let file_module = self.module_for_file(position.file_id);
89 let mut def_map = file_module.def_map(self); 94 let mut def_map = file_module.def_map(self);
95 let module = self.mod_at_position(&def_map, position);
90 96
91 def_map = match self.block_at_position(&def_map, position) { 97 def_map = match self.block_at_position(&def_map, position) {
92 Some(it) => it, 98 Some(it) => it,
93 None => return file_module, 99 None => return def_map.module_id(module),
94 }; 100 };
95 loop { 101 loop {
96 let new_map = self.block_at_position(&def_map, position); 102 let new_map = self.block_at_position(&def_map, position);
@@ -106,6 +112,47 @@ impl TestDB {
106 } 112 }
107 } 113 }
108 114
115 /// Finds the smallest/innermost module in `def_map` containing `position`.
116 fn mod_at_position(&self, def_map: &DefMap, position: FilePosition) -> LocalModuleId {
117 let mut size = None;
118 let mut res = def_map.root();
119 for (module, data) in def_map.modules() {
120 let src = data.definition_source(self);
121 if src.file_id != position.file_id.into() {
122 continue;
123 }
124
125 let range = match src.value {
126 ModuleSource::SourceFile(it) => it.syntax().text_range(),
127 ModuleSource::Module(it) => it.syntax().text_range(),
128 ModuleSource::BlockExpr(it) => it.syntax().text_range(),
129 };
130
131 if !range.contains(position.offset) {
132 continue;
133 }
134
135 let new_size = match size {
136 None => range.len(),
137 Some(size) => {
138 if range.len() < size {
139 range.len()
140 } else {
141 size
142 }
143 }
144 };
145
146 if size != Some(new_size) {
147 cov_mark::hit!(submodule_in_testdb);
148 size = Some(new_size);
149 res = module;
150 }
151 }
152
153 res
154 }
155
109 fn block_at_position(&self, def_map: &DefMap, position: FilePosition) -> Option<Arc<DefMap>> { 156 fn block_at_position(&self, def_map: &DefMap, position: FilePosition) -> Option<Arc<DefMap>> {
110 // Find the smallest (innermost) function in `def_map` containing the cursor. 157 // Find the smallest (innermost) function in `def_map` containing the cursor.
111 let mut size = None; 158 let mut size = None;
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_def/src/visibility.rs b/crates/hir_def/src/visibility.rs
index 9908cd926..d4b7c9970 100644
--- a/crates/hir_def/src/visibility.rs
+++ b/crates/hir_def/src/visibility.rs
@@ -123,11 +123,19 @@ impl Visibility {
123 def_map: &DefMap, 123 def_map: &DefMap,
124 mut from_module: crate::LocalModuleId, 124 mut from_module: crate::LocalModuleId,
125 ) -> bool { 125 ) -> bool {
126 let to_module = match self { 126 let mut to_module = match self {
127 Visibility::Module(m) => m, 127 Visibility::Module(m) => m,
128 Visibility::Public => return true, 128 Visibility::Public => return true,
129 }; 129 };
130 130
131 // `to_module` might be the root module of a block expression. Those have the same
132 // visibility as the containing module (even though no items are directly nameable from
133 // there, getting this right is important for method resolution).
134 // In that case, we adjust the visibility of `to_module` to point to the containing module.
135 if to_module.is_block_root(db) {
136 to_module = to_module.containing_module(db).unwrap();
137 }
138
131 // from_module needs to be a descendant of to_module 139 // from_module needs to be a descendant of to_module
132 let mut def_map = def_map; 140 let mut def_map = def_map;
133 let mut parent_arc; 141 let mut parent_arc;
diff --git a/crates/hir_expand/src/builtin_macro.rs b/crates/hir_expand/src/builtin_macro.rs
index 80365fc16..179de61f9 100644
--- a/crates/hir_expand/src/builtin_macro.rs
+++ b/crates/hir_expand/src/builtin_macro.rs
@@ -110,6 +110,7 @@ register_builtin! {
110 (format_args_nl, FormatArgsNl) => format_args_expand, 110 (format_args_nl, FormatArgsNl) => format_args_expand,
111 (llvm_asm, LlvmAsm) => asm_expand, 111 (llvm_asm, LlvmAsm) => asm_expand,
112 (asm, Asm) => asm_expand, 112 (asm, Asm) => asm_expand,
113 (global_asm, GlobalAsm) => global_asm_expand,
113 (cfg, Cfg) => cfg_expand, 114 (cfg, Cfg) => cfg_expand,
114 (core_panic, CorePanic) => panic_expand, 115 (core_panic, CorePanic) => panic_expand,
115 (std_panic, StdPanic) => panic_expand, 116 (std_panic, StdPanic) => panic_expand,
@@ -274,6 +275,15 @@ fn asm_expand(
274 ExpandResult::ok(expanded) 275 ExpandResult::ok(expanded)
275} 276}
276 277
278fn global_asm_expand(
279 _db: &dyn AstDatabase,
280 _id: LazyMacroId,
281 _tt: &tt::Subtree,
282) -> ExpandResult<tt::Subtree> {
283 // Expand to nothing (at item-level)
284 ExpandResult::ok(quote! {})
285}
286
277fn cfg_expand( 287fn cfg_expand(
278 db: &dyn AstDatabase, 288 db: &dyn AstDatabase,
279 id: LazyMacroId, 289 id: LazyMacroId,
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_expand/src/name.rs b/crates/hir_expand/src/name.rs
index a0f8766b0..bcfd3e524 100644
--- a/crates/hir_expand/src/name.rs
+++ b/crates/hir_expand/src/name.rs
@@ -221,6 +221,7 @@ pub mod known {
221 option_env, 221 option_env,
222 llvm_asm, 222 llvm_asm,
223 asm, 223 asm,
224 global_asm,
224 // Builtin derives 225 // Builtin derives
225 Copy, 226 Copy,
226 Clone, 227 Clone,
diff --git a/crates/hir_ty/src/autoderef.rs b/crates/hir_ty/src/autoderef.rs
index 71bc436e6..2c07494a9 100644
--- a/crates/hir_ty/src/autoderef.rs
+++ b/crates/hir_ty/src/autoderef.rs
@@ -36,6 +36,7 @@ pub(crate) fn deref(
36 krate: CrateId, 36 krate: CrateId,
37 ty: InEnvironment<&Canonical<Ty>>, 37 ty: InEnvironment<&Canonical<Ty>>,
38) -> Option<Canonical<Ty>> { 38) -> Option<Canonical<Ty>> {
39 let _p = profile::span("deref");
39 if let Some(derefed) = builtin_deref(&ty.goal.value) { 40 if let Some(derefed) = builtin_deref(&ty.goal.value) {
40 Some(Canonical { value: derefed, binders: ty.goal.binders.clone() }) 41 Some(Canonical { value: derefed, binders: ty.goal.binders.clone() })
41 } else { 42 } else {
@@ -56,6 +57,7 @@ fn deref_by_trait(
56 krate: CrateId, 57 krate: CrateId,
57 ty: InEnvironment<&Canonical<Ty>>, 58 ty: InEnvironment<&Canonical<Ty>>,
58) -> Option<Canonical<Ty>> { 59) -> Option<Canonical<Ty>> {
60 let _p = profile::span("deref_by_trait");
59 let deref_trait = match db.lang_item(krate, "deref".into())? { 61 let deref_trait = match db.lang_item(krate, "deref".into())? {
60 LangItemTarget::TraitId(it) => it, 62 LangItemTarget::TraitId(it) => it,
61 _ => return None, 63 _ => return None,
diff --git a/crates/hir_ty/src/db.rs b/crates/hir_ty/src/db.rs
index 1690926ad..cf67d4266 100644
--- a/crates/hir_ty/src/db.rs
+++ b/crates/hir_ty/src/db.rs
@@ -128,13 +128,21 @@ pub trait HirDatabase: DefDatabase + Upcast<dyn DefDatabase> {
128 id: chalk_db::AssociatedTyValueId, 128 id: chalk_db::AssociatedTyValueId,
129 ) -> Arc<chalk_db::AssociatedTyValue>; 129 ) -> Arc<chalk_db::AssociatedTyValue>;
130 130
131 #[salsa::invoke(crate::traits::trait_solve_query)] 131 #[salsa::invoke(trait_solve_wait)]
132 #[salsa::transparent]
132 fn trait_solve( 133 fn trait_solve(
133 &self, 134 &self,
134 krate: CrateId, 135 krate: CrateId,
135 goal: crate::Canonical<crate::InEnvironment<crate::DomainGoal>>, 136 goal: crate::Canonical<crate::InEnvironment<crate::DomainGoal>>,
136 ) -> Option<crate::Solution>; 137 ) -> Option<crate::Solution>;
137 138
139 #[salsa::invoke(crate::traits::trait_solve_query)]
140 fn trait_solve_query(
141 &self,
142 krate: CrateId,
143 goal: crate::Canonical<crate::InEnvironment<crate::DomainGoal>>,
144 ) -> Option<crate::Solution>;
145
138 #[salsa::invoke(chalk_db::program_clauses_for_chalk_env_query)] 146 #[salsa::invoke(chalk_db::program_clauses_for_chalk_env_query)]
139 fn program_clauses_for_chalk_env( 147 fn program_clauses_for_chalk_env(
140 &self, 148 &self,
@@ -156,6 +164,15 @@ fn infer_wait(db: &dyn HirDatabase, def: DefWithBodyId) -> Arc<InferenceResult>
156 db.infer_query(def) 164 db.infer_query(def)
157} 165}
158 166
167fn trait_solve_wait(
168 db: &dyn HirDatabase,
169 krate: CrateId,
170 goal: crate::Canonical<crate::InEnvironment<crate::DomainGoal>>,
171) -> Option<crate::Solution> {
172 let _p = profile::span("trait_solve::wait");
173 db.trait_solve_query(krate, goal)
174}
175
159#[test] 176#[test]
160fn hir_database_is_object_safe() { 177fn hir_database_is_object_safe() {
161 fn _assert_object_safe(_: &dyn HirDatabase) {} 178 fn _assert_object_safe(_: &dyn HirDatabase) {}
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/lib.rs b/crates/hir_ty/src/lib.rs
index 113234fa4..0505fa4ae 100644
--- a/crates/hir_ty/src/lib.rs
+++ b/crates/hir_ty/src/lib.rs
@@ -56,6 +56,7 @@ pub use mapping::{
56 to_foreign_def_id, to_placeholder_idx, 56 to_foreign_def_id, to_placeholder_idx,
57}; 57};
58pub use traits::TraitEnvironment; 58pub use traits::TraitEnvironment;
59pub use utils::all_super_traits;
59pub use walk::TypeWalk; 60pub use walk::TypeWalk;
60 61
61pub use chalk_ir::{ 62pub use chalk_ir::{
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/method_resolution.rs b/crates/hir_ty/src/method_resolution.rs
index 3693e3284..48bbcfd9f 100644
--- a/crates/hir_ty/src/method_resolution.rs
+++ b/crates/hir_ty/src/method_resolution.rs
@@ -246,29 +246,39 @@ pub struct InherentImpls {
246 246
247impl InherentImpls { 247impl InherentImpls {
248 pub(crate) fn inherent_impls_in_crate_query(db: &dyn HirDatabase, krate: CrateId) -> Arc<Self> { 248 pub(crate) fn inherent_impls_in_crate_query(db: &dyn HirDatabase, krate: CrateId) -> Arc<Self> {
249 let mut map: FxHashMap<_, Vec<_>> = FxHashMap::default(); 249 let mut impls = Self { map: FxHashMap::default() };
250 250
251 let crate_def_map = db.crate_def_map(krate); 251 let crate_def_map = db.crate_def_map(krate);
252 for (_module_id, module_data) in crate_def_map.modules() { 252 collect_def_map(db, &crate_def_map, &mut impls);
253 for impl_id in module_data.scope.impls() { 253
254 let data = db.impl_data(impl_id); 254 return Arc::new(impls);
255 if data.target_trait.is_some() { 255
256 continue; 256 fn collect_def_map(db: &dyn HirDatabase, def_map: &DefMap, impls: &mut InherentImpls) {
257 for (_module_id, module_data) in def_map.modules() {
258 for impl_id in module_data.scope.impls() {
259 let data = db.impl_data(impl_id);
260 if data.target_trait.is_some() {
261 continue;
262 }
263
264 let self_ty = db.impl_self_ty(impl_id);
265 let fp = TyFingerprint::for_inherent_impl(self_ty.skip_binders());
266 if let Some(fp) = fp {
267 impls.map.entry(fp).or_default().push(impl_id);
268 }
269 // `fp` should only be `None` in error cases (either erroneous code or incomplete name resolution)
257 } 270 }
258 271
259 let self_ty = db.impl_self_ty(impl_id); 272 // To better support custom derives, collect impls in all unnamed const items.
260 let fp = TyFingerprint::for_inherent_impl(self_ty.skip_binders()); 273 // const _: () = { ... };
261 if let Some(fp) = fp { 274 for konst in module_data.scope.unnamed_consts() {
262 map.entry(fp).or_default().push(impl_id); 275 let body = db.body(konst.into());
276 for (_, block_def_map) in body.blocks(db.upcast()) {
277 collect_def_map(db, &block_def_map, impls);
278 }
263 } 279 }
264 // `fp` should only be `None` in error cases (either erroneous code or incomplete name resolution)
265 } 280 }
266 } 281 }
267
268 // NOTE: We're not collecting inherent impls from unnamed consts here, we intentionally only
269 // support trait impls there.
270
271 Arc::new(Self { map })
272 } 282 }
273 283
274 pub fn for_self_ty(&self, self_ty: &Ty) -> &[ImplId] { 284 pub fn for_self_ty(&self, self_ty: &Ty) -> &[ImplId] {
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/hir_ty/src/tests/method_resolution.rs b/crates/hir_ty/src/tests/method_resolution.rs
index 4b2c82b41..a4c132bc5 100644
--- a/crates/hir_ty/src/tests/method_resolution.rs
+++ b/crates/hir_ty/src/tests/method_resolution.rs
@@ -1294,7 +1294,7 @@ mod b {
1294} 1294}
1295 1295
1296#[test] 1296#[test]
1297fn impl_in_unnamed_const() { 1297fn trait_impl_in_unnamed_const() {
1298 check_types( 1298 check_types(
1299 r#" 1299 r#"
1300struct S; 1300struct S;
@@ -1314,3 +1314,38 @@ fn f() {
1314 "#, 1314 "#,
1315 ); 1315 );
1316} 1316}
1317
1318#[test]
1319fn inherent_impl_in_unnamed_const() {
1320 check_types(
1321 r#"
1322struct S;
1323
1324const _: () = {
1325 impl S {
1326 fn method(&self) -> u16 { 0 }
1327
1328 pub(super) fn super_method(&self) -> u16 { 0 }
1329
1330 pub(crate) fn crate_method(&self) -> u16 { 0 }
1331
1332 pub fn pub_method(&self) -> u16 { 0 }
1333 }
1334};
1335
1336fn f() {
1337 S.method();
1338 //^^^^^^^^^^ u16
1339
1340 S.super_method();
1341 //^^^^^^^^^^^^^^^^ u16
1342
1343 S.crate_method();
1344 //^^^^^^^^^^^^^^^^ u16
1345
1346 S.pub_method();
1347 //^^^^^^^^^^^^^^ u16
1348}
1349 "#,
1350 );
1351}
diff --git a/crates/hir_ty/src/tests/simple.rs b/crates/hir_ty/src/tests/simple.rs
index 84c5c05fd..5948d0bc2 100644
--- a/crates/hir_ty/src/tests/simple.rs
+++ b/crates/hir_ty/src/tests/simple.rs
@@ -1765,6 +1765,24 @@ fn main() {
1765} 1765}
1766 1766
1767#[test] 1767#[test]
1768fn shadowing_primitive_with_inner_items() {
1769 check_types(
1770 r#"
1771struct i32;
1772struct Foo;
1773
1774impl i32 { fn foo(&self) -> Foo { Foo } }
1775
1776fn main() {
1777 fn inner() {}
1778 let x: i32 = i32;
1779 x.foo();
1780 //^ Foo
1781}"#,
1782 );
1783}
1784
1785#[test]
1768fn not_shadowing_primitive_by_module() { 1786fn not_shadowing_primitive_by_module() {
1769 check_types( 1787 check_types(
1770 r#" 1788 r#"
diff --git a/crates/hir_ty/src/utils.rs b/crates/hir_ty/src/utils.rs
index 5f6cb052a..2f04ee57a 100644
--- a/crates/hir_ty/src/utils.rs
+++ b/crates/hir_ty/src/utils.rs
@@ -78,7 +78,7 @@ fn direct_super_trait_refs(db: &dyn HirDatabase, trait_ref: &TraitRef) -> Vec<Tr
78 78
79/// Returns an iterator over the whole super trait hierarchy (including the 79/// Returns an iterator over the whole super trait hierarchy (including the
80/// trait itself). 80/// trait itself).
81pub(super) fn all_super_traits(db: &dyn DefDatabase, trait_: TraitId) -> Vec<TraitId> { 81pub fn all_super_traits(db: &dyn DefDatabase, trait_: TraitId) -> Vec<TraitId> {
82 // we need to take care a bit here to avoid infinite loops in case of cycles 82 // we need to take care a bit here to avoid infinite loops in case of cycles
83 // (i.e. if we have `trait A: B; trait B: A;`) 83 // (i.e. if we have `trait A: B; trait B: A;`)
84 let mut result = vec![trait_]; 84 let mut result = vec![trait_];
diff --git a/crates/ide/src/diagnostics.rs b/crates/ide/src/diagnostics.rs
index 9a883acb9..1c911a8b2 100644
--- a/crates/ide/src/diagnostics.rs
+++ b/crates/ide/src/diagnostics.rs
@@ -725,7 +725,7 @@ fn test_fn() {
725 expect![[r#" 725 expect![[r#"
726 [ 726 [
727 Diagnostic { 727 Diagnostic {
728 message: "unresolved macro call", 728 message: "unresolved macro `foo::bar!`",
729 range: 5..8, 729 range: 5..8,
730 severity: Error, 730 severity: Error,
731 fix: None, 731 fix: None,
diff --git a/crates/ide/src/lib.rs b/crates/ide/src/lib.rs
index b24c664ba..99e45633e 100644
--- a/crates/ide/src/lib.rs
+++ b/crates/ide/src/lib.rs
@@ -244,6 +244,12 @@ impl Analysis {
244 self.with_db(|db| db.parse(file_id).tree()) 244 self.with_db(|db| db.parse(file_id).tree())
245 } 245 }
246 246
247 /// Returns true if this file belongs to an immutable library.
248 pub fn is_library_file(&self, file_id: FileId) -> Cancelable<bool> {
249 use ide_db::base_db::SourceDatabaseExt;
250 self.with_db(|db| db.source_root(db.file_source_root(file_id)).is_library)
251 }
252
247 /// Gets the file's `LineIndex`: data structure to convert between absolute 253 /// Gets the file's `LineIndex`: data structure to convert between absolute
248 /// offsets and line/column representation. 254 /// offsets and line/column representation.
249 pub fn file_line_index(&self, file_id: FileId) -> Cancelable<Arc<LineIndex>> { 255 pub fn file_line_index(&self, file_id: FileId) -> Cancelable<Arc<LineIndex>> {
diff --git a/crates/ide/src/move_item.rs b/crates/ide/src/move_item.rs
index 8d37f4f92..246f10a0a 100644
--- a/crates/ide/src/move_item.rs
+++ b/crates/ide/src/move_item.rs
@@ -1,4 +1,4 @@
1use std::iter::once; 1use std::{iter::once, mem};
2 2
3use hir::Semantics; 3use hir::Semantics;
4use ide_db::{base_db::FileRange, RootDatabase}; 4use ide_db::{base_db::FileRange, RootDatabase};
@@ -102,7 +102,7 @@ fn move_in_direction(
102 ast::GenericArgList(it) => swap_sibling_in_list(node, it.generic_args(), range, direction), 102 ast::GenericArgList(it) => swap_sibling_in_list(node, it.generic_args(), range, direction),
103 ast::VariantList(it) => swap_sibling_in_list(node, it.variants(), range, direction), 103 ast::VariantList(it) => swap_sibling_in_list(node, it.variants(), range, direction),
104 ast::TypeBoundList(it) => swap_sibling_in_list(node, it.bounds(), range, direction), 104 ast::TypeBoundList(it) => swap_sibling_in_list(node, it.bounds(), range, direction),
105 _ => Some(replace_nodes(node, &match direction { 105 _ => Some(replace_nodes(range, node, &match direction {
106 Direction::Up => node.prev_sibling(), 106 Direction::Up => node.prev_sibling(),
107 Direction::Down => node.next_sibling(), 107 Direction::Down => node.next_sibling(),
108 }?)) 108 }?))
@@ -125,7 +125,7 @@ fn swap_sibling_in_list<A: AstNode + Clone, I: Iterator<Item = A>>(
125 .next(); 125 .next();
126 126
127 if let Some((l, r)) = list_lookup { 127 if let Some((l, r)) = list_lookup {
128 Some(replace_nodes(l.syntax(), r.syntax())) 128 Some(replace_nodes(range, l.syntax(), r.syntax()))
129 } else { 129 } else {
130 // Cursor is beyond any movable list item (for example, on curly brace in enum). 130 // Cursor is beyond any movable list item (for example, on curly brace in enum).
131 // It's not necessary, that parent of list is movable (arg list's parent is not, for example), 131 // It's not necessary, that parent of list is movable (arg list's parent is not, for example),
@@ -134,11 +134,38 @@ fn swap_sibling_in_list<A: AstNode + Clone, I: Iterator<Item = A>>(
134 } 134 }
135} 135}
136 136
137fn replace_nodes(first: &SyntaxNode, second: &SyntaxNode) -> TextEdit { 137fn replace_nodes<'a>(
138 range: TextRange,
139 mut first: &'a SyntaxNode,
140 mut second: &'a SyntaxNode,
141) -> TextEdit {
142 let cursor_offset = if range.is_empty() {
143 // FIXME: `applySnippetTextEdits` does not support non-empty selection ranges
144 if first.text_range().contains_range(range) {
145 Some(range.start() - first.text_range().start())
146 } else if second.text_range().contains_range(range) {
147 mem::swap(&mut first, &mut second);
148 Some(range.start() - first.text_range().start())
149 } else {
150 None
151 }
152 } else {
153 None
154 };
155
156 let first_with_cursor = match cursor_offset {
157 Some(offset) => {
158 let mut item_text = first.text().to_string();
159 item_text.insert_str(offset.into(), "$0");
160 item_text
161 }
162 None => first.text().to_string(),
163 };
164
138 let mut edit = TextEditBuilder::default(); 165 let mut edit = TextEditBuilder::default();
139 166
140 algo::diff(first, second).into_text_edit(&mut edit); 167 algo::diff(first, second).into_text_edit(&mut edit);
141 algo::diff(second, first).into_text_edit(&mut edit); 168 edit.replace(second.text_range(), first_with_cursor);
142 169
143 edit.finish() 170 edit.finish()
144} 171}
@@ -188,7 +215,7 @@ fn main() {
188 expect![[r#" 215 expect![[r#"
189fn main() { 216fn main() {
190 match true { 217 match true {
191 false => { 218 false =>$0 {
192 println!("Test"); 219 println!("Test");
193 }, 220 },
194 true => { 221 true => {
@@ -222,7 +249,7 @@ fn main() {
222 false => { 249 false => {
223 println!("Test"); 250 println!("Test");
224 }, 251 },
225 true => { 252 true =>$0 {
226 println!("Hello, world"); 253 println!("Hello, world");
227 } 254 }
228 }; 255 };
@@ -274,7 +301,7 @@ fn main() {
274 "#, 301 "#,
275 expect![[r#" 302 expect![[r#"
276fn main() { 303fn main() {
277 let test2 = 456; 304 let test2$0 = 456;
278 let test = 123; 305 let test = 123;
279} 306}
280 "#]], 307 "#]],
@@ -293,7 +320,7 @@ fn main() {
293 "#, 320 "#,
294 expect![[r#" 321 expect![[r#"
295fn main() { 322fn main() {
296 println!("All I want to say is..."); 323 println!("All I want to say is...");$0
297 println!("Hello, world"); 324 println!("Hello, world");
298} 325}
299 "#]], 326 "#]],
@@ -313,7 +340,7 @@ fn main() {
313fn main() { 340fn main() {
314 if true { 341 if true {
315 println!("Test"); 342 println!("Test");
316 } 343 }$0
317 344
318 println!("Hello, world"); 345 println!("Hello, world");
319} 346}
@@ -334,7 +361,7 @@ fn main() {
334fn main() { 361fn main() {
335 for i in 0..10 { 362 for i in 0..10 {
336 println!("Test"); 363 println!("Test");
337 } 364 }$0
338 365
339 println!("Hello, world"); 366 println!("Hello, world");
340} 367}
@@ -355,7 +382,7 @@ fn main() {
355fn main() { 382fn main() {
356 loop { 383 loop {
357 println!("Test"); 384 println!("Test");
358 } 385 }$0
359 386
360 println!("Hello, world"); 387 println!("Hello, world");
361} 388}
@@ -376,7 +403,7 @@ fn main() {
376fn main() { 403fn main() {
377 while true { 404 while true {
378 println!("Test"); 405 println!("Test");
379 } 406 }$0
380 407
381 println!("Hello, world"); 408 println!("Hello, world");
382} 409}
@@ -393,7 +420,7 @@ fn main() {
393 "#, 420 "#,
394 expect![[r#" 421 expect![[r#"
395fn main() { 422fn main() {
396 return 123; 423 return 123;$0
397 424
398 println!("Hello, world"); 425 println!("Hello, world");
399} 426}
@@ -430,7 +457,7 @@ fn main() {}
430fn foo() {}$0$0 457fn foo() {}$0$0
431 "#, 458 "#,
432 expect![[r#" 459 expect![[r#"
433fn foo() {} 460fn foo() {}$0
434 461
435fn main() {} 462fn main() {}
436 "#]], 463 "#]],
@@ -451,7 +478,7 @@ impl Wow for Yay $0$0{}
451 expect![[r#" 478 expect![[r#"
452struct Yay; 479struct Yay;
453 480
454impl Wow for Yay {} 481impl Wow for Yay $0{}
455 482
456trait Wow {} 483trait Wow {}
457 "#]], 484 "#]],
@@ -467,7 +494,7 @@ use std::vec::Vec;
467use std::collections::HashMap$0$0; 494use std::collections::HashMap$0$0;
468 "#, 495 "#,
469 expect![[r#" 496 expect![[r#"
470use std::collections::HashMap; 497use std::collections::HashMap$0;
471use std::vec::Vec; 498use std::vec::Vec;
472 "#]], 499 "#]],
473 Direction::Up, 500 Direction::Up,
@@ -502,7 +529,7 @@ fn main() {
502 } 529 }
503 530
504 #[test] 531 #[test]
505 fn test_moves_param_up() { 532 fn test_moves_param() {
506 check( 533 check(
507 r#" 534 r#"
508fn test(one: i32, two$0$0: u32) {} 535fn test(one: i32, two$0$0: u32) {}
@@ -512,7 +539,7 @@ fn main() {
512} 539}
513 "#, 540 "#,
514 expect![[r#" 541 expect![[r#"
515fn test(two: u32, one: i32) {} 542fn test(two$0: u32, one: i32) {}
516 543
517fn main() { 544fn main() {
518 test(123, 456); 545 test(123, 456);
@@ -520,6 +547,15 @@ fn main() {
520 "#]], 547 "#]],
521 Direction::Up, 548 Direction::Up,
522 ); 549 );
550 check(
551 r#"
552fn f($0$0arg: u8, arg2: u16) {}
553 "#,
554 expect![[r#"
555fn f(arg2: u16, $0arg: u8) {}
556 "#]],
557 Direction::Down,
558 );
523 } 559 }
524 560
525 #[test] 561 #[test]
@@ -536,7 +572,7 @@ fn main() {
536fn test(one: i32, two: u32) {} 572fn test(one: i32, two: u32) {}
537 573
538fn main() { 574fn main() {
539 test(456, 123); 575 test(456$0, 123);
540} 576}
541 "#]], 577 "#]],
542 Direction::Up, 578 Direction::Up,
@@ -557,7 +593,7 @@ fn main() {
557fn test(one: i32, two: u32) {} 593fn test(one: i32, two: u32) {}
558 594
559fn main() { 595fn main() {
560 test(456, 123); 596 test(456, 123$0);
561} 597}
562 "#]], 598 "#]],
563 Direction::Down, 599 Direction::Down,
@@ -594,7 +630,7 @@ struct Test<A, B$0$0>(A, B);
594fn main() {} 630fn main() {}
595 "#, 631 "#,
596 expect![[r#" 632 expect![[r#"
597struct Test<B, A>(A, B); 633struct Test<B$0, A>(A, B);
598 634
599fn main() {} 635fn main() {}
600 "#]], 636 "#]],
@@ -616,7 +652,7 @@ fn main() {
616struct Test<A, B>(A, B); 652struct Test<A, B>(A, B);
617 653
618fn main() { 654fn main() {
619 let t = Test::<&str, i32>(123, "yay"); 655 let t = Test::<&str$0, i32>(123, "yay");
620} 656}
621 "#]], 657 "#]],
622 Direction::Up, 658 Direction::Up,
@@ -636,7 +672,7 @@ fn main() {}
636 "#, 672 "#,
637 expect![[r#" 673 expect![[r#"
638enum Hello { 674enum Hello {
639 Two, 675 Two$0,
640 One 676 One
641} 677}
642 678
@@ -663,7 +699,7 @@ trait One {}
663 699
664trait Two {} 700trait Two {}
665 701
666fn test<T: Two + One>(t: T) {} 702fn test<T: Two$0 + One>(t: T) {}
667 703
668fn main() {} 704fn main() {}
669 "#]], 705 "#]],
@@ -709,7 +745,7 @@ trait Yay {
709impl Yay for Test { 745impl Yay for Test {
710 type One = i32; 746 type One = i32;
711 747
712 fn inner() { 748 fn inner() {$0
713 println!("Mmmm"); 749 println!("Mmmm");
714 } 750 }
715 751
@@ -736,7 +772,7 @@ fn test() {
736 "#, 772 "#,
737 expect![[r#" 773 expect![[r#"
738fn test() { 774fn test() {
739 mod hi { 775 mod hi {$0
740 fn inner() {} 776 fn inner() {}
741 } 777 }
742 778
@@ -764,7 +800,7 @@ fn main() {}
764 expect![[r#" 800 expect![[r#"
765fn main() {} 801fn main() {}
766 802
767#[derive(Debug)] 803$0#[derive(Debug)]
768enum FooBar { 804enum FooBar {
769 Foo, 805 Foo,
770 Bar, 806 Bar,
@@ -784,7 +820,7 @@ fn main() {}
784 expect![[r#" 820 expect![[r#"
785fn main() {} 821fn main() {}
786 822
787enum FooBar { 823$0enum FooBar {
788 Foo, 824 Foo,
789 Bar, 825 Bar,
790} 826}
@@ -804,7 +840,7 @@ fn main() {}
804 expect![[r#" 840 expect![[r#"
805struct Test; 841struct Test;
806 842
807impl SomeTrait for Test {} 843$0impl SomeTrait for Test {}
808 844
809trait SomeTrait {} 845trait SomeTrait {}
810 846
@@ -831,7 +867,7 @@ fn main() {}
831enum FooBar { 867enum FooBar {
832 Foo, 868 Foo,
833 Bar, 869 Bar,
834} 870}$0
835 "#]], 871 "#]],
836 Direction::Down, 872 Direction::Down,
837 ); 873 );
@@ -848,7 +884,7 @@ fn main() {}
848 expect![[r#" 884 expect![[r#"
849struct Test; 885struct Test;
850 886
851impl SomeTrait for Test {} 887impl SomeTrait for Test {}$0
852 888
853trait SomeTrait {} 889trait SomeTrait {}
854 890
diff --git a/crates/ide/src/syntax_highlighting/inject.rs b/crates/ide/src/syntax_highlighting/inject.rs
index 04fafd244..bc221d599 100644
--- a/crates/ide/src/syntax_highlighting/inject.rs
+++ b/crates/ide/src/syntax_highlighting/inject.rs
@@ -4,7 +4,7 @@ use std::mem;
4 4
5use either::Either; 5use either::Either;
6use hir::{InFile, Semantics}; 6use hir::{InFile, Semantics};
7use ide_db::{call_info::ActiveParameter, SymbolKind}; 7use ide_db::{call_info::ActiveParameter, helpers::rust_doc::is_rust_fence, SymbolKind};
8use syntax::{ 8use syntax::{
9 ast::{self, AstNode}, 9 ast::{self, AstNode},
10 AstToken, NodeOrToken, SyntaxNode, SyntaxToken, TextRange, TextSize, 10 AstToken, NodeOrToken, SyntaxNode, SyntaxToken, TextRange, TextSize,
@@ -78,17 +78,6 @@ pub(super) fn ra_fixture(
78} 78}
79 79
80const RUSTDOC_FENCE: &'static str = "```"; 80const RUSTDOC_FENCE: &'static str = "```";
81const RUSTDOC_FENCE_TOKENS: &[&'static str] = &[
82 "",
83 "rust",
84 "should_panic",
85 "ignore",
86 "no_run",
87 "compile_fail",
88 "edition2015",
89 "edition2018",
90 "edition2021",
91];
92 81
93/// Injection of syntax highlighting of doctests. 82/// Injection of syntax highlighting of doctests.
94pub(super) fn doc_comment( 83pub(super) fn doc_comment(
@@ -174,8 +163,7 @@ pub(super) fn doc_comment(
174 is_codeblock = !is_codeblock; 163 is_codeblock = !is_codeblock;
175 // Check whether code is rust by inspecting fence guards 164 // Check whether code is rust by inspecting fence guards
176 let guards = &line[idx + RUSTDOC_FENCE.len()..]; 165 let guards = &line[idx + RUSTDOC_FENCE.len()..];
177 let is_rust = 166 let is_rust = is_rust_fence(guards);
178 guards.split(',').all(|sub| RUSTDOC_FENCE_TOKENS.contains(&sub.trim()));
179 is_doctest = is_codeblock && is_rust; 167 is_doctest = is_codeblock && is_rust;
180 continue; 168 continue;
181 } 169 }
diff --git a/crates/ide/src/syntax_highlighting/tests.rs b/crates/ide/src/syntax_highlighting/tests.rs
index 933cfa6f3..17cc6334b 100644
--- a/crates/ide/src/syntax_highlighting/tests.rs
+++ b/crates/ide/src/syntax_highlighting/tests.rs
@@ -307,7 +307,7 @@ fn benchmark_syntax_highlighting_parser() {
307 .filter(|it| it.highlight.tag == HlTag::Symbol(SymbolKind::Function)) 307 .filter(|it| it.highlight.tag == HlTag::Symbol(SymbolKind::Function))
308 .count() 308 .count()
309 }; 309 };
310 assert_eq!(hash, 1629); 310 assert_eq!(hash, 1632);
311} 311}
312 312
313#[test] 313#[test]
diff --git a/crates/ide_assists/src/handlers/fill_match_arms.rs b/crates/ide_assists/src/handlers/fill_match_arms.rs
index 878b3a3fa..a30c4d04e 100644
--- a/crates/ide_assists/src/handlers/fill_match_arms.rs
+++ b/crates/ide_assists/src/handlers/fill_match_arms.rs
@@ -1,5 +1,6 @@
1use std::iter; 1use std::iter;
2 2
3use either::Either;
3use hir::{Adt, HasSource, ModuleDef, Semantics}; 4use hir::{Adt, HasSource, ModuleDef, Semantics};
4use ide_db::helpers::{mod_path_to_ast, FamousDefs}; 5use ide_db::helpers::{mod_path_to_ast, FamousDefs};
5use ide_db::RootDatabase; 6use ide_db::RootDatabase;
@@ -7,7 +8,7 @@ use itertools::Itertools;
7use syntax::ast::{self, make, AstNode, MatchArm, NameOwner, Pat}; 8use syntax::ast::{self, make, AstNode, MatchArm, NameOwner, Pat};
8 9
9use crate::{ 10use crate::{
10 utils::{does_pat_match_variant, render_snippet, Cursor}, 11 utils::{self, render_snippet, Cursor},
11 AssistContext, AssistId, AssistKind, Assists, 12 AssistContext, AssistId, AssistKind, Assists,
12}; 13};
13 14
@@ -48,6 +49,18 @@ pub(crate) fn fill_match_arms(acc: &mut Assists, ctx: &AssistContext) -> Option<
48 } 49 }
49 } 50 }
50 51
52 let top_lvl_pats: Vec<_> = arms
53 .iter()
54 .filter_map(ast::MatchArm::pat)
55 .flat_map(|pat| match pat {
56 // Special casee OrPat as separate top-level pats
57 Pat::OrPat(or_pat) => Either::Left(or_pat.pats()),
58 _ => Either::Right(iter::once(pat)),
59 })
60 // Exclude top level wildcards so that they are expanded by this assist, retains status quo in #8129.
61 .filter(|pat| !matches!(pat, Pat::WildcardPat(_)))
62 .collect();
63
51 let module = ctx.sema.scope(expr.syntax()).module()?; 64 let module = ctx.sema.scope(expr.syntax()).module()?;
52 65
53 let missing_arms: Vec<MatchArm> = if let Some(enum_def) = resolve_enum_def(&ctx.sema, &expr) { 66 let missing_arms: Vec<MatchArm> = if let Some(enum_def) = resolve_enum_def(&ctx.sema, &expr) {
@@ -56,7 +69,7 @@ pub(crate) fn fill_match_arms(acc: &mut Assists, ctx: &AssistContext) -> Option<
56 let mut variants = variants 69 let mut variants = variants
57 .into_iter() 70 .into_iter()
58 .filter_map(|variant| build_pat(ctx.db(), module, variant)) 71 .filter_map(|variant| build_pat(ctx.db(), module, variant))
59 .filter(|variant_pat| is_variant_missing(&mut arms, variant_pat)) 72 .filter(|variant_pat| is_variant_missing(&top_lvl_pats, variant_pat))
60 .map(|pat| make::match_arm(iter::once(pat), make::expr_empty_block())) 73 .map(|pat| make::match_arm(iter::once(pat), make::expr_empty_block()))
61 .collect::<Vec<_>>(); 74 .collect::<Vec<_>>();
62 if Some(enum_def) == FamousDefs(&ctx.sema, Some(module.krate())).core_option_Option() { 75 if Some(enum_def) == FamousDefs(&ctx.sema, Some(module.krate())).core_option_Option() {
@@ -66,17 +79,6 @@ pub(crate) fn fill_match_arms(acc: &mut Assists, ctx: &AssistContext) -> Option<
66 } 79 }
67 variants 80 variants
68 } else if let Some(enum_defs) = resolve_tuple_of_enum_def(&ctx.sema, &expr) { 81 } else if let Some(enum_defs) = resolve_tuple_of_enum_def(&ctx.sema, &expr) {
69 // Partial fill not currently supported for tuple of enums.
70 if !arms.is_empty() {
71 return None;
72 }
73
74 // We do not currently support filling match arms for a tuple
75 // containing a single enum.
76 if enum_defs.len() < 2 {
77 return None;
78 }
79
80 // When calculating the match arms for a tuple of enums, we want 82 // When calculating the match arms for a tuple of enums, we want
81 // to create a match arm for each possible combination of enum 83 // to create a match arm for each possible combination of enum
82 // values. The `multi_cartesian_product` method transforms 84 // values. The `multi_cartesian_product` method transforms
@@ -91,7 +93,7 @@ pub(crate) fn fill_match_arms(acc: &mut Assists, ctx: &AssistContext) -> Option<
91 variants.into_iter().filter_map(|variant| build_pat(ctx.db(), module, variant)); 93 variants.into_iter().filter_map(|variant| build_pat(ctx.db(), module, variant));
92 ast::Pat::from(make::tuple_pat(patterns)) 94 ast::Pat::from(make::tuple_pat(patterns))
93 }) 95 })
94 .filter(|variant_pat| is_variant_missing(&mut arms, variant_pat)) 96 .filter(|variant_pat| is_variant_missing(&top_lvl_pats, variant_pat))
95 .map(|pat| make::match_arm(iter::once(pat), make::expr_empty_block())) 97 .map(|pat| make::match_arm(iter::once(pat), make::expr_empty_block()))
96 .collect() 98 .collect()
97 } else { 99 } else {
@@ -134,16 +136,19 @@ pub(crate) fn fill_match_arms(acc: &mut Assists, ctx: &AssistContext) -> Option<
134 ) 136 )
135} 137}
136 138
137fn is_variant_missing(existing_arms: &mut Vec<MatchArm>, var: &Pat) -> bool { 139fn is_variant_missing(existing_pats: &[Pat], var: &Pat) -> bool {
138 existing_arms.iter().filter_map(|arm| arm.pat()).all(|pat| { 140 !existing_pats.iter().any(|pat| does_pat_match_variant(pat, var))
139 // Special casee OrPat as separate top-level pats 141}
140 let top_level_pats: Vec<Pat> = match pat {
141 Pat::OrPat(pats) => pats.pats().collect::<Vec<_>>(),
142 _ => vec![pat],
143 };
144 142
145 !top_level_pats.iter().any(|pat| does_pat_match_variant(pat, var)) 143// Fixme: this is still somewhat limited, use hir_ty::diagnostics::match_check?
146 }) 144fn does_pat_match_variant(pat: &Pat, var: &Pat) -> bool {
145 match (pat, var) {
146 (Pat::WildcardPat(_), _) => true,
147 (Pat::TuplePat(tpat), Pat::TuplePat(tvar)) => {
148 tpat.fields().zip(tvar.fields()).all(|(p, v)| does_pat_match_variant(&p, &v))
149 }
150 _ => utils::does_pat_match_variant(pat, var),
151 }
147} 152}
148 153
149fn resolve_enum_def(sema: &Semantics<RootDatabase>, expr: &ast::Expr) -> Option<hir::Enum> { 154fn resolve_enum_def(sema: &Semantics<RootDatabase>, expr: &ast::Expr) -> Option<hir::Enum> {
@@ -473,20 +478,81 @@ fn main() {
473 478
474 #[test] 479 #[test]
475 fn fill_match_arms_tuple_of_enum_partial() { 480 fn fill_match_arms_tuple_of_enum_partial() {
476 check_assist_not_applicable( 481 check_assist(
477 fill_match_arms, 482 fill_match_arms,
478 r#" 483 r#"
479 enum A { One, Two } 484enum A { One, Two }
480 enum B { One, Two } 485enum B { One, Two }
481 486
482 fn main() { 487fn main() {
483 let a = A::One; 488 let a = A::One;
484 let b = B::One; 489 let b = B::One;
485 match (a$0, b) { 490 match (a$0, b) {
486 (A::Two, B::One) => {} 491 (A::Two, B::One) => {}
487 } 492 }
488 } 493}
489 "#, 494"#,
495 r#"
496enum A { One, Two }
497enum B { One, Two }
498
499fn main() {
500 let a = A::One;
501 let b = B::One;
502 match (a, b) {
503 (A::Two, B::One) => {}
504 $0(A::One, B::One) => {}
505 (A::One, B::Two) => {}
506 (A::Two, B::Two) => {}
507 }
508}
509"#,
510 );
511 }
512
513 #[test]
514 fn fill_match_arms_tuple_of_enum_partial_with_wildcards() {
515 let ra_fixture = r#"
516fn main() {
517 let a = Some(1);
518 let b = Some(());
519 match (a$0, b) {
520 (Some(_), _) => {}
521 (None, Some(_)) => {}
522 }
523}
524"#;
525 check_assist(
526 fill_match_arms,
527 &format!("//- /main.rs crate:main deps:core{}{}", ra_fixture, FamousDefs::FIXTURE),
528 r#"
529fn main() {
530 let a = Some(1);
531 let b = Some(());
532 match (a, b) {
533 (Some(_), _) => {}
534 (None, Some(_)) => {}
535 $0(None, None) => {}
536 }
537}
538"#,
539 );
540 }
541
542 #[test]
543 fn fill_match_arms_partial_with_deep_pattern() {
544 // Fixme: cannot handle deep patterns
545 let ra_fixture = r#"
546fn main() {
547 match $0Some(true) {
548 Some(true) => {}
549 None => {}
550 }
551}
552"#;
553 check_assist_not_applicable(
554 fill_match_arms,
555 &format!("//- /main.rs crate:main deps:core{}{}", ra_fixture, FamousDefs::FIXTURE),
490 ); 556 );
491 } 557 }
492 558
@@ -514,10 +580,7 @@ fn main() {
514 580
515 #[test] 581 #[test]
516 fn fill_match_arms_single_element_tuple_of_enum() { 582 fn fill_match_arms_single_element_tuple_of_enum() {
517 // For now we don't hande the case of a single element tuple, but 583 check_assist(
518 // we could handle this in the future if `make::tuple_pat` allowed
519 // creating a tuple with a single pattern.
520 check_assist_not_applicable(
521 fill_match_arms, 584 fill_match_arms,
522 r#" 585 r#"
523 enum A { One, Two } 586 enum A { One, Two }
@@ -528,6 +591,17 @@ fn main() {
528 } 591 }
529 } 592 }
530 "#, 593 "#,
594 r#"
595 enum A { One, Two }
596
597 fn main() {
598 let a = A::One;
599 match (a, ) {
600 $0(A::One,) => {}
601 (A::Two,) => {}
602 }
603 }
604 "#,
531 ); 605 );
532 } 606 }
533 607
diff --git a/crates/ide_assists/src/handlers/generate_deref.rs b/crates/ide_assists/src/handlers/generate_deref.rs
new file mode 100644
index 000000000..4998ff7a4
--- /dev/null
+++ b/crates/ide_assists/src/handlers/generate_deref.rs
@@ -0,0 +1,227 @@
1use std::fmt::Display;
2
3use ide_db::{helpers::FamousDefs, RootDatabase};
4use syntax::{
5 ast::{self, NameOwner},
6 AstNode, SyntaxNode,
7};
8
9use crate::{
10 assist_context::{AssistBuilder, AssistContext, Assists},
11 utils::generate_trait_impl_text,
12 AssistId, AssistKind,
13};
14
15// Assist: generate_deref
16//
17// Generate `Deref` impl using the given struct field.
18//
19// ```
20// struct A;
21// struct B {
22// $0a: A
23// }
24// ```
25// ->
26// ```
27// struct A;
28// struct B {
29// a: A
30// }
31//
32// impl std::ops::Deref for B {
33// type Target = A;
34//
35// fn deref(&self) -> &Self::Target {
36// &self.a
37// }
38// }
39// ```
40pub(crate) fn generate_deref(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
41 generate_record_deref(acc, ctx).or_else(|| generate_tuple_deref(acc, ctx))
42}
43
44fn generate_record_deref(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
45 let strukt = ctx.find_node_at_offset::<ast::Struct>()?;
46 let field = ctx.find_node_at_offset::<ast::RecordField>()?;
47
48 if existing_deref_impl(&ctx.sema, &strukt).is_some() {
49 cov_mark::hit!(test_add_record_deref_impl_already_exists);
50 return None;
51 }
52
53 let field_type = field.ty()?;
54 let field_name = field.name()?;
55 let target = field.syntax().text_range();
56 acc.add(
57 AssistId("generate_deref", AssistKind::Generate),
58 format!("Generate `Deref` impl using `{}`", field_name),
59 target,
60 |edit| generate_edit(edit, strukt, field_type.syntax(), field_name.syntax()),
61 )
62}
63
64fn generate_tuple_deref(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
65 let strukt = ctx.find_node_at_offset::<ast::Struct>()?;
66 let field = ctx.find_node_at_offset::<ast::TupleField>()?;
67 let field_list = ctx.find_node_at_offset::<ast::TupleFieldList>()?;
68 let field_list_index =
69 field_list.syntax().children().into_iter().position(|s| &s == field.syntax())?;
70
71 if existing_deref_impl(&ctx.sema, &strukt).is_some() {
72 cov_mark::hit!(test_add_field_deref_impl_already_exists);
73 return None;
74 }
75
76 let field_type = field.ty()?;
77 let target = field.syntax().text_range();
78 acc.add(
79 AssistId("generate_deref", AssistKind::Generate),
80 format!("Generate `Deref` impl using `{}`", field.syntax()),
81 target,
82 |edit| generate_edit(edit, strukt, field_type.syntax(), field_list_index),
83 )
84}
85
86fn generate_edit(
87 edit: &mut AssistBuilder,
88 strukt: ast::Struct,
89 field_type_syntax: &SyntaxNode,
90 field_name: impl Display,
91) {
92 let start_offset = strukt.syntax().text_range().end();
93 let impl_code = format!(
94 r#" type Target = {0};
95
96 fn deref(&self) -> &Self::Target {{
97 &self.{1}
98 }}"#,
99 field_type_syntax, field_name
100 );
101 let strukt_adt = ast::Adt::Struct(strukt);
102 let deref_impl = generate_trait_impl_text(&strukt_adt, "std::ops::Deref", &impl_code);
103 edit.insert(start_offset, deref_impl);
104}
105
106fn existing_deref_impl(
107 sema: &'_ hir::Semantics<'_, RootDatabase>,
108 strukt: &ast::Struct,
109) -> Option<()> {
110 let strukt = sema.to_def(strukt)?;
111 let krate = strukt.module(sema.db).krate();
112
113 let deref_trait = FamousDefs(sema, Some(krate)).core_ops_Deref()?;
114 let strukt_type = strukt.ty(sema.db);
115
116 if strukt_type.impls_trait(sema.db, deref_trait, &[]) {
117 Some(())
118 } else {
119 None
120 }
121}
122
123#[cfg(test)]
124mod tests {
125 use crate::tests::{check_assist, check_assist_not_applicable};
126
127 use super::*;
128
129 #[test]
130 fn test_generate_record_deref() {
131 check_assist(
132 generate_deref,
133 r#"struct A { }
134struct B { $0a: A }"#,
135 r#"struct A { }
136struct B { a: A }
137
138impl std::ops::Deref for B {
139 type Target = A;
140
141 fn deref(&self) -> &Self::Target {
142 &self.a
143 }
144}"#,
145 );
146 }
147
148 #[test]
149 fn test_generate_field_deref_idx_0() {
150 check_assist(
151 generate_deref,
152 r#"struct A { }
153struct B($0A);"#,
154 r#"struct A { }
155struct B(A);
156
157impl std::ops::Deref for B {
158 type Target = A;
159
160 fn deref(&self) -> &Self::Target {
161 &self.0
162 }
163}"#,
164 );
165 }
166 #[test]
167 fn test_generate_field_deref_idx_1() {
168 check_assist(
169 generate_deref,
170 r#"struct A { }
171struct B(u8, $0A);"#,
172 r#"struct A { }
173struct B(u8, A);
174
175impl std::ops::Deref for B {
176 type Target = A;
177
178 fn deref(&self) -> &Self::Target {
179 &self.1
180 }
181}"#,
182 );
183 }
184
185 fn check_not_applicable(ra_fixture: &str) {
186 let fixture = format!(
187 "//- /main.rs crate:main deps:core,std\n{}\n{}",
188 ra_fixture,
189 FamousDefs::FIXTURE
190 );
191 check_assist_not_applicable(generate_deref, &fixture)
192 }
193
194 #[test]
195 fn test_generate_record_deref_not_applicable_if_already_impl() {
196 cov_mark::check!(test_add_record_deref_impl_already_exists);
197 check_not_applicable(
198 r#"struct A { }
199struct B { $0a: A }
200
201impl std::ops::Deref for B {
202 type Target = A;
203
204 fn deref(&self) -> &Self::Target {
205 &self.a
206 }
207}"#,
208 )
209 }
210
211 #[test]
212 fn test_generate_field_deref_not_applicable_if_already_impl() {
213 cov_mark::check!(test_add_field_deref_impl_already_exists);
214 check_not_applicable(
215 r#"struct A { }
216struct B($0A)
217
218impl std::ops::Deref for B {
219 type Target = A;
220
221 fn deref(&self) -> &Self::Target {
222 &self.0
223 }
224}"#,
225 )
226 }
227}
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/ide_assists/src/lib.rs b/crates/ide_assists/src/lib.rs
index 3694f468f..8996c1b61 100644
--- a/crates/ide_assists/src/lib.rs
+++ b/crates/ide_assists/src/lib.rs
@@ -134,6 +134,7 @@ mod handlers {
134 mod generate_default_from_enum_variant; 134 mod generate_default_from_enum_variant;
135 mod generate_default_from_new; 135 mod generate_default_from_new;
136 mod generate_is_empty_from_len; 136 mod generate_is_empty_from_len;
137 mod generate_deref;
137 mod generate_derive; 138 mod generate_derive;
138 mod generate_enum_is_method; 139 mod generate_enum_is_method;
139 mod generate_enum_projection_method; 140 mod generate_enum_projection_method;
@@ -201,6 +202,7 @@ mod handlers {
201 generate_default_from_enum_variant::generate_default_from_enum_variant, 202 generate_default_from_enum_variant::generate_default_from_enum_variant,
202 generate_default_from_new::generate_default_from_new, 203 generate_default_from_new::generate_default_from_new,
203 generate_is_empty_from_len::generate_is_empty_from_len, 204 generate_is_empty_from_len::generate_is_empty_from_len,
205 generate_deref::generate_deref,
204 generate_derive::generate_derive, 206 generate_derive::generate_derive,
205 generate_enum_is_method::generate_enum_is_method, 207 generate_enum_is_method::generate_enum_is_method,
206 generate_enum_projection_method::generate_enum_as_method, 208 generate_enum_projection_method::generate_enum_as_method,
diff --git a/crates/ide_assists/src/tests.rs b/crates/ide_assists/src/tests.rs
index a7a923beb..49533e7d2 100644
--- a/crates/ide_assists/src/tests.rs
+++ b/crates/ide_assists/src/tests.rs
@@ -84,7 +84,8 @@ fn check_doc_test(assist_id: &str, before: &str, after: &str) {
84 }); 84 });
85 85
86 let actual = { 86 let actual = {
87 let source_change = assist.source_change.unwrap(); 87 let source_change =
88 assist.source_change.expect("Assist did not contain any source changes");
88 let mut actual = before; 89 let mut actual = before;
89 if let Some(source_file_edit) = source_change.get_source_edit(file_id) { 90 if let Some(source_file_edit) = source_change.get_source_edit(file_id) {
90 source_file_edit.apply(&mut actual); 91 source_file_edit.apply(&mut actual);
@@ -121,7 +122,8 @@ fn check(handler: Handler, before: &str, expected: ExpectedResult, assist_label:
121 122
122 match (assist, expected) { 123 match (assist, expected) {
123 (Some(assist), ExpectedResult::After(after)) => { 124 (Some(assist), ExpectedResult::After(after)) => {
124 let source_change = assist.source_change.unwrap(); 125 let source_change =
126 assist.source_change.expect("Assist did not contain any source changes");
125 assert!(!source_change.source_file_edits.is_empty()); 127 assert!(!source_change.source_file_edits.is_empty());
126 let skip_header = source_change.source_file_edits.len() == 1 128 let skip_header = source_change.source_file_edits.len() == 1
127 && source_change.file_system_edits.len() == 0; 129 && source_change.file_system_edits.len() == 0;
@@ -191,6 +193,7 @@ fn assist_order_field_struct() {
191 let mut assists = assists.iter(); 193 let mut assists = assists.iter();
192 194
193 assert_eq!(assists.next().expect("expected assist").label, "Change visibility to pub(crate)"); 195 assert_eq!(assists.next().expect("expected assist").label, "Change visibility to pub(crate)");
196 assert_eq!(assists.next().expect("expected assist").label, "Generate `Deref` impl using `bar`");
194 assert_eq!(assists.next().expect("expected assist").label, "Generate a mut getter method"); 197 assert_eq!(assists.next().expect("expected assist").label, "Generate a mut getter method");
195 assert_eq!(assists.next().expect("expected assist").label, "Generate a getter method"); 198 assert_eq!(assists.next().expect("expected assist").label, "Generate a getter method");
196 assert_eq!(assists.next().expect("expected assist").label, "Generate a setter method"); 199 assert_eq!(assists.next().expect("expected assist").label, "Generate a setter method");
diff --git a/crates/ide_assists/src/tests/generated.rs b/crates/ide_assists/src/tests/generated.rs
index 27a22ca10..41559b43a 100644
--- a/crates/ide_assists/src/tests/generated.rs
+++ b/crates/ide_assists/src/tests/generated.rs
@@ -552,6 +552,33 @@ impl Default for Example {
552} 552}
553 553
554#[test] 554#[test]
555fn doctest_generate_deref() {
556 check_doc_test(
557 "generate_deref",
558 r#####"
559struct A;
560struct B {
561 $0a: A
562}
563"#####,
564 r#####"
565struct A;
566struct B {
567 a: A
568}
569
570impl std::ops::Deref for B {
571 type Target = A;
572
573 fn deref(&self) -> &Self::Target {
574 &self.a
575 }
576}
577"#####,
578 )
579}
580
581#[test]
555fn doctest_generate_derive() { 582fn doctest_generate_derive() {
556 check_doc_test( 583 check_doc_test(
557 "generate_derive", 584 "generate_derive",
diff --git a/crates/ide_completion/src/completions/flyimport.rs b/crates/ide_completion/src/completions/flyimport.rs
index 9ace13e41..8e211ae1e 100644
--- a/crates/ide_completion/src/completions/flyimport.rs
+++ b/crates/ide_completion/src/completions/flyimport.rs
@@ -114,6 +114,8 @@ pub(crate) fn import_on_the_fly(acc: &mut Completions, ctx: &CompletionContext)
114 || ctx.attribute_under_caret.is_some() 114 || ctx.attribute_under_caret.is_some()
115 || ctx.mod_declaration_under_caret.is_some() 115 || ctx.mod_declaration_under_caret.is_some()
116 || ctx.record_lit_syntax.is_some() 116 || ctx.record_lit_syntax.is_some()
117 || ctx.has_trait_parent
118 || ctx.has_impl_parent
117 { 119 {
118 return None; 120 return None;
119 } 121 }
@@ -1077,4 +1079,75 @@ fn main() {
1077 "#]], 1079 "#]],
1078 ); 1080 );
1079 } 1081 }
1082
1083 #[test]
1084 fn no_flyimports_in_traits_and_impl_declarations() {
1085 check(
1086 r#"
1087mod m {
1088 pub fn some_fn() -> i32 {
1089 42
1090 }
1091}
1092trait Foo {
1093 som$0
1094}
1095"#,
1096 expect![[r#""#]],
1097 );
1098
1099 check(
1100 r#"
1101mod m {
1102 pub fn some_fn() -> i32 {
1103 42
1104 }
1105}
1106struct Foo;
1107impl Foo {
1108 som$0
1109}
1110"#,
1111 expect![[r#""#]],
1112 );
1113
1114 check(
1115 r#"
1116mod m {
1117 pub fn some_fn() -> i32 {
1118 42
1119 }
1120}
1121struct Foo;
1122trait Bar {}
1123impl Bar for Foo {
1124 som$0
1125}
1126"#,
1127 expect![[r#""#]],
1128 );
1129 }
1130
1131 #[test]
1132 fn no_inherent_candidates_proposed() {
1133 check(
1134 r#"
1135mod baz {
1136 pub trait DefDatabase {
1137 fn method1(&self);
1138 }
1139 pub trait HirDatabase: DefDatabase {
1140 fn method2(&self);
1141 }
1142}
1143
1144mod bar {
1145 fn test(db: &dyn crate::baz::HirDatabase) {
1146 db.metho$0
1147 }
1148}
1149 "#,
1150 expect![[r#""#]],
1151 );
1152 }
1080} 1153}
diff --git a/crates/ide_db/src/apply_change.rs b/crates/ide_db/src/apply_change.rs
index 6c67425d7..eac5ef6b9 100644
--- a/crates/ide_db/src/apply_change.rs
+++ b/crates/ide_db/src/apply_change.rs
@@ -201,7 +201,7 @@ impl RootDatabase {
201 hir::db::InternImplTraitIdQuery 201 hir::db::InternImplTraitIdQuery
202 hir::db::InternClosureQuery 202 hir::db::InternClosureQuery
203 hir::db::AssociatedTyValueQuery 203 hir::db::AssociatedTyValueQuery
204 hir::db::TraitSolveQuery 204 hir::db::TraitSolveQueryQuery
205 205
206 // SymbolsDatabase 206 // SymbolsDatabase
207 crate::symbol_index::FileSymbolsQuery 207 crate::symbol_index::FileSymbolsQuery
diff --git a/crates/ide_db/src/call_info.rs b/crates/ide_db/src/call_info.rs
index e583a52f4..bad277a95 100644
--- a/crates/ide_db/src/call_info.rs
+++ b/crates/ide_db/src/call_info.rs
@@ -4,8 +4,9 @@ use either::Either;
4use hir::{HasAttrs, HirDisplay, Semantics, Type}; 4use hir::{HasAttrs, HirDisplay, Semantics, Type};
5use stdx::format_to; 5use stdx::format_to;
6use syntax::{ 6use syntax::{
7 algo,
7 ast::{self, ArgListOwner, NameOwner}, 8 ast::{self, ArgListOwner, NameOwner},
8 match_ast, AstNode, SyntaxNode, SyntaxToken, TextRange, TextSize, 9 match_ast, AstNode, Direction, SyntaxNode, SyntaxToken, TextRange, TextSize,
9}; 10};
10 11
11use crate::RootDatabase; 12use crate::RootDatabase;
@@ -43,7 +44,12 @@ pub fn call_info(db: &RootDatabase, position: FilePosition) -> Option<CallInfo>
43 let sema = Semantics::new(db); 44 let sema = Semantics::new(db);
44 let file = sema.parse(position.file_id); 45 let file = sema.parse(position.file_id);
45 let file = file.syntax(); 46 let file = file.syntax();
46 let token = file.token_at_offset(position.offset).next()?; 47 let token = file
48 .token_at_offset(position.offset)
49 .left_biased()
50 // if the cursor is sandwiched between two space tokens and the call is unclosed
51 // this prevents us from leaving the CallExpression
52 .and_then(|tok| algo::skip_trivia_token(tok, Direction::Prev))?;
47 let token = sema.descend_into_macros(token); 53 let token = sema.descend_into_macros(token);
48 54
49 let (callable, active_parameter) = call_info_impl(&sema, token)?; 55 let (callable, active_parameter) = call_info_impl(&sema, token)?;
diff --git a/crates/ide_db/src/call_info/tests.rs b/crates/ide_db/src/call_info/tests.rs
index 281a081a3..be1cc12de 100644
--- a/crates/ide_db/src/call_info/tests.rs
+++ b/crates/ide_db/src/call_info/tests.rs
@@ -522,3 +522,30 @@ fn main(f: fn(i32, f64) -> char) {
522 "#]], 522 "#]],
523 ) 523 )
524} 524}
525
526#[test]
527fn call_info_for_unclosed_call() {
528 check(
529 r#"
530fn foo(foo: u32, bar: u32) {}
531fn main() {
532 foo($0
533}"#,
534 expect![[r#"
535 fn foo(foo: u32, bar: u32)
536 (<foo: u32>, bar: u32)
537 "#]],
538 );
539 // check with surrounding space
540 check(
541 r#"
542fn foo(foo: u32, bar: u32) {}
543fn main() {
544 foo( $0
545}"#,
546 expect![[r#"
547 fn foo(foo: u32, bar: u32)
548 (<foo: u32>, bar: u32)
549 "#]],
550 )
551}
diff --git a/crates/ide_db/src/helpers.rs b/crates/ide_db/src/helpers.rs
index 66798ea3a..720de0d1f 100644
--- a/crates/ide_db/src/helpers.rs
+++ b/crates/ide_db/src/helpers.rs
@@ -1,6 +1,7 @@
1//! A module with ide helpers for high-level ide features. 1//! A module with ide helpers for high-level ide features.
2pub mod insert_use; 2pub mod insert_use;
3pub mod import_assets; 3pub mod import_assets;
4pub mod rust_doc;
4 5
5use std::collections::VecDeque; 6use std::collections::VecDeque;
6 7
@@ -113,6 +114,10 @@ impl FamousDefs<'_, '_> {
113 self.find_module("core:iter") 114 self.find_module("core:iter")
114 } 115 }
115 116
117 pub fn core_ops_Deref(&self) -> Option<Trait> {
118 self.find_trait("core:ops:Deref")
119 }
120
116 fn find_trait(&self, path: &str) -> Option<Trait> { 121 fn find_trait(&self, path: &str) -> Option<Trait> {
117 match self.find_def(path)? { 122 match self.find_def(path)? {
118 hir::ScopeDef::ModuleDef(hir::ModuleDef::Trait(it)) => Some(it), 123 hir::ScopeDef::ModuleDef(hir::ModuleDef::Trait(it)) => Some(it),
diff --git a/crates/ide_db/src/helpers/famous_defs_fixture.rs b/crates/ide_db/src/helpers/famous_defs_fixture.rs
index 4d79e064e..29ae12dcf 100644
--- a/crates/ide_db/src/helpers/famous_defs_fixture.rs
+++ b/crates/ide_db/src/helpers/famous_defs_fixture.rs
@@ -112,6 +112,12 @@ pub mod ops {
112 type Output; 112 type Output;
113 extern "rust-call" fn call_once(self, args: Args) -> Self::Output; 113 extern "rust-call" fn call_once(self, args: Args) -> Self::Output;
114 } 114 }
115
116 #[lang = "deref"]
117 pub trait Deref {
118 type Target: ?Sized;
119 fn deref(&self) -> &Self::Target;
120 }
115} 121}
116 122
117pub mod option { 123pub mod option {
@@ -141,3 +147,5 @@ mod return_keyword {}
141 147
142/// Docs for prim_str 148/// Docs for prim_str
143mod prim_str {} 149mod prim_str {}
150
151pub use core::ops; \ No newline at end of file
diff --git a/crates/ide_db/src/helpers/import_assets.rs b/crates/ide_db/src/helpers/import_assets.rs
index 8ce648367..91d6a4665 100644
--- a/crates/ide_db/src/helpers/import_assets.rs
+++ b/crates/ide_db/src/helpers/import_assets.rs
@@ -436,6 +436,8 @@ fn trait_applicable_items(
436 }) 436 })
437 .collect(); 437 .collect();
438 438
439 let related_dyn_traits =
440 trait_candidate.receiver_ty.applicable_inherent_traits(db).collect::<FxHashSet<_>>();
439 let mut located_imports = FxHashSet::default(); 441 let mut located_imports = FxHashSet::default();
440 442
441 if trait_assoc_item { 443 if trait_assoc_item {
@@ -451,12 +453,16 @@ fn trait_applicable_items(
451 return None; 453 return None;
452 } 454 }
453 } 455 }
456 let located_trait = assoc.containing_trait(db)?;
457 if related_dyn_traits.contains(&located_trait) {
458 return None;
459 }
454 460
455 let item = ItemInNs::from(ModuleDef::from(assoc.containing_trait(db)?)); 461 let trait_item = ItemInNs::from(ModuleDef::from(located_trait));
456 let original_item = assoc_to_item(assoc); 462 let original_item = assoc_to_item(assoc);
457 located_imports.insert(LocatedImport::new( 463 located_imports.insert(LocatedImport::new(
458 mod_path(item)?, 464 mod_path(trait_item)?,
459 item, 465 trait_item,
460 original_item, 466 original_item,
461 mod_path(original_item), 467 mod_path(original_item),
462 )); 468 ));
@@ -473,11 +479,15 @@ fn trait_applicable_items(
473 |_, function| { 479 |_, function| {
474 let assoc = function.as_assoc_item(db)?; 480 let assoc = function.as_assoc_item(db)?;
475 if required_assoc_items.contains(&assoc) { 481 if required_assoc_items.contains(&assoc) {
476 let item = ItemInNs::from(ModuleDef::from(assoc.containing_trait(db)?)); 482 let located_trait = assoc.containing_trait(db)?;
483 if related_dyn_traits.contains(&located_trait) {
484 return None;
485 }
486 let trait_item = ItemInNs::from(ModuleDef::from(located_trait));
477 let original_item = assoc_to_item(assoc); 487 let original_item = assoc_to_item(assoc);
478 located_imports.insert(LocatedImport::new( 488 located_imports.insert(LocatedImport::new(
479 mod_path(item)?, 489 mod_path(trait_item)?,
480 item, 490 trait_item,
481 original_item, 491 original_item,
482 mod_path(original_item), 492 mod_path(original_item),
483 )); 493 ));
diff --git a/crates/ide_db/src/helpers/rust_doc.rs b/crates/ide_db/src/helpers/rust_doc.rs
new file mode 100644
index 000000000..e27e23867
--- /dev/null
+++ b/crates/ide_db/src/helpers/rust_doc.rs
@@ -0,0 +1,34 @@
1//! Rustdoc specific doc comment handling
2
3// stripped down version of https://github.com/rust-lang/rust/blob/392ba2ba1a7d6c542d2459fb8133bebf62a4a423/src/librustdoc/html/markdown.rs#L810-L933
4pub fn is_rust_fence(s: &str) -> bool {
5 let mut seen_rust_tags = false;
6 let mut seen_other_tags = false;
7
8 let tokens = s
9 .trim()
10 .split(|c| c == ',' || c == ' ' || c == '\t')
11 .map(str::trim)
12 .filter(|t| !t.is_empty());
13
14 for token in tokens {
15 match token {
16 "should_panic" | "no_run" | "ignore" | "allow_fail" => {
17 seen_rust_tags = !seen_other_tags
18 }
19 "rust" => seen_rust_tags = true,
20 "test_harness" | "compile_fail" => seen_rust_tags = !seen_other_tags || seen_rust_tags,
21 x if x.starts_with("edition") => {}
22 x if x.starts_with('E') && x.len() == 5 => {
23 if x[1..].parse::<u32>().is_ok() {
24 seen_rust_tags = !seen_other_tags || seen_rust_tags;
25 } else {
26 seen_other_tags = true;
27 }
28 }
29 _ => seen_other_tags = true,
30 }
31 }
32
33 !seen_other_tags || seen_rust_tags
34}
diff --git a/crates/mbe/src/syntax_bridge.rs b/crates/mbe/src/syntax_bridge.rs
index 9ba98f7fb..a7c8c13c6 100644
--- a/crates/mbe/src/syntax_bridge.rs
+++ b/crates/mbe/src/syntax_bridge.rs
@@ -213,7 +213,7 @@ fn doc_comment_text(comment: &ast::Comment) -> SmolStr {
213 213
214 // Quote the string 214 // Quote the string
215 // Note that `tt::Literal` expect an escaped string 215 // Note that `tt::Literal` expect an escaped string
216 let text = format!("{:?}", text.escape_debug().to_string()); 216 let text = format!("\"{}\"", text.escape_debug());
217 text.into() 217 text.into()
218} 218}
219 219
diff --git a/crates/mbe/src/tests/expand.rs b/crates/mbe/src/tests/expand.rs
index e02d038b6..3a1d840ea 100644
--- a/crates/mbe/src/tests/expand.rs
+++ b/crates/mbe/src/tests/expand.rs
@@ -936,7 +936,25 @@ fn test_meta_doc_comments() {
936 MultiLines Doc 936 MultiLines Doc
937 */ 937 */
938 }"#, 938 }"#,
939 "# [doc = \" Single Line Doc 1\"] # [doc = \"\\\\n MultiLines Doc\\\\n \"] fn bar () {}", 939 "# [doc = \" Single Line Doc 1\"] # [doc = \"\\n MultiLines Doc\\n \"] fn bar () {}",
940 );
941}
942
943#[test]
944fn test_meta_extended_key_value_attributes() {
945 parse_macro(
946 r#"
947macro_rules! foo {
948 (#[$i:meta]) => (
949 #[$ i]
950 fn bar() {}
951 )
952}
953"#,
954 )
955 .assert_expand_items(
956 r#"foo! { #[doc = concat!("The `", "bla", "` lang item.")] }"#,
957 r#"# [doc = concat ! ("The `" , "bla" , "` lang item.")] fn bar () {}"#,
940 ); 958 );
941} 959}
942 960
@@ -959,7 +977,27 @@ fn test_meta_doc_comments_non_latin() {
959 莊生曉夢迷蝴蝶,望帝春心託杜鵑。 977 莊生曉夢迷蝴蝶,望帝春心託杜鵑。
960 */ 978 */
961 }"#, 979 }"#,
962 "# [doc = \" 錦瑟無端五十弦,一弦一柱思華年。\"] # [doc = \"\\\\n 莊生曉夢迷蝴蝶,望帝春心託杜鵑。\\\\n \"] fn bar () {}", 980 "# [doc = \" 錦瑟無端五十弦,一弦一柱思華年。\"] # [doc = \"\\n 莊生曉夢迷蝴蝶,望帝春心託杜鵑。\\n \"] fn bar () {}",
981 );
982}
983
984#[test]
985fn test_meta_doc_comments_escaped_characters() {
986 parse_macro(
987 r#"
988 macro_rules! foo {
989 ($(#[$ i:meta])+) => (
990 $(#[$ i])+
991 fn bar() {}
992 )
993 }
994"#,
995 )
996 .assert_expand_items(
997 r#"foo! {
998 /// \ " '
999 }"#,
1000 r#"# [doc = " \\ \" \'"] fn bar () {}"#,
963 ); 1001 );
964} 1002}
965 1003
diff --git a/crates/parser/src/grammar.rs b/crates/parser/src/grammar.rs
index cebb8f400..9bdf0b5fa 100644
--- a/crates/parser/src/grammar.rs
+++ b/crates/parser/src/grammar.rs
@@ -76,42 +76,7 @@ pub(crate) mod fragments {
76 76
77 // Parse a meta item , which excluded [], e.g : #[ MetaItem ] 77 // Parse a meta item , which excluded [], e.g : #[ MetaItem ]
78 pub(crate) fn meta_item(p: &mut Parser) { 78 pub(crate) fn meta_item(p: &mut Parser) {
79 fn is_delimiter(p: &mut Parser) -> bool { 79 attributes::meta(p);
80 matches!(p.current(), T!['{'] | T!['('] | T!['['])
81 }
82
83 if is_delimiter(p) {
84 items::token_tree(p);
85 return;
86 }
87
88 let m = p.start();
89 while !p.at(EOF) {
90 if is_delimiter(p) {
91 items::token_tree(p);
92 break;
93 } else {
94 // https://doc.rust-lang.org/reference/attributes.html
95 // https://doc.rust-lang.org/reference/paths.html#simple-paths
96 // The start of an meta must be a simple path
97 match p.current() {
98 IDENT | T![super] | T![self] | T![crate] => p.bump_any(),
99 T![=] => {
100 p.bump_any();
101 match p.current() {
102 c if c.is_literal() => p.bump_any(),
103 T![true] | T![false] => p.bump_any(),
104 _ => {}
105 }
106 break;
107 }
108 _ if p.at(T![::]) => p.bump(T![::]),
109 _ => break,
110 }
111 }
112 }
113
114 m.complete(p, TOKEN_TREE);
115 } 80 }
116 81
117 pub(crate) fn item(p: &mut Parser) { 82 pub(crate) fn item(p: &mut Parser) {
diff --git a/crates/parser/src/grammar/attributes.rs b/crates/parser/src/grammar/attributes.rs
index 96791ffc2..124a10eb2 100644
--- a/crates/parser/src/grammar/attributes.rs
+++ b/crates/parser/src/grammar/attributes.rs
@@ -14,6 +14,21 @@ pub(super) fn outer_attrs(p: &mut Parser) {
14 } 14 }
15} 15}
16 16
17pub(super) fn meta(p: &mut Parser) {
18 paths::use_path(p);
19
20 match p.current() {
21 T![=] => {
22 p.bump(T![=]);
23 if expressions::expr(p).0.is_none() {
24 p.error("expected expression");
25 }
26 }
27 T!['('] | T!['['] | T!['{'] => items::token_tree(p),
28 _ => {}
29 }
30}
31
17fn attr(p: &mut Parser, inner: bool) { 32fn attr(p: &mut Parser, inner: bool) {
18 let attr = p.start(); 33 let attr = p.start();
19 assert!(p.at(T![#])); 34 assert!(p.at(T![#]));
@@ -25,18 +40,7 @@ fn attr(p: &mut Parser, inner: bool) {
25 } 40 }
26 41
27 if p.eat(T!['[']) { 42 if p.eat(T!['[']) {
28 paths::use_path(p); 43 meta(p);
29
30 match p.current() {
31 T![=] => {
32 p.bump(T![=]);
33 if expressions::expr(p).0.is_none() {
34 p.error("expected expression");
35 }
36 }
37 T!['('] | T!['['] | T!['{'] => items::token_tree(p),
38 _ => {}
39 }
40 44
41 if !p.eat(T![']']) { 45 if !p.eat(T![']']) {
42 p.error("expected `]`"); 46 p.error("expected `]`");
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/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs
index 1edaa394a..7ddea22c8 100644
--- a/crates/rust-analyzer/src/config.rs
+++ b/crates/rust-analyzer/src/config.rs
@@ -400,6 +400,17 @@ impl Config {
400 pub fn will_rename(&self) -> bool { 400 pub fn will_rename(&self) -> bool {
401 try_or!(self.caps.workspace.as_ref()?.file_operations.as_ref()?.will_rename?, false) 401 try_or!(self.caps.workspace.as_ref()?.file_operations.as_ref()?.will_rename?, false)
402 } 402 }
403 pub fn change_annotation_support(&self) -> bool {
404 try_!(self
405 .caps
406 .workspace
407 .as_ref()?
408 .workspace_edit
409 .as_ref()?
410 .change_annotation_support
411 .as_ref()?)
412 .is_some()
413 }
403 pub fn code_action_resolve(&self) -> bool { 414 pub fn code_action_resolve(&self) -> bool {
404 try_or!( 415 try_or!(
405 self.caps 416 self.caps
diff --git a/crates/rust-analyzer/src/diagnostics/test_data/clippy_pass_by_ref.txt b/crates/rust-analyzer/src/diagnostics/test_data/clippy_pass_by_ref.txt
index 23ec2efba..227d96d51 100644
--- a/crates/rust-analyzer/src/diagnostics/test_data/clippy_pass_by_ref.txt
+++ b/crates/rust-analyzer/src/diagnostics/test_data/clippy_pass_by_ref.txt
@@ -326,6 +326,7 @@
326 }, 326 },
327 ), 327 ),
328 document_changes: None, 328 document_changes: None,
329 change_annotations: None,
329 }, 330 },
330 ), 331 ),
331 is_preferred: Some( 332 is_preferred: Some(
diff --git a/crates/rust-analyzer/src/diagnostics/test_data/rustc_unused_variable.txt b/crates/rust-analyzer/src/diagnostics/test_data/rustc_unused_variable.txt
index b6acb5f42..f8adfad3b 100644
--- a/crates/rust-analyzer/src/diagnostics/test_data/rustc_unused_variable.txt
+++ b/crates/rust-analyzer/src/diagnostics/test_data/rustc_unused_variable.txt
@@ -179,6 +179,7 @@
179 }, 179 },
180 ), 180 ),
181 document_changes: None, 181 document_changes: None,
182 change_annotations: None,
182 }, 183 },
183 ), 184 ),
184 is_preferred: Some( 185 is_preferred: Some(
diff --git a/crates/rust-analyzer/src/diagnostics/test_data/rustc_unused_variable_as_hint.txt b/crates/rust-analyzer/src/diagnostics/test_data/rustc_unused_variable_as_hint.txt
index d765257c4..5a70d2ed7 100644
--- a/crates/rust-analyzer/src/diagnostics/test_data/rustc_unused_variable_as_hint.txt
+++ b/crates/rust-analyzer/src/diagnostics/test_data/rustc_unused_variable_as_hint.txt
@@ -179,6 +179,7 @@
179 }, 179 },
180 ), 180 ),
181 document_changes: None, 181 document_changes: None,
182 change_annotations: None,
182 }, 183 },
183 ), 184 ),
184 is_preferred: Some( 185 is_preferred: Some(
diff --git a/crates/rust-analyzer/src/diagnostics/test_data/rustc_unused_variable_as_info.txt b/crates/rust-analyzer/src/diagnostics/test_data/rustc_unused_variable_as_info.txt
index 6b0d94878..04ca0c9c2 100644
--- a/crates/rust-analyzer/src/diagnostics/test_data/rustc_unused_variable_as_info.txt
+++ b/crates/rust-analyzer/src/diagnostics/test_data/rustc_unused_variable_as_info.txt
@@ -179,6 +179,7 @@
179 }, 179 },
180 ), 180 ),
181 document_changes: None, 181 document_changes: None,
182 change_annotations: None,
182 }, 183 },
183 ), 184 ),
184 is_preferred: Some( 185 is_preferred: Some(
diff --git a/crates/rust-analyzer/src/diagnostics/test_data/snap_multi_line_fix.txt b/crates/rust-analyzer/src/diagnostics/test_data/snap_multi_line_fix.txt
index a0cfb8d33..57d2f1ae3 100644
--- a/crates/rust-analyzer/src/diagnostics/test_data/snap_multi_line_fix.txt
+++ b/crates/rust-analyzer/src/diagnostics/test_data/snap_multi_line_fix.txt
@@ -339,6 +339,7 @@
339 }, 339 },
340 ), 340 ),
341 document_changes: None, 341 document_changes: None,
342 change_annotations: None,
342 }, 343 },
343 ), 344 ),
344 is_preferred: Some( 345 is_preferred: Some(
diff --git a/crates/rust-analyzer/src/diagnostics/to_proto.rs b/crates/rust-analyzer/src/diagnostics/to_proto.rs
index e2f319f6b..ca18997e4 100644
--- a/crates/rust-analyzer/src/diagnostics/to_proto.rs
+++ b/crates/rust-analyzer/src/diagnostics/to_proto.rs
@@ -136,6 +136,7 @@ fn map_rust_child_diagnostic(
136 // FIXME: there's no good reason to use edit_map here.... 136 // FIXME: there's no good reason to use edit_map here....
137 changes: Some(edit_map), 137 changes: Some(edit_map),
138 document_changes: None, 138 document_changes: None,
139 change_annotations: None,
139 }), 140 }),
140 is_preferred: Some(true), 141 is_preferred: Some(true),
141 data: None, 142 data: None,
diff --git a/crates/rust-analyzer/src/handlers.rs b/crates/rust-analyzer/src/handlers.rs
index 4f0c9d23c..1f59402e5 100644
--- a/crates/rust-analyzer/src/handlers.rs
+++ b/crates/rust-analyzer/src/handlers.rs
@@ -1410,7 +1410,7 @@ pub(crate) fn handle_open_cargo_toml(
1410pub(crate) fn handle_move_item( 1410pub(crate) fn handle_move_item(
1411 snap: GlobalStateSnapshot, 1411 snap: GlobalStateSnapshot,
1412 params: lsp_ext::MoveItemParams, 1412 params: lsp_ext::MoveItemParams,
1413) -> Result<Option<lsp_types::TextDocumentEdit>> { 1413) -> Result<Vec<lsp_ext::SnippetTextEdit>> {
1414 let _p = profile::span("handle_move_item"); 1414 let _p = profile::span("handle_move_item");
1415 let file_id = from_proto::file_id(&snap, &params.text_document.uri)?; 1415 let file_id = from_proto::file_id(&snap, &params.text_document.uri)?;
1416 let range = from_proto::file_range(&snap, params.text_document, params.range)?; 1416 let range = from_proto::file_range(&snap, params.text_document, params.range)?;
@@ -1421,8 +1421,11 @@ pub(crate) fn handle_move_item(
1421 }; 1421 };
1422 1422
1423 match snap.analysis.move_item(range, direction)? { 1423 match snap.analysis.move_item(range, direction)? {
1424 Some(text_edit) => Ok(Some(to_proto::text_document_edit(&snap, file_id, text_edit)?)), 1424 Some(text_edit) => {
1425 None => Ok(None), 1425 let line_index = snap.file_line_index(file_id)?;
1426 Ok(to_proto::snippet_text_edit_vec(&line_index, true, text_edit))
1427 }
1428 None => Ok(vec![]),
1426 } 1429 }
1427} 1430}
1428 1431
diff --git a/crates/rust-analyzer/src/lsp_ext.rs b/crates/rust-analyzer/src/lsp_ext.rs
index 81a6f22f1..b8835a534 100644
--- a/crates/rust-analyzer/src/lsp_ext.rs
+++ b/crates/rust-analyzer/src/lsp_ext.rs
@@ -312,6 +312,9 @@ pub struct SnippetWorkspaceEdit {
312 pub changes: Option<HashMap<lsp_types::Url, Vec<lsp_types::TextEdit>>>, 312 pub changes: Option<HashMap<lsp_types::Url, Vec<lsp_types::TextEdit>>>,
313 #[serde(skip_serializing_if = "Option::is_none")] 313 #[serde(skip_serializing_if = "Option::is_none")]
314 pub document_changes: Option<Vec<SnippetDocumentChangeOperation>>, 314 pub document_changes: Option<Vec<SnippetDocumentChangeOperation>>,
315 #[serde(skip_serializing_if = "Option::is_none")]
316 pub change_annotations:
317 Option<HashMap<lsp_types::ChangeAnnotationIdentifier, lsp_types::ChangeAnnotation>>,
315} 318}
316 319
317#[derive(Debug, Eq, PartialEq, Clone, Deserialize, Serialize)] 320#[derive(Debug, Eq, PartialEq, Clone, Deserialize, Serialize)]
@@ -335,6 +338,9 @@ pub struct SnippetTextEdit {
335 pub new_text: String, 338 pub new_text: String,
336 #[serde(skip_serializing_if = "Option::is_none")] 339 #[serde(skip_serializing_if = "Option::is_none")]
337 pub insert_text_format: Option<lsp_types::InsertTextFormat>, 340 pub insert_text_format: Option<lsp_types::InsertTextFormat>,
341 /// The annotation id if this is an annotated
342 #[serde(skip_serializing_if = "Option::is_none")]
343 pub annotation_id: Option<lsp_types::ChangeAnnotationIdentifier>,
338} 344}
339 345
340pub enum HoverRequest {} 346pub enum HoverRequest {}
@@ -407,7 +413,7 @@ pub enum MoveItem {}
407 413
408impl Request for MoveItem { 414impl Request for MoveItem {
409 type Params = MoveItemParams; 415 type Params = MoveItemParams;
410 type Result = Option<lsp_types::TextDocumentEdit>; 416 type Result = Vec<SnippetTextEdit>;
411 const METHOD: &'static str = "experimental/moveItem"; 417 const METHOD: &'static str = "experimental/moveItem";
412} 418}
413 419
diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs
index b3d4c6ec5..6ea775d68 100644
--- a/crates/rust-analyzer/src/main_loop.rs
+++ b/crates/rust-analyzer/src/main_loop.rs
@@ -248,7 +248,7 @@ impl GlobalState {
248 } 248 }
249 }; 249 };
250 250
251 self.report_progress("fetching", state, msg, None); 251 self.report_progress("Fetching", state, msg, None);
252 } 252 }
253 Task::FetchBuildData(progress) => { 253 Task::FetchBuildData(progress) => {
254 let (state, msg) = match progress { 254 let (state, msg) = match progress {
@@ -266,7 +266,7 @@ impl GlobalState {
266 }; 266 };
267 267
268 if let Some(state) = state { 268 if let Some(state) = state {
269 self.report_progress("loading", state, msg, None); 269 self.report_progress("Loading", state, msg, None);
270 } 270 }
271 } 271 }
272 } 272 }
@@ -298,7 +298,7 @@ impl GlobalState {
298 } 298 }
299 }; 299 };
300 300
301 self.report_progress("indexing", state, message, Some(fraction)); 301 self.report_progress("Indexing", state, message, Some(fraction));
302 } 302 }
303 } 303 }
304 Event::Vfs(mut task) => { 304 Event::Vfs(mut task) => {
@@ -330,7 +330,7 @@ impl GlobalState {
330 Progress::End 330 Progress::End
331 }; 331 };
332 self.report_progress( 332 self.report_progress(
333 "roots scanned", 333 "Roots Scanned",
334 state, 334 state,
335 Some(format!("{}/{}", n_done, n_total)), 335 Some(format!("{}/{}", n_done, n_total)),
336 Some(Progress::fraction(n_done, n_total)), 336 Some(Progress::fraction(n_done, n_total)),
diff --git a/crates/rust-analyzer/src/markdown.rs b/crates/rust-analyzer/src/markdown.rs
index 865eaae9b..35eaffba8 100644
--- a/crates/rust-analyzer/src/markdown.rs
+++ b/crates/rust-analyzer/src/markdown.rs
@@ -1,17 +1,7 @@
1//! Transforms markdown 1//! Transforms markdown
2use ide_db::helpers::rust_doc::is_rust_fence;
2 3
3const RUSTDOC_FENCE: &str = "```"; 4const RUSTDOC_FENCE: &str = "```";
4const RUSTDOC_CODE_BLOCK_ATTRIBUTES_RUST_SPECIFIC: &[&str] = &[
5 "",
6 "rust",
7 "should_panic",
8 "ignore",
9 "no_run",
10 "compile_fail",
11 "edition2015",
12 "edition2018",
13 "edition2021",
14];
15 5
16pub(crate) fn format_docs(src: &str) -> String { 6pub(crate) fn format_docs(src: &str) -> String {
17 let mut processed_lines = Vec::new(); 7 let mut processed_lines = Vec::new();
@@ -27,9 +17,7 @@ pub(crate) fn format_docs(src: &str) -> String {
27 in_code_block ^= true; 17 in_code_block ^= true;
28 18
29 if in_code_block { 19 if in_code_block {
30 is_rust = header 20 is_rust = is_rust_fence(header);
31 .split(',')
32 .all(|sub| RUSTDOC_CODE_BLOCK_ATTRIBUTES_RUST_SPECIFIC.contains(&sub.trim()));
33 21
34 if is_rust { 22 if is_rust {
35 line = "```rust"; 23 line = "```rust";
@@ -82,6 +70,12 @@ mod tests {
82 } 70 }
83 71
84 #[test] 72 #[test]
73 fn test_format_docs_handles_error_codes() {
74 let comment = "```compile_fail,E0641\nlet b = 0 as *const _;\n```";
75 assert_eq!(format_docs(comment), "```rust\nlet b = 0 as *const _;\n```");
76 }
77
78 #[test]
85 fn test_format_docs_skips_comments_in_rust_block() { 79 fn test_format_docs_skips_comments_in_rust_block() {
86 let comment = 80 let comment =
87 "```rust\n # skip1\n# skip2\n#stay1\nstay2\n#\n #\n # \n #\tskip3\n\t#\t\n```"; 81 "```rust\n # skip1\n# skip2\n#stay1\nstay2\n#\n #\n # \n #\tskip3\n\t#\t\n```";
diff --git a/crates/rust-analyzer/src/to_proto.rs b/crates/rust-analyzer/src/to_proto.rs
index 8d7cb9b74..fe4d0733d 100644
--- a/crates/rust-analyzer/src/to_proto.rs
+++ b/crates/rust-analyzer/src/to_proto.rs
@@ -1,15 +1,16 @@
1//! Conversion of rust-analyzer specific types to lsp_types equivalents. 1//! Conversion of rust-analyzer specific types to lsp_types equivalents.
2use std::{ 2use std::{
3 iter::once,
3 path::{self, Path}, 4 path::{self, Path},
4 sync::atomic::{AtomicU32, Ordering}, 5 sync::atomic::{AtomicU32, Ordering},
5}; 6};
6 7
7use ide::{ 8use ide::{
8 Annotation, AnnotationKind, Assist, AssistKind, CallInfo, CompletionItem, CompletionItemKind, 9 Annotation, AnnotationKind, Assist, AssistKind, CallInfo, Cancelable, CompletionItem,
9 CompletionRelevance, Documentation, FileId, FileRange, FileSystemEdit, Fold, FoldKind, 10 CompletionItemKind, CompletionRelevance, Documentation, FileId, FileRange, FileSystemEdit,
10 Highlight, HlMod, HlOperator, HlPunct, HlRange, HlTag, Indel, InlayHint, InlayKind, 11 Fold, FoldKind, Highlight, HlMod, HlOperator, HlPunct, HlRange, HlTag, Indel, InlayHint,
11 InsertTextFormat, Markup, NavigationTarget, ReferenceAccess, RenameError, Runnable, Severity, 12 InlayKind, InsertTextFormat, Markup, NavigationTarget, ReferenceAccess, RenameError, Runnable,
12 SourceChange, StructureNodeKind, SymbolKind, TextEdit, TextRange, TextSize, 13 Severity, SourceChange, StructureNodeKind, SymbolKind, TextEdit, TextRange, TextSize,
13}; 14};
14use itertools::Itertools; 15use itertools::Itertools;
15use serde_json::to_value; 16use serde_json::to_value;
@@ -174,6 +175,7 @@ pub(crate) fn snippet_text_edit(
174 range: text_edit.range, 175 range: text_edit.range,
175 new_text: text_edit.new_text, 176 new_text: text_edit.new_text,
176 insert_text_format, 177 insert_text_format,
178 annotation_id: None,
177 } 179 }
178} 180}
179 181
@@ -688,16 +690,8 @@ pub(crate) fn goto_definition_response(
688 } 690 }
689} 691}
690 692
691pub(crate) fn text_document_edit( 693fn outside_workspace_annotation_id() -> String {
692 snap: &GlobalStateSnapshot, 694 String::from("OutsideWorkspace")
693 file_id: FileId,
694 edit: TextEdit,
695) -> Result<lsp_types::TextDocumentEdit> {
696 let text_document = optional_versioned_text_document_identifier(snap, file_id);
697 let line_index = snap.file_line_index(file_id)?;
698 let edits =
699 edit.into_iter().map(|it| lsp_types::OneOf::Left(text_edit(&line_index, it))).collect();
700 Ok(lsp_types::TextDocumentEdit { text_document, edits })
701} 695}
702 696
703pub(crate) fn snippet_text_document_edit( 697pub(crate) fn snippet_text_document_edit(
@@ -708,14 +702,21 @@ pub(crate) fn snippet_text_document_edit(
708) -> Result<lsp_ext::SnippetTextDocumentEdit> { 702) -> Result<lsp_ext::SnippetTextDocumentEdit> {
709 let text_document = optional_versioned_text_document_identifier(snap, file_id); 703 let text_document = optional_versioned_text_document_identifier(snap, file_id);
710 let line_index = snap.file_line_index(file_id)?; 704 let line_index = snap.file_line_index(file_id)?;
711 let edits = edit.into_iter().map(|it| snippet_text_edit(&line_index, is_snippet, it)).collect(); 705 let mut edits: Vec<_> =
706 edit.into_iter().map(|it| snippet_text_edit(&line_index, is_snippet, it)).collect();
707
708 if snap.analysis.is_library_file(file_id)? && snap.config.change_annotation_support() {
709 for edit in &mut edits {
710 edit.annotation_id = Some(outside_workspace_annotation_id())
711 }
712 }
712 Ok(lsp_ext::SnippetTextDocumentEdit { text_document, edits }) 713 Ok(lsp_ext::SnippetTextDocumentEdit { text_document, edits })
713} 714}
714 715
715pub(crate) fn snippet_text_document_ops( 716pub(crate) fn snippet_text_document_ops(
716 snap: &GlobalStateSnapshot, 717 snap: &GlobalStateSnapshot,
717 file_system_edit: FileSystemEdit, 718 file_system_edit: FileSystemEdit,
718) -> Vec<lsp_ext::SnippetDocumentChangeOperation> { 719) -> Cancelable<Vec<lsp_ext::SnippetDocumentChangeOperation>> {
719 let mut ops = Vec::new(); 720 let mut ops = Vec::new();
720 match file_system_edit { 721 match file_system_edit {
721 FileSystemEdit::CreateFile { dst, initial_contents } => { 722 FileSystemEdit::CreateFile { dst, initial_contents } => {
@@ -733,6 +734,7 @@ pub(crate) fn snippet_text_document_ops(
733 range: lsp_types::Range::default(), 734 range: lsp_types::Range::default(),
734 new_text: initial_contents, 735 new_text: initial_contents,
735 insert_text_format: Some(lsp_types::InsertTextFormat::PlainText), 736 insert_text_format: Some(lsp_types::InsertTextFormat::PlainText),
737 annotation_id: None,
736 }; 738 };
737 let edit_file = 739 let edit_file =
738 lsp_ext::SnippetTextDocumentEdit { text_document, edits: vec![text_edit] }; 740 lsp_ext::SnippetTextDocumentEdit { text_document, edits: vec![text_edit] };
@@ -742,16 +744,19 @@ pub(crate) fn snippet_text_document_ops(
742 FileSystemEdit::MoveFile { src, dst } => { 744 FileSystemEdit::MoveFile { src, dst } => {
743 let old_uri = snap.file_id_to_url(src); 745 let old_uri = snap.file_id_to_url(src);
744 let new_uri = snap.anchored_path(&dst); 746 let new_uri = snap.anchored_path(&dst);
745 let rename_file = lsp_types::ResourceOp::Rename(lsp_types::RenameFile { 747 let mut rename_file =
746 old_uri, 748 lsp_types::RenameFile { old_uri, new_uri, options: None, annotation_id: None };
747 new_uri, 749 if snap.analysis.is_library_file(src) == Ok(true)
748 options: None, 750 && snap.config.change_annotation_support()
749 annotation_id: None, 751 {
750 }); 752 rename_file.annotation_id = Some(outside_workspace_annotation_id())
751 ops.push(lsp_ext::SnippetDocumentChangeOperation::Op(rename_file)) 753 }
754 ops.push(lsp_ext::SnippetDocumentChangeOperation::Op(lsp_types::ResourceOp::Rename(
755 rename_file,
756 )))
752 } 757 }
753 } 758 }
754 ops 759 Ok(ops)
755} 760}
756 761
757pub(crate) fn snippet_workspace_edit( 762pub(crate) fn snippet_workspace_edit(
@@ -759,16 +764,35 @@ pub(crate) fn snippet_workspace_edit(
759 source_change: SourceChange, 764 source_change: SourceChange,
760) -> Result<lsp_ext::SnippetWorkspaceEdit> { 765) -> Result<lsp_ext::SnippetWorkspaceEdit> {
761 let mut document_changes: Vec<lsp_ext::SnippetDocumentChangeOperation> = Vec::new(); 766 let mut document_changes: Vec<lsp_ext::SnippetDocumentChangeOperation> = Vec::new();
767
762 for op in source_change.file_system_edits { 768 for op in source_change.file_system_edits {
763 let ops = snippet_text_document_ops(snap, op); 769 let ops = snippet_text_document_ops(snap, op)?;
764 document_changes.extend_from_slice(&ops); 770 document_changes.extend_from_slice(&ops);
765 } 771 }
766 for (file_id, edit) in source_change.source_file_edits { 772 for (file_id, edit) in source_change.source_file_edits {
767 let edit = snippet_text_document_edit(&snap, source_change.is_snippet, file_id, edit)?; 773 let edit = snippet_text_document_edit(&snap, source_change.is_snippet, file_id, edit)?;
768 document_changes.push(lsp_ext::SnippetDocumentChangeOperation::Edit(edit)); 774 document_changes.push(lsp_ext::SnippetDocumentChangeOperation::Edit(edit));
769 } 775 }
770 let workspace_edit = 776 let mut workspace_edit = lsp_ext::SnippetWorkspaceEdit {
771 lsp_ext::SnippetWorkspaceEdit { changes: None, document_changes: Some(document_changes) }; 777 changes: None,
778 document_changes: Some(document_changes),
779 change_annotations: None,
780 };
781 if snap.config.change_annotation_support() {
782 workspace_edit.change_annotations = Some(
783 once((
784 outside_workspace_annotation_id(),
785 lsp_types::ChangeAnnotation {
786 label: String::from("Edit outside of the workspace"),
787 needs_confirmation: Some(true),
788 description: Some(String::from(
789 "This edit lies outside of the workspace and may affect dependencies",
790 )),
791 },
792 ))
793 .collect(),
794 )
795 }
772 Ok(workspace_edit) 796 Ok(workspace_edit)
773} 797}
774 798
@@ -796,16 +820,7 @@ impl From<lsp_ext::SnippetWorkspaceEdit> for lsp_types::WorkspaceEdit {
796 lsp_types::DocumentChangeOperation::Edit( 820 lsp_types::DocumentChangeOperation::Edit(
797 lsp_types::TextDocumentEdit { 821 lsp_types::TextDocumentEdit {
798 text_document: edit.text_document, 822 text_document: edit.text_document,
799 edits: edit 823 edits: edit.edits.into_iter().map(From::from).collect(),
800 .edits
801 .into_iter()
802 .map(|edit| {
803 lsp_types::OneOf::Left(lsp_types::TextEdit {
804 range: edit.range,
805 new_text: edit.new_text,
806 })
807 })
808 .collect(),
809 }, 824 },
810 ) 825 )
811 } 826 }
@@ -813,7 +828,23 @@ impl From<lsp_ext::SnippetWorkspaceEdit> for lsp_types::WorkspaceEdit {
813 .collect(), 828 .collect(),
814 ) 829 )
815 }), 830 }),
816 change_annotations: None, 831 change_annotations: snippet_workspace_edit.change_annotations,
832 }
833 }
834}
835
836impl From<lsp_ext::SnippetTextEdit>
837 for lsp_types::OneOf<lsp_types::TextEdit, lsp_types::AnnotatedTextEdit>
838{
839 fn from(
840 lsp_ext::SnippetTextEdit { annotation_id, insert_text_format:_, new_text, range }: lsp_ext::SnippetTextEdit,
841 ) -> Self {
842 match annotation_id {
843 Some(annotation_id) => lsp_types::OneOf::Right(lsp_types::AnnotatedTextEdit {
844 text_edit: lsp_types::TextEdit { range, new_text },
845 annotation_id,
846 }),
847 None => lsp_types::OneOf::Left(lsp_types::TextEdit { range, new_text }),
817 } 848 }
818 } 849 }
819} 850}
diff --git a/crates/syntax/Cargo.toml b/crates/syntax/Cargo.toml
index f09cdf28a..a8c1a8075 100644
--- a/crates/syntax/Cargo.toml
+++ b/crates/syntax/Cargo.toml
@@ -13,7 +13,7 @@ doctest = false
13[dependencies] 13[dependencies]
14cov-mark = { version = "1.1", features = ["thread-local"] } 14cov-mark = { version = "1.1", features = ["thread-local"] }
15itertools = "0.10.0" 15itertools = "0.10.0"
16rowan = "0.13.0-pre.3" 16rowan = "=0.13.0-pre.3"
17rustc_lexer = { version = "714.0.0", package = "rustc-ap-rustc_lexer" } 17rustc_lexer = { version = "714.0.0", package = "rustc-ap-rustc_lexer" }
18rustc-hash = "1.1.0" 18rustc-hash = "1.1.0"
19arrayvec = "0.7" 19arrayvec = "0.7"
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/ast/make.rs b/crates/syntax/src/ast/make.rs
index c6a7b99b7..94d4f2cf0 100644
--- a/crates/syntax/src/ast/make.rs
+++ b/crates/syntax/src/ast/make.rs
@@ -29,9 +29,13 @@ pub fn ty(text: &str) -> ast::Type {
29pub fn ty_unit() -> ast::Type { 29pub fn ty_unit() -> ast::Type {
30 ty("()") 30 ty("()")
31} 31}
32// FIXME: handle types of length == 1
33pub fn ty_tuple(types: impl IntoIterator<Item = ast::Type>) -> ast::Type { 32pub fn ty_tuple(types: impl IntoIterator<Item = ast::Type>) -> ast::Type {
34 let contents = types.into_iter().join(", "); 33 let mut count: usize = 0;
34 let mut contents = types.into_iter().inspect(|_| count += 1).join(", ");
35 if count == 1 {
36 contents.push(',');
37 }
38
35 ty(&format!("({})", contents)) 39 ty(&format!("({})", contents))
36} 40}
37// FIXME: handle path to type 41// FIXME: handle path to type
@@ -292,11 +296,13 @@ pub fn wildcard_pat() -> ast::WildcardPat {
292 296
293/// Creates a tuple of patterns from an iterator of patterns. 297/// Creates a tuple of patterns from an iterator of patterns.
294/// 298///
295/// Invariant: `pats` must be length > 1 299/// Invariant: `pats` must be length > 0
296///
297/// FIXME handle `pats` length == 1
298pub fn tuple_pat(pats: impl IntoIterator<Item = ast::Pat>) -> ast::TuplePat { 300pub fn tuple_pat(pats: impl IntoIterator<Item = ast::Pat>) -> ast::TuplePat {
299 let pats_str = pats.into_iter().map(|p| p.to_string()).join(", "); 301 let mut count: usize = 0;
302 let mut pats_str = pats.into_iter().inspect(|_| count += 1).join(", ");
303 if count == 1 {
304 pats_str.push(',');
305 }
300 return from_text(&format!("({})", pats_str)); 306 return from_text(&format!("({})", pats_str));
301 307
302 fn from_text(text: &str) -> ast::TuplePat { 308 fn from_text(text: &str) -> ast::TuplePat {
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"