diff options
Diffstat (limited to 'crates')
82 files changed, 5829 insertions, 1552 deletions
diff --git a/crates/ra_assists/Cargo.toml b/crates/ra_assists/Cargo.toml index 50be8d9bc..0d2109e4e 100644 --- a/crates/ra_assists/Cargo.toml +++ b/crates/ra_assists/Cargo.toml | |||
@@ -11,7 +11,7 @@ doctest = false | |||
11 | format-buf = "1.0.0" | 11 | format-buf = "1.0.0" |
12 | join_to_string = "0.1.3" | 12 | join_to_string = "0.1.3" |
13 | rustc-hash = "1.0" | 13 | rustc-hash = "1.0" |
14 | itertools = "0.8.0" | 14 | either = "1.5" |
15 | 15 | ||
16 | ra_syntax = { path = "../ra_syntax" } | 16 | ra_syntax = { path = "../ra_syntax" } |
17 | ra_text_edit = { path = "../ra_text_edit" } | 17 | ra_text_edit = { path = "../ra_text_edit" } |
diff --git a/crates/ra_assists/src/assist_ctx.rs b/crates/ra_assists/src/assist_ctx.rs index 28152f724..43f0d664b 100644 --- a/crates/ra_assists/src/assist_ctx.rs +++ b/crates/ra_assists/src/assist_ctx.rs | |||
@@ -1,5 +1,6 @@ | |||
1 | //! This module defines `AssistCtx` -- the API surface that is exposed to assists. | 1 | //! This module defines `AssistCtx` -- the API surface that is exposed to assists. |
2 | use hir::{db::HirDatabase, InFile, SourceAnalyzer}; | 2 | use either::Either; |
3 | use hir::{db::HirDatabase, InFile, SourceAnalyzer, SourceBinder}; | ||
3 | use ra_db::FileRange; | 4 | use ra_db::FileRange; |
4 | use ra_fmt::{leading_indent, reindent}; | 5 | use ra_fmt::{leading_indent, reindent}; |
5 | use ra_syntax::{ | 6 | use ra_syntax::{ |
@@ -9,12 +10,12 @@ use ra_syntax::{ | |||
9 | }; | 10 | }; |
10 | use ra_text_edit::TextEditBuilder; | 11 | use ra_text_edit::TextEditBuilder; |
11 | 12 | ||
12 | use crate::{AssistAction, AssistId, AssistLabel}; | 13 | use crate::{AssistAction, AssistId, AssistLabel, ResolvedAssist}; |
13 | 14 | ||
14 | #[derive(Clone, Debug)] | 15 | #[derive(Clone, Debug)] |
15 | pub(crate) enum Assist { | 16 | pub(crate) enum Assist { |
16 | Unresolved { label: AssistLabel }, | 17 | Unresolved { label: AssistLabel }, |
17 | Resolved { label: AssistLabel, action: AssistAction }, | 18 | Resolved { assist: ResolvedAssist }, |
18 | } | 19 | } |
19 | 20 | ||
20 | /// `AssistCtx` allows to apply an assist or check if it could be applied. | 21 | /// `AssistCtx` allows to apply an assist or check if it could be applied. |
@@ -81,16 +82,45 @@ impl<'a, DB: HirDatabase> AssistCtx<'a, DB> { | |||
81 | self, | 82 | self, |
82 | id: AssistId, | 83 | id: AssistId, |
83 | label: impl Into<String>, | 84 | label: impl Into<String>, |
84 | f: impl FnOnce(&mut AssistBuilder), | 85 | f: impl FnOnce(&mut ActionBuilder), |
85 | ) -> Option<Assist> { | 86 | ) -> Option<Assist> { |
86 | let label = AssistLabel { label: label.into(), id }; | 87 | let label = AssistLabel { label: label.into(), id }; |
88 | assert!(label.label.chars().nth(0).unwrap().is_uppercase()); | ||
89 | |||
87 | let assist = if self.should_compute_edit { | 90 | let assist = if self.should_compute_edit { |
88 | let action = { | 91 | let action = { |
89 | let mut edit = AssistBuilder::default(); | 92 | let mut edit = ActionBuilder::default(); |
90 | f(&mut edit); | 93 | f(&mut edit); |
91 | edit.build() | 94 | edit.build() |
92 | }; | 95 | }; |
93 | Assist::Resolved { label, action } | 96 | Assist::Resolved { assist: ResolvedAssist { label, action_data: Either::Left(action) } } |
97 | } else { | ||
98 | Assist::Unresolved { label } | ||
99 | }; | ||
100 | |||
101 | Some(assist) | ||
102 | } | ||
103 | |||
104 | #[allow(dead_code)] // will be used for auto import assist with multiple actions | ||
105 | pub(crate) fn add_assist_group( | ||
106 | self, | ||
107 | id: AssistId, | ||
108 | label: impl Into<String>, | ||
109 | f: impl FnOnce() -> Vec<ActionBuilder>, | ||
110 | ) -> Option<Assist> { | ||
111 | let label = AssistLabel { label: label.into(), id }; | ||
112 | let assist = if self.should_compute_edit { | ||
113 | let actions = f(); | ||
114 | assert!(!actions.is_empty(), "Assist cannot have no"); | ||
115 | |||
116 | Assist::Resolved { | ||
117 | assist: ResolvedAssist { | ||
118 | label, | ||
119 | action_data: Either::Right( | ||
120 | actions.into_iter().map(ActionBuilder::build).collect(), | ||
121 | ), | ||
122 | }, | ||
123 | } | ||
94 | } else { | 124 | } else { |
95 | Assist::Unresolved { label } | 125 | Assist::Unresolved { label } |
96 | }; | 126 | }; |
@@ -112,12 +142,16 @@ impl<'a, DB: HirDatabase> AssistCtx<'a, DB> { | |||
112 | pub(crate) fn covering_element(&self) -> SyntaxElement { | 142 | pub(crate) fn covering_element(&self) -> SyntaxElement { |
113 | find_covering_element(self.source_file.syntax(), self.frange.range) | 143 | find_covering_element(self.source_file.syntax(), self.frange.range) |
114 | } | 144 | } |
145 | pub(crate) fn source_binder(&self) -> SourceBinder<'a, DB> { | ||
146 | SourceBinder::new(self.db) | ||
147 | } | ||
115 | pub(crate) fn source_analyzer( | 148 | pub(crate) fn source_analyzer( |
116 | &self, | 149 | &self, |
117 | node: &SyntaxNode, | 150 | node: &SyntaxNode, |
118 | offset: Option<TextUnit>, | 151 | offset: Option<TextUnit>, |
119 | ) -> SourceAnalyzer { | 152 | ) -> SourceAnalyzer { |
120 | SourceAnalyzer::new(self.db, InFile::new(self.frange.file_id.into(), node), offset) | 153 | let src = InFile::new(self.frange.file_id.into(), node); |
154 | self.source_binder().analyze(src, offset) | ||
121 | } | 155 | } |
122 | 156 | ||
123 | pub(crate) fn covering_node_for_range(&self, range: TextRange) -> SyntaxElement { | 157 | pub(crate) fn covering_node_for_range(&self, range: TextRange) -> SyntaxElement { |
@@ -126,13 +160,20 @@ impl<'a, DB: HirDatabase> AssistCtx<'a, DB> { | |||
126 | } | 160 | } |
127 | 161 | ||
128 | #[derive(Default)] | 162 | #[derive(Default)] |
129 | pub(crate) struct AssistBuilder { | 163 | pub(crate) struct ActionBuilder { |
130 | edit: TextEditBuilder, | 164 | edit: TextEditBuilder, |
131 | cursor_position: Option<TextUnit>, | 165 | cursor_position: Option<TextUnit>, |
132 | target: Option<TextRange>, | 166 | target: Option<TextRange>, |
167 | label: Option<String>, | ||
133 | } | 168 | } |
134 | 169 | ||
135 | impl AssistBuilder { | 170 | impl ActionBuilder { |
171 | #[allow(dead_code)] | ||
172 | /// Adds a custom label to the action, if it needs to be different from the assist label | ||
173 | pub(crate) fn label(&mut self, label: impl Into<String>) { | ||
174 | self.label = Some(label.into()) | ||
175 | } | ||
176 | |||
136 | /// Replaces specified `range` of text with a given string. | 177 | /// Replaces specified `range` of text with a given string. |
137 | pub(crate) fn replace(&mut self, range: TextRange, replace_with: impl Into<String>) { | 178 | pub(crate) fn replace(&mut self, range: TextRange, replace_with: impl Into<String>) { |
138 | self.edit.replace(range, replace_with.into()) | 179 | self.edit.replace(range, replace_with.into()) |
@@ -191,6 +232,7 @@ impl AssistBuilder { | |||
191 | edit: self.edit.finish(), | 232 | edit: self.edit.finish(), |
192 | cursor_position: self.cursor_position, | 233 | cursor_position: self.cursor_position, |
193 | target: self.target, | 234 | target: self.target, |
235 | label: self.label, | ||
194 | } | 236 | } |
195 | } | 237 | } |
196 | } | 238 | } |
diff --git a/crates/ra_assists/src/assists/add_custom_impl.rs b/crates/ra_assists/src/assists/add_custom_impl.rs index 9b8955710..f91034967 100644 --- a/crates/ra_assists/src/assists/add_custom_impl.rs +++ b/crates/ra_assists/src/assists/add_custom_impl.rs | |||
@@ -49,7 +49,10 @@ pub(crate) fn add_custom_impl(ctx: AssistCtx<impl HirDatabase>) -> Option<Assist | |||
49 | let annotated_name = annotated.syntax().text().to_string(); | 49 | let annotated_name = annotated.syntax().text().to_string(); |
50 | let start_offset = annotated.syntax().parent()?.text_range().end(); | 50 | let start_offset = annotated.syntax().parent()?.text_range().end(); |
51 | 51 | ||
52 | ctx.add_assist(AssistId("add_custom_impl"), "add custom impl", |edit| { | 52 | let label = |
53 | format!("Add custom impl '{}' for '{}'", trait_token.text().as_str(), annotated_name); | ||
54 | |||
55 | ctx.add_assist(AssistId("add_custom_impl"), label, |edit| { | ||
53 | edit.target(attr.syntax().text_range()); | 56 | edit.target(attr.syntax().text_range()); |
54 | 57 | ||
55 | let new_attr_input = input | 58 | let new_attr_input = input |
diff --git a/crates/ra_assists/src/assists/add_derive.rs b/crates/ra_assists/src/assists/add_derive.rs index 764b17bd8..6d9af3905 100644 --- a/crates/ra_assists/src/assists/add_derive.rs +++ b/crates/ra_assists/src/assists/add_derive.rs | |||
@@ -28,7 +28,7 @@ use crate::{Assist, AssistCtx, AssistId}; | |||
28 | pub(crate) fn add_derive(ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> { | 28 | pub(crate) fn add_derive(ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> { |
29 | let nominal = ctx.find_node_at_offset::<ast::NominalDef>()?; | 29 | let nominal = ctx.find_node_at_offset::<ast::NominalDef>()?; |
30 | let node_start = derive_insertion_offset(&nominal)?; | 30 | let node_start = derive_insertion_offset(&nominal)?; |
31 | ctx.add_assist(AssistId("add_derive"), "add `#[derive]`", |edit| { | 31 | ctx.add_assist(AssistId("add_derive"), "Add `#[derive]`", |edit| { |
32 | let derive_attr = nominal | 32 | let derive_attr = nominal |
33 | .attrs() | 33 | .attrs() |
34 | .filter_map(|x| x.as_simple_call()) | 34 | .filter_map(|x| x.as_simple_call()) |
diff --git a/crates/ra_assists/src/assists/add_explicit_type.rs b/crates/ra_assists/src/assists/add_explicit_type.rs index 2c602a79e..f9f826b88 100644 --- a/crates/ra_assists/src/assists/add_explicit_type.rs +++ b/crates/ra_assists/src/assists/add_explicit_type.rs | |||
@@ -47,10 +47,14 @@ pub(crate) fn add_explicit_type(ctx: AssistCtx<impl HirDatabase>) -> Option<Assi | |||
47 | return None; | 47 | return None; |
48 | } | 48 | } |
49 | 49 | ||
50 | ctx.add_assist(AssistId("add_explicit_type"), "add explicit type", |edit| { | 50 | ctx.add_assist( |
51 | edit.target(pat_range); | 51 | AssistId("add_explicit_type"), |
52 | edit.insert(name_range.end(), format!(": {}", ty.display(db))); | 52 | format!("Insert explicit type '{}'", ty.display(db)), |
53 | }) | 53 | |edit| { |
54 | edit.target(pat_range); | ||
55 | edit.insert(name_range.end(), format!(": {}", ty.display(db))); | ||
56 | }, | ||
57 | ) | ||
54 | } | 58 | } |
55 | 59 | ||
56 | #[cfg(test)] | 60 | #[cfg(test)] |
diff --git a/crates/ra_assists/src/assists/add_impl.rs b/crates/ra_assists/src/assists/add_impl.rs index 7da0cfd0d..4b326c837 100644 --- a/crates/ra_assists/src/assists/add_impl.rs +++ b/crates/ra_assists/src/assists/add_impl.rs | |||
@@ -30,7 +30,7 @@ use crate::{Assist, AssistCtx, AssistId}; | |||
30 | pub(crate) fn add_impl(ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> { | 30 | pub(crate) fn add_impl(ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> { |
31 | let nominal = ctx.find_node_at_offset::<ast::NominalDef>()?; | 31 | let nominal = ctx.find_node_at_offset::<ast::NominalDef>()?; |
32 | let name = nominal.name()?; | 32 | let name = nominal.name()?; |
33 | ctx.add_assist(AssistId("add_impl"), "add impl", |edit| { | 33 | ctx.add_assist(AssistId("add_impl"), format!("Implement {}", name.text().as_str()), |edit| { |
34 | edit.target(nominal.syntax().text_range()); | 34 | edit.target(nominal.syntax().text_range()); |
35 | let type_params = nominal.type_param_list(); | 35 | let type_params = nominal.type_param_list(); |
36 | let start_offset = nominal.syntax().text_range().end(); | 36 | let start_offset = nominal.syntax().text_range().end(); |
diff --git a/crates/ra_assists/src/assists/add_import.rs b/crates/ra_assists/src/assists/add_import.rs index b8752cbad..bf6cfe865 100644 --- a/crates/ra_assists/src/assists/add_import.rs +++ b/crates/ra_assists/src/assists/add_import.rs | |||
@@ -72,7 +72,7 @@ pub(crate) fn add_import(ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> { | |||
72 | } | 72 | } |
73 | }; | 73 | }; |
74 | 74 | ||
75 | ctx.add_assist(AssistId("add_import"), format!("import {}", fmt_segments(&segments)), |edit| { | 75 | ctx.add_assist(AssistId("add_import"), format!("Import {}", fmt_segments(&segments)), |edit| { |
76 | apply_auto_import(&position, &path, &segments, edit.text_edit_builder()); | 76 | apply_auto_import(&position, &path, &segments, edit.text_edit_builder()); |
77 | }) | 77 | }) |
78 | } | 78 | } |
diff --git a/crates/ra_assists/src/assists/add_missing_impl_members.rs b/crates/ra_assists/src/assists/add_missing_impl_members.rs index bf1136193..5bb937bde 100644 --- a/crates/ra_assists/src/assists/add_missing_impl_members.rs +++ b/crates/ra_assists/src/assists/add_missing_impl_members.rs | |||
@@ -48,7 +48,7 @@ pub(crate) fn add_missing_impl_members(ctx: AssistCtx<impl HirDatabase>) -> Opti | |||
48 | ctx, | 48 | ctx, |
49 | AddMissingImplMembersMode::NoDefaultMethods, | 49 | AddMissingImplMembersMode::NoDefaultMethods, |
50 | "add_impl_missing_members", | 50 | "add_impl_missing_members", |
51 | "add missing impl members", | 51 | "Implement missing members", |
52 | ) | 52 | ) |
53 | } | 53 | } |
54 | 54 | ||
@@ -89,7 +89,7 @@ pub(crate) fn add_missing_default_members(ctx: AssistCtx<impl HirDatabase>) -> O | |||
89 | ctx, | 89 | ctx, |
90 | AddMissingImplMembersMode::DefaultMethodsOnly, | 90 | AddMissingImplMembersMode::DefaultMethodsOnly, |
91 | "add_impl_default_members", | 91 | "add_impl_default_members", |
92 | "add impl default members", | 92 | "Implement default members", |
93 | ) | 93 | ) |
94 | } | 94 | } |
95 | 95 | ||
diff --git a/crates/ra_assists/src/assists/add_new.rs b/crates/ra_assists/src/assists/add_new.rs index b2f946fac..8db63f762 100644 --- a/crates/ra_assists/src/assists/add_new.rs +++ b/crates/ra_assists/src/assists/add_new.rs | |||
@@ -1,5 +1,5 @@ | |||
1 | use format_buf::format; | 1 | use format_buf::format; |
2 | use hir::{db::HirDatabase, FromSource, InFile}; | 2 | use hir::{db::HirDatabase, InFile}; |
3 | use join_to_string::join; | 3 | use join_to_string::join; |
4 | use ra_syntax::{ | 4 | use ra_syntax::{ |
5 | ast::{ | 5 | ast::{ |
@@ -43,7 +43,7 @@ pub(crate) fn add_new(ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> { | |||
43 | // Return early if we've found an existing new fn | 43 | // Return early if we've found an existing new fn |
44 | let impl_block = find_struct_impl(&ctx, &strukt)?; | 44 | let impl_block = find_struct_impl(&ctx, &strukt)?; |
45 | 45 | ||
46 | ctx.add_assist(AssistId("add_new"), "add new fn", |edit| { | 46 | ctx.add_assist(AssistId("add_new"), "Add default constructor", |edit| { |
47 | edit.target(strukt.syntax().text_range()); | 47 | edit.target(strukt.syntax().text_range()); |
48 | 48 | ||
49 | let mut buf = String::with_capacity(512); | 49 | let mut buf = String::with_capacity(512); |
@@ -136,15 +136,16 @@ fn find_struct_impl( | |||
136 | let module = strukt.syntax().ancestors().find(|node| { | 136 | let module = strukt.syntax().ancestors().find(|node| { |
137 | ast::Module::can_cast(node.kind()) || ast::SourceFile::can_cast(node.kind()) | 137 | ast::Module::can_cast(node.kind()) || ast::SourceFile::can_cast(node.kind()) |
138 | })?; | 138 | })?; |
139 | let mut sb = ctx.source_binder(); | ||
139 | 140 | ||
140 | let struct_ty = { | 141 | let struct_ty = { |
141 | let src = InFile { file_id: ctx.frange.file_id.into(), value: strukt.clone() }; | 142 | let src = InFile { file_id: ctx.frange.file_id.into(), value: strukt.clone() }; |
142 | hir::Struct::from_source(db, src)?.ty(db) | 143 | sb.to_def(src)?.ty(db) |
143 | }; | 144 | }; |
144 | 145 | ||
145 | let block = module.descendants().filter_map(ast::ImplBlock::cast).find_map(|impl_blk| { | 146 | let block = module.descendants().filter_map(ast::ImplBlock::cast).find_map(|impl_blk| { |
146 | let src = InFile { file_id: ctx.frange.file_id.into(), value: impl_blk.clone() }; | 147 | let src = InFile { file_id: ctx.frange.file_id.into(), value: impl_blk.clone() }; |
147 | let blk = hir::ImplBlock::from_source(db, src)?; | 148 | let blk = sb.to_def(src)?; |
148 | 149 | ||
149 | let same_ty = blk.target_ty(db) == struct_ty; | 150 | let same_ty = blk.target_ty(db) == struct_ty; |
150 | let not_trait_impl = blk.target_trait(db).is_none(); | 151 | let not_trait_impl = blk.target_trait(db).is_none(); |
diff --git a/crates/ra_assists/src/assists/apply_demorgan.rs b/crates/ra_assists/src/assists/apply_demorgan.rs index 7c57c0560..666dce4e6 100644 --- a/crates/ra_assists/src/assists/apply_demorgan.rs +++ b/crates/ra_assists/src/assists/apply_demorgan.rs | |||
@@ -39,7 +39,7 @@ pub(crate) fn apply_demorgan(ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> | |||
39 | let not_lhs = invert_boolean_expression(&lhs)?; | 39 | let not_lhs = invert_boolean_expression(&lhs)?; |
40 | let not_rhs = invert_boolean_expression(&rhs)?; | 40 | let not_rhs = invert_boolean_expression(&rhs)?; |
41 | 41 | ||
42 | ctx.add_assist(AssistId("apply_demorgan"), "apply demorgan's law", |edit| { | 42 | ctx.add_assist(AssistId("apply_demorgan"), "Apply De Morgan's law", |edit| { |
43 | edit.target(op_range); | 43 | edit.target(op_range); |
44 | edit.replace(op_range, opposite_op); | 44 | edit.replace(op_range, opposite_op); |
45 | edit.replace(lhs_range, format!("!({}", not_lhs.syntax().text())); | 45 | edit.replace(lhs_range, format!("!({}", not_lhs.syntax().text())); |
diff --git a/crates/ra_assists/src/assists/change_visibility.rs b/crates/ra_assists/src/assists/change_visibility.rs index 132c9dc1d..fd766bb46 100644 --- a/crates/ra_assists/src/assists/change_visibility.rs +++ b/crates/ra_assists/src/assists/change_visibility.rs | |||
@@ -57,7 +57,7 @@ fn add_vis(ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> { | |||
57 | (vis_offset(field.syntax()), ident.text_range()) | 57 | (vis_offset(field.syntax()), ident.text_range()) |
58 | }; | 58 | }; |
59 | 59 | ||
60 | ctx.add_assist(AssistId("change_visibility"), "make pub(crate)", |edit| { | 60 | ctx.add_assist(AssistId("change_visibility"), "Change visibility to pub(crate)", |edit| { |
61 | edit.target(target); | 61 | edit.target(target); |
62 | edit.insert(offset, "pub(crate) "); | 62 | edit.insert(offset, "pub(crate) "); |
63 | edit.set_cursor(offset); | 63 | edit.set_cursor(offset); |
@@ -77,14 +77,18 @@ fn vis_offset(node: &SyntaxNode) -> TextUnit { | |||
77 | 77 | ||
78 | fn change_vis(ctx: AssistCtx<impl HirDatabase>, vis: ast::Visibility) -> Option<Assist> { | 78 | fn change_vis(ctx: AssistCtx<impl HirDatabase>, vis: ast::Visibility) -> Option<Assist> { |
79 | if vis.syntax().text() == "pub" { | 79 | if vis.syntax().text() == "pub" { |
80 | return ctx.add_assist(AssistId("change_visibility"), "change to pub(crate)", |edit| { | 80 | return ctx.add_assist( |
81 | edit.target(vis.syntax().text_range()); | 81 | AssistId("change_visibility"), |
82 | edit.replace(vis.syntax().text_range(), "pub(crate)"); | 82 | "Change Visibility to pub(crate)", |
83 | edit.set_cursor(vis.syntax().text_range().start()) | 83 | |edit| { |
84 | }); | 84 | edit.target(vis.syntax().text_range()); |
85 | edit.replace(vis.syntax().text_range(), "pub(crate)"); | ||
86 | edit.set_cursor(vis.syntax().text_range().start()) | ||
87 | }, | ||
88 | ); | ||
85 | } | 89 | } |
86 | if vis.syntax().text() == "pub(crate)" { | 90 | if vis.syntax().text() == "pub(crate)" { |
87 | return ctx.add_assist(AssistId("change_visibility"), "change to pub", |edit| { | 91 | return ctx.add_assist(AssistId("change_visibility"), "Change visibility to pub", |edit| { |
88 | edit.target(vis.syntax().text_range()); | 92 | edit.target(vis.syntax().text_range()); |
89 | edit.replace(vis.syntax().text_range(), "pub"); | 93 | edit.replace(vis.syntax().text_range(), "pub"); |
90 | edit.set_cursor(vis.syntax().text_range().start()); | 94 | edit.set_cursor(vis.syntax().text_range().start()); |
diff --git a/crates/ra_assists/src/assists/early_return.rs b/crates/ra_assists/src/assists/early_return.rs index 023917aca..487ee9eef 100644 --- a/crates/ra_assists/src/assists/early_return.rs +++ b/crates/ra_assists/src/assists/early_return.rs | |||
@@ -95,7 +95,7 @@ pub(crate) fn convert_to_guarded_return(ctx: AssistCtx<impl HirDatabase>) -> Opt | |||
95 | then_block.syntax().last_child_or_token().filter(|t| t.kind() == R_CURLY)?; | 95 | then_block.syntax().last_child_or_token().filter(|t| t.kind() == R_CURLY)?; |
96 | let cursor_position = ctx.frange.range.start(); | 96 | let cursor_position = ctx.frange.range.start(); |
97 | 97 | ||
98 | ctx.add_assist(AssistId("convert_to_guarded_return"), "convert to guarded return", |edit| { | 98 | ctx.add_assist(AssistId("convert_to_guarded_return"), "Convert to guarded return", |edit| { |
99 | let if_indent_level = IndentLevel::from_node(&if_expr.syntax()); | 99 | let if_indent_level = IndentLevel::from_node(&if_expr.syntax()); |
100 | let new_block = match if_let_pat { | 100 | let new_block = match if_let_pat { |
101 | None => { | 101 | None => { |
diff --git a/crates/ra_assists/src/assists/fill_match_arms.rs b/crates/ra_assists/src/assists/fill_match_arms.rs index 99d80998c..01758d23a 100644 --- a/crates/ra_assists/src/assists/fill_match_arms.rs +++ b/crates/ra_assists/src/assists/fill_match_arms.rs | |||
@@ -57,7 +57,7 @@ pub(crate) fn fill_match_arms(ctx: AssistCtx<impl HirDatabase>) -> Option<Assist | |||
57 | 57 | ||
58 | let db = ctx.db; | 58 | let db = ctx.db; |
59 | 59 | ||
60 | ctx.add_assist(AssistId("fill_match_arms"), "fill match arms", |edit| { | 60 | ctx.add_assist(AssistId("fill_match_arms"), "Fill match arms", |edit| { |
61 | let indent_level = IndentLevel::from_node(match_arm_list.syntax()); | 61 | let indent_level = IndentLevel::from_node(match_arm_list.syntax()); |
62 | 62 | ||
63 | let new_arm_list = { | 63 | let new_arm_list = { |
diff --git a/crates/ra_assists/src/assists/flip_binexpr.rs b/crates/ra_assists/src/assists/flip_binexpr.rs index 2d91b2a7e..2074087cd 100644 --- a/crates/ra_assists/src/assists/flip_binexpr.rs +++ b/crates/ra_assists/src/assists/flip_binexpr.rs | |||
@@ -34,7 +34,7 @@ pub(crate) fn flip_binexpr(ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> { | |||
34 | return None; | 34 | return None; |
35 | } | 35 | } |
36 | 36 | ||
37 | ctx.add_assist(AssistId("flip_binexpr"), "flip binary expression", |edit| { | 37 | ctx.add_assist(AssistId("flip_binexpr"), "Flip binary expression", |edit| { |
38 | edit.target(op_range); | 38 | edit.target(op_range); |
39 | if let FlipAction::FlipAndReplaceOp(new_op) = action { | 39 | if let FlipAction::FlipAndReplaceOp(new_op) = action { |
40 | edit.replace(op_range, new_op); | 40 | edit.replace(op_range, new_op); |
diff --git a/crates/ra_assists/src/assists/flip_comma.rs b/crates/ra_assists/src/assists/flip_comma.rs index 9be1c1dc6..dd0c405ed 100644 --- a/crates/ra_assists/src/assists/flip_comma.rs +++ b/crates/ra_assists/src/assists/flip_comma.rs | |||
@@ -29,7 +29,7 @@ pub(crate) fn flip_comma(ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> { | |||
29 | return None; | 29 | return None; |
30 | } | 30 | } |
31 | 31 | ||
32 | ctx.add_assist(AssistId("flip_comma"), "flip comma", |edit| { | 32 | ctx.add_assist(AssistId("flip_comma"), "Flip comma", |edit| { |
33 | edit.target(comma.text_range()); | 33 | edit.target(comma.text_range()); |
34 | edit.replace(prev.text_range(), next.to_string()); | 34 | edit.replace(prev.text_range(), next.to_string()); |
35 | edit.replace(next.text_range(), prev.to_string()); | 35 | edit.replace(next.text_range(), prev.to_string()); |
diff --git a/crates/ra_assists/src/assists/flip_trait_bound.rs b/crates/ra_assists/src/assists/flip_trait_bound.rs index 6017b39dd..50b3fa492 100644 --- a/crates/ra_assists/src/assists/flip_trait_bound.rs +++ b/crates/ra_assists/src/assists/flip_trait_bound.rs | |||
@@ -33,7 +33,7 @@ pub(crate) fn flip_trait_bound(ctx: AssistCtx<impl HirDatabase>) -> Option<Assis | |||
33 | non_trivia_sibling(plus.clone().into(), Direction::Next)?, | 33 | non_trivia_sibling(plus.clone().into(), Direction::Next)?, |
34 | ); | 34 | ); |
35 | 35 | ||
36 | ctx.add_assist(AssistId("flip_trait_bound"), "flip trait bound", |edit| { | 36 | ctx.add_assist(AssistId("flip_trait_bound"), "Flip trait bounds", |edit| { |
37 | edit.target(plus.text_range()); | 37 | edit.target(plus.text_range()); |
38 | edit.replace(before.text_range(), after.to_string()); | 38 | edit.replace(before.text_range(), after.to_string()); |
39 | edit.replace(after.text_range(), before.to_string()); | 39 | edit.replace(after.text_range(), before.to_string()); |
diff --git a/crates/ra_assists/src/assists/inline_local_variable.rs b/crates/ra_assists/src/assists/inline_local_variable.rs index 18a34502c..45e0f983f 100644 --- a/crates/ra_assists/src/assists/inline_local_variable.rs +++ b/crates/ra_assists/src/assists/inline_local_variable.rs | |||
@@ -4,7 +4,7 @@ use ra_syntax::{ | |||
4 | TextRange, | 4 | TextRange, |
5 | }; | 5 | }; |
6 | 6 | ||
7 | use crate::assist_ctx::AssistBuilder; | 7 | use crate::assist_ctx::ActionBuilder; |
8 | use crate::{Assist, AssistCtx, AssistId}; | 8 | use crate::{Assist, AssistCtx, AssistId}; |
9 | 9 | ||
10 | // Assist: inline_local_variable | 10 | // Assist: inline_local_variable |
@@ -93,8 +93,8 @@ pub(crate) fn inline_local_varialbe(ctx: AssistCtx<impl HirDatabase>) -> Option< | |||
93 | 93 | ||
94 | ctx.add_assist( | 94 | ctx.add_assist( |
95 | AssistId("inline_local_variable"), | 95 | AssistId("inline_local_variable"), |
96 | "inline local variable", | 96 | "Inline variable", |
97 | move |edit: &mut AssistBuilder| { | 97 | move |edit: &mut ActionBuilder| { |
98 | edit.delete(delete_range); | 98 | edit.delete(delete_range); |
99 | for (desc, should_wrap) in refs.iter().zip(wrap_in_parens) { | 99 | for (desc, should_wrap) in refs.iter().zip(wrap_in_parens) { |
100 | if should_wrap { | 100 | if should_wrap { |
diff --git a/crates/ra_assists/src/assists/introduce_variable.rs b/crates/ra_assists/src/assists/introduce_variable.rs index 0623d4475..19e211e0f 100644 --- a/crates/ra_assists/src/assists/introduce_variable.rs +++ b/crates/ra_assists/src/assists/introduce_variable.rs | |||
@@ -43,7 +43,7 @@ pub(crate) fn introduce_variable(ctx: AssistCtx<impl HirDatabase>) -> Option<Ass | |||
43 | if indent.kind() != WHITESPACE { | 43 | if indent.kind() != WHITESPACE { |
44 | return None; | 44 | return None; |
45 | } | 45 | } |
46 | ctx.add_assist(AssistId("introduce_variable"), "introduce variable", move |edit| { | 46 | ctx.add_assist(AssistId("introduce_variable"), "Extract into variable", move |edit| { |
47 | let mut buf = String::new(); | 47 | let mut buf = String::new(); |
48 | 48 | ||
49 | let cursor_offset = if wrap_in_block { | 49 | let cursor_offset = if wrap_in_block { |
diff --git a/crates/ra_assists/src/assists/invert_if.rs b/crates/ra_assists/src/assists/invert_if.rs index bababa3e2..16352c040 100644 --- a/crates/ra_assists/src/assists/invert_if.rs +++ b/crates/ra_assists/src/assists/invert_if.rs | |||
@@ -41,7 +41,7 @@ pub(crate) fn invert_if(ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> { | |||
41 | let else_node = else_block.syntax(); | 41 | let else_node = else_block.syntax(); |
42 | let else_range = else_node.text_range(); | 42 | let else_range = else_node.text_range(); |
43 | let then_range = then_node.text_range(); | 43 | let then_range = then_node.text_range(); |
44 | return ctx.add_assist(AssistId("invert_if"), "invert if branches", |edit| { | 44 | return ctx.add_assist(AssistId("invert_if"), "Invert if", |edit| { |
45 | edit.target(if_range); | 45 | edit.target(if_range); |
46 | edit.replace(cond_range, flip_cond.syntax().text()); | 46 | edit.replace(cond_range, flip_cond.syntax().text()); |
47 | edit.replace(else_range, then_node.text()); | 47 | edit.replace(else_range, then_node.text()); |
diff --git a/crates/ra_assists/src/assists/merge_match_arms.rs b/crates/ra_assists/src/assists/merge_match_arms.rs index e9f2cae91..aca391155 100644 --- a/crates/ra_assists/src/assists/merge_match_arms.rs +++ b/crates/ra_assists/src/assists/merge_match_arms.rs | |||
@@ -52,7 +52,7 @@ pub(crate) fn merge_match_arms(ctx: AssistCtx<impl HirDatabase>) -> Option<Assis | |||
52 | 52 | ||
53 | let cursor_to_end = current_arm.syntax().text_range().end() - ctx.frange.range.start(); | 53 | let cursor_to_end = current_arm.syntax().text_range().end() - ctx.frange.range.start(); |
54 | 54 | ||
55 | ctx.add_assist(AssistId("merge_match_arms"), "merge match arms", |edit| { | 55 | ctx.add_assist(AssistId("merge_match_arms"), "Merge match arms", |edit| { |
56 | fn contains_placeholder(a: &MatchArm) -> bool { | 56 | fn contains_placeholder(a: &MatchArm) -> bool { |
57 | a.pats().any(|x| match x { | 57 | a.pats().any(|x| match x { |
58 | ra_syntax::ast::Pat::PlaceholderPat(..) => true, | 58 | ra_syntax::ast::Pat::PlaceholderPat(..) => true, |
diff --git a/crates/ra_assists/src/assists/move_bounds.rs b/crates/ra_assists/src/assists/move_bounds.rs index 3145d7625..355adddc3 100644 --- a/crates/ra_assists/src/assists/move_bounds.rs +++ b/crates/ra_assists/src/assists/move_bounds.rs | |||
@@ -46,7 +46,7 @@ pub(crate) fn move_bounds_to_where_clause(ctx: AssistCtx<impl HirDatabase>) -> O | |||
46 | _ => return None, | 46 | _ => return None, |
47 | }; | 47 | }; |
48 | 48 | ||
49 | ctx.add_assist(AssistId("move_bounds_to_where_clause"), "move_bounds_to_where_clause", |edit| { | 49 | ctx.add_assist(AssistId("move_bounds_to_where_clause"), "Move to where clause", |edit| { |
50 | let new_params = type_param_list | 50 | let new_params = type_param_list |
51 | .type_params() | 51 | .type_params() |
52 | .filter(|it| it.type_bound_list().is_some()) | 52 | .filter(|it| it.type_bound_list().is_some()) |
diff --git a/crates/ra_assists/src/assists/move_guard.rs b/crates/ra_assists/src/assists/move_guard.rs index b49ec6172..41a31e677 100644 --- a/crates/ra_assists/src/assists/move_guard.rs +++ b/crates/ra_assists/src/assists/move_guard.rs | |||
@@ -41,7 +41,7 @@ pub(crate) fn move_guard_to_arm_body(ctx: AssistCtx<impl HirDatabase>) -> Option | |||
41 | let arm_expr = match_arm.expr()?; | 41 | let arm_expr = match_arm.expr()?; |
42 | let buf = format!("if {} {{ {} }}", guard_conditions.syntax().text(), arm_expr.syntax().text()); | 42 | let buf = format!("if {} {{ {} }}", guard_conditions.syntax().text(), arm_expr.syntax().text()); |
43 | 43 | ||
44 | ctx.add_assist(AssistId("move_guard_to_arm_body"), "move guard to arm body", |edit| { | 44 | ctx.add_assist(AssistId("move_guard_to_arm_body"), "Move guard to arm body", |edit| { |
45 | edit.target(guard.syntax().text_range()); | 45 | edit.target(guard.syntax().text_range()); |
46 | let offseting_amount = match space_before_guard.and_then(|it| it.into_token()) { | 46 | let offseting_amount = match space_before_guard.and_then(|it| it.into_token()) { |
47 | Some(tok) => { | 47 | Some(tok) => { |
@@ -111,7 +111,7 @@ pub(crate) fn move_arm_cond_to_match_guard(ctx: AssistCtx<impl HirDatabase>) -> | |||
111 | 111 | ||
112 | ctx.add_assist( | 112 | ctx.add_assist( |
113 | AssistId("move_arm_cond_to_match_guard"), | 113 | AssistId("move_arm_cond_to_match_guard"), |
114 | "move condition to match guard", | 114 | "Move condition to match guard", |
115 | |edit| { | 115 | |edit| { |
116 | edit.target(if_expr.syntax().text_range()); | 116 | edit.target(if_expr.syntax().text_range()); |
117 | let then_only_expr = then_block.block().and_then(|it| it.statements().next()).is_none(); | 117 | let then_only_expr = then_block.block().and_then(|it| it.statements().next()).is_none(); |
diff --git a/crates/ra_assists/src/assists/raw_string.rs b/crates/ra_assists/src/assists/raw_string.rs index 93912a470..e79c51673 100644 --- a/crates/ra_assists/src/assists/raw_string.rs +++ b/crates/ra_assists/src/assists/raw_string.rs | |||
@@ -25,7 +25,7 @@ use crate::{Assist, AssistCtx, AssistId}; | |||
25 | pub(crate) fn make_raw_string(ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> { | 25 | pub(crate) fn make_raw_string(ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> { |
26 | let token = ctx.find_token_at_offset(STRING).and_then(ast::String::cast)?; | 26 | let token = ctx.find_token_at_offset(STRING).and_then(ast::String::cast)?; |
27 | let value = token.value()?; | 27 | let value = token.value()?; |
28 | ctx.add_assist(AssistId("make_raw_string"), "make raw string", |edit| { | 28 | ctx.add_assist(AssistId("make_raw_string"), "Rewrite as raw string", |edit| { |
29 | edit.target(token.syntax().text_range()); | 29 | edit.target(token.syntax().text_range()); |
30 | let max_hash_streak = count_hashes(&value); | 30 | let max_hash_streak = count_hashes(&value); |
31 | let mut hashes = String::with_capacity(max_hash_streak + 1); | 31 | let mut hashes = String::with_capacity(max_hash_streak + 1); |
@@ -54,7 +54,7 @@ pub(crate) fn make_raw_string(ctx: AssistCtx<impl HirDatabase>) -> Option<Assist | |||
54 | pub(crate) fn make_usual_string(ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> { | 54 | pub(crate) fn make_usual_string(ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> { |
55 | let token = ctx.find_token_at_offset(RAW_STRING).and_then(ast::RawString::cast)?; | 55 | let token = ctx.find_token_at_offset(RAW_STRING).and_then(ast::RawString::cast)?; |
56 | let value = token.value()?; | 56 | let value = token.value()?; |
57 | ctx.add_assist(AssistId("make_usual_string"), "make usual string", |edit| { | 57 | ctx.add_assist(AssistId("make_usual_string"), "Rewrite as regular string", |edit| { |
58 | edit.target(token.syntax().text_range()); | 58 | edit.target(token.syntax().text_range()); |
59 | // parse inside string to escape `"` | 59 | // parse inside string to escape `"` |
60 | let escaped = value.escape_default().to_string(); | 60 | let escaped = value.escape_default().to_string(); |
@@ -79,7 +79,7 @@ pub(crate) fn make_usual_string(ctx: AssistCtx<impl HirDatabase>) -> Option<Assi | |||
79 | // ``` | 79 | // ``` |
80 | pub(crate) fn add_hash(ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> { | 80 | pub(crate) fn add_hash(ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> { |
81 | let token = ctx.find_token_at_offset(RAW_STRING)?; | 81 | let token = ctx.find_token_at_offset(RAW_STRING)?; |
82 | ctx.add_assist(AssistId("add_hash"), "add hash to raw string", |edit| { | 82 | ctx.add_assist(AssistId("add_hash"), "Add # to raw string", |edit| { |
83 | edit.target(token.text_range()); | 83 | edit.target(token.text_range()); |
84 | edit.insert(token.text_range().start() + TextUnit::of_char('r'), "#"); | 84 | edit.insert(token.text_range().start() + TextUnit::of_char('r'), "#"); |
85 | edit.insert(token.text_range().end(), "#"); | 85 | edit.insert(token.text_range().end(), "#"); |
@@ -108,7 +108,7 @@ pub(crate) fn remove_hash(ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> { | |||
108 | // no hash to remove | 108 | // no hash to remove |
109 | return None; | 109 | return None; |
110 | } | 110 | } |
111 | ctx.add_assist(AssistId("remove_hash"), "remove hash from raw string", |edit| { | 111 | ctx.add_assist(AssistId("remove_hash"), "Remove hash from raw string", |edit| { |
112 | edit.target(token.text_range()); | 112 | edit.target(token.text_range()); |
113 | let result = &text[2..text.len() - 1]; | 113 | let result = &text[2..text.len() - 1]; |
114 | let result = if result.starts_with('\"') { | 114 | let result = if result.starts_with('\"') { |
diff --git a/crates/ra_assists/src/assists/remove_dbg.rs b/crates/ra_assists/src/assists/remove_dbg.rs index aedf8747f..cf211ab84 100644 --- a/crates/ra_assists/src/assists/remove_dbg.rs +++ b/crates/ra_assists/src/assists/remove_dbg.rs | |||
@@ -58,7 +58,7 @@ pub(crate) fn remove_dbg(ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> { | |||
58 | text.slice(without_parens).to_string() | 58 | text.slice(without_parens).to_string() |
59 | }; | 59 | }; |
60 | 60 | ||
61 | ctx.add_assist(AssistId("remove_dbg"), "remove dbg!()", |edit| { | 61 | ctx.add_assist(AssistId("remove_dbg"), "Remove dbg!()", |edit| { |
62 | edit.target(macro_call.syntax().text_range()); | 62 | edit.target(macro_call.syntax().text_range()); |
63 | edit.replace(macro_range, macro_content); | 63 | edit.replace(macro_range, macro_content); |
64 | edit.set_cursor(cursor_pos); | 64 | edit.set_cursor(cursor_pos); |
diff --git a/crates/ra_assists/src/assists/replace_if_let_with_match.rs b/crates/ra_assists/src/assists/replace_if_let_with_match.rs index 3272801ff..c9b62e5ff 100644 --- a/crates/ra_assists/src/assists/replace_if_let_with_match.rs +++ b/crates/ra_assists/src/assists/replace_if_let_with_match.rs | |||
@@ -42,7 +42,7 @@ pub(crate) fn replace_if_let_with_match(ctx: AssistCtx<impl HirDatabase>) -> Opt | |||
42 | ast::ElseBranch::IfExpr(_) => return None, | 42 | ast::ElseBranch::IfExpr(_) => return None, |
43 | }; | 43 | }; |
44 | 44 | ||
45 | ctx.add_assist(AssistId("replace_if_let_with_match"), "replace with match", |edit| { | 45 | ctx.add_assist(AssistId("replace_if_let_with_match"), "Replace with match", |edit| { |
46 | let match_expr = build_match_expr(expr, pat, then_block, else_block); | 46 | let match_expr = build_match_expr(expr, pat, then_block, else_block); |
47 | edit.target(if_expr.syntax().text_range()); | 47 | edit.target(if_expr.syntax().text_range()); |
48 | edit.replace_node_and_indent(if_expr.syntax(), match_expr); | 48 | edit.replace_node_and_indent(if_expr.syntax(), match_expr); |
diff --git a/crates/ra_assists/src/assists/split_import.rs b/crates/ra_assists/src/assists/split_import.rs index 5f8d6b0be..6038c4858 100644 --- a/crates/ra_assists/src/assists/split_import.rs +++ b/crates/ra_assists/src/assists/split_import.rs | |||
@@ -32,7 +32,7 @@ pub(crate) fn split_import(ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> { | |||
32 | None => top_path.syntax().text_range().end(), | 32 | None => top_path.syntax().text_range().end(), |
33 | }; | 33 | }; |
34 | 34 | ||
35 | ctx.add_assist(AssistId("split_import"), "split import", |edit| { | 35 | ctx.add_assist(AssistId("split_import"), "Split import", |edit| { |
36 | edit.target(colon_colon.text_range()); | 36 | edit.target(colon_colon.text_range()); |
37 | edit.insert(l_curly, "{"); | 37 | edit.insert(l_curly, "{"); |
38 | edit.insert(r_curly, "}"); | 38 | edit.insert(r_curly, "}"); |
diff --git a/crates/ra_assists/src/ast_transform.rs b/crates/ra_assists/src/ast_transform.rs index eac2903d1..56b7588ef 100644 --- a/crates/ra_assists/src/ast_transform.rs +++ b/crates/ra_assists/src/ast_transform.rs | |||
@@ -2,7 +2,7 @@ | |||
2 | use rustc_hash::FxHashMap; | 2 | use rustc_hash::FxHashMap; |
3 | 3 | ||
4 | use hir::{db::HirDatabase, InFile, PathResolution}; | 4 | use hir::{db::HirDatabase, InFile, PathResolution}; |
5 | use ra_syntax::ast::{self, make, AstNode}; | 5 | use ra_syntax::ast::{self, AstNode}; |
6 | 6 | ||
7 | pub trait AstTransform<'a> { | 7 | pub trait AstTransform<'a> { |
8 | fn get_substitution( | 8 | fn get_substitution( |
@@ -134,11 +134,18 @@ impl<'a, DB: HirDatabase> QualifyPaths<'a, DB> { | |||
134 | match resolution { | 134 | match resolution { |
135 | PathResolution::Def(def) => { | 135 | PathResolution::Def(def) => { |
136 | let found_path = from.find_use_path(self.db, def)?; | 136 | let found_path = from.find_use_path(self.db, def)?; |
137 | let args = p | 137 | let mut path = path_to_ast(found_path); |
138 | |||
139 | let type_args = p | ||
138 | .segment() | 140 | .segment() |
139 | .and_then(|s| s.type_arg_list()) | 141 | .and_then(|s| s.type_arg_list()) |
140 | .map(|arg_list| apply(self, node.with_value(arg_list))); | 142 | .map(|arg_list| apply(self, node.with_value(arg_list))); |
141 | Some(make::path_with_type_arg_list(path_to_ast(found_path), args).syntax().clone()) | 143 | if let Some(type_args) = type_args { |
144 | let last_segment = path.segment().unwrap(); | ||
145 | path = path.with_segment(last_segment.with_type_args(type_args)) | ||
146 | } | ||
147 | |||
148 | Some(path.syntax().clone()) | ||
142 | } | 149 | } |
143 | PathResolution::Local(_) | 150 | PathResolution::Local(_) |
144 | | PathResolution::TypeParam(_) | 151 | | PathResolution::TypeParam(_) |
diff --git a/crates/ra_assists/src/doc_tests.rs b/crates/ra_assists/src/doc_tests.rs index a8f8446cb..5dc1ee233 100644 --- a/crates/ra_assists/src/doc_tests.rs +++ b/crates/ra_assists/src/doc_tests.rs | |||
@@ -15,21 +15,21 @@ fn check(assist_id: &str, before: &str, after: &str) { | |||
15 | let (db, file_id) = TestDB::with_single_file(&before); | 15 | let (db, file_id) = TestDB::with_single_file(&before); |
16 | let frange = FileRange { file_id, range: selection.into() }; | 16 | let frange = FileRange { file_id, range: selection.into() }; |
17 | 17 | ||
18 | let (_assist_id, action) = crate::assists(&db, frange) | 18 | let assist = crate::assists(&db, frange) |
19 | .into_iter() | 19 | .into_iter() |
20 | .find(|(id, _)| id.id.0 == assist_id) | 20 | .find(|assist| assist.label.id.0 == assist_id) |
21 | .unwrap_or_else(|| { | 21 | .unwrap_or_else(|| { |
22 | panic!( | 22 | panic!( |
23 | "\n\nAssist is not applicable: {}\nAvailable assists: {}", | 23 | "\n\nAssist is not applicable: {}\nAvailable assists: {}", |
24 | assist_id, | 24 | assist_id, |
25 | crate::assists(&db, frange) | 25 | crate::assists(&db, frange) |
26 | .into_iter() | 26 | .into_iter() |
27 | .map(|(id, _)| id.id.0) | 27 | .map(|assist| assist.label.id.0) |
28 | .collect::<Vec<_>>() | 28 | .collect::<Vec<_>>() |
29 | .join(", ") | 29 | .join(", ") |
30 | ) | 30 | ) |
31 | }); | 31 | }); |
32 | 32 | ||
33 | let actual = action.edit.apply(&before); | 33 | let actual = assist.get_first_action().edit.apply(&before); |
34 | assert_eq_text!(after, &actual); | 34 | assert_eq_text!(after, &actual); |
35 | } | 35 | } |
diff --git a/crates/ra_assists/src/lib.rs b/crates/ra_assists/src/lib.rs index 712ff6f6a..d45b58966 100644 --- a/crates/ra_assists/src/lib.rs +++ b/crates/ra_assists/src/lib.rs | |||
@@ -13,6 +13,7 @@ mod doc_tests; | |||
13 | mod test_db; | 13 | mod test_db; |
14 | pub mod ast_transform; | 14 | pub mod ast_transform; |
15 | 15 | ||
16 | use either::Either; | ||
16 | use hir::db::HirDatabase; | 17 | use hir::db::HirDatabase; |
17 | use ra_db::FileRange; | 18 | use ra_db::FileRange; |
18 | use ra_syntax::{TextRange, TextUnit}; | 19 | use ra_syntax::{TextRange, TextUnit}; |
@@ -35,11 +36,27 @@ pub struct AssistLabel { | |||
35 | 36 | ||
36 | #[derive(Debug, Clone)] | 37 | #[derive(Debug, Clone)] |
37 | pub struct AssistAction { | 38 | pub struct AssistAction { |
39 | pub label: Option<String>, | ||
38 | pub edit: TextEdit, | 40 | pub edit: TextEdit, |
39 | pub cursor_position: Option<TextUnit>, | 41 | pub cursor_position: Option<TextUnit>, |
40 | pub target: Option<TextRange>, | 42 | pub target: Option<TextRange>, |
41 | } | 43 | } |
42 | 44 | ||
45 | #[derive(Debug, Clone)] | ||
46 | pub struct ResolvedAssist { | ||
47 | pub label: AssistLabel, | ||
48 | pub action_data: Either<AssistAction, Vec<AssistAction>>, | ||
49 | } | ||
50 | |||
51 | impl ResolvedAssist { | ||
52 | pub fn get_first_action(&self) -> AssistAction { | ||
53 | match &self.action_data { | ||
54 | Either::Left(action) => action.clone(), | ||
55 | Either::Right(actions) => actions[0].clone(), | ||
56 | } | ||
57 | } | ||
58 | } | ||
59 | |||
43 | /// Return all the assists applicable at the given position. | 60 | /// Return all the assists applicable at the given position. |
44 | /// | 61 | /// |
45 | /// Assists are returned in the "unresolved" state, that is only labels are | 62 | /// Assists are returned in the "unresolved" state, that is only labels are |
@@ -64,7 +81,7 @@ where | |||
64 | /// | 81 | /// |
65 | /// Assists are returned in the "resolved" state, that is with edit fully | 82 | /// Assists are returned in the "resolved" state, that is with edit fully |
66 | /// computed. | 83 | /// computed. |
67 | pub fn assists<H>(db: &H, range: FileRange) -> Vec<(AssistLabel, AssistAction)> | 84 | pub fn assists<H>(db: &H, range: FileRange) -> Vec<ResolvedAssist> |
68 | where | 85 | where |
69 | H: HirDatabase + 'static, | 86 | H: HirDatabase + 'static, |
70 | { | 87 | { |
@@ -75,11 +92,11 @@ where | |||
75 | .iter() | 92 | .iter() |
76 | .filter_map(|f| f(ctx.clone())) | 93 | .filter_map(|f| f(ctx.clone())) |
77 | .map(|a| match a { | 94 | .map(|a| match a { |
78 | Assist::Resolved { label, action } => (label, action), | 95 | Assist::Resolved { assist } => assist, |
79 | Assist::Unresolved { .. } => unreachable!(), | 96 | Assist::Unresolved { .. } => unreachable!(), |
80 | }) | 97 | }) |
81 | .collect::<Vec<_>>(); | 98 | .collect::<Vec<_>>(); |
82 | a.sort_by(|a, b| match (a.1.target, b.1.target) { | 99 | a.sort_by(|a, b| match (a.get_first_action().target, b.get_first_action().target) { |
83 | (Some(a), Some(b)) => a.len().cmp(&b.len()), | 100 | (Some(a), Some(b)) => a.len().cmp(&b.len()), |
84 | (Some(_), None) => Ordering::Less, | 101 | (Some(_), None) => Ordering::Less, |
85 | (None, Some(_)) => Ordering::Greater, | 102 | (None, Some(_)) => Ordering::Greater, |
@@ -174,7 +191,7 @@ mod helpers { | |||
174 | AssistCtx::with_ctx(&db, frange, true, assist).expect("code action is not applicable"); | 191 | AssistCtx::with_ctx(&db, frange, true, assist).expect("code action is not applicable"); |
175 | let action = match assist { | 192 | let action = match assist { |
176 | Assist::Unresolved { .. } => unreachable!(), | 193 | Assist::Unresolved { .. } => unreachable!(), |
177 | Assist::Resolved { action, .. } => action, | 194 | Assist::Resolved { assist } => assist.get_first_action(), |
178 | }; | 195 | }; |
179 | 196 | ||
180 | let actual = action.edit.apply(&before); | 197 | let actual = action.edit.apply(&before); |
@@ -201,7 +218,7 @@ mod helpers { | |||
201 | AssistCtx::with_ctx(&db, frange, true, assist).expect("code action is not applicable"); | 218 | AssistCtx::with_ctx(&db, frange, true, assist).expect("code action is not applicable"); |
202 | let action = match assist { | 219 | let action = match assist { |
203 | Assist::Unresolved { .. } => unreachable!(), | 220 | Assist::Unresolved { .. } => unreachable!(), |
204 | Assist::Resolved { action, .. } => action, | 221 | Assist::Resolved { assist } => assist.get_first_action(), |
205 | }; | 222 | }; |
206 | 223 | ||
207 | let mut actual = action.edit.apply(&before); | 224 | let mut actual = action.edit.apply(&before); |
@@ -224,7 +241,7 @@ mod helpers { | |||
224 | AssistCtx::with_ctx(&db, frange, true, assist).expect("code action is not applicable"); | 241 | AssistCtx::with_ctx(&db, frange, true, assist).expect("code action is not applicable"); |
225 | let action = match assist { | 242 | let action = match assist { |
226 | Assist::Unresolved { .. } => unreachable!(), | 243 | Assist::Unresolved { .. } => unreachable!(), |
227 | Assist::Resolved { action, .. } => action, | 244 | Assist::Resolved { assist } => assist.get_first_action(), |
228 | }; | 245 | }; |
229 | 246 | ||
230 | let range = action.target.expect("expected target on action"); | 247 | let range = action.target.expect("expected target on action"); |
@@ -243,7 +260,7 @@ mod helpers { | |||
243 | AssistCtx::with_ctx(&db, frange, true, assist).expect("code action is not applicable"); | 260 | AssistCtx::with_ctx(&db, frange, true, assist).expect("code action is not applicable"); |
244 | let action = match assist { | 261 | let action = match assist { |
245 | Assist::Unresolved { .. } => unreachable!(), | 262 | Assist::Unresolved { .. } => unreachable!(), |
246 | Assist::Resolved { action, .. } => action, | 263 | Assist::Resolved { assist } => assist.get_first_action(), |
247 | }; | 264 | }; |
248 | 265 | ||
249 | let range = action.target.expect("expected target on action"); | 266 | let range = action.target.expect("expected target on action"); |
@@ -292,8 +309,11 @@ mod tests { | |||
292 | let assists = super::assists(&db, frange); | 309 | let assists = super::assists(&db, frange); |
293 | let mut assists = assists.iter(); | 310 | let mut assists = assists.iter(); |
294 | 311 | ||
295 | assert_eq!(assists.next().expect("expected assist").0.label, "make pub(crate)"); | 312 | assert_eq!( |
296 | assert_eq!(assists.next().expect("expected assist").0.label, "add `#[derive]`"); | 313 | assists.next().expect("expected assist").label.label, |
314 | "Change visibility to pub(crate)" | ||
315 | ); | ||
316 | assert_eq!(assists.next().expect("expected assist").label.label, "Add `#[derive]`"); | ||
297 | } | 317 | } |
298 | 318 | ||
299 | #[test] | 319 | #[test] |
@@ -312,7 +332,7 @@ mod tests { | |||
312 | let assists = super::assists(&db, frange); | 332 | let assists = super::assists(&db, frange); |
313 | let mut assists = assists.iter(); | 333 | let mut assists = assists.iter(); |
314 | 334 | ||
315 | assert_eq!(assists.next().expect("expected assist").0.label, "introduce variable"); | 335 | assert_eq!(assists.next().expect("expected assist").label.label, "Extract into variable"); |
316 | assert_eq!(assists.next().expect("expected assist").0.label, "replace with match"); | 336 | assert_eq!(assists.next().expect("expected assist").label.label, "Replace with match"); |
317 | } | 337 | } |
318 | } | 338 | } |
diff --git a/crates/ra_cargo_watch/Cargo.toml b/crates/ra_cargo_watch/Cargo.toml index 2e411d23b..9ead48abf 100644 --- a/crates/ra_cargo_watch/Cargo.toml +++ b/crates/ra_cargo_watch/Cargo.toml | |||
@@ -6,7 +6,7 @@ authors = ["rust-analyzer developers"] | |||
6 | 6 | ||
7 | [dependencies] | 7 | [dependencies] |
8 | crossbeam-channel = "0.4" | 8 | crossbeam-channel = "0.4" |
9 | lsp-types = { version = "0.68.0", features = ["proposed"] } | 9 | lsp-types = { version = "0.69.0", features = ["proposed"] } |
10 | log = "0.4.3" | 10 | log = "0.4.3" |
11 | cargo_metadata = "0.9.1" | 11 | cargo_metadata = "0.9.1" |
12 | jod-thread = "0.1.0" | 12 | jod-thread = "0.1.0" |
diff --git a/crates/ra_cargo_watch/src/lib.rs b/crates/ra_cargo_watch/src/lib.rs index 20fa5a924..7f4c9280c 100644 --- a/crates/ra_cargo_watch/src/lib.rs +++ b/crates/ra_cargo_watch/src/lib.rs | |||
@@ -38,7 +38,7 @@ pub struct CheckOptions { | |||
38 | #[derive(Debug)] | 38 | #[derive(Debug)] |
39 | pub struct CheckWatcher { | 39 | pub struct CheckWatcher { |
40 | pub task_recv: Receiver<CheckTask>, | 40 | pub task_recv: Receiver<CheckTask>, |
41 | pub shared: Arc<RwLock<CheckWatcherSharedState>>, | 41 | pub state: Arc<RwLock<CheckState>>, |
42 | cmd_send: Option<Sender<CheckCommand>>, | 42 | cmd_send: Option<Sender<CheckCommand>>, |
43 | handle: Option<JoinHandle<()>>, | 43 | handle: Option<JoinHandle<()>>, |
44 | } | 44 | } |
@@ -46,22 +46,21 @@ pub struct CheckWatcher { | |||
46 | impl CheckWatcher { | 46 | impl CheckWatcher { |
47 | pub fn new(options: &CheckOptions, workspace_root: PathBuf) -> CheckWatcher { | 47 | pub fn new(options: &CheckOptions, workspace_root: PathBuf) -> CheckWatcher { |
48 | let options = options.clone(); | 48 | let options = options.clone(); |
49 | let shared = Arc::new(RwLock::new(CheckWatcherSharedState::new())); | 49 | let state = Arc::new(RwLock::new(CheckState::new())); |
50 | 50 | ||
51 | let (task_send, task_recv) = unbounded::<CheckTask>(); | 51 | let (task_send, task_recv) = unbounded::<CheckTask>(); |
52 | let (cmd_send, cmd_recv) = unbounded::<CheckCommand>(); | 52 | let (cmd_send, cmd_recv) = unbounded::<CheckCommand>(); |
53 | let shared_ = shared.clone(); | ||
54 | let handle = std::thread::spawn(move || { | 53 | let handle = std::thread::spawn(move || { |
55 | let mut check = CheckWatcherState::new(options, workspace_root, shared_); | 54 | let mut check = CheckWatcherThread::new(options, workspace_root); |
56 | check.run(&task_send, &cmd_recv); | 55 | check.run(&task_send, &cmd_recv); |
57 | }); | 56 | }); |
58 | CheckWatcher { task_recv, cmd_send: Some(cmd_send), handle: Some(handle), shared } | 57 | CheckWatcher { task_recv, cmd_send: Some(cmd_send), handle: Some(handle), state } |
59 | } | 58 | } |
60 | 59 | ||
61 | /// Returns a CheckWatcher that doesn't actually do anything | 60 | /// Returns a CheckWatcher that doesn't actually do anything |
62 | pub fn dummy() -> CheckWatcher { | 61 | pub fn dummy() -> CheckWatcher { |
63 | let shared = Arc::new(RwLock::new(CheckWatcherSharedState::new())); | 62 | let state = Arc::new(RwLock::new(CheckState::new())); |
64 | CheckWatcher { task_recv: never(), cmd_send: None, handle: None, shared } | 63 | CheckWatcher { task_recv: never(), cmd_send: None, handle: None, state } |
65 | } | 64 | } |
66 | 65 | ||
67 | /// Schedule a re-start of the cargo check worker. | 66 | /// Schedule a re-start of the cargo check worker. |
@@ -89,14 +88,14 @@ impl std::ops::Drop for CheckWatcher { | |||
89 | } | 88 | } |
90 | 89 | ||
91 | #[derive(Debug)] | 90 | #[derive(Debug)] |
92 | pub struct CheckWatcherSharedState { | 91 | pub struct CheckState { |
93 | diagnostic_collection: HashMap<Url, Vec<Diagnostic>>, | 92 | diagnostic_collection: HashMap<Url, Vec<Diagnostic>>, |
94 | suggested_fix_collection: HashMap<Url, Vec<SuggestedFix>>, | 93 | suggested_fix_collection: HashMap<Url, Vec<SuggestedFix>>, |
95 | } | 94 | } |
96 | 95 | ||
97 | impl CheckWatcherSharedState { | 96 | impl CheckState { |
98 | fn new() -> CheckWatcherSharedState { | 97 | fn new() -> CheckState { |
99 | CheckWatcherSharedState { | 98 | CheckState { |
100 | diagnostic_collection: HashMap::new(), | 99 | diagnostic_collection: HashMap::new(), |
101 | suggested_fix_collection: HashMap::new(), | 100 | suggested_fix_collection: HashMap::new(), |
102 | } | 101 | } |
@@ -104,15 +103,11 @@ impl CheckWatcherSharedState { | |||
104 | 103 | ||
105 | /// Clear the cached diagnostics, and schedule updating diagnostics by the | 104 | /// Clear the cached diagnostics, and schedule updating diagnostics by the |
106 | /// server, to clear stale results. | 105 | /// server, to clear stale results. |
107 | pub fn clear(&mut self, task_send: &Sender<CheckTask>) { | 106 | pub fn clear(&mut self) -> Vec<Url> { |
108 | let cleared_files: Vec<Url> = self.diagnostic_collection.keys().cloned().collect(); | 107 | let cleared_files: Vec<Url> = self.diagnostic_collection.keys().cloned().collect(); |
109 | |||
110 | self.diagnostic_collection.clear(); | 108 | self.diagnostic_collection.clear(); |
111 | self.suggested_fix_collection.clear(); | 109 | self.suggested_fix_collection.clear(); |
112 | 110 | cleared_files | |
113 | for uri in cleared_files { | ||
114 | task_send.send(CheckTask::Update(uri.clone())).unwrap(); | ||
115 | } | ||
116 | } | 111 | } |
117 | 112 | ||
118 | pub fn diagnostics_for(&self, uri: &Url) -> Option<&[Diagnostic]> { | 113 | pub fn diagnostics_for(&self, uri: &Url) -> Option<&[Diagnostic]> { |
@@ -123,6 +118,13 @@ impl CheckWatcherSharedState { | |||
123 | self.suggested_fix_collection.get(uri).map(|d| d.as_slice()) | 118 | self.suggested_fix_collection.get(uri).map(|d| d.as_slice()) |
124 | } | 119 | } |
125 | 120 | ||
121 | pub fn add_diagnostic_with_fixes(&mut self, file_uri: Url, diagnostic: DiagnosticWithFixes) { | ||
122 | for fix in diagnostic.suggested_fixes { | ||
123 | self.add_suggested_fix_for_diagnostic(fix, &diagnostic.diagnostic); | ||
124 | } | ||
125 | self.add_diagnostic(file_uri, diagnostic.diagnostic); | ||
126 | } | ||
127 | |||
126 | fn add_diagnostic(&mut self, file_uri: Url, diagnostic: Diagnostic) { | 128 | fn add_diagnostic(&mut self, file_uri: Url, diagnostic: Diagnostic) { |
127 | let diagnostics = self.diagnostic_collection.entry(file_uri).or_default(); | 129 | let diagnostics = self.diagnostic_collection.entry(file_uri).or_default(); |
128 | 130 | ||
@@ -158,8 +160,11 @@ impl CheckWatcherSharedState { | |||
158 | 160 | ||
159 | #[derive(Debug)] | 161 | #[derive(Debug)] |
160 | pub enum CheckTask { | 162 | pub enum CheckTask { |
161 | /// Request a update of the given files diagnostics | 163 | /// Request a clearing of all cached diagnostics from the check watcher |
162 | Update(Url), | 164 | ClearDiagnostics, |
165 | |||
166 | /// Request adding a diagnostic with fixes included to a file | ||
167 | AddDiagnostic(Url, DiagnosticWithFixes), | ||
163 | 168 | ||
164 | /// Request check progress notification to client | 169 | /// Request check progress notification to client |
165 | Status(WorkDoneProgress), | 170 | Status(WorkDoneProgress), |
@@ -170,26 +175,20 @@ pub enum CheckCommand { | |||
170 | Update, | 175 | Update, |
171 | } | 176 | } |
172 | 177 | ||
173 | struct CheckWatcherState { | 178 | struct CheckWatcherThread { |
174 | options: CheckOptions, | 179 | options: CheckOptions, |
175 | workspace_root: PathBuf, | 180 | workspace_root: PathBuf, |
176 | watcher: WatchThread, | 181 | watcher: WatchThread, |
177 | last_update_req: Option<Instant>, | 182 | last_update_req: Option<Instant>, |
178 | shared: Arc<RwLock<CheckWatcherSharedState>>, | ||
179 | } | 183 | } |
180 | 184 | ||
181 | impl CheckWatcherState { | 185 | impl CheckWatcherThread { |
182 | fn new( | 186 | fn new(options: CheckOptions, workspace_root: PathBuf) -> CheckWatcherThread { |
183 | options: CheckOptions, | 187 | CheckWatcherThread { |
184 | workspace_root: PathBuf, | ||
185 | shared: Arc<RwLock<CheckWatcherSharedState>>, | ||
186 | ) -> CheckWatcherState { | ||
187 | CheckWatcherState { | ||
188 | options, | 188 | options, |
189 | workspace_root, | 189 | workspace_root, |
190 | watcher: WatchThread::dummy(), | 190 | watcher: WatchThread::dummy(), |
191 | last_update_req: None, | 191 | last_update_req: None, |
192 | shared, | ||
193 | } | 192 | } |
194 | } | 193 | } |
195 | 194 | ||
@@ -215,7 +214,7 @@ impl CheckWatcherState { | |||
215 | 214 | ||
216 | if self.should_recheck() { | 215 | if self.should_recheck() { |
217 | self.last_update_req.take(); | 216 | self.last_update_req.take(); |
218 | self.shared.write().clear(task_send); | 217 | task_send.send(CheckTask::ClearDiagnostics).unwrap(); |
219 | 218 | ||
220 | // By replacing the watcher, we drop the previous one which | 219 | // By replacing the watcher, we drop the previous one which |
221 | // causes it to shut down automatically. | 220 | // causes it to shut down automatically. |
@@ -240,7 +239,7 @@ impl CheckWatcherState { | |||
240 | } | 239 | } |
241 | } | 240 | } |
242 | 241 | ||
243 | fn handle_message(&mut self, msg: CheckEvent, task_send: &Sender<CheckTask>) { | 242 | fn handle_message(&self, msg: CheckEvent, task_send: &Sender<CheckTask>) { |
244 | match msg { | 243 | match msg { |
245 | CheckEvent::Begin => { | 244 | CheckEvent::Begin => { |
246 | task_send | 245 | task_send |
@@ -279,18 +278,9 @@ impl CheckWatcherState { | |||
279 | }; | 278 | }; |
280 | 279 | ||
281 | let MappedRustDiagnostic { location, diagnostic, suggested_fixes } = map_result; | 280 | let MappedRustDiagnostic { location, diagnostic, suggested_fixes } = map_result; |
282 | let file_uri = location.uri.clone(); | ||
283 | 281 | ||
284 | if !suggested_fixes.is_empty() { | 282 | let diagnostic = DiagnosticWithFixes { diagnostic, suggested_fixes }; |
285 | for suggested_fix in suggested_fixes { | 283 | task_send.send(CheckTask::AddDiagnostic(location.uri, diagnostic)).unwrap(); |
286 | self.shared | ||
287 | .write() | ||
288 | .add_suggested_fix_for_diagnostic(suggested_fix, &diagnostic); | ||
289 | } | ||
290 | } | ||
291 | self.shared.write().add_diagnostic(file_uri, diagnostic); | ||
292 | |||
293 | task_send.send(CheckTask::Update(location.uri)).unwrap(); | ||
294 | } | 284 | } |
295 | 285 | ||
296 | CheckEvent::Msg(Message::BuildScriptExecuted(_msg)) => {} | 286 | CheckEvent::Msg(Message::BuildScriptExecuted(_msg)) => {} |
@@ -299,6 +289,12 @@ impl CheckWatcherState { | |||
299 | } | 289 | } |
300 | } | 290 | } |
301 | 291 | ||
292 | #[derive(Debug)] | ||
293 | pub struct DiagnosticWithFixes { | ||
294 | diagnostic: Diagnostic, | ||
295 | suggested_fixes: Vec<SuggestedFix>, | ||
296 | } | ||
297 | |||
302 | /// WatchThread exists to wrap around the communication needed to be able to | 298 | /// WatchThread exists to wrap around the communication needed to be able to |
303 | /// run `cargo check` without blocking. Currently the Rust standard library | 299 | /// run `cargo check` without blocking. Currently the Rust standard library |
304 | /// doesn't provide a way to read sub-process output without blocking, so we | 300 | /// doesn't provide a way to read sub-process output without blocking, so we |
diff --git a/crates/ra_hir/src/code_model.rs b/crates/ra_hir/src/code_model.rs index a177cebca..500b34c17 100644 --- a/crates/ra_hir/src/code_model.rs +++ b/crates/ra_hir/src/code_model.rs | |||
@@ -7,7 +7,6 @@ use hir_def::{ | |||
7 | builtin_type::BuiltinType, | 7 | builtin_type::BuiltinType, |
8 | docs::Documentation, | 8 | docs::Documentation, |
9 | expr::{BindingAnnotation, Pat, PatId}, | 9 | expr::{BindingAnnotation, Pat, PatId}, |
10 | nameres::ModuleSource, | ||
11 | per_ns::PerNs, | 10 | per_ns::PerNs, |
12 | resolver::HasResolver, | 11 | resolver::HasResolver, |
13 | type_ref::{Mutability, TypeRef}, | 12 | type_ref::{Mutability, TypeRef}, |
@@ -21,8 +20,8 @@ use hir_expand::{ | |||
21 | MacroDefId, | 20 | MacroDefId, |
22 | }; | 21 | }; |
23 | use hir_ty::{ | 22 | use hir_ty::{ |
24 | autoderef, display::HirFormatter, expr::ExprValidator, ApplicationTy, Canonical, InEnvironment, | 23 | autoderef, display::HirFormatter, expr::ExprValidator, method_resolution, ApplicationTy, |
25 | TraitEnvironment, Ty, TyDefId, TypeCtor, TypeWalk, | 24 | Canonical, InEnvironment, TraitEnvironment, Ty, TyDefId, TypeCtor, TypeWalk, |
26 | }; | 25 | }; |
27 | use ra_db::{CrateId, Edition, FileId}; | 26 | use ra_db::{CrateId, Edition, FileId}; |
28 | use ra_prof::profile; | 27 | use ra_prof::profile; |
@@ -120,7 +119,8 @@ impl_froms!( | |||
120 | BuiltinType | 119 | BuiltinType |
121 | ); | 120 | ); |
122 | 121 | ||
123 | pub use hir_def::{attr::Attrs, visibility::Visibility}; | 122 | pub use hir_def::{attr::Attrs, visibility::Visibility, AssocItemId}; |
123 | use rustc_hash::FxHashSet; | ||
124 | 124 | ||
125 | impl Module { | 125 | impl Module { |
126 | pub(crate) fn new(krate: Crate, crate_module_id: LocalModuleId) -> Module { | 126 | pub(crate) fn new(krate: Crate, crate_module_id: LocalModuleId) -> Module { |
@@ -192,13 +192,14 @@ impl Module { | |||
192 | 192 | ||
193 | pub fn diagnostics(self, db: &impl HirDatabase, sink: &mut DiagnosticSink) { | 193 | pub fn diagnostics(self, db: &impl HirDatabase, sink: &mut DiagnosticSink) { |
194 | let _p = profile("Module::diagnostics"); | 194 | let _p = profile("Module::diagnostics"); |
195 | db.crate_def_map(self.id.krate).add_diagnostics(db, self.id.local_id, sink); | 195 | let crate_def_map = db.crate_def_map(self.id.krate); |
196 | crate_def_map.add_diagnostics(db, self.id.local_id, sink); | ||
196 | for decl in self.declarations(db) { | 197 | for decl in self.declarations(db) { |
197 | match decl { | 198 | match decl { |
198 | crate::ModuleDef::Function(f) => f.diagnostics(db, sink), | 199 | crate::ModuleDef::Function(f) => f.diagnostics(db, sink), |
199 | crate::ModuleDef::Module(m) => { | 200 | crate::ModuleDef::Module(m) => { |
200 | // Only add diagnostics from inline modules | 201 | // Only add diagnostics from inline modules |
201 | if let ModuleSource::Module(_) = m.definition_source(db).value { | 202 | if crate_def_map[m.id.local_id].origin.is_inline() { |
202 | m.diagnostics(db, sink) | 203 | m.diagnostics(db, sink) |
203 | } | 204 | } |
204 | } | 205 | } |
@@ -878,6 +879,28 @@ impl Type { | |||
878 | } | 879 | } |
879 | } | 880 | } |
880 | 881 | ||
882 | /// Checks that particular type `ty` implements `std::future::Future`. | ||
883 | /// This function is used in `.await` syntax completion. | ||
884 | pub fn impls_future(&self, db: &impl HirDatabase) -> bool { | ||
885 | let krate = self.krate; | ||
886 | |||
887 | let std_future_trait = | ||
888 | db.lang_item(krate, "future_trait".into()).and_then(|it| it.as_trait()); | ||
889 | let std_future_trait = match std_future_trait { | ||
890 | Some(it) => it, | ||
891 | None => return false, | ||
892 | }; | ||
893 | |||
894 | let canonical_ty = Canonical { value: self.ty.value.clone(), num_vars: 0 }; | ||
895 | method_resolution::implements_trait( | ||
896 | &canonical_ty, | ||
897 | db, | ||
898 | self.ty.environment.clone(), | ||
899 | krate, | ||
900 | std_future_trait, | ||
901 | ) | ||
902 | } | ||
903 | |||
881 | // FIXME: this method is broken, as it doesn't take closures into account. | 904 | // FIXME: this method is broken, as it doesn't take closures into account. |
882 | pub fn as_callable(&self) -> Option<CallableDef> { | 905 | pub fn as_callable(&self) -> Option<CallableDef> { |
883 | Some(self.ty.value.as_callable()?.0) | 906 | Some(self.ty.value.as_callable()?.0) |
@@ -986,6 +1009,65 @@ impl Type { | |||
986 | None | 1009 | None |
987 | } | 1010 | } |
988 | 1011 | ||
1012 | pub fn iterate_method_candidates<T>( | ||
1013 | &self, | ||
1014 | db: &impl HirDatabase, | ||
1015 | krate: Crate, | ||
1016 | traits_in_scope: &FxHashSet<TraitId>, | ||
1017 | name: Option<&Name>, | ||
1018 | mut callback: impl FnMut(&Ty, Function) -> Option<T>, | ||
1019 | ) -> Option<T> { | ||
1020 | // There should be no inference vars in types passed here | ||
1021 | // FIXME check that? | ||
1022 | // FIXME replace Unknown by bound vars here | ||
1023 | let canonical = Canonical { value: self.ty.value.clone(), num_vars: 0 }; | ||
1024 | |||
1025 | let env = self.ty.environment.clone(); | ||
1026 | let krate = krate.id; | ||
1027 | |||
1028 | method_resolution::iterate_method_candidates( | ||
1029 | &canonical, | ||
1030 | db, | ||
1031 | env, | ||
1032 | krate, | ||
1033 | traits_in_scope, | ||
1034 | name, | ||
1035 | method_resolution::LookupMode::MethodCall, | ||
1036 | |ty, it| match it { | ||
1037 | AssocItemId::FunctionId(f) => callback(ty, f.into()), | ||
1038 | _ => None, | ||
1039 | }, | ||
1040 | ) | ||
1041 | } | ||
1042 | |||
1043 | pub fn iterate_path_candidates<T>( | ||
1044 | &self, | ||
1045 | db: &impl HirDatabase, | ||
1046 | krate: Crate, | ||
1047 | traits_in_scope: &FxHashSet<TraitId>, | ||
1048 | name: Option<&Name>, | ||
1049 | mut callback: impl FnMut(&Ty, AssocItem) -> Option<T>, | ||
1050 | ) -> Option<T> { | ||
1051 | // There should be no inference vars in types passed here | ||
1052 | // FIXME check that? | ||
1053 | // FIXME replace Unknown by bound vars here | ||
1054 | let canonical = Canonical { value: self.ty.value.clone(), num_vars: 0 }; | ||
1055 | |||
1056 | let env = self.ty.environment.clone(); | ||
1057 | let krate = krate.id; | ||
1058 | |||
1059 | method_resolution::iterate_method_candidates( | ||
1060 | &canonical, | ||
1061 | db, | ||
1062 | env, | ||
1063 | krate, | ||
1064 | traits_in_scope, | ||
1065 | name, | ||
1066 | method_resolution::LookupMode::Path, | ||
1067 | |ty, it| callback(ty, it.into()), | ||
1068 | ) | ||
1069 | } | ||
1070 | |||
989 | pub fn as_adt(&self) -> Option<Adt> { | 1071 | pub fn as_adt(&self) -> Option<Adt> { |
990 | let (adt, _subst) = self.ty.value.as_adt()?; | 1072 | let (adt, _subst) = self.ty.value.as_adt()?; |
991 | Some(adt.into()) | 1073 | Some(adt.into()) |
diff --git a/crates/ra_hir/src/from_source.rs b/crates/ra_hir/src/from_source.rs deleted file mode 100644 index 6314be8d4..000000000 --- a/crates/ra_hir/src/from_source.rs +++ /dev/null | |||
@@ -1,265 +0,0 @@ | |||
1 | //! Finds a corresponding hir data structure for a syntax node in a specific | ||
2 | //! file. | ||
3 | |||
4 | use hir_def::{ | ||
5 | child_by_source::ChildBySource, dyn_map::DynMap, keys, keys::Key, nameres::ModuleSource, | ||
6 | ConstId, DefWithBodyId, EnumId, EnumVariantId, FunctionId, GenericDefId, ImplId, ModuleId, | ||
7 | StaticId, StructId, TraitId, TypeAliasId, UnionId, VariantId, | ||
8 | }; | ||
9 | use hir_expand::{name::AsName, AstId, MacroDefId, MacroDefKind}; | ||
10 | use ra_db::FileId; | ||
11 | use ra_prof::profile; | ||
12 | use ra_syntax::{ | ||
13 | ast::{self, AstNode, NameOwner}, | ||
14 | match_ast, SyntaxNode, | ||
15 | }; | ||
16 | |||
17 | use crate::{ | ||
18 | db::{DefDatabase, HirDatabase}, | ||
19 | Const, DefWithBody, Enum, EnumVariant, FieldSource, Function, ImplBlock, InFile, Local, | ||
20 | MacroDef, Module, Static, Struct, StructField, Trait, TypeAlias, TypeParam, Union, | ||
21 | }; | ||
22 | |||
23 | pub trait FromSource: Sized { | ||
24 | type Ast; | ||
25 | fn from_source(db: &impl DefDatabase, src: InFile<Self::Ast>) -> Option<Self>; | ||
26 | } | ||
27 | |||
28 | pub trait FromSourceByContainer: Sized { | ||
29 | type Ast: AstNode + 'static; | ||
30 | type Id: Copy + 'static; | ||
31 | const KEY: Key<Self::Ast, Self::Id>; | ||
32 | } | ||
33 | |||
34 | impl<T: FromSourceByContainer> FromSource for T | ||
35 | where | ||
36 | T: From<<T as FromSourceByContainer>::Id>, | ||
37 | { | ||
38 | type Ast = <T as FromSourceByContainer>::Ast; | ||
39 | fn from_source(db: &impl DefDatabase, src: InFile<Self::Ast>) -> Option<Self> { | ||
40 | analyze_container(db, src.as_ref().map(|it| it.syntax()))[T::KEY] | ||
41 | .get(&src) | ||
42 | .copied() | ||
43 | .map(Self::from) | ||
44 | } | ||
45 | } | ||
46 | |||
47 | macro_rules! from_source_by_container_impls { | ||
48 | ($(($hir:ident, $id:ident, $ast:path, $key:path)),* ,) => {$( | ||
49 | impl FromSourceByContainer for $hir { | ||
50 | type Ast = $ast; | ||
51 | type Id = $id; | ||
52 | const KEY: Key<Self::Ast, Self::Id> = $key; | ||
53 | } | ||
54 | )*} | ||
55 | } | ||
56 | |||
57 | from_source_by_container_impls![ | ||
58 | (Struct, StructId, ast::StructDef, keys::STRUCT), | ||
59 | (Union, UnionId, ast::UnionDef, keys::UNION), | ||
60 | (Enum, EnumId, ast::EnumDef, keys::ENUM), | ||
61 | (Trait, TraitId, ast::TraitDef, keys::TRAIT), | ||
62 | (Function, FunctionId, ast::FnDef, keys::FUNCTION), | ||
63 | (Static, StaticId, ast::StaticDef, keys::STATIC), | ||
64 | (Const, ConstId, ast::ConstDef, keys::CONST), | ||
65 | (TypeAlias, TypeAliasId, ast::TypeAliasDef, keys::TYPE_ALIAS), | ||
66 | (ImplBlock, ImplId, ast::ImplBlock, keys::IMPL), | ||
67 | ]; | ||
68 | |||
69 | impl FromSource for MacroDef { | ||
70 | type Ast = ast::MacroCall; | ||
71 | fn from_source(db: &impl DefDatabase, src: InFile<Self::Ast>) -> Option<Self> { | ||
72 | let kind = MacroDefKind::Declarative; | ||
73 | |||
74 | let module_src = ModuleSource::from_child_node(db, src.as_ref().map(|it| it.syntax())); | ||
75 | let module = Module::from_definition(db, InFile::new(src.file_id, module_src))?; | ||
76 | let krate = Some(module.krate().id); | ||
77 | |||
78 | let ast_id = Some(AstId::new(src.file_id, db.ast_id_map(src.file_id).ast_id(&src.value))); | ||
79 | |||
80 | let id: MacroDefId = MacroDefId { krate, ast_id, kind }; | ||
81 | Some(MacroDef { id }) | ||
82 | } | ||
83 | } | ||
84 | |||
85 | impl FromSource for EnumVariant { | ||
86 | type Ast = ast::EnumVariant; | ||
87 | fn from_source(db: &impl DefDatabase, src: InFile<Self::Ast>) -> Option<Self> { | ||
88 | let parent_enum = src.value.parent_enum(); | ||
89 | let src_enum = InFile { file_id: src.file_id, value: parent_enum }; | ||
90 | let parent_enum = Enum::from_source(db, src_enum)?; | ||
91 | parent_enum.id.child_by_source(db)[keys::ENUM_VARIANT] | ||
92 | .get(&src) | ||
93 | .copied() | ||
94 | .map(EnumVariant::from) | ||
95 | } | ||
96 | } | ||
97 | |||
98 | impl FromSource for StructField { | ||
99 | type Ast = FieldSource; | ||
100 | fn from_source(db: &impl DefDatabase, src: InFile<Self::Ast>) -> Option<Self> { | ||
101 | let src = src.as_ref(); | ||
102 | |||
103 | // FIXME this is buggy | ||
104 | let variant_id: VariantId = match src.value { | ||
105 | FieldSource::Named(field) => { | ||
106 | let value = field.syntax().ancestors().find_map(ast::StructDef::cast)?; | ||
107 | let src = InFile { file_id: src.file_id, value }; | ||
108 | let def = Struct::from_source(db, src)?; | ||
109 | def.id.into() | ||
110 | } | ||
111 | FieldSource::Pos(field) => { | ||
112 | let value = field.syntax().ancestors().find_map(ast::EnumVariant::cast)?; | ||
113 | let src = InFile { file_id: src.file_id, value }; | ||
114 | let def = EnumVariant::from_source(db, src)?; | ||
115 | EnumVariantId::from(def).into() | ||
116 | } | ||
117 | }; | ||
118 | |||
119 | let dyn_map = variant_id.child_by_source(db); | ||
120 | match src.value { | ||
121 | FieldSource::Pos(it) => dyn_map[keys::TUPLE_FIELD].get(&src.with_value(it.clone())), | ||
122 | FieldSource::Named(it) => dyn_map[keys::RECORD_FIELD].get(&src.with_value(it.clone())), | ||
123 | } | ||
124 | .copied() | ||
125 | .map(StructField::from) | ||
126 | } | ||
127 | } | ||
128 | |||
129 | impl Local { | ||
130 | pub fn from_source(db: &impl HirDatabase, src: InFile<ast::BindPat>) -> Option<Self> { | ||
131 | let file_id = src.file_id; | ||
132 | let parent: DefWithBody = src.value.syntax().ancestors().find_map(|it| { | ||
133 | let res = match_ast! { | ||
134 | match it { | ||
135 | ast::ConstDef(value) => { Const::from_source(db, InFile { value, file_id})?.into() }, | ||
136 | ast::StaticDef(value) => { Static::from_source(db, InFile { value, file_id})?.into() }, | ||
137 | ast::FnDef(value) => { Function::from_source(db, InFile { value, file_id})?.into() }, | ||
138 | _ => return None, | ||
139 | } | ||
140 | }; | ||
141 | Some(res) | ||
142 | })?; | ||
143 | let (_body, source_map) = db.body_with_source_map(parent.into()); | ||
144 | let src = src.map(ast::Pat::from); | ||
145 | let pat_id = source_map.node_pat(src.as_ref())?; | ||
146 | Some(Local { parent, pat_id }) | ||
147 | } | ||
148 | } | ||
149 | |||
150 | impl TypeParam { | ||
151 | pub fn from_source(db: &impl HirDatabase, src: InFile<ast::TypeParam>) -> Option<Self> { | ||
152 | let file_id = src.file_id; | ||
153 | let parent: GenericDefId = src.value.syntax().ancestors().find_map(|it| { | ||
154 | let res = match_ast! { | ||
155 | match it { | ||
156 | ast::FnDef(value) => { Function::from_source(db, InFile { value, file_id})?.id.into() }, | ||
157 | ast::StructDef(value) => { Struct::from_source(db, InFile { value, file_id})?.id.into() }, | ||
158 | ast::EnumDef(value) => { Enum::from_source(db, InFile { value, file_id})?.id.into() }, | ||
159 | ast::TraitDef(value) => { Trait::from_source(db, InFile { value, file_id})?.id.into() }, | ||
160 | ast::TypeAliasDef(value) => { TypeAlias::from_source(db, InFile { value, file_id})?.id.into() }, | ||
161 | ast::ImplBlock(value) => { ImplBlock::from_source(db, InFile { value, file_id})?.id.into() }, | ||
162 | _ => return None, | ||
163 | } | ||
164 | }; | ||
165 | Some(res) | ||
166 | })?; | ||
167 | let &id = parent.child_by_source(db)[keys::TYPE_PARAM].get(&src)?; | ||
168 | Some(TypeParam { id }) | ||
169 | } | ||
170 | } | ||
171 | |||
172 | impl Module { | ||
173 | pub fn from_declaration(db: &impl DefDatabase, src: InFile<ast::Module>) -> Option<Self> { | ||
174 | let _p = profile("Module::from_declaration"); | ||
175 | let parent_declaration = src.value.syntax().ancestors().skip(1).find_map(ast::Module::cast); | ||
176 | |||
177 | let parent_module = match parent_declaration { | ||
178 | Some(parent_declaration) => { | ||
179 | let src_parent = InFile { file_id: src.file_id, value: parent_declaration }; | ||
180 | Module::from_declaration(db, src_parent) | ||
181 | } | ||
182 | None => { | ||
183 | let source_file = db.parse(src.file_id.original_file(db)).tree(); | ||
184 | let src_parent = | ||
185 | InFile { file_id: src.file_id, value: ModuleSource::SourceFile(source_file) }; | ||
186 | Module::from_definition(db, src_parent) | ||
187 | } | ||
188 | }?; | ||
189 | |||
190 | let child_name = src.value.name()?.as_name(); | ||
191 | let def_map = db.crate_def_map(parent_module.id.krate); | ||
192 | let child_id = def_map[parent_module.id.local_id].children.get(&child_name)?; | ||
193 | Some(parent_module.with_module_id(*child_id)) | ||
194 | } | ||
195 | |||
196 | pub fn from_definition(db: &impl DefDatabase, src: InFile<ModuleSource>) -> Option<Self> { | ||
197 | let _p = profile("Module::from_definition"); | ||
198 | match src.value { | ||
199 | ModuleSource::Module(ref module) => { | ||
200 | assert!(!module.has_semi()); | ||
201 | return Module::from_declaration( | ||
202 | db, | ||
203 | InFile { file_id: src.file_id, value: module.clone() }, | ||
204 | ); | ||
205 | } | ||
206 | ModuleSource::SourceFile(_) => (), | ||
207 | }; | ||
208 | |||
209 | let original_file = src.file_id.original_file(db); | ||
210 | Module::from_file(db, original_file) | ||
211 | } | ||
212 | |||
213 | fn from_file(db: &impl DefDatabase, file: FileId) -> Option<Self> { | ||
214 | let _p = profile("Module::from_file"); | ||
215 | let (krate, local_id) = db.relevant_crates(file).iter().find_map(|&crate_id| { | ||
216 | let crate_def_map = db.crate_def_map(crate_id); | ||
217 | let local_id = crate_def_map.modules_for_file(file).next()?; | ||
218 | Some((crate_id, local_id)) | ||
219 | })?; | ||
220 | Some(Module { id: ModuleId { krate, local_id } }) | ||
221 | } | ||
222 | } | ||
223 | |||
224 | fn analyze_container(db: &impl DefDatabase, src: InFile<&SyntaxNode>) -> DynMap { | ||
225 | let _p = profile("analyze_container"); | ||
226 | return child_by_source(db, src).unwrap_or_default(); | ||
227 | |||
228 | fn child_by_source(db: &impl DefDatabase, src: InFile<&SyntaxNode>) -> Option<DynMap> { | ||
229 | for container in src.value.ancestors().skip(1) { | ||
230 | let res = match_ast! { | ||
231 | match container { | ||
232 | ast::TraitDef(it) => { | ||
233 | let def = Trait::from_source(db, src.with_value(it))?; | ||
234 | def.id.child_by_source(db) | ||
235 | }, | ||
236 | ast::ImplBlock(it) => { | ||
237 | let def = ImplBlock::from_source(db, src.with_value(it))?; | ||
238 | def.id.child_by_source(db) | ||
239 | }, | ||
240 | ast::FnDef(it) => { | ||
241 | let def = Function::from_source(db, src.with_value(it))?; | ||
242 | DefWithBodyId::from(def.id) | ||
243 | .child_by_source(db) | ||
244 | }, | ||
245 | ast::StaticDef(it) => { | ||
246 | let def = Static::from_source(db, src.with_value(it))?; | ||
247 | DefWithBodyId::from(def.id) | ||
248 | .child_by_source(db) | ||
249 | }, | ||
250 | ast::ConstDef(it) => { | ||
251 | let def = Const::from_source(db, src.with_value(it))?; | ||
252 | DefWithBodyId::from(def.id) | ||
253 | .child_by_source(db) | ||
254 | }, | ||
255 | _ => { continue }, | ||
256 | } | ||
257 | }; | ||
258 | return Some(res); | ||
259 | } | ||
260 | |||
261 | let module_source = ModuleSource::from_child_node(db, src); | ||
262 | let c = Module::from_definition(db, src.with_value(module_source))?; | ||
263 | Some(c.id.child_by_source(db)) | ||
264 | } | ||
265 | } | ||
diff --git a/crates/ra_hir/src/lib.rs b/crates/ra_hir/src/lib.rs index 3d13978d4..e1c7b7a20 100644 --- a/crates/ra_hir/src/lib.rs +++ b/crates/ra_hir/src/lib.rs | |||
@@ -8,7 +8,7 @@ | |||
8 | #![recursion_limit = "512"] | 8 | #![recursion_limit = "512"] |
9 | 9 | ||
10 | macro_rules! impl_froms { | 10 | macro_rules! impl_froms { |
11 | ($e:ident: $($v:ident $(($($sv:ident),*))?),*) => { | 11 | ($e:ident: $($v:ident $(($($sv:ident),*))?),*$(,)?) => { |
12 | $( | 12 | $( |
13 | impl From<$v> for $e { | 13 | impl From<$v> for $e { |
14 | fn from(it: $v) -> $e { | 14 | fn from(it: $v) -> $e { |
@@ -27,6 +27,7 @@ macro_rules! impl_froms { | |||
27 | } | 27 | } |
28 | 28 | ||
29 | pub mod db; | 29 | pub mod db; |
30 | pub mod source_analyzer; | ||
30 | pub mod source_binder; | 31 | pub mod source_binder; |
31 | 32 | ||
32 | pub mod diagnostics; | 33 | pub mod diagnostics; |
@@ -35,7 +36,6 @@ mod from_id; | |||
35 | mod code_model; | 36 | mod code_model; |
36 | 37 | ||
37 | mod has_source; | 38 | mod has_source; |
38 | mod from_source; | ||
39 | 39 | ||
40 | pub use crate::{ | 40 | pub use crate::{ |
41 | code_model::{ | 41 | code_model::{ |
@@ -44,9 +44,9 @@ pub use crate::{ | |||
44 | MacroDef, Module, ModuleDef, ScopeDef, Static, Struct, StructField, Trait, Type, TypeAlias, | 44 | MacroDef, Module, ModuleDef, ScopeDef, Static, Struct, StructField, Trait, Type, TypeAlias, |
45 | TypeParam, Union, VariantDef, | 45 | TypeParam, Union, VariantDef, |
46 | }, | 46 | }, |
47 | from_source::FromSource, | ||
48 | has_source::HasSource, | 47 | has_source::HasSource, |
49 | source_binder::{PathResolution, ScopeEntryWithSyntax, SourceAnalyzer}, | 48 | source_analyzer::{PathResolution, ScopeEntryWithSyntax, SourceAnalyzer}, |
49 | source_binder::SourceBinder, | ||
50 | }; | 50 | }; |
51 | 51 | ||
52 | pub use hir_def::{ | 52 | pub use hir_def::{ |
diff --git a/crates/ra_hir/src/source_analyzer.rs b/crates/ra_hir/src/source_analyzer.rs new file mode 100644 index 000000000..4f8fc9602 --- /dev/null +++ b/crates/ra_hir/src/source_analyzer.rs | |||
@@ -0,0 +1,450 @@ | |||
1 | //! Lookup hir elements using positions in the source code. This is a lossy | ||
2 | //! transformation: in general, a single source might correspond to several | ||
3 | //! modules, functions, etc, due to macros, cfgs and `#[path=]` attributes on | ||
4 | //! modules. | ||
5 | //! | ||
6 | //! So, this modules should not be used during hir construction, it exists | ||
7 | //! purely for "IDE needs". | ||
8 | use std::sync::Arc; | ||
9 | |||
10 | use either::Either; | ||
11 | use hir_def::{ | ||
12 | body::{ | ||
13 | scope::{ExprScopes, ScopeId}, | ||
14 | BodySourceMap, | ||
15 | }, | ||
16 | expr::{ExprId, PatId}, | ||
17 | resolver::{self, resolver_for_scope, Resolver, TypeNs, ValueNs}, | ||
18 | DefWithBodyId, TraitId, | ||
19 | }; | ||
20 | use hir_expand::{ | ||
21 | hygiene::Hygiene, name::AsName, AstId, HirFileId, InFile, MacroCallId, MacroCallKind, | ||
22 | }; | ||
23 | use hir_ty::{InEnvironment, InferenceResult, TraitEnvironment}; | ||
24 | use ra_syntax::{ | ||
25 | ast::{self, AstNode}, | ||
26 | AstPtr, SyntaxNode, SyntaxNodePtr, SyntaxToken, TextRange, TextUnit, | ||
27 | }; | ||
28 | use rustc_hash::FxHashSet; | ||
29 | |||
30 | use crate::{ | ||
31 | db::HirDatabase, Adt, Const, DefWithBody, EnumVariant, Function, Local, MacroDef, Name, Path, | ||
32 | ScopeDef, Static, Struct, Trait, Type, TypeAlias, TypeParam, | ||
33 | }; | ||
34 | |||
35 | /// `SourceAnalyzer` is a convenience wrapper which exposes HIR API in terms of | ||
36 | /// original source files. It should not be used inside the HIR itself. | ||
37 | #[derive(Debug)] | ||
38 | pub struct SourceAnalyzer { | ||
39 | file_id: HirFileId, | ||
40 | resolver: Resolver, | ||
41 | body_owner: Option<DefWithBody>, | ||
42 | body_source_map: Option<Arc<BodySourceMap>>, | ||
43 | infer: Option<Arc<InferenceResult>>, | ||
44 | scopes: Option<Arc<ExprScopes>>, | ||
45 | } | ||
46 | |||
47 | #[derive(Debug, Clone, PartialEq, Eq)] | ||
48 | pub enum PathResolution { | ||
49 | /// An item | ||
50 | Def(crate::ModuleDef), | ||
51 | /// A local binding (only value namespace) | ||
52 | Local(Local), | ||
53 | /// A generic parameter | ||
54 | TypeParam(TypeParam), | ||
55 | SelfType(crate::ImplBlock), | ||
56 | Macro(MacroDef), | ||
57 | AssocItem(crate::AssocItem), | ||
58 | } | ||
59 | |||
60 | #[derive(Debug, Clone, PartialEq, Eq)] | ||
61 | pub struct ScopeEntryWithSyntax { | ||
62 | pub(crate) name: Name, | ||
63 | pub(crate) ptr: Either<AstPtr<ast::Pat>, AstPtr<ast::SelfParam>>, | ||
64 | } | ||
65 | |||
66 | impl ScopeEntryWithSyntax { | ||
67 | pub fn name(&self) -> &Name { | ||
68 | &self.name | ||
69 | } | ||
70 | |||
71 | pub fn ptr(&self) -> Either<AstPtr<ast::Pat>, AstPtr<ast::SelfParam>> { | ||
72 | self.ptr | ||
73 | } | ||
74 | } | ||
75 | |||
76 | #[derive(Debug)] | ||
77 | pub struct ReferenceDescriptor { | ||
78 | pub range: TextRange, | ||
79 | pub name: String, | ||
80 | } | ||
81 | |||
82 | #[derive(Debug)] | ||
83 | pub struct Expansion { | ||
84 | macro_call_id: MacroCallId, | ||
85 | } | ||
86 | |||
87 | impl Expansion { | ||
88 | pub fn map_token_down( | ||
89 | &self, | ||
90 | db: &impl HirDatabase, | ||
91 | token: InFile<&SyntaxToken>, | ||
92 | ) -> Option<InFile<SyntaxToken>> { | ||
93 | let exp_info = self.file_id().expansion_info(db)?; | ||
94 | exp_info.map_token_down(token) | ||
95 | } | ||
96 | |||
97 | pub fn file_id(&self) -> HirFileId { | ||
98 | self.macro_call_id.as_file() | ||
99 | } | ||
100 | } | ||
101 | |||
102 | impl SourceAnalyzer { | ||
103 | pub fn new( | ||
104 | db: &impl HirDatabase, | ||
105 | node: InFile<&SyntaxNode>, | ||
106 | offset: Option<TextUnit>, | ||
107 | ) -> SourceAnalyzer { | ||
108 | crate::source_binder::SourceBinder::new(db).analyze(node, offset) | ||
109 | } | ||
110 | |||
111 | pub(crate) fn new_for_body( | ||
112 | db: &impl HirDatabase, | ||
113 | def: DefWithBodyId, | ||
114 | node: InFile<&SyntaxNode>, | ||
115 | offset: Option<TextUnit>, | ||
116 | ) -> SourceAnalyzer { | ||
117 | let (_body, source_map) = db.body_with_source_map(def); | ||
118 | let scopes = db.expr_scopes(def); | ||
119 | let scope = match offset { | ||
120 | None => scope_for(&scopes, &source_map, node), | ||
121 | Some(offset) => scope_for_offset(&scopes, &source_map, node.with_value(offset)), | ||
122 | }; | ||
123 | let resolver = resolver_for_scope(db, def, scope); | ||
124 | SourceAnalyzer { | ||
125 | resolver, | ||
126 | body_owner: Some(def.into()), | ||
127 | body_source_map: Some(source_map), | ||
128 | infer: Some(db.infer(def)), | ||
129 | scopes: Some(scopes), | ||
130 | file_id: node.file_id, | ||
131 | } | ||
132 | } | ||
133 | |||
134 | pub(crate) fn new_for_resolver( | ||
135 | resolver: Resolver, | ||
136 | node: InFile<&SyntaxNode>, | ||
137 | ) -> SourceAnalyzer { | ||
138 | SourceAnalyzer { | ||
139 | resolver, | ||
140 | body_owner: None, | ||
141 | body_source_map: None, | ||
142 | infer: None, | ||
143 | scopes: None, | ||
144 | file_id: node.file_id, | ||
145 | } | ||
146 | } | ||
147 | |||
148 | pub fn module(&self) -> Option<crate::code_model::Module> { | ||
149 | Some(crate::code_model::Module { id: self.resolver.module()? }) | ||
150 | } | ||
151 | |||
152 | fn expr_id(&self, expr: &ast::Expr) -> Option<ExprId> { | ||
153 | let src = InFile { file_id: self.file_id, value: expr }; | ||
154 | self.body_source_map.as_ref()?.node_expr(src) | ||
155 | } | ||
156 | |||
157 | fn pat_id(&self, pat: &ast::Pat) -> Option<PatId> { | ||
158 | let src = InFile { file_id: self.file_id, value: pat }; | ||
159 | self.body_source_map.as_ref()?.node_pat(src) | ||
160 | } | ||
161 | |||
162 | fn expand_expr( | ||
163 | &self, | ||
164 | db: &impl HirDatabase, | ||
165 | expr: InFile<&ast::Expr>, | ||
166 | ) -> Option<InFile<ast::Expr>> { | ||
167 | let macro_call = ast::MacroCall::cast(expr.value.syntax().clone())?; | ||
168 | let macro_file = | ||
169 | self.body_source_map.as_ref()?.node_macro_file(expr.with_value(¯o_call))?; | ||
170 | let expanded = db.parse_or_expand(macro_file)?; | ||
171 | let kind = expanded.kind(); | ||
172 | let expr = InFile::new(macro_file, ast::Expr::cast(expanded)?); | ||
173 | |||
174 | if ast::MacroCall::can_cast(kind) { | ||
175 | self.expand_expr(db, expr.as_ref()) | ||
176 | } else { | ||
177 | Some(expr) | ||
178 | } | ||
179 | } | ||
180 | |||
181 | pub fn type_of(&self, db: &impl HirDatabase, expr: &ast::Expr) -> Option<Type> { | ||
182 | let expr_id = if let Some(expr) = self.expand_expr(db, InFile::new(self.file_id, expr)) { | ||
183 | self.body_source_map.as_ref()?.node_expr(expr.as_ref())? | ||
184 | } else { | ||
185 | self.expr_id(expr)? | ||
186 | }; | ||
187 | |||
188 | let ty = self.infer.as_ref()?[expr_id].clone(); | ||
189 | let environment = TraitEnvironment::lower(db, &self.resolver); | ||
190 | Some(Type { krate: self.resolver.krate()?, ty: InEnvironment { value: ty, environment } }) | ||
191 | } | ||
192 | |||
193 | pub fn type_of_pat(&self, db: &impl HirDatabase, pat: &ast::Pat) -> Option<Type> { | ||
194 | let pat_id = self.pat_id(pat)?; | ||
195 | let ty = self.infer.as_ref()?[pat_id].clone(); | ||
196 | let environment = TraitEnvironment::lower(db, &self.resolver); | ||
197 | Some(Type { krate: self.resolver.krate()?, ty: InEnvironment { value: ty, environment } }) | ||
198 | } | ||
199 | |||
200 | pub fn resolve_method_call(&self, call: &ast::MethodCallExpr) -> Option<Function> { | ||
201 | let expr_id = self.expr_id(&call.clone().into())?; | ||
202 | self.infer.as_ref()?.method_resolution(expr_id).map(Function::from) | ||
203 | } | ||
204 | |||
205 | pub fn resolve_field(&self, field: &ast::FieldExpr) -> Option<crate::StructField> { | ||
206 | let expr_id = self.expr_id(&field.clone().into())?; | ||
207 | self.infer.as_ref()?.field_resolution(expr_id).map(|it| it.into()) | ||
208 | } | ||
209 | |||
210 | pub fn resolve_record_field(&self, field: &ast::RecordField) -> Option<crate::StructField> { | ||
211 | let expr_id = match field.expr() { | ||
212 | Some(it) => self.expr_id(&it)?, | ||
213 | None => { | ||
214 | let src = InFile { file_id: self.file_id, value: field }; | ||
215 | self.body_source_map.as_ref()?.field_init_shorthand_expr(src)? | ||
216 | } | ||
217 | }; | ||
218 | self.infer.as_ref()?.record_field_resolution(expr_id).map(|it| it.into()) | ||
219 | } | ||
220 | |||
221 | pub fn resolve_record_literal(&self, record_lit: &ast::RecordLit) -> Option<crate::VariantDef> { | ||
222 | let expr_id = self.expr_id(&record_lit.clone().into())?; | ||
223 | self.infer.as_ref()?.variant_resolution_for_expr(expr_id).map(|it| it.into()) | ||
224 | } | ||
225 | |||
226 | pub fn resolve_record_pattern(&self, record_pat: &ast::RecordPat) -> Option<crate::VariantDef> { | ||
227 | let pat_id = self.pat_id(&record_pat.clone().into())?; | ||
228 | self.infer.as_ref()?.variant_resolution_for_pat(pat_id).map(|it| it.into()) | ||
229 | } | ||
230 | |||
231 | pub fn resolve_macro_call( | ||
232 | &self, | ||
233 | db: &impl HirDatabase, | ||
234 | macro_call: InFile<&ast::MacroCall>, | ||
235 | ) -> Option<MacroDef> { | ||
236 | let hygiene = Hygiene::new(db, macro_call.file_id); | ||
237 | let path = macro_call.value.path().and_then(|ast| Path::from_src(ast, &hygiene))?; | ||
238 | self.resolver.resolve_path_as_macro(db, path.mod_path()).map(|it| it.into()) | ||
239 | } | ||
240 | |||
241 | pub fn resolve_hir_path( | ||
242 | &self, | ||
243 | db: &impl HirDatabase, | ||
244 | path: &crate::Path, | ||
245 | ) -> Option<PathResolution> { | ||
246 | let types = | ||
247 | self.resolver.resolve_path_in_type_ns_fully(db, path.mod_path()).map(|ty| match ty { | ||
248 | TypeNs::SelfType(it) => PathResolution::SelfType(it.into()), | ||
249 | TypeNs::GenericParam(id) => PathResolution::TypeParam(TypeParam { id }), | ||
250 | TypeNs::AdtSelfType(it) | TypeNs::AdtId(it) => { | ||
251 | PathResolution::Def(Adt::from(it).into()) | ||
252 | } | ||
253 | TypeNs::EnumVariantId(it) => PathResolution::Def(EnumVariant::from(it).into()), | ||
254 | TypeNs::TypeAliasId(it) => PathResolution::Def(TypeAlias::from(it).into()), | ||
255 | TypeNs::BuiltinType(it) => PathResolution::Def(it.into()), | ||
256 | TypeNs::TraitId(it) => PathResolution::Def(Trait::from(it).into()), | ||
257 | }); | ||
258 | let values = | ||
259 | self.resolver.resolve_path_in_value_ns_fully(db, path.mod_path()).and_then(|val| { | ||
260 | let res = match val { | ||
261 | ValueNs::LocalBinding(pat_id) => { | ||
262 | let var = Local { parent: self.body_owner?, pat_id }; | ||
263 | PathResolution::Local(var) | ||
264 | } | ||
265 | ValueNs::FunctionId(it) => PathResolution::Def(Function::from(it).into()), | ||
266 | ValueNs::ConstId(it) => PathResolution::Def(Const::from(it).into()), | ||
267 | ValueNs::StaticId(it) => PathResolution::Def(Static::from(it).into()), | ||
268 | ValueNs::StructId(it) => PathResolution::Def(Struct::from(it).into()), | ||
269 | ValueNs::EnumVariantId(it) => PathResolution::Def(EnumVariant::from(it).into()), | ||
270 | }; | ||
271 | Some(res) | ||
272 | }); | ||
273 | |||
274 | let items = self | ||
275 | .resolver | ||
276 | .resolve_module_path_in_items(db, path.mod_path()) | ||
277 | .take_types() | ||
278 | .map(|it| PathResolution::Def(it.into())); | ||
279 | types.or(values).or(items).or_else(|| { | ||
280 | self.resolver | ||
281 | .resolve_path_as_macro(db, path.mod_path()) | ||
282 | .map(|def| PathResolution::Macro(def.into())) | ||
283 | }) | ||
284 | } | ||
285 | |||
286 | pub fn resolve_path(&self, db: &impl HirDatabase, path: &ast::Path) -> Option<PathResolution> { | ||
287 | if let Some(path_expr) = path.syntax().parent().and_then(ast::PathExpr::cast) { | ||
288 | let expr_id = self.expr_id(&path_expr.into())?; | ||
289 | if let Some(assoc) = self.infer.as_ref()?.assoc_resolutions_for_expr(expr_id) { | ||
290 | return Some(PathResolution::AssocItem(assoc.into())); | ||
291 | } | ||
292 | } | ||
293 | if let Some(path_pat) = path.syntax().parent().and_then(ast::PathPat::cast) { | ||
294 | let pat_id = self.pat_id(&path_pat.into())?; | ||
295 | if let Some(assoc) = self.infer.as_ref()?.assoc_resolutions_for_pat(pat_id) { | ||
296 | return Some(PathResolution::AssocItem(assoc.into())); | ||
297 | } | ||
298 | } | ||
299 | // This must be a normal source file rather than macro file. | ||
300 | let hir_path = crate::Path::from_ast(path.clone())?; | ||
301 | self.resolve_hir_path(db, &hir_path) | ||
302 | } | ||
303 | |||
304 | fn resolve_local_name(&self, name_ref: &ast::NameRef) -> Option<ScopeEntryWithSyntax> { | ||
305 | let name = name_ref.as_name(); | ||
306 | let source_map = self.body_source_map.as_ref()?; | ||
307 | let scopes = self.scopes.as_ref()?; | ||
308 | let scope = scope_for(scopes, source_map, InFile::new(self.file_id, name_ref.syntax()))?; | ||
309 | let entry = scopes.resolve_name_in_scope(scope, &name)?; | ||
310 | Some(ScopeEntryWithSyntax { | ||
311 | name: entry.name().clone(), | ||
312 | ptr: source_map.pat_syntax(entry.pat())?.value, | ||
313 | }) | ||
314 | } | ||
315 | |||
316 | pub fn process_all_names(&self, db: &impl HirDatabase, f: &mut dyn FnMut(Name, ScopeDef)) { | ||
317 | self.resolver.process_all_names(db, &mut |name, def| { | ||
318 | let def = match def { | ||
319 | resolver::ScopeDef::PerNs(it) => it.into(), | ||
320 | resolver::ScopeDef::ImplSelfType(it) => ScopeDef::ImplSelfType(it.into()), | ||
321 | resolver::ScopeDef::AdtSelfType(it) => ScopeDef::AdtSelfType(it.into()), | ||
322 | resolver::ScopeDef::GenericParam(id) => ScopeDef::GenericParam(TypeParam { id }), | ||
323 | resolver::ScopeDef::Local(pat_id) => { | ||
324 | let parent = self.resolver.body_owner().unwrap().into(); | ||
325 | ScopeDef::Local(Local { parent, pat_id }) | ||
326 | } | ||
327 | }; | ||
328 | f(name, def) | ||
329 | }) | ||
330 | } | ||
331 | |||
332 | // FIXME: we only use this in `inline_local_variable` assist, ideally, we | ||
333 | // should switch to general reference search infra there. | ||
334 | pub fn find_all_refs(&self, pat: &ast::BindPat) -> Vec<ReferenceDescriptor> { | ||
335 | let fn_def = pat.syntax().ancestors().find_map(ast::FnDef::cast).unwrap(); | ||
336 | let ptr = Either::Left(AstPtr::new(&ast::Pat::from(pat.clone()))); | ||
337 | fn_def | ||
338 | .syntax() | ||
339 | .descendants() | ||
340 | .filter_map(ast::NameRef::cast) | ||
341 | .filter(|name_ref| match self.resolve_local_name(&name_ref) { | ||
342 | None => false, | ||
343 | Some(entry) => entry.ptr() == ptr, | ||
344 | }) | ||
345 | .map(|name_ref| ReferenceDescriptor { | ||
346 | name: name_ref.text().to_string(), | ||
347 | range: name_ref.syntax().text_range(), | ||
348 | }) | ||
349 | .collect() | ||
350 | } | ||
351 | |||
352 | /// Note: `FxHashSet<TraitId>` should be treated as an opaque type, passed into `Type | ||
353 | pub fn traits_in_scope(&self, db: &impl HirDatabase) -> FxHashSet<TraitId> { | ||
354 | self.resolver.traits_in_scope(db) | ||
355 | } | ||
356 | |||
357 | pub fn expand( | ||
358 | &self, | ||
359 | db: &impl HirDatabase, | ||
360 | macro_call: InFile<&ast::MacroCall>, | ||
361 | ) -> Option<Expansion> { | ||
362 | let def = self.resolve_macro_call(db, macro_call)?.id; | ||
363 | let ast_id = AstId::new( | ||
364 | macro_call.file_id, | ||
365 | db.ast_id_map(macro_call.file_id).ast_id(macro_call.value), | ||
366 | ); | ||
367 | Some(Expansion { macro_call_id: def.as_call_id(db, MacroCallKind::FnLike(ast_id)) }) | ||
368 | } | ||
369 | } | ||
370 | |||
371 | fn scope_for( | ||
372 | scopes: &ExprScopes, | ||
373 | source_map: &BodySourceMap, | ||
374 | node: InFile<&SyntaxNode>, | ||
375 | ) -> Option<ScopeId> { | ||
376 | node.value | ||
377 | .ancestors() | ||
378 | .filter_map(ast::Expr::cast) | ||
379 | .filter_map(|it| source_map.node_expr(InFile::new(node.file_id, &it))) | ||
380 | .find_map(|it| scopes.scope_for(it)) | ||
381 | } | ||
382 | |||
383 | fn scope_for_offset( | ||
384 | scopes: &ExprScopes, | ||
385 | source_map: &BodySourceMap, | ||
386 | offset: InFile<TextUnit>, | ||
387 | ) -> Option<ScopeId> { | ||
388 | scopes | ||
389 | .scope_by_expr() | ||
390 | .iter() | ||
391 | .filter_map(|(id, scope)| { | ||
392 | let source = source_map.expr_syntax(*id)?; | ||
393 | // FIXME: correctly handle macro expansion | ||
394 | if source.file_id != offset.file_id { | ||
395 | return None; | ||
396 | } | ||
397 | let syntax_node_ptr = | ||
398 | source.value.either(|it| it.syntax_node_ptr(), |it| it.syntax_node_ptr()); | ||
399 | Some((syntax_node_ptr, scope)) | ||
400 | }) | ||
401 | // find containing scope | ||
402 | .min_by_key(|(ptr, _scope)| { | ||
403 | ( | ||
404 | !(ptr.range().start() <= offset.value && offset.value <= ptr.range().end()), | ||
405 | ptr.range().len(), | ||
406 | ) | ||
407 | }) | ||
408 | .map(|(ptr, scope)| { | ||
409 | adjust(scopes, source_map, ptr, offset.file_id, offset.value).unwrap_or(*scope) | ||
410 | }) | ||
411 | } | ||
412 | |||
413 | // XXX: during completion, cursor might be outside of any particular | ||
414 | // expression. Try to figure out the correct scope... | ||
415 | fn adjust( | ||
416 | scopes: &ExprScopes, | ||
417 | source_map: &BodySourceMap, | ||
418 | ptr: SyntaxNodePtr, | ||
419 | file_id: HirFileId, | ||
420 | offset: TextUnit, | ||
421 | ) -> Option<ScopeId> { | ||
422 | let r = ptr.range(); | ||
423 | let child_scopes = scopes | ||
424 | .scope_by_expr() | ||
425 | .iter() | ||
426 | .filter_map(|(id, scope)| { | ||
427 | let source = source_map.expr_syntax(*id)?; | ||
428 | // FIXME: correctly handle macro expansion | ||
429 | if source.file_id != file_id { | ||
430 | return None; | ||
431 | } | ||
432 | let syntax_node_ptr = | ||
433 | source.value.either(|it| it.syntax_node_ptr(), |it| it.syntax_node_ptr()); | ||
434 | Some((syntax_node_ptr, scope)) | ||
435 | }) | ||
436 | .map(|(ptr, scope)| (ptr.range(), scope)) | ||
437 | .filter(|(range, _)| range.start() <= offset && range.is_subrange(&r) && *range != r); | ||
438 | |||
439 | child_scopes | ||
440 | .max_by(|(r1, _), (r2, _)| { | ||
441 | if r2.is_subrange(&r1) { | ||
442 | std::cmp::Ordering::Greater | ||
443 | } else if r1.is_subrange(&r2) { | ||
444 | std::cmp::Ordering::Less | ||
445 | } else { | ||
446 | r1.start().cmp(&r2.start()) | ||
447 | } | ||
448 | }) | ||
449 | .map(|(_ptr, scope)| *scope) | ||
450 | } | ||
diff --git a/crates/ra_hir/src/source_binder.rs b/crates/ra_hir/src/source_binder.rs index a2a9d968c..f3150f578 100644 --- a/crates/ra_hir/src/source_binder.rs +++ b/crates/ra_hir/src/source_binder.rs | |||
@@ -1,569 +1,357 @@ | |||
1 | //! Lookup hir elements using positions in the source code. This is a lossy | 1 | //! `SourceBinder` is the main entry point for getting info about source code. |
2 | //! transformation: in general, a single source might correspond to several | 2 | //! It's main task is to map source syntax trees to hir-level IDs. |
3 | //! modules, functions, etc, due to macros, cfgs and `#[path=]` attributes on | 3 | |
4 | //! modules. | ||
5 | //! | ||
6 | //! So, this modules should not be used during hir construction, it exists | ||
7 | //! purely for "IDE needs". | ||
8 | use std::sync::Arc; | ||
9 | |||
10 | use either::Either; | ||
11 | use hir_def::{ | 4 | use hir_def::{ |
12 | body::{ | 5 | child_by_source::ChildBySource, |
13 | scope::{ExprScopes, ScopeId}, | 6 | dyn_map::DynMap, |
14 | BodySourceMap, | 7 | keys::{self, Key}, |
15 | }, | 8 | resolver::{HasResolver, Resolver}, |
16 | expr::{ExprId, PatId}, | 9 | ConstId, DefWithBodyId, EnumId, EnumVariantId, FunctionId, GenericDefId, ImplId, ModuleId, |
17 | nameres::ModuleSource, | 10 | StaticId, StructFieldId, StructId, TraitId, TypeAliasId, UnionId, VariantId, |
18 | path::path, | ||
19 | resolver::{self, resolver_for_scope, HasResolver, Resolver, TypeNs, ValueNs}, | ||
20 | AssocItemId, DefWithBodyId, | ||
21 | }; | ||
22 | use hir_expand::{ | ||
23 | hygiene::Hygiene, name::AsName, AstId, HirFileId, InFile, MacroCallId, MacroCallKind, | ||
24 | }; | ||
25 | use hir_ty::{ | ||
26 | method_resolution::{self, implements_trait}, | ||
27 | Canonical, InEnvironment, InferenceResult, TraitEnvironment, Ty, | ||
28 | }; | 11 | }; |
12 | use hir_expand::{name::AsName, AstId, InFile, MacroDefId, MacroDefKind}; | ||
29 | use ra_prof::profile; | 13 | use ra_prof::profile; |
30 | use ra_syntax::{ | 14 | use ra_syntax::{ |
31 | ast::{self, AstNode}, | 15 | ast::{self, NameOwner}, |
32 | match_ast, AstPtr, | 16 | match_ast, AstNode, SyntaxNode, TextUnit, |
33 | SyntaxKind::*, | ||
34 | SyntaxNode, SyntaxNodePtr, SyntaxToken, TextRange, TextUnit, | ||
35 | }; | 17 | }; |
18 | use rustc_hash::FxHashMap; | ||
36 | 19 | ||
37 | use crate::{ | 20 | use crate::{db::HirDatabase, Local, Module, SourceAnalyzer, TypeParam}; |
38 | db::HirDatabase, Adt, AssocItem, Const, DefWithBody, Enum, EnumVariant, FromSource, Function, | 21 | use ra_db::FileId; |
39 | ImplBlock, Local, MacroDef, Name, Path, ScopeDef, Static, Struct, Trait, Type, TypeAlias, | ||
40 | TypeParam, | ||
41 | }; | ||
42 | |||
43 | fn try_get_resolver_for_node(db: &impl HirDatabase, node: InFile<&SyntaxNode>) -> Option<Resolver> { | ||
44 | match_ast! { | ||
45 | match (node.value) { | ||
46 | ast::Module(it) => { | ||
47 | let src = node.with_value(it); | ||
48 | Some(crate::Module::from_declaration(db, src)?.id.resolver(db)) | ||
49 | }, | ||
50 | ast::SourceFile(it) => { | ||
51 | let src = node.with_value(ModuleSource::SourceFile(it)); | ||
52 | Some(crate::Module::from_definition(db, src)?.id.resolver(db)) | ||
53 | }, | ||
54 | ast::StructDef(it) => { | ||
55 | let src = node.with_value(it); | ||
56 | Some(Struct::from_source(db, src)?.id.resolver(db)) | ||
57 | }, | ||
58 | ast::EnumDef(it) => { | ||
59 | let src = node.with_value(it); | ||
60 | Some(Enum::from_source(db, src)?.id.resolver(db)) | ||
61 | }, | ||
62 | ast::ImplBlock(it) => { | ||
63 | let src = node.with_value(it); | ||
64 | Some(ImplBlock::from_source(db, src)?.id.resolver(db)) | ||
65 | }, | ||
66 | ast::TraitDef(it) => { | ||
67 | let src = node.with_value(it); | ||
68 | Some(Trait::from_source(db, src)?.id.resolver(db)) | ||
69 | }, | ||
70 | _ => match node.value.kind() { | ||
71 | FN_DEF | CONST_DEF | STATIC_DEF => { | ||
72 | let def = def_with_body_from_child_node(db, node)?; | ||
73 | let def = DefWithBodyId::from(def); | ||
74 | Some(def.resolver(db)) | ||
75 | } | ||
76 | // FIXME add missing cases | ||
77 | _ => None | ||
78 | } | ||
79 | } | ||
80 | } | ||
81 | } | ||
82 | |||
83 | fn def_with_body_from_child_node( | ||
84 | db: &impl HirDatabase, | ||
85 | child: InFile<&SyntaxNode>, | ||
86 | ) -> Option<DefWithBody> { | ||
87 | let _p = profile("def_with_body_from_child_node"); | ||
88 | child.cloned().ancestors_with_macros(db).find_map(|node| { | ||
89 | let n = &node.value; | ||
90 | match_ast! { | ||
91 | match n { | ||
92 | ast::FnDef(def) => { return Function::from_source(db, node.with_value(def)).map(DefWithBody::from); }, | ||
93 | ast::ConstDef(def) => { return Const::from_source(db, node.with_value(def)).map(DefWithBody::from); }, | ||
94 | ast::StaticDef(def) => { return Static::from_source(db, node.with_value(def)).map(DefWithBody::from); }, | ||
95 | _ => { None }, | ||
96 | } | ||
97 | } | ||
98 | }) | ||
99 | } | ||
100 | |||
101 | /// `SourceAnalyzer` is a convenience wrapper which exposes HIR API in terms of | ||
102 | /// original source files. It should not be used inside the HIR itself. | ||
103 | #[derive(Debug)] | ||
104 | pub struct SourceAnalyzer { | ||
105 | file_id: HirFileId, | ||
106 | resolver: Resolver, | ||
107 | body_owner: Option<DefWithBody>, | ||
108 | body_source_map: Option<Arc<BodySourceMap>>, | ||
109 | infer: Option<Arc<InferenceResult>>, | ||
110 | scopes: Option<Arc<ExprScopes>>, | ||
111 | } | ||
112 | |||
113 | #[derive(Debug, Clone, PartialEq, Eq)] | ||
114 | pub enum PathResolution { | ||
115 | /// An item | ||
116 | Def(crate::ModuleDef), | ||
117 | /// A local binding (only value namespace) | ||
118 | Local(Local), | ||
119 | /// A generic parameter | ||
120 | TypeParam(TypeParam), | ||
121 | SelfType(crate::ImplBlock), | ||
122 | Macro(MacroDef), | ||
123 | AssocItem(crate::AssocItem), | ||
124 | } | ||
125 | |||
126 | #[derive(Debug, Clone, PartialEq, Eq)] | ||
127 | pub struct ScopeEntryWithSyntax { | ||
128 | pub(crate) name: Name, | ||
129 | pub(crate) ptr: Either<AstPtr<ast::Pat>, AstPtr<ast::SelfParam>>, | ||
130 | } | ||
131 | |||
132 | impl ScopeEntryWithSyntax { | ||
133 | pub fn name(&self) -> &Name { | ||
134 | &self.name | ||
135 | } | ||
136 | |||
137 | pub fn ptr(&self) -> Either<AstPtr<ast::Pat>, AstPtr<ast::SelfParam>> { | ||
138 | self.ptr | ||
139 | } | ||
140 | } | ||
141 | |||
142 | #[derive(Debug)] | ||
143 | pub struct ReferenceDescriptor { | ||
144 | pub range: TextRange, | ||
145 | pub name: String, | ||
146 | } | ||
147 | 22 | ||
148 | #[derive(Debug)] | 23 | pub struct SourceBinder<'a, DB> { |
149 | pub struct Expansion { | 24 | pub db: &'a DB, |
150 | macro_call_id: MacroCallId, | 25 | child_by_source_cache: FxHashMap<ChildContainer, DynMap>, |
151 | } | 26 | } |
152 | 27 | ||
153 | impl Expansion { | 28 | impl<DB: HirDatabase> SourceBinder<'_, DB> { |
154 | pub fn map_token_down( | 29 | pub fn new(db: &DB) -> SourceBinder<DB> { |
155 | &self, | 30 | SourceBinder { db, child_by_source_cache: FxHashMap::default() } |
156 | db: &impl HirDatabase, | ||
157 | token: InFile<&SyntaxToken>, | ||
158 | ) -> Option<InFile<SyntaxToken>> { | ||
159 | let exp_info = self.file_id().expansion_info(db)?; | ||
160 | exp_info.map_token_down(token) | ||
161 | } | ||
162 | |||
163 | pub fn file_id(&self) -> HirFileId { | ||
164 | self.macro_call_id.as_file() | ||
165 | } | 31 | } |
166 | } | ||
167 | 32 | ||
168 | impl SourceAnalyzer { | 33 | pub fn analyze( |
169 | pub fn new( | 34 | &mut self, |
170 | db: &impl HirDatabase, | 35 | src: InFile<&SyntaxNode>, |
171 | node: InFile<&SyntaxNode>, | ||
172 | offset: Option<TextUnit>, | 36 | offset: Option<TextUnit>, |
173 | ) -> SourceAnalyzer { | 37 | ) -> SourceAnalyzer { |
174 | let _p = profile("SourceAnalyzer::new"); | 38 | let _p = profile("SourceBinder::analyzer"); |
175 | let def_with_body = def_with_body_from_child_node(db, node); | 39 | let container = match self.find_container(src) { |
176 | if let Some(def) = def_with_body { | 40 | Some(it) => it, |
177 | let (_body, source_map) = db.body_with_source_map(def.into()); | 41 | None => return SourceAnalyzer::new_for_resolver(Resolver::default(), src), |
178 | let scopes = db.expr_scopes(def.into()); | 42 | }; |
179 | let scope = match offset { | 43 | |
180 | None => scope_for(&scopes, &source_map, node), | 44 | let resolver = match container { |
181 | Some(offset) => scope_for_offset(&scopes, &source_map, node.with_value(offset)), | 45 | ChildContainer::DefWithBodyId(def) => { |
182 | }; | 46 | return SourceAnalyzer::new_for_body(self.db, def, src, offset) |
183 | let resolver = resolver_for_scope(db, def.into(), scope); | ||
184 | SourceAnalyzer { | ||
185 | resolver, | ||
186 | body_owner: Some(def), | ||
187 | body_source_map: Some(source_map), | ||
188 | infer: Some(db.infer(def.into())), | ||
189 | scopes: Some(scopes), | ||
190 | file_id: node.file_id, | ||
191 | } | ||
192 | } else { | ||
193 | SourceAnalyzer { | ||
194 | resolver: node | ||
195 | .value | ||
196 | .ancestors() | ||
197 | .find_map(|it| try_get_resolver_for_node(db, node.with_value(&it))) | ||
198 | .unwrap_or_default(), | ||
199 | body_owner: None, | ||
200 | body_source_map: None, | ||
201 | infer: None, | ||
202 | scopes: None, | ||
203 | file_id: node.file_id, | ||
204 | } | 47 | } |
205 | } | 48 | ChildContainer::TraitId(it) => it.resolver(self.db), |
49 | ChildContainer::ImplId(it) => it.resolver(self.db), | ||
50 | ChildContainer::ModuleId(it) => it.resolver(self.db), | ||
51 | ChildContainer::EnumId(it) => it.resolver(self.db), | ||
52 | ChildContainer::VariantId(it) => it.resolver(self.db), | ||
53 | ChildContainer::GenericDefId(it) => it.resolver(self.db), | ||
54 | }; | ||
55 | SourceAnalyzer::new_for_resolver(resolver, src) | ||
206 | } | 56 | } |
207 | 57 | ||
208 | pub fn module(&self) -> Option<crate::code_model::Module> { | 58 | pub fn to_def<T: ToDef>(&mut self, src: InFile<T>) -> Option<T::Def> { |
209 | Some(crate::code_model::Module { id: self.resolver.module()? }) | 59 | T::to_def(self, src) |
210 | } | 60 | } |
211 | 61 | ||
212 | fn expr_id(&self, expr: &ast::Expr) -> Option<ExprId> { | 62 | pub fn to_module_def(&mut self, file: FileId) -> Option<Module> { |
213 | let src = InFile { file_id: self.file_id, value: expr }; | 63 | let _p = profile("SourceBinder::to_module_def"); |
214 | self.body_source_map.as_ref()?.node_expr(src) | 64 | let (krate, local_id) = self.db.relevant_crates(file).iter().find_map(|&crate_id| { |
65 | let crate_def_map = self.db.crate_def_map(crate_id); | ||
66 | let local_id = crate_def_map.modules_for_file(file).next()?; | ||
67 | Some((crate_id, local_id)) | ||
68 | })?; | ||
69 | Some(Module { id: ModuleId { krate, local_id } }) | ||
215 | } | 70 | } |
216 | 71 | ||
217 | fn pat_id(&self, pat: &ast::Pat) -> Option<PatId> { | 72 | fn to_id<T: ToId>(&mut self, src: InFile<T>) -> Option<T::ID> { |
218 | let src = InFile { file_id: self.file_id, value: pat }; | 73 | T::to_id(self, src) |
219 | self.body_source_map.as_ref()?.node_pat(src) | ||
220 | } | 74 | } |
221 | 75 | ||
222 | fn expand_expr( | 76 | fn find_container(&mut self, src: InFile<&SyntaxNode>) -> Option<ChildContainer> { |
223 | &self, | 77 | for container in src.cloned().ancestors_with_macros(self.db).skip(1) { |
224 | db: &impl HirDatabase, | 78 | let res: ChildContainer = match_ast! { |
225 | expr: InFile<&ast::Expr>, | 79 | match (container.value) { |
226 | ) -> Option<InFile<ast::Expr>> { | 80 | ast::TraitDef(it) => { |
227 | let macro_call = ast::MacroCall::cast(expr.value.syntax().clone())?; | 81 | let def: TraitId = self.to_id(container.with_value(it))?; |
228 | let macro_file = | 82 | def.into() |
229 | self.body_source_map.as_ref()?.node_macro_file(expr.with_value(¯o_call))?; | 83 | }, |
230 | let expanded = db.parse_or_expand(macro_file)?; | 84 | ast::ImplBlock(it) => { |
231 | let kind = expanded.kind(); | 85 | let def: ImplId = self.to_id(container.with_value(it))?; |
232 | let expr = InFile::new(macro_file, ast::Expr::cast(expanded)?); | 86 | def.into() |
233 | 87 | }, | |
234 | if ast::MacroCall::can_cast(kind) { | 88 | ast::FnDef(it) => { |
235 | self.expand_expr(db, expr.as_ref()) | 89 | let def: FunctionId = self.to_id(container.with_value(it))?; |
236 | } else { | 90 | DefWithBodyId::from(def).into() |
237 | Some(expr) | 91 | }, |
92 | ast::StaticDef(it) => { | ||
93 | let def: StaticId = self.to_id(container.with_value(it))?; | ||
94 | DefWithBodyId::from(def).into() | ||
95 | }, | ||
96 | ast::ConstDef(it) => { | ||
97 | let def: ConstId = self.to_id(container.with_value(it))?; | ||
98 | DefWithBodyId::from(def).into() | ||
99 | }, | ||
100 | ast::EnumDef(it) => { | ||
101 | let def: EnumId = self.to_id(container.with_value(it))?; | ||
102 | def.into() | ||
103 | }, | ||
104 | ast::StructDef(it) => { | ||
105 | let def: StructId = self.to_id(container.with_value(it))?; | ||
106 | VariantId::from(def).into() | ||
107 | }, | ||
108 | ast::UnionDef(it) => { | ||
109 | let def: UnionId = self.to_id(container.with_value(it))?; | ||
110 | VariantId::from(def).into() | ||
111 | }, | ||
112 | ast::Module(it) => { | ||
113 | let def: ModuleId = self.to_id(container.with_value(it))?; | ||
114 | def.into() | ||
115 | }, | ||
116 | _ => { continue }, | ||
117 | } | ||
118 | }; | ||
119 | return Some(res); | ||
238 | } | 120 | } |
239 | } | ||
240 | 121 | ||
241 | pub fn type_of(&self, db: &impl HirDatabase, expr: &ast::Expr) -> Option<Type> { | 122 | let c = self.to_module_def(src.file_id.original_file(self.db))?; |
242 | let expr_id = if let Some(expr) = self.expand_expr(db, InFile::new(self.file_id, expr)) { | 123 | Some(c.id.into()) |
243 | self.body_source_map.as_ref()?.node_expr(expr.as_ref())? | ||
244 | } else { | ||
245 | self.expr_id(expr)? | ||
246 | }; | ||
247 | |||
248 | let ty = self.infer.as_ref()?[expr_id].clone(); | ||
249 | let environment = TraitEnvironment::lower(db, &self.resolver); | ||
250 | Some(Type { krate: self.resolver.krate()?, ty: InEnvironment { value: ty, environment } }) | ||
251 | } | ||
252 | |||
253 | pub fn type_of_pat(&self, db: &impl HirDatabase, pat: &ast::Pat) -> Option<Type> { | ||
254 | let pat_id = self.pat_id(pat)?; | ||
255 | let ty = self.infer.as_ref()?[pat_id].clone(); | ||
256 | let environment = TraitEnvironment::lower(db, &self.resolver); | ||
257 | Some(Type { krate: self.resolver.krate()?, ty: InEnvironment { value: ty, environment } }) | ||
258 | } | 124 | } |
259 | 125 | ||
260 | pub fn resolve_method_call(&self, call: &ast::MethodCallExpr) -> Option<Function> { | 126 | fn child_by_source(&mut self, container: ChildContainer) -> &DynMap { |
261 | let expr_id = self.expr_id(&call.clone().into())?; | 127 | let db = self.db; |
262 | self.infer.as_ref()?.method_resolution(expr_id).map(Function::from) | 128 | self.child_by_source_cache.entry(container).or_insert_with(|| match container { |
129 | ChildContainer::DefWithBodyId(it) => it.child_by_source(db), | ||
130 | ChildContainer::ModuleId(it) => it.child_by_source(db), | ||
131 | ChildContainer::TraitId(it) => it.child_by_source(db), | ||
132 | ChildContainer::ImplId(it) => it.child_by_source(db), | ||
133 | ChildContainer::EnumId(it) => it.child_by_source(db), | ||
134 | ChildContainer::VariantId(it) => it.child_by_source(db), | ||
135 | ChildContainer::GenericDefId(it) => it.child_by_source(db), | ||
136 | }) | ||
263 | } | 137 | } |
138 | } | ||
264 | 139 | ||
265 | pub fn resolve_field(&self, field: &ast::FieldExpr) -> Option<crate::StructField> { | 140 | pub trait ToId: Sized { |
266 | let expr_id = self.expr_id(&field.clone().into())?; | 141 | type ID: Sized + Copy + 'static; |
267 | self.infer.as_ref()?.field_resolution(expr_id).map(|it| it.into()) | 142 | fn to_id<DB: HirDatabase>(sb: &mut SourceBinder<'_, DB>, src: InFile<Self>) |
268 | } | 143 | -> Option<Self::ID>; |
144 | } | ||
269 | 145 | ||
270 | pub fn resolve_record_field(&self, field: &ast::RecordField) -> Option<crate::StructField> { | 146 | pub trait ToDef: Sized + AstNode + 'static { |
271 | let expr_id = match field.expr() { | 147 | type Def; |
272 | Some(it) => self.expr_id(&it)?, | 148 | fn to_def<DB: HirDatabase>( |
273 | None => { | 149 | sb: &mut SourceBinder<'_, DB>, |
274 | let src = InFile { file_id: self.file_id, value: field }; | 150 | src: InFile<Self>, |
275 | self.body_source_map.as_ref()?.field_init_shorthand_expr(src)? | 151 | ) -> Option<Self::Def>; |
276 | } | 152 | } |
277 | }; | ||
278 | self.infer.as_ref()?.record_field_resolution(expr_id).map(|it| it.into()) | ||
279 | } | ||
280 | 153 | ||
281 | pub fn resolve_record_literal(&self, record_lit: &ast::RecordLit) -> Option<crate::VariantDef> { | 154 | macro_rules! to_def_impls { |
282 | let expr_id = self.expr_id(&record_lit.clone().into())?; | 155 | ($(($def:path, $ast:path)),* ,) => {$( |
283 | self.infer.as_ref()?.variant_resolution_for_expr(expr_id).map(|it| it.into()) | 156 | impl ToDef for $ast { |
284 | } | 157 | type Def = $def; |
158 | fn to_def<DB: HirDatabase>(sb: &mut SourceBinder<'_, DB>, src: InFile<Self>) | ||
159 | -> Option<Self::Def> | ||
160 | { sb.to_id(src).map(Into::into) } | ||
161 | } | ||
162 | )*} | ||
163 | } | ||
285 | 164 | ||
286 | pub fn resolve_record_pattern(&self, record_pat: &ast::RecordPat) -> Option<crate::VariantDef> { | 165 | to_def_impls![ |
287 | let pat_id = self.pat_id(&record_pat.clone().into())?; | 166 | (crate::Module, ast::Module), |
288 | self.infer.as_ref()?.variant_resolution_for_pat(pat_id).map(|it| it.into()) | 167 | (crate::Struct, ast::StructDef), |
289 | } | 168 | (crate::Enum, ast::EnumDef), |
169 | (crate::Union, ast::UnionDef), | ||
170 | (crate::Trait, ast::TraitDef), | ||
171 | (crate::ImplBlock, ast::ImplBlock), | ||
172 | (crate::TypeAlias, ast::TypeAliasDef), | ||
173 | (crate::Const, ast::ConstDef), | ||
174 | (crate::Static, ast::StaticDef), | ||
175 | (crate::Function, ast::FnDef), | ||
176 | (crate::StructField, ast::RecordFieldDef), | ||
177 | (crate::EnumVariant, ast::EnumVariant), | ||
178 | (crate::MacroDef, ast::MacroCall), // this one is dubious, not all calls are macros | ||
179 | ]; | ||
180 | |||
181 | #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)] | ||
182 | enum ChildContainer { | ||
183 | DefWithBodyId(DefWithBodyId), | ||
184 | ModuleId(ModuleId), | ||
185 | TraitId(TraitId), | ||
186 | ImplId(ImplId), | ||
187 | EnumId(EnumId), | ||
188 | VariantId(VariantId), | ||
189 | /// XXX: this might be the same def as, for example an `EnumId`. However, | ||
190 | /// here the children generic parameters, and not, eg enum variants. | ||
191 | GenericDefId(GenericDefId), | ||
192 | } | ||
193 | impl_froms! { | ||
194 | ChildContainer: | ||
195 | DefWithBodyId, | ||
196 | ModuleId, | ||
197 | TraitId, | ||
198 | ImplId, | ||
199 | EnumId, | ||
200 | VariantId, | ||
201 | GenericDefId | ||
202 | } | ||
290 | 203 | ||
291 | pub fn resolve_macro_call( | 204 | pub trait ToIdByKey: Sized + AstNode + 'static { |
292 | &self, | 205 | type ID: Sized + Copy + 'static; |
293 | db: &impl HirDatabase, | 206 | const KEY: Key<Self, Self::ID>; |
294 | macro_call: InFile<&ast::MacroCall>, | 207 | } |
295 | ) -> Option<MacroDef> { | ||
296 | let hygiene = Hygiene::new(db, macro_call.file_id); | ||
297 | let path = macro_call.value.path().and_then(|ast| Path::from_src(ast, &hygiene))?; | ||
298 | self.resolver.resolve_path_as_macro(db, path.mod_path()).map(|it| it.into()) | ||
299 | } | ||
300 | 208 | ||
301 | pub fn resolve_hir_path( | 209 | impl<T: ToIdByKey> ToId for T { |
302 | &self, | 210 | type ID = <T as ToIdByKey>::ID; |
303 | db: &impl HirDatabase, | 211 | fn to_id<DB: HirDatabase>( |
304 | path: &crate::Path, | 212 | sb: &mut SourceBinder<'_, DB>, |
305 | ) -> Option<PathResolution> { | 213 | src: InFile<Self>, |
306 | let types = | 214 | ) -> Option<Self::ID> { |
307 | self.resolver.resolve_path_in_type_ns_fully(db, path.mod_path()).map(|ty| match ty { | 215 | let container = sb.find_container(src.as_ref().map(|it| it.syntax()))?; |
308 | TypeNs::SelfType(it) => PathResolution::SelfType(it.into()), | 216 | let db = sb.db; |
309 | TypeNs::GenericParam(id) => PathResolution::TypeParam(TypeParam { id }), | 217 | let dyn_map = |
310 | TypeNs::AdtSelfType(it) | TypeNs::AdtId(it) => { | 218 | &*sb.child_by_source_cache.entry(container).or_insert_with(|| match container { |
311 | PathResolution::Def(Adt::from(it).into()) | 219 | ChildContainer::DefWithBodyId(it) => it.child_by_source(db), |
312 | } | 220 | ChildContainer::ModuleId(it) => it.child_by_source(db), |
313 | TypeNs::EnumVariantId(it) => PathResolution::Def(EnumVariant::from(it).into()), | 221 | ChildContainer::TraitId(it) => it.child_by_source(db), |
314 | TypeNs::TypeAliasId(it) => PathResolution::Def(TypeAlias::from(it).into()), | 222 | ChildContainer::ImplId(it) => it.child_by_source(db), |
315 | TypeNs::BuiltinType(it) => PathResolution::Def(it.into()), | 223 | ChildContainer::EnumId(it) => it.child_by_source(db), |
316 | TypeNs::TraitId(it) => PathResolution::Def(Trait::from(it).into()), | 224 | ChildContainer::VariantId(it) => it.child_by_source(db), |
317 | }); | 225 | ChildContainer::GenericDefId(it) => it.child_by_source(db), |
318 | let values = | ||
319 | self.resolver.resolve_path_in_value_ns_fully(db, path.mod_path()).and_then(|val| { | ||
320 | let res = match val { | ||
321 | ValueNs::LocalBinding(pat_id) => { | ||
322 | let var = Local { parent: self.body_owner?, pat_id }; | ||
323 | PathResolution::Local(var) | ||
324 | } | ||
325 | ValueNs::FunctionId(it) => PathResolution::Def(Function::from(it).into()), | ||
326 | ValueNs::ConstId(it) => PathResolution::Def(Const::from(it).into()), | ||
327 | ValueNs::StaticId(it) => PathResolution::Def(Static::from(it).into()), | ||
328 | ValueNs::StructId(it) => PathResolution::Def(Struct::from(it).into()), | ||
329 | ValueNs::EnumVariantId(it) => PathResolution::Def(EnumVariant::from(it).into()), | ||
330 | }; | ||
331 | Some(res) | ||
332 | }); | 226 | }); |
333 | 227 | dyn_map[T::KEY].get(&src).copied() | |
334 | let items = self | ||
335 | .resolver | ||
336 | .resolve_module_path_in_items(db, path.mod_path()) | ||
337 | .take_types() | ||
338 | .map(|it| PathResolution::Def(it.into())); | ||
339 | types.or(values).or(items).or_else(|| { | ||
340 | self.resolver | ||
341 | .resolve_path_as_macro(db, path.mod_path()) | ||
342 | .map(|def| PathResolution::Macro(def.into())) | ||
343 | }) | ||
344 | } | 228 | } |
229 | } | ||
345 | 230 | ||
346 | pub fn resolve_path(&self, db: &impl HirDatabase, path: &ast::Path) -> Option<PathResolution> { | 231 | macro_rules! to_id_key_impls { |
347 | if let Some(path_expr) = path.syntax().parent().and_then(ast::PathExpr::cast) { | 232 | ($(($id:ident, $ast:path, $key:path)),* ,) => {$( |
348 | let expr_id = self.expr_id(&path_expr.into())?; | 233 | impl ToIdByKey for $ast { |
349 | if let Some(assoc) = self.infer.as_ref()?.assoc_resolutions_for_expr(expr_id) { | 234 | type ID = $id; |
350 | return Some(PathResolution::AssocItem(assoc.into())); | 235 | const KEY: Key<Self, Self::ID> = $key; |
351 | } | ||
352 | } | 236 | } |
353 | if let Some(path_pat) = path.syntax().parent().and_then(ast::PathPat::cast) { | 237 | )*} |
354 | let pat_id = self.pat_id(&path_pat.into())?; | 238 | } |
355 | if let Some(assoc) = self.infer.as_ref()?.assoc_resolutions_for_pat(pat_id) { | ||
356 | return Some(PathResolution::AssocItem(assoc.into())); | ||
357 | } | ||
358 | } | ||
359 | // This must be a normal source file rather than macro file. | ||
360 | let hir_path = crate::Path::from_ast(path.clone())?; | ||
361 | self.resolve_hir_path(db, &hir_path) | ||
362 | } | ||
363 | 239 | ||
364 | fn resolve_local_name(&self, name_ref: &ast::NameRef) -> Option<ScopeEntryWithSyntax> { | 240 | to_id_key_impls![ |
365 | let name = name_ref.as_name(); | 241 | (StructId, ast::StructDef, keys::STRUCT), |
366 | let source_map = self.body_source_map.as_ref()?; | 242 | (UnionId, ast::UnionDef, keys::UNION), |
367 | let scopes = self.scopes.as_ref()?; | 243 | (EnumId, ast::EnumDef, keys::ENUM), |
368 | let scope = scope_for(scopes, source_map, InFile::new(self.file_id, name_ref.syntax()))?; | 244 | (TraitId, ast::TraitDef, keys::TRAIT), |
369 | let entry = scopes.resolve_name_in_scope(scope, &name)?; | 245 | (FunctionId, ast::FnDef, keys::FUNCTION), |
370 | Some(ScopeEntryWithSyntax { | 246 | (StaticId, ast::StaticDef, keys::STATIC), |
371 | name: entry.name().clone(), | 247 | (ConstId, ast::ConstDef, keys::CONST), |
372 | ptr: source_map.pat_syntax(entry.pat())?.value, | 248 | (TypeAliasId, ast::TypeAliasDef, keys::TYPE_ALIAS), |
373 | }) | 249 | (ImplId, ast::ImplBlock, keys::IMPL), |
250 | (StructFieldId, ast::RecordFieldDef, keys::RECORD_FIELD), | ||
251 | (EnumVariantId, ast::EnumVariant, keys::ENUM_VARIANT), | ||
252 | ]; | ||
253 | |||
254 | // FIXME: use DynMap as well? | ||
255 | impl ToId for ast::MacroCall { | ||
256 | type ID = MacroDefId; | ||
257 | fn to_id<DB: HirDatabase>( | ||
258 | sb: &mut SourceBinder<'_, DB>, | ||
259 | src: InFile<Self>, | ||
260 | ) -> Option<Self::ID> { | ||
261 | let kind = MacroDefKind::Declarative; | ||
262 | |||
263 | let krate = sb.to_module_def(src.file_id.original_file(sb.db))?.id.krate; | ||
264 | |||
265 | let ast_id = | ||
266 | Some(AstId::new(src.file_id, sb.db.ast_id_map(src.file_id).ast_id(&src.value))); | ||
267 | |||
268 | Some(MacroDefId { krate: Some(krate), ast_id, kind }) | ||
374 | } | 269 | } |
270 | } | ||
375 | 271 | ||
376 | pub fn process_all_names(&self, db: &impl HirDatabase, f: &mut dyn FnMut(Name, ScopeDef)) { | 272 | impl ToDef for ast::BindPat { |
377 | self.resolver.process_all_names(db, &mut |name, def| { | 273 | type Def = Local; |
378 | let def = match def { | 274 | |
379 | resolver::ScopeDef::PerNs(it) => it.into(), | 275 | fn to_def<DB: HirDatabase>(sb: &mut SourceBinder<'_, DB>, src: InFile<Self>) -> Option<Local> { |
380 | resolver::ScopeDef::ImplSelfType(it) => ScopeDef::ImplSelfType(it.into()), | 276 | let file_id = src.file_id; |
381 | resolver::ScopeDef::AdtSelfType(it) => ScopeDef::AdtSelfType(it.into()), | 277 | let parent: DefWithBodyId = src.value.syntax().ancestors().find_map(|it| { |
382 | resolver::ScopeDef::GenericParam(id) => ScopeDef::GenericParam(TypeParam { id }), | 278 | let res = match_ast! { |
383 | resolver::ScopeDef::Local(pat_id) => { | 279 | match it { |
384 | let parent = self.resolver.body_owner().unwrap().into(); | 280 | ast::ConstDef(value) => { sb.to_id(InFile { value, file_id})?.into() }, |
385 | ScopeDef::Local(Local { parent, pat_id }) | 281 | ast::StaticDef(value) => { sb.to_id(InFile { value, file_id})?.into() }, |
282 | ast::FnDef(value) => { sb.to_id(InFile { value, file_id})?.into() }, | ||
283 | _ => return None, | ||
386 | } | 284 | } |
387 | }; | 285 | }; |
388 | f(name, def) | 286 | Some(res) |
389 | }) | 287 | })?; |
390 | } | 288 | let (_body, source_map) = sb.db.body_with_source_map(parent); |
391 | 289 | let src = src.map(ast::Pat::from); | |
392 | // FIXME: we only use this in `inline_local_variable` assist, ideally, we | 290 | let pat_id = source_map.node_pat(src.as_ref())?; |
393 | // should switch to general reference search infra there. | 291 | Some(Local { parent: parent.into(), pat_id }) |
394 | pub fn find_all_refs(&self, pat: &ast::BindPat) -> Vec<ReferenceDescriptor> { | ||
395 | let fn_def = pat.syntax().ancestors().find_map(ast::FnDef::cast).unwrap(); | ||
396 | let ptr = Either::Left(AstPtr::new(&ast::Pat::from(pat.clone()))); | ||
397 | fn_def | ||
398 | .syntax() | ||
399 | .descendants() | ||
400 | .filter_map(ast::NameRef::cast) | ||
401 | .filter(|name_ref| match self.resolve_local_name(&name_ref) { | ||
402 | None => false, | ||
403 | Some(entry) => entry.ptr() == ptr, | ||
404 | }) | ||
405 | .map(|name_ref| ReferenceDescriptor { | ||
406 | name: name_ref.text().to_string(), | ||
407 | range: name_ref.syntax().text_range(), | ||
408 | }) | ||
409 | .collect() | ||
410 | } | ||
411 | |||
412 | pub fn iterate_method_candidates<T>( | ||
413 | &self, | ||
414 | db: &impl HirDatabase, | ||
415 | ty: &Type, | ||
416 | name: Option<&Name>, | ||
417 | mut callback: impl FnMut(&Ty, Function) -> Option<T>, | ||
418 | ) -> Option<T> { | ||
419 | // There should be no inference vars in types passed here | ||
420 | // FIXME check that? | ||
421 | // FIXME replace Unknown by bound vars here | ||
422 | let canonical = Canonical { value: ty.ty.value.clone(), num_vars: 0 }; | ||
423 | method_resolution::iterate_method_candidates( | ||
424 | &canonical, | ||
425 | db, | ||
426 | &self.resolver, | ||
427 | name, | ||
428 | method_resolution::LookupMode::MethodCall, | ||
429 | |ty, it| match it { | ||
430 | AssocItemId::FunctionId(f) => callback(ty, f.into()), | ||
431 | _ => None, | ||
432 | }, | ||
433 | ) | ||
434 | } | ||
435 | |||
436 | pub fn iterate_path_candidates<T>( | ||
437 | &self, | ||
438 | db: &impl HirDatabase, | ||
439 | ty: &Type, | ||
440 | name: Option<&Name>, | ||
441 | mut callback: impl FnMut(&Ty, AssocItem) -> Option<T>, | ||
442 | ) -> Option<T> { | ||
443 | // There should be no inference vars in types passed here | ||
444 | // FIXME check that? | ||
445 | // FIXME replace Unknown by bound vars here | ||
446 | let canonical = Canonical { value: ty.ty.value.clone(), num_vars: 0 }; | ||
447 | method_resolution::iterate_method_candidates( | ||
448 | &canonical, | ||
449 | db, | ||
450 | &self.resolver, | ||
451 | name, | ||
452 | method_resolution::LookupMode::Path, | ||
453 | |ty, it| callback(ty, it.into()), | ||
454 | ) | ||
455 | } | ||
456 | |||
457 | /// Checks that particular type `ty` implements `std::future::Future`. | ||
458 | /// This function is used in `.await` syntax completion. | ||
459 | pub fn impls_future(&self, db: &impl HirDatabase, ty: Type) -> bool { | ||
460 | let std_future_path = path![std::future::Future]; | ||
461 | |||
462 | let std_future_trait = match self.resolver.resolve_known_trait(db, &std_future_path) { | ||
463 | Some(it) => it.into(), | ||
464 | _ => return false, | ||
465 | }; | ||
466 | |||
467 | let krate = match self.resolver.krate() { | ||
468 | Some(krate) => krate, | ||
469 | _ => return false, | ||
470 | }; | ||
471 | |||
472 | let canonical_ty = Canonical { value: ty.ty.value, num_vars: 0 }; | ||
473 | implements_trait(&canonical_ty, db, &self.resolver, krate.into(), std_future_trait) | ||
474 | } | ||
475 | |||
476 | pub fn expand( | ||
477 | &self, | ||
478 | db: &impl HirDatabase, | ||
479 | macro_call: InFile<&ast::MacroCall>, | ||
480 | ) -> Option<Expansion> { | ||
481 | let def = self.resolve_macro_call(db, macro_call)?.id; | ||
482 | let ast_id = AstId::new( | ||
483 | macro_call.file_id, | ||
484 | db.ast_id_map(macro_call.file_id).ast_id(macro_call.value), | ||
485 | ); | ||
486 | Some(Expansion { macro_call_id: def.as_call_id(db, MacroCallKind::FnLike(ast_id)) }) | ||
487 | } | 292 | } |
488 | } | 293 | } |
489 | 294 | ||
490 | fn scope_for( | 295 | impl ToDef for ast::TypeParam { |
491 | scopes: &ExprScopes, | 296 | type Def = TypeParam; |
492 | source_map: &BodySourceMap, | 297 | |
493 | node: InFile<&SyntaxNode>, | 298 | fn to_def<DB: HirDatabase>( |
494 | ) -> Option<ScopeId> { | 299 | sb: &mut SourceBinder<'_, DB>, |
495 | node.value | 300 | src: InFile<ast::TypeParam>, |
496 | .ancestors() | 301 | ) -> Option<TypeParam> { |
497 | .filter_map(ast::Expr::cast) | 302 | let mut sb = SourceBinder::new(sb.db); |
498 | .filter_map(|it| source_map.node_expr(InFile::new(node.file_id, &it))) | 303 | let file_id = src.file_id; |
499 | .find_map(|it| scopes.scope_for(it)) | 304 | let parent: GenericDefId = src.value.syntax().ancestors().find_map(|it| { |
305 | let res = match_ast! { | ||
306 | match it { | ||
307 | ast::FnDef(value) => { sb.to_id(InFile { value, file_id})?.into() }, | ||
308 | ast::StructDef(value) => { sb.to_id(InFile { value, file_id})?.into() }, | ||
309 | ast::EnumDef(value) => { sb.to_id(InFile { value, file_id})?.into() }, | ||
310 | ast::TraitDef(value) => { sb.to_id(InFile { value, file_id})?.into() }, | ||
311 | ast::TypeAliasDef(value) => { sb.to_id(InFile { value, file_id})?.into() }, | ||
312 | ast::ImplBlock(value) => { sb.to_id(InFile { value, file_id})?.into() }, | ||
313 | _ => return None, | ||
314 | } | ||
315 | }; | ||
316 | Some(res) | ||
317 | })?; | ||
318 | let &id = sb.child_by_source(parent.into())[keys::TYPE_PARAM].get(&src)?; | ||
319 | Some(TypeParam { id }) | ||
320 | } | ||
500 | } | 321 | } |
501 | 322 | ||
502 | fn scope_for_offset( | 323 | impl ToId for ast::Module { |
503 | scopes: &ExprScopes, | 324 | type ID = ModuleId; |
504 | source_map: &BodySourceMap, | 325 | |
505 | offset: InFile<TextUnit>, | 326 | fn to_id<DB: HirDatabase>( |
506 | ) -> Option<ScopeId> { | 327 | sb: &mut SourceBinder<'_, DB>, |
507 | scopes | 328 | src: InFile<ast::Module>, |
508 | .scope_by_expr() | 329 | ) -> Option<ModuleId> { |
509 | .iter() | 330 | { |
510 | .filter_map(|(id, scope)| { | 331 | let _p = profile("ast::Module::to_def"); |
511 | let source = source_map.expr_syntax(*id)?; | 332 | let parent_declaration = src |
512 | // FIXME: correctly handle macro expansion | 333 | .as_ref() |
513 | if source.file_id != offset.file_id { | 334 | .map(|it| it.syntax()) |
514 | return None; | 335 | .cloned() |
515 | } | 336 | .ancestors_with_macros(sb.db) |
516 | let syntax_node_ptr = | 337 | .skip(1) |
517 | source.value.either(|it| it.syntax_node_ptr(), |it| it.syntax_node_ptr()); | 338 | .find_map(|it| { |
518 | Some((syntax_node_ptr, scope)) | 339 | let m = ast::Module::cast(it.value.clone())?; |
519 | }) | 340 | Some(it.with_value(m)) |
520 | // find containing scope | 341 | }); |
521 | .min_by_key(|(ptr, _scope)| { | 342 | |
522 | ( | 343 | let parent_module = match parent_declaration { |
523 | !(ptr.range().start() <= offset.value && offset.value <= ptr.range().end()), | 344 | Some(parent_declaration) => sb.to_id(parent_declaration)?, |
524 | ptr.range().len(), | 345 | None => { |
525 | ) | 346 | let file_id = src.file_id.original_file(sb.db); |
526 | }) | 347 | sb.to_module_def(file_id)?.id |
527 | .map(|(ptr, scope)| { | 348 | } |
528 | adjust(scopes, source_map, ptr, offset.file_id, offset.value).unwrap_or(*scope) | 349 | }; |
529 | }) | ||
530 | } | ||
531 | 350 | ||
532 | // XXX: during completion, cursor might be outside of any particular | 351 | let child_name = src.value.name()?.as_name(); |
533 | // expression. Try to figure out the correct scope... | 352 | let def_map = sb.db.crate_def_map(parent_module.krate); |
534 | fn adjust( | 353 | let child_id = *def_map[parent_module.local_id].children.get(&child_name)?; |
535 | scopes: &ExprScopes, | 354 | Some(ModuleId { krate: parent_module.krate, local_id: child_id }) |
536 | source_map: &BodySourceMap, | 355 | } |
537 | ptr: SyntaxNodePtr, | 356 | } |
538 | file_id: HirFileId, | ||
539 | offset: TextUnit, | ||
540 | ) -> Option<ScopeId> { | ||
541 | let r = ptr.range(); | ||
542 | let child_scopes = scopes | ||
543 | .scope_by_expr() | ||
544 | .iter() | ||
545 | .filter_map(|(id, scope)| { | ||
546 | let source = source_map.expr_syntax(*id)?; | ||
547 | // FIXME: correctly handle macro expansion | ||
548 | if source.file_id != file_id { | ||
549 | return None; | ||
550 | } | ||
551 | let syntax_node_ptr = | ||
552 | source.value.either(|it| it.syntax_node_ptr(), |it| it.syntax_node_ptr()); | ||
553 | Some((syntax_node_ptr, scope)) | ||
554 | }) | ||
555 | .map(|(ptr, scope)| (ptr.range(), scope)) | ||
556 | .filter(|(range, _)| range.start() <= offset && range.is_subrange(&r) && *range != r); | ||
557 | |||
558 | child_scopes | ||
559 | .max_by(|(r1, _), (r2, _)| { | ||
560 | if r2.is_subrange(&r1) { | ||
561 | std::cmp::Ordering::Greater | ||
562 | } else if r1.is_subrange(&r2) { | ||
563 | std::cmp::Ordering::Less | ||
564 | } else { | ||
565 | r1.start().cmp(&r2.start()) | ||
566 | } | ||
567 | }) | ||
568 | .map(|(_ptr, scope)| *scope) | ||
569 | } | 357 | } |
diff --git a/crates/ra_hir_def/src/find_path.rs b/crates/ra_hir_def/src/find_path.rs index f7dc8acb7..8cc2fb160 100644 --- a/crates/ra_hir_def/src/find_path.rs +++ b/crates/ra_hir_def/src/find_path.rs | |||
@@ -35,7 +35,7 @@ fn find_path_inner( | |||
35 | let def_map = db.crate_def_map(from.krate); | 35 | let def_map = db.crate_def_map(from.krate); |
36 | let from_scope: &crate::item_scope::ItemScope = &def_map.modules[from.local_id].scope; | 36 | let from_scope: &crate::item_scope::ItemScope = &def_map.modules[from.local_id].scope; |
37 | if let Some((name, _)) = from_scope.name_of(item) { | 37 | if let Some((name, _)) = from_scope.name_of(item) { |
38 | return Some(ModPath::from_simple_segments(PathKind::Plain, vec![name.clone()])); | 38 | return Some(ModPath::from_segments(PathKind::Plain, vec![name.clone()])); |
39 | } | 39 | } |
40 | 40 | ||
41 | // - if the item is the crate root, return `crate` | 41 | // - if the item is the crate root, return `crate` |
@@ -45,12 +45,12 @@ fn find_path_inner( | |||
45 | local_id: def_map.root, | 45 | local_id: def_map.root, |
46 | })) | 46 | })) |
47 | { | 47 | { |
48 | return Some(ModPath::from_simple_segments(PathKind::Crate, Vec::new())); | 48 | return Some(ModPath::from_segments(PathKind::Crate, Vec::new())); |
49 | } | 49 | } |
50 | 50 | ||
51 | // - if the item is the module we're in, use `self` | 51 | // - if the item is the module we're in, use `self` |
52 | if item == ItemInNs::Types(from.into()) { | 52 | if item == ItemInNs::Types(from.into()) { |
53 | return Some(ModPath::from_simple_segments(PathKind::Super(0), Vec::new())); | 53 | return Some(ModPath::from_segments(PathKind::Super(0), Vec::new())); |
54 | } | 54 | } |
55 | 55 | ||
56 | // - if the item is the parent module, use `super` (this is not used recursively, since `super::super` is ugly) | 56 | // - if the item is the parent module, use `super` (this is not used recursively, since `super::super` is ugly) |
@@ -61,14 +61,14 @@ fn find_path_inner( | |||
61 | local_id: parent_id, | 61 | local_id: parent_id, |
62 | })) | 62 | })) |
63 | { | 63 | { |
64 | return Some(ModPath::from_simple_segments(PathKind::Super(1), Vec::new())); | 64 | return Some(ModPath::from_segments(PathKind::Super(1), Vec::new())); |
65 | } | 65 | } |
66 | } | 66 | } |
67 | 67 | ||
68 | // - if the item is the crate root of a dependency crate, return the name from the extern prelude | 68 | // - if the item is the crate root of a dependency crate, return the name from the extern prelude |
69 | for (name, def_id) in &def_map.extern_prelude { | 69 | for (name, def_id) in &def_map.extern_prelude { |
70 | if item == ItemInNs::Types(*def_id) { | 70 | if item == ItemInNs::Types(*def_id) { |
71 | return Some(ModPath::from_simple_segments(PathKind::Plain, vec![name.clone()])); | 71 | return Some(ModPath::from_segments(PathKind::Plain, vec![name.clone()])); |
72 | } | 72 | } |
73 | } | 73 | } |
74 | 74 | ||
@@ -79,7 +79,7 @@ fn find_path_inner( | |||
79 | &prelude_def_map.modules[prelude_module.local_id].scope; | 79 | &prelude_def_map.modules[prelude_module.local_id].scope; |
80 | if let Some((name, vis)) = prelude_scope.name_of(item) { | 80 | if let Some((name, vis)) = prelude_scope.name_of(item) { |
81 | if vis.is_visible_from(db, from) { | 81 | if vis.is_visible_from(db, from) { |
82 | return Some(ModPath::from_simple_segments(PathKind::Plain, vec![name.clone()])); | 82 | return Some(ModPath::from_segments(PathKind::Plain, vec![name.clone()])); |
83 | } | 83 | } |
84 | } | 84 | } |
85 | } | 85 | } |
diff --git a/crates/ra_hir_def/src/keys.rs b/crates/ra_hir_def/src/keys.rs index d844f7a62..5913f12b1 100644 --- a/crates/ra_hir_def/src/keys.rs +++ b/crates/ra_hir_def/src/keys.rs | |||
@@ -2,7 +2,7 @@ | |||
2 | 2 | ||
3 | use std::marker::PhantomData; | 3 | use std::marker::PhantomData; |
4 | 4 | ||
5 | use hir_expand::InFile; | 5 | use hir_expand::{InFile, MacroDefId}; |
6 | use ra_syntax::{ast, AstNode, AstPtr}; | 6 | use ra_syntax::{ast, AstNode, AstPtr}; |
7 | use rustc_hash::FxHashMap; | 7 | use rustc_hash::FxHashMap; |
8 | 8 | ||
@@ -29,6 +29,8 @@ pub const TUPLE_FIELD: Key<ast::TupleFieldDef, StructFieldId> = Key::new(); | |||
29 | pub const RECORD_FIELD: Key<ast::RecordFieldDef, StructFieldId> = Key::new(); | 29 | pub const RECORD_FIELD: Key<ast::RecordFieldDef, StructFieldId> = Key::new(); |
30 | pub const TYPE_PARAM: Key<ast::TypeParam, TypeParamId> = Key::new(); | 30 | pub const TYPE_PARAM: Key<ast::TypeParam, TypeParamId> = Key::new(); |
31 | 31 | ||
32 | pub const MACRO: Key<ast::MacroCall, MacroDefId> = Key::new(); | ||
33 | |||
32 | /// XXX: AST Nodes and SyntaxNodes have identity equality semantics: nodes are | 34 | /// XXX: AST Nodes and SyntaxNodes have identity equality semantics: nodes are |
33 | /// equal if they point to exactly the same object. | 35 | /// equal if they point to exactly the same object. |
34 | /// | 36 | /// |
diff --git a/crates/ra_hir_def/src/lib.rs b/crates/ra_hir_def/src/lib.rs index ebc12e891..feb3a300d 100644 --- a/crates/ra_hir_def/src/lib.rs +++ b/crates/ra_hir_def/src/lib.rs | |||
@@ -332,7 +332,7 @@ pub enum VariantId { | |||
332 | StructId(StructId), | 332 | StructId(StructId), |
333 | UnionId(UnionId), | 333 | UnionId(UnionId), |
334 | } | 334 | } |
335 | impl_froms!(VariantId: EnumVariantId, StructId); | 335 | impl_froms!(VariantId: EnumVariantId, StructId, UnionId); |
336 | 336 | ||
337 | trait Intern { | 337 | trait Intern { |
338 | type ID; | 338 | type ID; |
diff --git a/crates/ra_hir_def/src/nameres.rs b/crates/ra_hir_def/src/nameres.rs index 4d210eab1..27c12e46c 100644 --- a/crates/ra_hir_def/src/nameres.rs +++ b/crates/ra_hir_def/src/nameres.rs | |||
@@ -59,12 +59,9 @@ use std::sync::Arc; | |||
59 | 59 | ||
60 | use hir_expand::{diagnostics::DiagnosticSink, name::Name, InFile}; | 60 | use hir_expand::{diagnostics::DiagnosticSink, name::Name, InFile}; |
61 | use ra_arena::Arena; | 61 | use ra_arena::Arena; |
62 | use ra_db::{CrateId, Edition, FileId, FilePosition}; | 62 | use ra_db::{CrateId, Edition, FileId}; |
63 | use ra_prof::profile; | 63 | use ra_prof::profile; |
64 | use ra_syntax::{ | 64 | use ra_syntax::ast; |
65 | ast::{self, AstNode}, | ||
66 | SyntaxNode, | ||
67 | }; | ||
68 | use rustc_hash::FxHashMap; | 65 | use rustc_hash::FxHashMap; |
69 | 66 | ||
70 | use crate::{ | 67 | use crate::{ |
@@ -145,6 +142,13 @@ impl ModuleOrigin { | |||
145 | } | 142 | } |
146 | } | 143 | } |
147 | 144 | ||
145 | pub fn is_inline(&self) -> bool { | ||
146 | match self { | ||
147 | ModuleOrigin::Inline { .. } => true, | ||
148 | ModuleOrigin::CrateRoot { .. } | ModuleOrigin::File { .. } => false, | ||
149 | } | ||
150 | } | ||
151 | |||
148 | /// Returns a node which defines this module. | 152 | /// Returns a node which defines this module. |
149 | /// That is, a file or a `mod foo {}` with items. | 153 | /// That is, a file or a `mod foo {}` with items. |
150 | fn definition_source(&self, db: &impl DefDatabase) -> InFile<ModuleSource> { | 154 | fn definition_source(&self, db: &impl DefDatabase) -> InFile<ModuleSource> { |
@@ -248,35 +252,6 @@ pub enum ModuleSource { | |||
248 | Module(ast::Module), | 252 | Module(ast::Module), |
249 | } | 253 | } |
250 | 254 | ||
251 | impl ModuleSource { | ||
252 | // FIXME: this methods do not belong here | ||
253 | pub fn from_position(db: &impl DefDatabase, position: FilePosition) -> ModuleSource { | ||
254 | let parse = db.parse(position.file_id); | ||
255 | match &ra_syntax::algo::find_node_at_offset::<ast::Module>( | ||
256 | parse.tree().syntax(), | ||
257 | position.offset, | ||
258 | ) { | ||
259 | Some(m) if !m.has_semi() => ModuleSource::Module(m.clone()), | ||
260 | _ => { | ||
261 | let source_file = parse.tree(); | ||
262 | ModuleSource::SourceFile(source_file) | ||
263 | } | ||
264 | } | ||
265 | } | ||
266 | |||
267 | pub fn from_child_node(db: &impl DefDatabase, child: InFile<&SyntaxNode>) -> ModuleSource { | ||
268 | if let Some(m) = | ||
269 | child.value.ancestors().filter_map(ast::Module::cast).find(|it| !it.has_semi()) | ||
270 | { | ||
271 | ModuleSource::Module(m) | ||
272 | } else { | ||
273 | let file_id = child.file_id.original_file(db); | ||
274 | let source_file = db.parse(file_id).tree(); | ||
275 | ModuleSource::SourceFile(source_file) | ||
276 | } | ||
277 | } | ||
278 | } | ||
279 | |||
280 | mod diagnostics { | 255 | mod diagnostics { |
281 | use hir_expand::diagnostics::DiagnosticSink; | 256 | use hir_expand::diagnostics::DiagnosticSink; |
282 | use ra_db::RelativePathBuf; | 257 | use ra_db::RelativePathBuf; |
diff --git a/crates/ra_hir_def/src/path.rs b/crates/ra_hir_def/src/path.rs index 9f93a5424..ab290e2c9 100644 --- a/crates/ra_hir_def/src/path.rs +++ b/crates/ra_hir_def/src/path.rs | |||
@@ -39,10 +39,7 @@ impl ModPath { | |||
39 | lower::lower_path(path, hygiene).map(|it| it.mod_path) | 39 | lower::lower_path(path, hygiene).map(|it| it.mod_path) |
40 | } | 40 | } |
41 | 41 | ||
42 | pub fn from_simple_segments( | 42 | pub fn from_segments(kind: PathKind, segments: impl IntoIterator<Item = Name>) -> ModPath { |
43 | kind: PathKind, | ||
44 | segments: impl IntoIterator<Item = Name>, | ||
45 | ) -> ModPath { | ||
46 | let segments = segments.into_iter().collect::<Vec<_>>(); | 43 | let segments = segments.into_iter().collect::<Vec<_>>(); |
47 | ModPath { kind, segments } | 44 | ModPath { kind, segments } |
48 | } | 45 | } |
@@ -240,7 +237,7 @@ impl From<Name> for Path { | |||
240 | fn from(name: Name) -> Path { | 237 | fn from(name: Name) -> Path { |
241 | Path { | 238 | Path { |
242 | type_anchor: None, | 239 | type_anchor: None, |
243 | mod_path: ModPath::from_simple_segments(PathKind::Plain, iter::once(name)), | 240 | mod_path: ModPath::from_segments(PathKind::Plain, iter::once(name)), |
244 | generic_args: vec![None], | 241 | generic_args: vec![None], |
245 | } | 242 | } |
246 | } | 243 | } |
@@ -248,7 +245,7 @@ impl From<Name> for Path { | |||
248 | 245 | ||
249 | impl From<Name> for ModPath { | 246 | impl From<Name> for ModPath { |
250 | fn from(name: Name) -> ModPath { | 247 | fn from(name: Name) -> ModPath { |
251 | ModPath::from_simple_segments(PathKind::Plain, iter::once(name)) | 248 | ModPath::from_segments(PathKind::Plain, iter::once(name)) |
252 | } | 249 | } |