diff options
Diffstat (limited to 'crates')
46 files changed, 1085 insertions, 132 deletions
diff --git a/crates/ra_assists/src/assist_context.rs b/crates/ra_assists/src/assist_context.rs index 3407df856..afd3fd4b9 100644 --- a/crates/ra_assists/src/assist_context.rs +++ b/crates/ra_assists/src/assist_context.rs | |||
@@ -73,6 +73,10 @@ impl<'a> AssistContext<'a> { | |||
73 | self.sema.db | 73 | self.sema.db |
74 | } | 74 | } |
75 | 75 | ||
76 | pub(crate) fn source_file(&self) -> &SourceFile { | ||
77 | &self.source_file | ||
78 | } | ||
79 | |||
76 | // NB, this ignores active selection. | 80 | // NB, this ignores active selection. |
77 | pub(crate) fn offset(&self) -> TextSize { | 81 | pub(crate) fn offset(&self) -> TextSize { |
78 | self.frange.range.start() | 82 | self.frange.range.start() |
diff --git a/crates/ra_assists/src/ast_transform.rs b/crates/ra_assists/src/ast_transform.rs index 28f3f3546..15ec75c95 100644 --- a/crates/ra_assists/src/ast_transform.rs +++ b/crates/ra_assists/src/ast_transform.rs | |||
@@ -63,7 +63,7 @@ impl<'a> SubstituteTypeParams<'a> { | |||
63 | let default = k.default(source_scope.db)?; | 63 | let default = k.default(source_scope.db)?; |
64 | Some(( | 64 | Some(( |
65 | k, | 65 | k, |
66 | ast::make::type_ref( | 66 | ast::make::ty( |
67 | &default | 67 | &default |
68 | .display_source_code(source_scope.db, source_scope.module()?.into()) | 68 | .display_source_code(source_scope.db, source_scope.module()?.into()) |
69 | .ok()?, | 69 | .ok()?, |
diff --git a/crates/ra_assists/src/handlers/early_return.rs b/crates/ra_assists/src/handlers/early_return.rs index 69852b611..6816a2709 100644 --- a/crates/ra_assists/src/handlers/early_return.rs +++ b/crates/ra_assists/src/handlers/early_return.rs | |||
@@ -123,7 +123,7 @@ pub(crate) fn convert_to_guarded_return(acc: &mut Assists, ctx: &AssistContext) | |||
123 | let happy_arm = { | 123 | let happy_arm = { |
124 | let pat = make::tuple_struct_pat( | 124 | let pat = make::tuple_struct_pat( |
125 | path, | 125 | path, |
126 | once(make::bind_pat(make::name("it")).into()), | 126 | once(make::ident_pat(make::name("it")).into()), |
127 | ); | 127 | ); |
128 | let expr = { | 128 | let expr = { |
129 | let name_ref = make::name_ref("it"); | 129 | let name_ref = make::name_ref("it"); |
@@ -136,7 +136,7 @@ pub(crate) fn convert_to_guarded_return(acc: &mut Assists, ctx: &AssistContext) | |||
136 | 136 | ||
137 | let sad_arm = make::match_arm( | 137 | let sad_arm = make::match_arm( |
138 | // FIXME: would be cool to use `None` or `Err(_)` if appropriate | 138 | // FIXME: would be cool to use `None` or `Err(_)` if appropriate |
139 | once(make::placeholder_pat().into()), | 139 | once(make::wildcard_pat().into()), |
140 | early_expression, | 140 | early_expression, |
141 | ); | 141 | ); |
142 | 142 | ||
@@ -144,7 +144,7 @@ pub(crate) fn convert_to_guarded_return(acc: &mut Assists, ctx: &AssistContext) | |||
144 | }; | 144 | }; |
145 | 145 | ||
146 | let let_stmt = make::let_stmt( | 146 | let let_stmt = make::let_stmt( |
147 | make::bind_pat(make::name(&bound_ident.syntax().to_string())).into(), | 147 | make::ident_pat(make::name(&bound_ident.syntax().to_string())).into(), |
148 | Some(match_expr), | 148 | Some(match_expr), |
149 | ); | 149 | ); |
150 | let let_stmt = let_stmt.indent(if_indent_level); | 150 | let let_stmt = let_stmt.indent(if_indent_level); |
diff --git a/crates/ra_assists/src/handlers/expand_glob_import.rs b/crates/ra_assists/src/handlers/expand_glob_import.rs new file mode 100644 index 000000000..eb216a81a --- /dev/null +++ b/crates/ra_assists/src/handlers/expand_glob_import.rs | |||
@@ -0,0 +1,391 @@ | |||
1 | use hir::{AssocItem, MacroDef, ModuleDef, Name, PathResolution, ScopeDef, SemanticsScope}; | ||
2 | use ra_ide_db::{ | ||
3 | defs::{classify_name_ref, Definition, NameRefClass}, | ||
4 | RootDatabase, | ||
5 | }; | ||
6 | use ra_syntax::{algo, ast, match_ast, AstNode, SyntaxNode, SyntaxToken, T}; | ||
7 | |||
8 | use crate::{ | ||
9 | assist_context::{AssistBuilder, AssistContext, Assists}, | ||
10 | AssistId, AssistKind, | ||
11 | }; | ||
12 | |||
13 | use either::Either; | ||
14 | |||
15 | // Assist: expand_glob_import | ||
16 | // | ||
17 | // Expands glob imports. | ||
18 | // | ||
19 | // ``` | ||
20 | // mod foo { | ||
21 | // pub struct Bar; | ||
22 | // pub struct Baz; | ||
23 | // } | ||
24 | // | ||
25 | // use foo::*<|>; | ||
26 | // | ||
27 | // fn qux(bar: Bar, baz: Baz) {} | ||
28 | // ``` | ||
29 | // -> | ||
30 | // ``` | ||
31 | // mod foo { | ||
32 | // pub struct Bar; | ||
33 | // pub struct Baz; | ||
34 | // } | ||
35 | // | ||
36 | // use foo::{Baz, Bar}; | ||
37 | // | ||
38 | // fn qux(bar: Bar, baz: Baz) {} | ||
39 | // ``` | ||
40 | pub(crate) fn expand_glob_import(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { | ||
41 | let star = ctx.find_token_at_offset(T![*])?; | ||
42 | let mod_path = find_mod_path(&star)?; | ||
43 | |||
44 | let source_file = ctx.source_file(); | ||
45 | let scope = ctx.sema.scope_at_offset(source_file.syntax(), ctx.offset()); | ||
46 | |||
47 | let defs_in_mod = find_defs_in_mod(ctx, scope, &mod_path)?; | ||
48 | let name_refs_in_source_file = | ||
49 | source_file.syntax().descendants().filter_map(ast::NameRef::cast).collect(); | ||
50 | let used_names = find_used_names(ctx, defs_in_mod, name_refs_in_source_file); | ||
51 | |||
52 | let parent = star.parent().parent()?; | ||
53 | acc.add( | ||
54 | AssistId("expand_glob_import", AssistKind::RefactorRewrite), | ||
55 | "Expand glob import", | ||
56 | parent.text_range(), | ||
57 | |builder| { | ||
58 | replace_ast(builder, &parent, mod_path, used_names); | ||
59 | }, | ||
60 | ) | ||
61 | } | ||
62 | |||
63 | fn find_mod_path(star: &SyntaxToken) -> Option<ast::Path> { | ||
64 | star.ancestors().find_map(|n| ast::UseTree::cast(n).and_then(|u| u.path())) | ||
65 | } | ||
66 | |||
67 | #[derive(PartialEq)] | ||
68 | enum Def { | ||
69 | ModuleDef(ModuleDef), | ||
70 | MacroDef(MacroDef), | ||
71 | } | ||
72 | |||
73 | impl Def { | ||
74 | fn name(&self, db: &RootDatabase) -> Option<Name> { | ||
75 | match self { | ||
76 | Def::ModuleDef(def) => def.name(db), | ||
77 | Def::MacroDef(def) => def.name(db), | ||
78 | } | ||
79 | } | ||
80 | } | ||
81 | |||
82 | fn find_defs_in_mod( | ||
83 | ctx: &AssistContext, | ||
84 | from: SemanticsScope<'_>, | ||
85 | path: &ast::Path, | ||
86 | ) -> Option<Vec<Def>> { | ||
87 | let hir_path = ctx.sema.lower_path(&path)?; | ||
88 | let module = if let Some(PathResolution::Def(ModuleDef::Module(module))) = | ||
89 | from.resolve_hir_path_qualifier(&hir_path) | ||
90 | { | ||
91 | module | ||
92 | } else { | ||
93 | return None; | ||
94 | }; | ||
95 | |||
96 | let module_scope = module.scope(ctx.db(), from.module()); | ||
97 | |||
98 | let mut defs = vec![]; | ||
99 | for (_, def) in module_scope { | ||
100 | match def { | ||
101 | ScopeDef::ModuleDef(def) => defs.push(Def::ModuleDef(def)), | ||
102 | ScopeDef::MacroDef(def) => defs.push(Def::MacroDef(def)), | ||
103 | _ => continue, | ||
104 | } | ||
105 | } | ||
106 | |||
107 | Some(defs) | ||
108 | } | ||
109 | |||
110 | fn find_used_names( | ||
111 | ctx: &AssistContext, | ||
112 | defs_in_mod: Vec<Def>, | ||
113 | name_refs_in_source_file: Vec<ast::NameRef>, | ||
114 | ) -> Vec<Name> { | ||
115 | let defs_in_source_file = name_refs_in_source_file | ||
116 | .iter() | ||
117 | .filter_map(|r| classify_name_ref(&ctx.sema, r)) | ||
118 | .filter_map(|rc| match rc { | ||
119 | NameRefClass::Definition(Definition::ModuleDef(def)) => Some(Def::ModuleDef(def)), | ||
120 | NameRefClass::Definition(Definition::Macro(def)) => Some(Def::MacroDef(def)), | ||
121 | _ => None, | ||
122 | }) | ||
123 | .collect::<Vec<Def>>(); | ||
124 | |||
125 | defs_in_mod | ||
126 | .iter() | ||
127 | .filter(|def| { | ||
128 | if let Def::ModuleDef(ModuleDef::Trait(tr)) = def { | ||
129 | for item in tr.items(ctx.db()) { | ||
130 | if let AssocItem::Function(f) = item { | ||
131 | if defs_in_source_file.contains(&Def::ModuleDef(ModuleDef::Function(f))) { | ||
132 | return true; | ||
133 | } | ||
134 | } | ||
135 | } | ||
136 | } | ||
137 | |||
138 | defs_in_source_file.contains(def) | ||
139 | }) | ||
140 | .filter_map(|d| d.name(ctx.db())) | ||
141 | .collect() | ||
142 | } | ||
143 | |||
144 | fn replace_ast( | ||
145 | builder: &mut AssistBuilder, | ||
146 | node: &SyntaxNode, | ||
147 | path: ast::Path, | ||
148 | used_names: Vec<Name>, | ||
149 | ) { | ||
150 | let replacement: Either<ast::UseTree, ast::UseTreeList> = match used_names.as_slice() { | ||
151 | [name] => Either::Left(ast::make::use_tree( | ||
152 | ast::make::path_from_text(&format!("{}::{}", path, name)), | ||
153 | None, | ||
154 | None, | ||
155 | false, | ||
156 | )), | ||
157 | names => Either::Right(ast::make::use_tree_list(names.iter().map(|n| { | ||
158 | ast::make::use_tree(ast::make::path_from_text(&n.to_string()), None, None, false) | ||
159 | }))), | ||
160 | }; | ||
161 | |||
162 | let mut replace_node = |replacement: Either<ast::UseTree, ast::UseTreeList>| { | ||
163 | algo::diff(node, &replacement.either(|u| u.syntax().clone(), |ut| ut.syntax().clone())) | ||
164 | .into_text_edit(builder.text_edit_builder()); | ||
165 | }; | ||
166 | |||
167 | match_ast! { | ||
168 | match node { | ||
169 | ast::UseTree(use_tree) => { | ||
170 | replace_node(replacement); | ||
171 | }, | ||
172 | ast::UseTreeList(use_tree_list) => { | ||
173 | replace_node(replacement); | ||
174 | }, | ||
175 | ast::Use(use_item) => { | ||
176 | builder.replace_ast(use_item, ast::make::use_(replacement.left_or_else(|ut| ast::make::use_tree(path, Some(ut), None, false)))); | ||
177 | }, | ||
178 | _ => {}, | ||
179 | } | ||
180 | } | ||
181 | } | ||
182 | |||
183 | #[cfg(test)] | ||
184 | mod tests { | ||
185 | use crate::tests::{check_assist, check_assist_not_applicable}; | ||
186 | |||
187 | use super::*; | ||
188 | |||
189 | #[test] | ||
190 | fn expanding_glob_import() { | ||
191 | check_assist( | ||
192 | expand_glob_import, | ||
193 | r" | ||
194 | mod foo { | ||
195 | pub struct Bar; | ||
196 | pub struct Baz; | ||
197 | pub struct Qux; | ||
198 | |||
199 | pub fn f() {} | ||
200 | } | ||
201 | |||
202 | use foo::*<|>; | ||
203 | |||
204 | fn qux(bar: Bar, baz: Baz) { | ||
205 | f(); | ||
206 | } | ||
207 | ", | ||
208 | r" | ||
209 | mod foo { | ||
210 | pub struct Bar; | ||
211 | pub struct Baz; | ||
212 | pub struct Qux; | ||
213 | |||
214 | pub fn f() {} | ||
215 | } | ||
216 | |||
217 | use foo::{Baz, Bar, f}; | ||
218 | |||
219 | fn qux(bar: Bar, baz: Baz) { | ||
220 | f(); | ||
221 | } | ||
222 | ", | ||
223 | ) | ||
224 | } | ||
225 | |||
226 | #[test] | ||
227 | fn expanding_glob_import_with_existing_explicit_names() { | ||
228 | check_assist( | ||
229 | expand_glob_import, | ||
230 | r" | ||
231 | mod foo { | ||
232 | pub struct Bar; | ||
233 | pub struct Baz; | ||
234 | pub struct Qux; | ||
235 | |||
236 | pub fn f() {} | ||
237 | } | ||
238 | |||
239 | use foo::{*<|>, f}; | ||
240 | |||
241 | fn qux(bar: Bar, baz: Baz) { | ||
242 | f(); | ||
243 | } | ||
244 | ", | ||
245 | r" | ||
246 | mod foo { | ||
247 | pub struct Bar; | ||
248 | pub struct Baz; | ||
249 | pub struct Qux; | ||
250 | |||
251 | pub fn f() {} | ||
252 | } | ||
253 | |||
254 | use foo::{Baz, Bar, f}; | ||
255 | |||
256 | fn qux(bar: Bar, baz: Baz) { | ||
257 | f(); | ||
258 | } | ||
259 | ", | ||
260 | ) | ||
261 | } | ||
262 | |||
263 | #[test] | ||
264 | fn expanding_nested_glob_import() { | ||
265 | check_assist( | ||
266 | expand_glob_import, | ||
267 | r" | ||
268 | mod foo { | ||
269 | mod bar { | ||
270 | pub struct Bar; | ||
271 | pub struct Baz; | ||
272 | pub struct Qux; | ||
273 | |||
274 | pub fn f() {} | ||
275 | } | ||
276 | |||
277 | mod baz { | ||
278 | pub fn g() {} | ||
279 | } | ||
280 | } | ||
281 | |||
282 | use foo::{bar::{*<|>, f}, baz::*}; | ||
283 | |||
284 | fn qux(bar: Bar, baz: Baz) { | ||
285 | f(); | ||
286 | g(); | ||
287 | } | ||
288 | ", | ||
289 | r" | ||
290 | mod foo { | ||
291 | mod bar { | ||
292 | pub struct Bar; | ||
293 | pub struct Baz; | ||
294 | pub struct Qux; | ||
295 | |||
296 | pub fn f() {} | ||
297 | } | ||
298 | |||
299 | mod baz { | ||
300 | pub fn g() {} | ||
301 | } | ||
302 | } | ||
303 | |||
304 | use foo::{bar::{Baz, Bar, f}, baz::*}; | ||
305 | |||
306 | fn qux(bar: Bar, baz: Baz) { | ||
307 | f(); | ||
308 | g(); | ||
309 | } | ||
310 | ", | ||
311 | ) | ||
312 | } | ||
313 | |||
314 | #[test] | ||
315 | fn expanding_glob_import_with_macro_defs() { | ||
316 | check_assist( | ||
317 | expand_glob_import, | ||
318 | r" | ||
319 | //- /lib.rs crate:foo | ||
320 | #[macro_export] | ||
321 | macro_rules! bar { | ||
322 | () => () | ||
323 | } | ||
324 | |||
325 | pub fn baz() {} | ||
326 | |||
327 | //- /main.rs crate:main deps:foo | ||
328 | use foo::*<|>; | ||
329 | |||
330 | fn main() { | ||
331 | bar!(); | ||
332 | baz(); | ||
333 | } | ||
334 | ", | ||
335 | r" | ||
336 | use foo::{bar, baz}; | ||
337 | |||
338 | fn main() { | ||
339 | bar!(); | ||
340 | baz(); | ||
341 | } | ||
342 | ", | ||
343 | ) | ||
344 | } | ||
345 | |||
346 | #[test] | ||
347 | fn expanding_glob_import_with_trait_method_uses() { | ||
348 | check_assist( | ||
349 | expand_glob_import, | ||
350 | r" | ||
351 | //- /lib.rs crate:foo | ||
352 | pub trait Tr { | ||
353 | fn method(&self) {} | ||
354 | } | ||
355 | impl Tr for () {} | ||
356 | |||
357 | //- /main.rs crate:main deps:foo | ||
358 | use foo::*<|>; | ||
359 | |||
360 | fn main() { | ||
361 | ().method(); | ||
362 | } | ||
363 | ", | ||
364 | r" | ||
365 | use foo::Tr; | ||
366 | |||
367 | fn main() { | ||
368 | ().method(); | ||
369 | } | ||
370 | ", | ||
371 | ) | ||
372 | } | ||
373 | |||
374 | #[test] | ||
375 | fn expanding_is_not_applicable_if_cursor_is_not_in_star_token() { | ||
376 | check_assist_not_applicable( | ||
377 | expand_glob_import, | ||
378 | r" | ||
379 | mod foo { | ||
380 | pub struct Bar; | ||
381 | pub struct Baz; | ||
382 | pub struct Qux; | ||
383 | } | ||
384 | |||
385 | use foo::Bar<|>; | ||
386 | |||
387 | fn qux(bar: Bar, baz: Baz) {} | ||
388 | ", | ||
389 | ) | ||
390 | } | ||
391 | } | ||
diff --git a/crates/ra_assists/src/handlers/fill_match_arms.rs b/crates/ra_assists/src/handlers/fill_match_arms.rs index b2e14f9d7..6698d1a27 100644 --- a/crates/ra_assists/src/handlers/fill_match_arms.rs +++ b/crates/ra_assists/src/handlers/fill_match_arms.rs | |||
@@ -197,12 +197,11 @@ fn build_pat(db: &RootDatabase, module: hir::Module, var: hir::EnumVariant) -> O | |||
197 | // FIXME: use HIR for this; it doesn't currently expose struct vs. tuple vs. unit variants though | 197 | // FIXME: use HIR for this; it doesn't currently expose struct vs. tuple vs. unit variants though |
198 | let pat: ast::Pat = match var.source(db).value.kind() { | 198 | let pat: ast::Pat = match var.source(db).value.kind() { |
199 | ast::StructKind::Tuple(field_list) => { | 199 | ast::StructKind::Tuple(field_list) => { |
200 | let pats = | 200 | let pats = iter::repeat(make::wildcard_pat().into()).take(field_list.fields().count()); |
201 | iter::repeat(make::placeholder_pat().into()).take(field_list.fields().count()); | ||
202 | make::tuple_struct_pat(path, pats).into() | 201 | make::tuple_struct_pat(path, pats).into() |
203 | } | 202 | } |
204 | ast::StructKind::Record(field_list) => { | 203 | ast::StructKind::Record(field_list) => { |
205 | let pats = field_list.fields().map(|f| make::bind_pat(f.name().unwrap()).into()); | 204 | let pats = field_list.fields().map(|f| make::ident_pat(f.name().unwrap()).into()); |
206 | make::record_pat(path, pats).into() | 205 | make::record_pat(path, pats).into() |
207 | } | 206 | } |
208 | ast::StructKind::Unit => make::path_pat(path), | 207 | ast::StructKind::Unit => make::path_pat(path), |
diff --git a/crates/ra_assists/src/handlers/generate_function.rs b/crates/ra_assists/src/handlers/generate_function.rs index 56510861d..acc97e648 100644 --- a/crates/ra_assists/src/handlers/generate_function.rs +++ b/crates/ra_assists/src/handlers/generate_function.rs | |||
@@ -142,7 +142,7 @@ impl FunctionBuilder { | |||
142 | let fn_body = make::block_expr(vec![], Some(placeholder_expr)); | 142 | let fn_body = make::block_expr(vec![], Some(placeholder_expr)); |
143 | let visibility = if self.needs_pub { Some(make::visibility_pub_crate()) } else { None }; | 143 | let visibility = if self.needs_pub { Some(make::visibility_pub_crate()) } else { None }; |
144 | let mut fn_def = | 144 | let mut fn_def = |
145 | make::fn_def(visibility, self.fn_name, self.type_params, self.params, fn_body); | 145 | make::fn_(visibility, self.fn_name, self.type_params, self.params, fn_body); |
146 | let leading_ws; | 146 | let leading_ws; |
147 | let trailing_ws; | 147 | let trailing_ws; |
148 | 148 | ||
diff --git a/crates/ra_assists/src/handlers/raw_string.rs b/crates/ra_assists/src/handlers/raw_string.rs index 4e8a0c2db..4c797178f 100644 --- a/crates/ra_assists/src/handlers/raw_string.rs +++ b/crates/ra_assists/src/handlers/raw_string.rs | |||
@@ -173,7 +173,7 @@ fn test_required_hashes() { | |||
173 | } | 173 | } |
174 | 174 | ||
175 | #[cfg(test)] | 175 | #[cfg(test)] |
176 | mod test { | 176 | mod tests { |
177 | use test_utils::mark; | 177 | use test_utils::mark; |
178 | 178 | ||
179 | use crate::tests::{check_assist, check_assist_not_applicable, check_assist_target}; | 179 | use crate::tests::{check_assist, check_assist_not_applicable, check_assist_target}; |
diff --git a/crates/ra_assists/src/handlers/replace_if_let_with_match.rs b/crates/ra_assists/src/handlers/replace_if_let_with_match.rs index b7e30a7f2..ecafb74a1 100644 --- a/crates/ra_assists/src/handlers/replace_if_let_with_match.rs +++ b/crates/ra_assists/src/handlers/replace_if_let_with_match.rs | |||
@@ -65,7 +65,7 @@ pub(crate) fn replace_if_let_with_match(acc: &mut Assists, ctx: &AssistContext) | |||
65 | .type_of_pat(&pat) | 65 | .type_of_pat(&pat) |
66 | .and_then(|ty| TryEnum::from_ty(&ctx.sema, &ty)) | 66 | .and_then(|ty| TryEnum::from_ty(&ctx.sema, &ty)) |
67 | .map(|it| it.sad_pattern()) | 67 | .map(|it| it.sad_pattern()) |
68 | .unwrap_or_else(|| make::placeholder_pat().into()); | 68 | .unwrap_or_else(|| make::wildcard_pat().into()); |
69 | let else_expr = unwrap_trivial_block(else_block); | 69 | let else_expr = unwrap_trivial_block(else_block); |
70 | make::match_arm(vec![pattern], else_expr) | 70 | make::match_arm(vec![pattern], else_expr) |
71 | }; | 71 | }; |
diff --git a/crates/ra_assists/src/handlers/replace_let_with_if_let.rs b/crates/ra_assists/src/handlers/replace_let_with_if_let.rs index 64ad15a23..e4d436dec 100644 --- a/crates/ra_assists/src/handlers/replace_let_with_if_let.rs +++ b/crates/ra_assists/src/handlers/replace_let_with_if_let.rs | |||
@@ -50,10 +50,10 @@ pub(crate) fn replace_let_with_if_let(acc: &mut Assists, ctx: &AssistContext) -> | |||
50 | target, | 50 | target, |
51 | |edit| { | 51 | |edit| { |
52 | let with_placeholder: ast::Pat = match happy_variant { | 52 | let with_placeholder: ast::Pat = match happy_variant { |
53 | None => make::placeholder_pat().into(), | 53 | None => make::wildcard_pat().into(), |
54 | Some(var_name) => make::tuple_struct_pat( | 54 | Some(var_name) => make::tuple_struct_pat( |
55 | make::path_unqualified(make::path_segment(make::name_ref(var_name))), | 55 | make::path_unqualified(make::path_segment(make::name_ref(var_name))), |
56 | once(make::placeholder_pat().into()), | 56 | once(make::wildcard_pat().into()), |
57 | ) | 57 | ) |
58 | .into(), | 58 | .into(), |
59 | }; | 59 | }; |
diff --git a/crates/ra_assists/src/handlers/replace_qualified_name_with_use.rs b/crates/ra_assists/src/handlers/replace_qualified_name_with_use.rs index 53496ede1..da0a860c5 100644 --- a/crates/ra_assists/src/handlers/replace_qualified_name_with_use.rs +++ b/crates/ra_assists/src/handlers/replace_qualified_name_with_use.rs | |||
@@ -643,4 +643,46 @@ fn main() { | |||
643 | ", | 643 | ", |
644 | ); | 644 | ); |
645 | } | 645 | } |
646 | |||
647 | #[test] | ||
648 | fn does_not_replace_pub_use() { | ||
649 | check_assist( | ||
650 | replace_qualified_name_with_use, | ||
651 | r" | ||
652 | pub use std::fmt; | ||
653 | |||
654 | impl std::io<|> for Foo { | ||
655 | } | ||
656 | ", | ||
657 | r" | ||
658 | use std::io; | ||
659 | |||
660 | pub use std::fmt; | ||
661 | |||
662 | impl io for Foo { | ||
663 | } | ||
664 | ", | ||
665 | ); | ||
666 | } | ||
667 | |||
668 | #[test] | ||
669 | fn does_not_replace_pub_crate_use() { | ||
670 | check_assist( | ||
671 | replace_qualified_name_with_use, | ||
672 | r" | ||
673 | pub(crate) use std::fmt; | ||
674 | |||
675 | impl std::io<|> for Foo { | ||
676 | } | ||
677 | ", | ||
678 | r" | ||
679 | use std::io; | ||
680 | |||
681 | pub(crate) use std::fmt; | ||
682 | |||
683 | impl io for Foo { | ||
684 | } | ||
685 | ", | ||
686 | ); | ||
687 | } | ||
646 | } | 688 | } |
diff --git a/crates/ra_assists/src/handlers/replace_unwrap_with_match.rs b/crates/ra_assists/src/handlers/replace_unwrap_with_match.rs index e5a4bb23c..d69f2c1b0 100644 --- a/crates/ra_assists/src/handlers/replace_unwrap_with_match.rs +++ b/crates/ra_assists/src/handlers/replace_unwrap_with_match.rs | |||
@@ -52,7 +52,7 @@ pub(crate) fn replace_unwrap_with_match(acc: &mut Assists, ctx: &AssistContext) | |||
52 | target, | 52 | target, |
53 | |builder| { | 53 | |builder| { |
54 | let ok_path = make::path_unqualified(make::path_segment(make::name_ref(happy_variant))); | 54 | let ok_path = make::path_unqualified(make::path_segment(make::name_ref(happy_variant))); |
55 | let it = make::bind_pat(make::name("a")).into(); | 55 | let it = make::ident_pat(make::name("a")).into(); |
56 | let ok_tuple = make::tuple_struct_pat(ok_path, iter::once(it)).into(); | 56 | let ok_tuple = make::tuple_struct_pat(ok_path, iter::once(it)).into(); |
57 | 57 | ||
58 | let bind_path = make::path_unqualified(make::path_segment(make::name_ref("a"))); | 58 | let bind_path = make::path_unqualified(make::path_segment(make::name_ref("a"))); |
@@ -60,7 +60,7 @@ pub(crate) fn replace_unwrap_with_match(acc: &mut Assists, ctx: &AssistContext) | |||
60 | 60 | ||
61 | let unreachable_call = make::expr_unreachable(); | 61 | let unreachable_call = make::expr_unreachable(); |
62 | let err_arm = | 62 | let err_arm = |
63 | make::match_arm(iter::once(make::placeholder_pat().into()), unreachable_call); | 63 | make::match_arm(iter::once(make::wildcard_pat().into()), unreachable_call); |
64 | 64 | ||
65 | let match_arm_list = make::match_arm_list(vec![ok_arm, err_arm]); | 65 | let match_arm_list = make::match_arm_list(vec![ok_arm, err_arm]); |
66 | let match_expr = make::expr_match(caller.clone(), match_arm_list) | 66 | let match_expr = make::expr_match(caller.clone(), match_arm_list) |
diff --git a/crates/ra_assists/src/lib.rs b/crates/ra_assists/src/lib.rs index 465b90415..507646cc8 100644 --- a/crates/ra_assists/src/lib.rs +++ b/crates/ra_assists/src/lib.rs | |||
@@ -140,6 +140,7 @@ mod handlers { | |||
140 | mod change_return_type_to_result; | 140 | mod change_return_type_to_result; |
141 | mod change_visibility; | 141 | mod change_visibility; |
142 | mod early_return; | 142 | mod early_return; |
143 | mod expand_glob_import; | ||
143 | mod extract_struct_from_enum_variant; | 144 | mod extract_struct_from_enum_variant; |
144 | mod extract_variable; | 145 | mod extract_variable; |
145 | mod fill_match_arms; | 146 | mod fill_match_arms; |
@@ -181,6 +182,7 @@ mod handlers { | |||
181 | change_return_type_to_result::change_return_type_to_result, | 182 | change_return_type_to_result::change_return_type_to_result, |
182 | change_visibility::change_visibility, | 183 | change_visibility::change_visibility, |
183 | early_return::convert_to_guarded_return, | 184 | early_return::convert_to_guarded_return, |
185 | expand_glob_import::expand_glob_import, | ||
184 | extract_struct_from_enum_variant::extract_struct_from_enum_variant, | 186 | extract_struct_from_enum_variant::extract_struct_from_enum_variant, |
185 | extract_variable::extract_variable, | 187 | extract_variable::extract_variable, |
186 | fill_match_arms::fill_match_arms, | 188 | fill_match_arms::fill_match_arms, |
diff --git a/crates/ra_assists/src/tests/generated.rs b/crates/ra_assists/src/tests/generated.rs index eff7feded..97978e7a2 100644 --- a/crates/ra_assists/src/tests/generated.rs +++ b/crates/ra_assists/src/tests/generated.rs | |||
@@ -229,6 +229,33 @@ fn main() { | |||
229 | } | 229 | } |
230 | 230 | ||
231 | #[test] | 231 | #[test] |
232 | fn doctest_expand_glob_import() { | ||
233 | check_doc_test( | ||
234 | "expand_glob_import", | ||
235 | r#####" | ||
236 | mod foo { | ||
237 | pub struct Bar; | ||
238 | pub struct Baz; | ||
239 | } | ||
240 | |||
241 | use foo::*<|>; | ||
242 | |||
243 | fn qux(bar: Bar, baz: Baz) {} | ||
244 | "#####, | ||
245 | r#####" | ||
246 | mod foo { | ||
247 | pub struct Bar; | ||
248 | pub struct Baz; | ||
249 | } | ||
250 | |||
251 | use foo::{Baz, Bar}; | ||
252 | |||
253 | fn qux(bar: Bar, baz: Baz) {} | ||
254 | "#####, | ||
255 | ) | ||
256 | } | ||
257 | |||
258 | #[test] | ||
232 | fn doctest_extract_struct_from_enum_variant() { | 259 | fn doctest_extract_struct_from_enum_variant() { |
233 | check_doc_test( | 260 | check_doc_test( |
234 | "extract_struct_from_enum_variant", | 261 | "extract_struct_from_enum_variant", |
diff --git a/crates/ra_assists/src/utils.rs b/crates/ra_assists/src/utils.rs index 373de273c..54d5678d1 100644 --- a/crates/ra_assists/src/utils.rs +++ b/crates/ra_assists/src/utils.rs | |||
@@ -181,10 +181,10 @@ impl TryEnum { | |||
181 | match self { | 181 | match self { |
182 | TryEnum::Result => make::tuple_struct_pat( | 182 | TryEnum::Result => make::tuple_struct_pat( |
183 | make::path_unqualified(make::path_segment(make::name_ref("Err"))), | 183 | make::path_unqualified(make::path_segment(make::name_ref("Err"))), |
184 | iter::once(make::placeholder_pat().into()), | 184 | iter::once(make::wildcard_pat().into()), |
185 | ) | 185 | ) |
186 | .into(), | 186 | .into(), |
187 | TryEnum::Option => make::bind_pat(make::name("None")).into(), | 187 | TryEnum::Option => make::ident_pat(make::name("None")).into(), |
188 | } | 188 | } |
189 | } | 189 | } |
190 | 190 | ||
diff --git a/crates/ra_assists/src/utils/insert_use.rs b/crates/ra_assists/src/utils/insert_use.rs index 617afe2e9..32780fceb 100644 --- a/crates/ra_assists/src/utils/insert_use.rs +++ b/crates/ra_assists/src/utils/insert_use.rs | |||
@@ -4,7 +4,7 @@ | |||
4 | 4 | ||
5 | use hir::{self, ModPath}; | 5 | use hir::{self, ModPath}; |
6 | use ra_syntax::{ | 6 | use ra_syntax::{ |
7 | ast::{self, NameOwner}, | 7 | ast::{self, NameOwner, VisibilityOwner}, |
8 | AstNode, Direction, SmolStr, | 8 | AstNode, Direction, SmolStr, |
9 | SyntaxKind::{PATH, PATH_SEGMENT}, | 9 | SyntaxKind::{PATH, PATH_SEGMENT}, |
10 | SyntaxNode, T, | 10 | SyntaxNode, T, |
@@ -378,6 +378,7 @@ fn best_action_for_target( | |||
378 | let best_action = container | 378 | let best_action = container |
379 | .children() | 379 | .children() |
380 | .filter_map(ast::Use::cast) | 380 | .filter_map(ast::Use::cast) |
381 | .filter(|u| u.visibility().is_none()) | ||
381 | .filter_map(|it| it.use_tree()) | 382 | .filter_map(|it| it.use_tree()) |
382 | .map(|u| walk_use_tree_for_best_action(&mut storage, None, u, target)) | 383 | .map(|u| walk_use_tree_for_best_action(&mut storage, None, u, target)) |
383 | .fold(None, |best, a| match best { | 384 | .fold(None, |best, a| match best { |
diff --git a/crates/ra_db/Cargo.toml b/crates/ra_db/Cargo.toml index 5f334d04f..fe73dc015 100644 --- a/crates/ra_db/Cargo.toml +++ b/crates/ra_db/Cargo.toml | |||
@@ -9,7 +9,7 @@ license = "MIT OR Apache-2.0" | |||
9 | doctest = false | 9 | doctest = false |
10 | 10 | ||
11 | [dependencies] | 11 | [dependencies] |
12 | salsa = "0.15.0" | 12 | salsa = "0.15.2" |
13 | rustc-hash = "1.1.0" | 13 | rustc-hash = "1.1.0" |
14 | 14 | ||
15 | ra_syntax = { path = "../ra_syntax" } | 15 | ra_syntax = { path = "../ra_syntax" } |
diff --git a/crates/ra_hir/src/db.rs b/crates/ra_hir/src/db.rs index a2b9f3e35..07333c453 100644 --- a/crates/ra_hir/src/db.rs +++ b/crates/ra_hir/src/db.rs | |||
@@ -13,14 +13,7 @@ pub use hir_expand::db::{ | |||
13 | AstDatabase, AstDatabaseStorage, AstIdMapQuery, InternEagerExpansionQuery, InternMacroQuery, | 13 | AstDatabase, AstDatabaseStorage, AstIdMapQuery, InternEagerExpansionQuery, InternMacroQuery, |
14 | MacroArgTextQuery, MacroDefQuery, MacroExpandQuery, ParseMacroQuery, | 14 | MacroArgTextQuery, MacroDefQuery, MacroExpandQuery, ParseMacroQuery, |
15 | }; | 15 | }; |
16 | pub use hir_ty::db::{ | 16 | pub use hir_ty::db::*; |
17 | AssociatedTyDataQuery, AssociatedTyValueQuery, CallableItemSignatureQuery, FieldTypesQuery, | ||
18 | GenericDefaultsQuery, GenericPredicatesForParamQuery, GenericPredicatesQuery, HirDatabase, | ||
19 | HirDatabaseStorage, ImplDatumQuery, ImplSelfTyQuery, ImplTraitQuery, InferQueryQuery, | ||
20 | InherentImplsInCrateQuery, InternTypeParamIdQuery, ReturnTypeImplTraitsQuery, StructDatumQuery, | ||
21 | TraitDatumQuery, TraitImplsInCrateQuery, TraitImplsInDepsQuery, TraitSolveQuery, TyQuery, | ||
22 | ValueTyQuery, | ||
23 | }; | ||
24 | 17 | ||
25 | #[test] | 18 | #[test] |
26 | fn hir_database_is_object_safe() { | 19 | fn hir_database_is_object_safe() { |
diff --git a/crates/ra_hir_expand/src/proc_macro.rs b/crates/ra_hir_expand/src/proc_macro.rs index 04c026004..2c0ec41d2 100644 --- a/crates/ra_hir_expand/src/proc_macro.rs +++ b/crates/ra_hir_expand/src/proc_macro.rs | |||
@@ -101,7 +101,7 @@ fn remove_derive_attrs(tt: &tt::Subtree) -> Option<tt::Subtree> { | |||
101 | } | 101 | } |
102 | 102 | ||
103 | #[cfg(test)] | 103 | #[cfg(test)] |
104 | mod test { | 104 | mod tests { |
105 | use super::*; | 105 | use super::*; |
106 | use test_utils::assert_eq_text; | 106 | use test_utils::assert_eq_text; |
107 | 107 | ||
diff --git a/crates/ra_hir_ty/Cargo.toml b/crates/ra_hir_ty/Cargo.toml index 623ce261a..83397d579 100644 --- a/crates/ra_hir_ty/Cargo.toml +++ b/crates/ra_hir_ty/Cargo.toml | |||
@@ -28,9 +28,9 @@ test_utils = { path = "../test_utils" } | |||
28 | 28 | ||
29 | scoped-tls = "1" | 29 | scoped-tls = "1" |
30 | 30 | ||
31 | chalk-solve = { version = "0.19.0" } | 31 | chalk-solve = { version = "0.21.0" } |
32 | chalk-ir = { version = "0.19.0" } | 32 | chalk-ir = { version = "0.21.0" } |
33 | chalk-recursive = { version = "0.19.0" } | 33 | chalk-recursive = { version = "0.21.0" } |
34 | 34 | ||
35 | [dev-dependencies] | 35 | [dev-dependencies] |
36 | expect = { path = "../expect" } | 36 | expect = { path = "../expect" } |
diff --git a/crates/ra_hir_ty/src/diagnostics/unsafe_check.rs b/crates/ra_hir_ty/src/diagnostics/unsafe_check.rs index 5cc76bdce..61ffbf5d1 100644 --- a/crates/ra_hir_ty/src/diagnostics/unsafe_check.rs +++ b/crates/ra_hir_ty/src/diagnostics/unsafe_check.rs | |||
@@ -6,6 +6,7 @@ use std::sync::Arc; | |||
6 | use hir_def::{ | 6 | use hir_def::{ |
7 | body::Body, | 7 | body::Body, |
8 | expr::{Expr, ExprId, UnaryOp}, | 8 | expr::{Expr, ExprId, UnaryOp}, |
9 | resolver::{resolver_for_expr, ResolveValueResult, ValueNs}, | ||
9 | DefWithBodyId, | 10 | DefWithBodyId, |
10 | }; | 11 | }; |
11 | use hir_expand::diagnostics::DiagnosticSink; | 12 | use hir_expand::diagnostics::DiagnosticSink; |
@@ -70,7 +71,7 @@ pub fn unsafe_expressions( | |||
70 | ) -> Vec<UnsafeExpr> { | 71 | ) -> Vec<UnsafeExpr> { |
71 | let mut unsafe_exprs = vec![]; | 72 | let mut unsafe_exprs = vec![]; |
72 | let body = db.body(def); | 73 | let body = db.body(def); |
73 | walk_unsafe(&mut unsafe_exprs, db, infer, &body, body.body_expr, false); | 74 | walk_unsafe(&mut unsafe_exprs, db, infer, def, &body, body.body_expr, false); |
74 | 75 | ||
75 | unsafe_exprs | 76 | unsafe_exprs |
76 | } | 77 | } |
@@ -79,6 +80,7 @@ fn walk_unsafe( | |||
79 | unsafe_exprs: &mut Vec<UnsafeExpr>, | 80 | unsafe_exprs: &mut Vec<UnsafeExpr>, |
80 | db: &dyn HirDatabase, | 81 | db: &dyn HirDatabase, |
81 | infer: &InferenceResult, | 82 | infer: &InferenceResult, |
83 | def: DefWithBodyId, | ||
82 | body: &Body, | 84 | body: &Body, |
83 | current: ExprId, | 85 | current: ExprId, |
84 | inside_unsafe_block: bool, | 86 | inside_unsafe_block: bool, |
@@ -97,6 +99,15 @@ fn walk_unsafe( | |||
97 | } | 99 | } |
98 | } | 100 | } |
99 | } | 101 | } |
102 | Expr::Path(path) => { | ||
103 | let resolver = resolver_for_expr(db.upcast(), def, current); | ||
104 | let value_or_partial = resolver.resolve_path_in_value_ns(db.upcast(), path.mod_path()); | ||
105 | if let Some(ResolveValueResult::ValueNs(ValueNs::StaticId(id))) = value_or_partial { | ||
106 | if db.static_data(id).mutable { | ||
107 | unsafe_exprs.push(UnsafeExpr { expr: current, inside_unsafe_block }); | ||
108 | } | ||
109 | } | ||
110 | } | ||
100 | Expr::MethodCall { .. } => { | 111 | Expr::MethodCall { .. } => { |
101 | if infer | 112 | if infer |
102 | .method_resolution(current) | 113 | .method_resolution(current) |
@@ -112,13 +123,13 @@ fn walk_unsafe( | |||
112 | } | 123 | } |
113 | } | 124 | } |
114 | Expr::Unsafe { body: child } => { | 125 | Expr::Unsafe { body: child } => { |
115 | return walk_unsafe(unsafe_exprs, db, infer, body, *child, true); | 126 | return walk_unsafe(unsafe_exprs, db, infer, def, body, *child, true); |
116 | } | 127 | } |
117 | _ => {} | 128 | _ => {} |
118 | } | 129 | } |
119 | 130 | ||
120 | expr.walk_child_exprs(|child| { | 131 | expr.walk_child_exprs(|child| { |
121 | walk_unsafe(unsafe_exprs, db, infer, body, child, inside_unsafe_block); | 132 | walk_unsafe(unsafe_exprs, db, infer, def, body, child, inside_unsafe_block); |
122 | }); | 133 | }); |
123 | } | 134 | } |
124 | 135 | ||
@@ -170,4 +181,25 @@ fn main() { | |||
170 | "#, | 181 | "#, |
171 | ); | 182 | ); |
172 | } | 183 | } |
184 | |||
185 | #[test] | ||
186 | fn missing_unsafe_diagnostic_with_static_mut() { | ||
187 | check_diagnostics( | ||
188 | r#" | ||
189 | struct Ty { | ||
190 | a: u8, | ||
191 | } | ||
192 | |||
193 | static mut static_mut: Ty = Ty { a: 0 }; | ||
194 | |||
195 | fn main() { | ||
196 | let x = static_mut.a; | ||
197 | //^^^^^^^^^^ This operation is unsafe and requires an unsafe function or block | ||
198 | unsafe { | ||
199 | let x = static_mut.a; | ||
200 | } | ||
201 | } | ||
202 | "#, | ||
203 | ); | ||
204 | } | ||
173 | } | 205 | } |
diff --git a/crates/ra_hir_ty/src/infer.rs b/crates/ra_hir_ty/src/infer.rs index 28f32a0a4..3d12039a6 100644 --- a/crates/ra_hir_ty/src/infer.rs +++ b/crates/ra_hir_ty/src/infer.rs | |||
@@ -440,6 +440,12 @@ impl<'a> InferenceContext<'a> { | |||
440 | let ty = self.insert_type_vars(ty.subst(&substs)); | 440 | let ty = self.insert_type_vars(ty.subst(&substs)); |
441 | forbid_unresolved_segments((ty, Some(strukt.into())), unresolved) | 441 | forbid_unresolved_segments((ty, Some(strukt.into())), unresolved) |
442 | } | 442 | } |
443 | TypeNs::AdtId(AdtId::UnionId(u)) => { | ||
444 | let substs = Ty::substs_from_path(&ctx, path, u.into(), true); | ||
445 | let ty = self.db.ty(u.into()); | ||
446 | let ty = self.insert_type_vars(ty.subst(&substs)); | ||
447 | forbid_unresolved_segments((ty, Some(u.into())), unresolved) | ||
448 | } | ||
443 | TypeNs::EnumVariantId(var) => { | 449 | TypeNs::EnumVariantId(var) => { |
444 | let substs = Ty::substs_from_path(&ctx, path, var.into(), true); | 450 | let substs = Ty::substs_from_path(&ctx, path, var.into(), true); |
445 | let ty = self.db.ty(var.parent.into()); | 451 | let ty = self.db.ty(var.parent.into()); |
@@ -490,10 +496,7 @@ impl<'a> InferenceContext<'a> { | |||
490 | // FIXME potentially resolve assoc type | 496 | // FIXME potentially resolve assoc type |
491 | (Ty::Unknown, None) | 497 | (Ty::Unknown, None) |
492 | } | 498 | } |
493 | TypeNs::AdtId(AdtId::EnumId(_)) | 499 | TypeNs::AdtId(AdtId::EnumId(_)) | TypeNs::BuiltinType(_) | TypeNs::TraitId(_) => { |
494 | | TypeNs::AdtId(AdtId::UnionId(_)) | ||
495 | | TypeNs::BuiltinType(_) | ||
496 | | TypeNs::TraitId(_) => { | ||
497 | // FIXME diagnostic | 500 | // FIXME diagnostic |
498 | (Ty::Unknown, None) | 501 | (Ty::Unknown, None) |
499 | } | 502 | } |
diff --git a/crates/ra_hir_ty/src/lower.rs b/crates/ra_hir_ty/src/lower.rs index 1eacc6f95..7638f167b 100644 --- a/crates/ra_hir_ty/src/lower.rs +++ b/crates/ra_hir_ty/src/lower.rs | |||
@@ -518,6 +518,7 @@ impl Ty { | |||
518 | let (segment, generic_def) = match resolved { | 518 | let (segment, generic_def) = match resolved { |
519 | ValueTyDefId::FunctionId(it) => (last, Some(it.into())), | 519 | ValueTyDefId::FunctionId(it) => (last, Some(it.into())), |
520 | ValueTyDefId::StructId(it) => (last, Some(it.into())), | 520 | ValueTyDefId::StructId(it) => (last, Some(it.into())), |
521 | ValueTyDefId::UnionId(it) => (last, Some(it.into())), | ||
521 | ValueTyDefId::ConstId(it) => (last, Some(it.into())), | 522 | ValueTyDefId::ConstId(it) => (last, Some(it.into())), |
522 | ValueTyDefId::StaticId(_) => (last, None), | 523 | ValueTyDefId::StaticId(_) => (last, None), |
523 | ValueTyDefId::EnumVariantId(var) => { | 524 | ValueTyDefId::EnumVariantId(var) => { |
@@ -1148,11 +1149,12 @@ impl_from!(BuiltinType, AdtId(StructId, EnumId, UnionId), TypeAliasId for TyDefI | |||
1148 | pub enum ValueTyDefId { | 1149 | pub enum ValueTyDefId { |
1149 | FunctionId(FunctionId), | 1150 | FunctionId(FunctionId), |
1150 | StructId(StructId), | 1151 | StructId(StructId), |
1152 | UnionId(UnionId), | ||
1151 | EnumVariantId(EnumVariantId), | 1153 | EnumVariantId(EnumVariantId), |
1152 | ConstId(ConstId), | 1154 | ConstId(ConstId), |
1153 | StaticId(StaticId), | 1155 | StaticId(StaticId), |
1154 | } | 1156 | } |
1155 | impl_from!(FunctionId, StructId, EnumVariantId, ConstId, StaticId for ValueTyDefId); | 1157 | impl_from!(FunctionId, StructId, UnionId, EnumVariantId, ConstId, StaticId for ValueTyDefId); |
1156 | 1158 | ||
1157 | /// Build the declared type of an item. This depends on the namespace; e.g. for | 1159 | /// Build the declared type of an item. This depends on the namespace; e.g. for |
1158 | /// `struct Foo(usize)`, we have two types: The type of the struct itself, and | 1160 | /// `struct Foo(usize)`, we have two types: The type of the struct itself, and |
@@ -1179,6 +1181,7 @@ pub(crate) fn value_ty_query(db: &dyn HirDatabase, def: ValueTyDefId) -> Binders | |||
1179 | match def { | 1181 | match def { |
1180 | ValueTyDefId::FunctionId(it) => type_for_fn(db, it), | 1182 | ValueTyDefId::FunctionId(it) => type_for_fn(db, it), |
1181 | ValueTyDefId::StructId(it) => type_for_struct_constructor(db, it), | 1183 | ValueTyDefId::StructId(it) => type_for_struct_constructor(db, it), |
1184 | ValueTyDefId::UnionId(it) => type_for_adt(db, it.into()), | ||
1182 | ValueTyDefId::EnumVariantId(it) => type_for_enum_variant_constructor(db, it), | 1185 | ValueTyDefId::EnumVariantId(it) => type_for_enum_variant_constructor(db, it), |
1183 | ValueTyDefId::ConstId(it) => type_for_const(db, it), | 1186 | ValueTyDefId::ConstId(it) => type_for_const(db, it), |
1184 | ValueTyDefId::StaticId(it) => type_for_static(db, it), | 1187 | ValueTyDefId::StaticId(it) => type_for_static(db, it), |
diff --git a/crates/ra_hir_ty/src/tests/simple.rs b/crates/ra_hir_ty/src/tests/simple.rs index 3fd7d5cd4..5a7cf9455 100644 --- a/crates/ra_hir_ty/src/tests/simple.rs +++ b/crates/ra_hir_ty/src/tests/simple.rs | |||
@@ -334,16 +334,44 @@ fn infer_union() { | |||
334 | bar: f32, | 334 | bar: f32, |
335 | } | 335 | } |
336 | 336 | ||
337 | fn test() { | ||
338 | let u = MyUnion { foo: 0 }; | ||
339 | unsafe { baz(u); } | ||
340 | let u = MyUnion { bar: 0.0 }; | ||
341 | unsafe { baz(u); } | ||
342 | } | ||
343 | |||
337 | unsafe fn baz(u: MyUnion) { | 344 | unsafe fn baz(u: MyUnion) { |
338 | let inner = u.foo; | 345 | let inner = u.foo; |
346 | let inner = u.bar; | ||
339 | } | 347 | } |
340 | "#, | 348 | "#, |
341 | expect![[r#" | 349 | expect![[r#" |
342 | 61..62 'u': MyUnion | 350 | 57..172 '{ ...); } }': () |
343 | 73..99 '{ ...foo; }': () | 351 | 67..68 'u': MyUnion |
344 | 83..88 'inner': u32 | 352 | 71..89 'MyUnio...o: 0 }': MyUnion |
345 | 91..92 'u': MyUnion | 353 | 86..87 '0': u32 |
346 | 91..96 'u.foo': u32 | 354 | 95..113 'unsafe...(u); }': () |
355 | 102..113 '{ baz(u); }': () | ||
356 | 104..107 'baz': fn baz(MyUnion) | ||
357 | 104..110 'baz(u)': () | ||
358 | 108..109 'u': MyUnion | ||
359 | 122..123 'u': MyUnion | ||
360 | 126..146 'MyUnio... 0.0 }': MyUnion | ||
361 | 141..144 '0.0': f32 | ||
362 | 152..170 'unsafe...(u); }': () | ||
363 | 159..170 '{ baz(u); }': () | ||
364 | 161..164 'baz': fn baz(MyUnion) | ||
365 | 161..167 'baz(u)': () | ||
366 | 165..166 'u': MyUnion | ||
367 | 188..189 'u': MyUnion | ||
368 | 200..249 '{ ...bar; }': () | ||
369 | 210..215 'inner': u32 | ||
370 | 218..219 'u': MyUnion | ||
371 | 218..223 'u.foo': u32 | ||
372 | 233..238 'inner': f32 | ||
373 | 241..242 'u': MyUnion | ||
374 | 241..246 'u.bar': f32 | ||
347 | "#]], | 375 | "#]], |
348 | ); | 376 | ); |
349 | } | 377 | } |
diff --git a/crates/ra_ide/src/diagnostics.rs b/crates/ra_ide/src/diagnostics.rs index dd8a7ffd9..73c0b8275 100644 --- a/crates/ra_ide/src/diagnostics.rs +++ b/crates/ra_ide/src/diagnostics.rs | |||
@@ -78,8 +78,10 @@ pub(crate) fn diagnostics( | |||
78 | } else { | 78 | } else { |
79 | let mut field_list = d.ast(db); | 79 | let mut field_list = d.ast(db); |
80 | for f in d.missed_fields.iter() { | 80 | for f in d.missed_fields.iter() { |
81 | let field = | 81 | let field = make::record_expr_field( |
82 | make::record_field(make::name_ref(&f.to_string()), Some(make::expr_unit())); | 82 | make::name_ref(&f.to_string()), |
83 | Some(make::expr_unit()), | ||
84 | ); | ||
83 | field_list = field_list.append_field(&field); | 85 | field_list = field_list.append_field(&field); |
84 | } | 86 | } |
85 | 87 | ||
@@ -178,9 +180,9 @@ fn missing_struct_field_fix( | |||
178 | if new_field_type.is_unknown() { | 180 | if new_field_type.is_unknown() { |
179 | return None; | 181 | return None; |
180 | } | 182 | } |
181 | let new_field = make::record_field_def( | 183 | let new_field = make::record_field( |
182 | record_expr.field_name()?, | 184 | record_expr.field_name()?, |
183 | make::type_ref(&new_field_type.display_source_code(sema.db, module.into()).ok()?), | 185 | make::ty(&new_field_type.display_source_code(sema.db, module.into()).ok()?), |
184 | ); | 186 | ); |
185 | 187 | ||
186 | let last_field = record_fields.fields().last()?; | 188 | let last_field = record_fields.fields().last()?; |
diff --git a/crates/ra_ide/src/folding_ranges.rs b/crates/ra_ide/src/folding_ranges.rs index 903c34996..0fbc9babd 100644 --- a/crates/ra_ide/src/folding_ranges.rs +++ b/crates/ra_ide/src/folding_ranges.rs | |||
@@ -85,7 +85,8 @@ fn fold_kind(kind: SyntaxKind) -> Option<FoldKind> { | |||
85 | COMMENT => Some(FoldKind::Comment), | 85 | COMMENT => Some(FoldKind::Comment), |
86 | USE => Some(FoldKind::Imports), | 86 | USE => Some(FoldKind::Imports), |
87 | ARG_LIST | PARAM_LIST => Some(FoldKind::ArgList), | 87 | ARG_LIST | PARAM_LIST => Some(FoldKind::ArgList), |
88 | RECORD_FIELD_LIST | 88 | ASSOC_ITEM_LIST |
89 | | RECORD_FIELD_LIST | ||
89 | | RECORD_PAT_FIELD_LIST | 90 | | RECORD_PAT_FIELD_LIST |
90 | | RECORD_EXPR_FIELD_LIST | 91 | | RECORD_EXPR_FIELD_LIST |
91 | | ITEM_LIST | 92 | | ITEM_LIST |
@@ -337,6 +338,26 @@ fn main() <fold block>{ | |||
337 | } | 338 | } |
338 | 339 | ||
339 | #[test] | 340 | #[test] |
341 | fn test_folds_structs() { | ||
342 | check( | ||
343 | r#" | ||
344 | struct Foo <fold block>{ | ||
345 | }</fold> | ||
346 | "#, | ||
347 | ); | ||
348 | } | ||
349 | |||
350 | #[test] | ||
351 | fn test_folds_traits() { | ||
352 | check( | ||
353 | r#" | ||
354 | trait Foo <fold block>{ | ||
355 | }</fold> | ||
356 | "#, | ||
357 | ); | ||
358 | } | ||
359 | |||
360 | #[test] | ||
340 | fn test_folds_macros() { | 361 | fn test_folds_macros() { |
341 | check( | 362 | check( |
342 | r#" | 363 | r#" |
diff --git a/crates/ra_ide/src/hover.rs b/crates/ra_ide/src/hover.rs index aa48cb412..385e3e64e 100644 --- a/crates/ra_ide/src/hover.rs +++ b/crates/ra_ide/src/hover.rs | |||
@@ -509,6 +509,37 @@ fn main() { } | |||
509 | } | 509 | } |
510 | 510 | ||
511 | #[test] | 511 | #[test] |
512 | fn hover_shows_fn_doc() { | ||
513 | check( | ||
514 | r#" | ||
515 | /// # Example | ||
516 | /// ``` | ||
517 | /// # use std::path::Path; | ||
518 | /// # | ||
519 | /// foo(Path::new("hello, world!")) | ||
520 | /// ``` | ||
521 | pub fn foo<|>(_: &Path) {} | ||
522 | |||
523 | fn main() { } | ||
524 | "#, | ||
525 | expect![[r#" | ||
526 | *foo* | ||
527 | ```rust | ||
528 | pub fn foo(_: &Path) | ||
529 | ``` | ||
530 | ___ | ||
531 | |||
532 | # Example | ||
533 | ``` | ||
534 | # use std::path::Path; | ||
535 | # | ||
536 | foo(Path::new("hello, world!")) | ||
537 | ``` | ||
538 | "#]], | ||
539 | ); | ||
540 | } | ||
541 | |||
542 | #[test] | ||
512 | fn hover_shows_struct_field_info() { | 543 | fn hover_shows_struct_field_info() { |
513 | // Hovering over the field when instantiating | 544 | // Hovering over the field when instantiating |
514 | check( | 545 | check( |
diff --git a/crates/ra_ide/src/syntax_highlighting.rs b/crates/ra_ide/src/syntax_highlighting.rs index a32ae0165..f71b804fe 100644 --- a/crates/ra_ide/src/syntax_highlighting.rs +++ b/crates/ra_ide/src/syntax_highlighting.rs | |||
@@ -4,7 +4,7 @@ mod injection; | |||
4 | #[cfg(test)] | 4 | #[cfg(test)] |
5 | mod tests; | 5 | mod tests; |
6 | 6 | ||
7 | use hir::{Name, Semantics}; | 7 | use hir::{Name, Semantics, VariantDef}; |
8 | use ra_ide_db::{ | 8 | use ra_ide_db::{ |
9 | defs::{classify_name, classify_name_ref, Definition, NameClass, NameRefClass}, | 9 | defs::{classify_name, classify_name_ref, Definition, NameClass, NameRefClass}, |
10 | RootDatabase, | 10 | RootDatabase, |
@@ -455,6 +455,18 @@ fn macro_call_range(macro_call: &ast::MacroCall) -> Option<TextRange> { | |||
455 | Some(TextRange::new(range_start, range_end)) | 455 | Some(TextRange::new(range_start, range_end)) |
456 | } | 456 | } |
457 | 457 | ||
458 | fn is_possibly_unsafe(name_ref: &ast::NameRef) -> bool { | ||
459 | name_ref | ||
460 | .syntax() | ||
461 | .parent() | ||
462 | .and_then(|parent| { | ||
463 | ast::FieldExpr::cast(parent.clone()) | ||
464 | .map(|_| true) | ||
465 | .or_else(|| ast::RecordPatField::cast(parent).map(|_| true)) | ||
466 | }) | ||
467 | .unwrap_or(false) | ||
468 | } | ||
469 | |||
458 | fn highlight_element( | 470 | fn highlight_element( |
459 | sema: &Semantics<RootDatabase>, | 471 | sema: &Semantics<RootDatabase>, |
460 | bindings_shadow_count: &mut FxHashMap<Name, u32>, | 472 | bindings_shadow_count: &mut FxHashMap<Name, u32>, |
@@ -484,10 +496,19 @@ fn highlight_element( | |||
484 | 496 | ||
485 | match name_kind { | 497 | match name_kind { |
486 | Some(NameClass::Definition(def)) => { | 498 | Some(NameClass::Definition(def)) => { |
487 | highlight_name(db, def) | HighlightModifier::Definition | 499 | highlight_name(db, def, false) | HighlightModifier::Definition |
500 | } | ||
501 | Some(NameClass::ConstReference(def)) => highlight_name(db, def, false), | ||
502 | Some(NameClass::FieldShorthand { field, .. }) => { | ||
503 | let mut h = HighlightTag::Field.into(); | ||
504 | if let Definition::Field(field) = field { | ||
505 | if let VariantDef::Union(_) = field.parent_def(db) { | ||
506 | h |= HighlightModifier::Unsafe; | ||
507 | } | ||
508 | } | ||
509 | |||
510 | h | ||
488 | } | 511 | } |
489 | Some(NameClass::ConstReference(def)) => highlight_name(db, def), | ||
490 | Some(NameClass::FieldShorthand { .. }) => HighlightTag::Field.into(), | ||
491 | None => highlight_name_by_syntax(name) | HighlightModifier::Definition, | 512 | None => highlight_name_by_syntax(name) | HighlightModifier::Definition, |
492 | } | 513 | } |
493 | } | 514 | } |
@@ -498,6 +519,7 @@ fn highlight_element( | |||
498 | } | 519 | } |
499 | NAME_REF => { | 520 | NAME_REF => { |
500 | let name_ref = element.into_node().and_then(ast::NameRef::cast).unwrap(); | 521 | let name_ref = element.into_node().and_then(ast::NameRef::cast).unwrap(); |
522 | let possibly_unsafe = is_possibly_unsafe(&name_ref); | ||
501 | match classify_name_ref(sema, &name_ref) { | 523 | match classify_name_ref(sema, &name_ref) { |
502 | Some(name_kind) => match name_kind { | 524 | Some(name_kind) => match name_kind { |
503 | NameRefClass::Definition(def) => { | 525 | NameRefClass::Definition(def) => { |
@@ -508,11 +530,13 @@ fn highlight_element( | |||
508 | binding_hash = Some(calc_binding_hash(&name, *shadow_count)) | 530 | binding_hash = Some(calc_binding_hash(&name, *shadow_count)) |
509 | } | 531 | } |
510 | }; | 532 | }; |
511 | highlight_name(db, def) | 533 | highlight_name(db, def, possibly_unsafe) |
512 | } | 534 | } |
513 | NameRefClass::FieldShorthand { .. } => HighlightTag::Field.into(), | 535 | NameRefClass::FieldShorthand { .. } => HighlightTag::Field.into(), |
514 | }, | 536 | }, |
515 | None if syntactic_name_ref_highlighting => highlight_name_ref_by_syntax(name_ref), | 537 | None if syntactic_name_ref_highlighting => { |
538 | highlight_name_ref_by_syntax(name_ref, sema) | ||
539 | } | ||
516 | None => HighlightTag::UnresolvedReference.into(), | 540 | None => HighlightTag::UnresolvedReference.into(), |
517 | } | 541 | } |
518 | } | 542 | } |
@@ -652,10 +676,19 @@ fn is_child_of_impl(element: &SyntaxElement) -> bool { | |||
652 | } | 676 | } |
653 | } | 677 | } |
654 | 678 | ||
655 | fn highlight_name(db: &RootDatabase, def: Definition) -> Highlight { | 679 | fn highlight_name(db: &RootDatabase, def: Definition, possibly_unsafe: bool) -> Highlight { |
656 | match def { | 680 | match def { |
657 | Definition::Macro(_) => HighlightTag::Macro, | 681 | Definition::Macro(_) => HighlightTag::Macro, |
658 | Definition::Field(_) => HighlightTag::Field, | 682 | Definition::Field(field) => { |
683 | let mut h = HighlightTag::Field.into(); | ||
684 | if possibly_unsafe { | ||
685 | if let VariantDef::Union(_) = field.parent_def(db) { | ||
686 | h |= HighlightModifier::Unsafe; | ||
687 | } | ||
688 | } | ||
689 | |||
690 | return h; | ||
691 | } | ||
659 | Definition::ModuleDef(def) => match def { | 692 | Definition::ModuleDef(def) => match def { |
660 | hir::ModuleDef::Module(_) => HighlightTag::Module, | 693 | hir::ModuleDef::Module(_) => HighlightTag::Module, |
661 | hir::ModuleDef::Function(func) => { | 694 | hir::ModuleDef::Function(func) => { |
@@ -677,6 +710,7 @@ fn highlight_name(db: &RootDatabase, def: Definition) -> Highlight { | |||
677 | let mut h = Highlight::new(HighlightTag::Static); | 710 | let mut h = Highlight::new(HighlightTag::Static); |
678 | if s.is_mut(db) { | 711 | if s.is_mut(db) { |
679 | h |= HighlightModifier::Mutable; | 712 | h |= HighlightModifier::Mutable; |
713 | h |= HighlightModifier::Unsafe; | ||
680 | } | 714 | } |
681 | return h; | 715 | return h; |
682 | } | 716 | } |
@@ -724,7 +758,7 @@ fn highlight_name_by_syntax(name: ast::Name) -> Highlight { | |||
724 | tag.into() | 758 | tag.into() |
725 | } | 759 | } |
726 | 760 | ||
727 | fn highlight_name_ref_by_syntax(name: ast::NameRef) -> Highlight { | 761 | fn highlight_name_ref_by_syntax(name: ast::NameRef, sema: &Semantics<RootDatabase>) -> Highlight { |
728 | let default = HighlightTag::UnresolvedReference; | 762 | let default = HighlightTag::UnresolvedReference; |
729 | 763 | ||
730 | let parent = match name.syntax().parent() { | 764 | let parent = match name.syntax().parent() { |
@@ -734,7 +768,20 @@ fn highlight_name_ref_by_syntax(name: ast::NameRef) -> Highlight { | |||
734 | 768 | ||
735 | let tag = match parent.kind() { | 769 | let tag = match parent.kind() { |
736 | METHOD_CALL_EXPR => HighlightTag::Function, | 770 | METHOD_CALL_EXPR => HighlightTag::Function, |
737 | FIELD_EXPR => HighlightTag::Field, | 771 | FIELD_EXPR => { |
772 | let h = HighlightTag::Field; | ||
773 | let is_union = ast::FieldExpr::cast(parent) | ||
774 | .and_then(|field_expr| { | ||
775 | let field = sema.resolve_field(&field_expr)?; | ||
776 | Some(if let VariantDef::Union(_) = field.parent_def(sema.db) { | ||
777 | true | ||
778 | } else { | ||
779 | false | ||
780 | }) | ||
781 | }) | ||
782 | .unwrap_or(false); | ||
783 | return if is_union { h | HighlightModifier::Unsafe } else { h.into() }; | ||
784 | } | ||
738 | PATH_SEGMENT => { | 785 | PATH_SEGMENT => { |
739 | let path = match parent.parent().and_then(ast::Path::cast) { | 786 | let path = match parent.parent().and_then(ast::Path::cast) { |
740 | Some(it) => it, | 787 | Some(it) => it, |
diff --git a/crates/ra_ide/src/syntax_highlighting/tests.rs b/crates/ra_ide/src/syntax_highlighting/tests.rs index 87a6e2523..730efff0d 100644 --- a/crates/ra_ide/src/syntax_highlighting/tests.rs +++ b/crates/ra_ide/src/syntax_highlighting/tests.rs | |||
@@ -9,6 +9,9 @@ use crate::{mock_analysis::single_file, FileRange, TextRange}; | |||
9 | fn test_highlighting() { | 9 | fn test_highlighting() { |
10 | check_highlighting( | 10 | check_highlighting( |
11 | r#" | 11 | r#" |
12 | use inner::{self as inner_mod}; | ||
13 | mod inner {} | ||
14 | |||
12 | #[derive(Clone, Debug)] | 15 | #[derive(Clone, Debug)] |
13 | struct Foo { | 16 | struct Foo { |
14 | pub x: i32, | 17 | pub x: i32, |
@@ -272,19 +275,37 @@ fn test_unsafe_highlighting() { | |||
272 | r#" | 275 | r#" |
273 | unsafe fn unsafe_fn() {} | 276 | unsafe fn unsafe_fn() {} |
274 | 277 | ||
278 | union Union { | ||
279 | a: u32, | ||
280 | b: f32, | ||
281 | } | ||
282 | |||
275 | struct HasUnsafeFn; | 283 | struct HasUnsafeFn; |
276 | 284 | ||
277 | impl HasUnsafeFn { | 285 | impl HasUnsafeFn { |
278 | unsafe fn unsafe_method(&self) {} | 286 | unsafe fn unsafe_method(&self) {} |
279 | } | 287 | } |
280 | 288 | ||
289 | struct TypeForStaticMut { | ||
290 | a: u8 | ||
291 | } | ||
292 | |||
293 | static mut global_mut: TypeForStaticMut = TypeForStaticMut { a: 0 }; | ||
294 | |||
281 | fn main() { | 295 | fn main() { |
282 | let x = &5 as *const usize; | 296 | let x = &5 as *const usize; |
297 | let u = Union { b: 0 }; | ||
283 | unsafe { | 298 | unsafe { |
284 | unsafe_fn(); | 299 | unsafe_fn(); |
300 | let b = u.b; | ||
301 | match u { | ||
302 | Union { b: 0 } => (), | ||
303 | Union { a } => (), | ||
304 | } | ||
285 | HasUnsafeFn.unsafe_method(); | 305 | HasUnsafeFn.unsafe_method(); |
286 | let y = *(x); | 306 | let y = *(x); |
287 | let z = -x; | 307 | let z = -x; |
308 | let a = global_mut.a; | ||
288 | } | 309 | } |
289 | } | 310 | } |
290 | "# | 311 | "# |
diff --git a/crates/ra_ide/test_data/highlight_unsafe.html b/crates/ra_ide/test_data/highlight_unsafe.html index b81b6f1c3..79409fe81 100644 --- a/crates/ra_ide/test_data/highlight_unsafe.html +++ b/crates/ra_ide/test_data/highlight_unsafe.html | |||
@@ -37,18 +37,36 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd | |||
37 | </style> | 37 | </style> |
38 | <pre><code><span class="keyword unsafe">unsafe</span> <span class="keyword">fn</span> <span class="function declaration unsafe">unsafe_fn</span><span class="punctuation">(</span><span class="punctuation">)</span> <span class="punctuation">{</span><span class="punctuation">}</span> | 38 | <pre><code><span class="keyword unsafe">unsafe</span> <span class="keyword">fn</span> <span class="function declaration unsafe">unsafe_fn</span><span class="punctuation">(</span><span class="punctuation">)</span> <span class="punctuation">{</span><span class="punctuation">}</span> |
39 | 39 | ||
40 | <span class="keyword">union</span> <span class="union declaration">Union</span> <span class="punctuation">{</span> | ||
41 | <span class="field declaration">a</span><span class="punctuation">:</span> <span class="builtin_type">u32</span><span class="punctuation">,</span> | ||
42 | <span class="field declaration">b</span><span class="punctuation">:</span> <span class="builtin_type">f32</span><span class="punctuation">,</span> | ||
43 | <span class="punctuation">}</span> | ||
44 | |||
40 | <span class="keyword">struct</span> <span class="struct declaration">HasUnsafeFn</span><span class="punctuation">;</span> | 45 | <span class="keyword">struct</span> <span class="struct declaration">HasUnsafeFn</span><span class="punctuation">;</span> |
41 | 46 | ||
42 | <span class="keyword">impl</span> <span class="struct">HasUnsafeFn</span> <span class="punctuation">{</span> | 47 | <span class="keyword">impl</span> <span class="struct">HasUnsafeFn</span> <span class="punctuation">{</span> |
43 | <span class="keyword unsafe">unsafe</span> <span class="keyword">fn</span> <span class="function declaration unsafe">unsafe_method</span><span class="punctuation">(</span><span class="operator">&</span><span class="self_keyword">self</span><span class="punctuation">)</span> <span class="punctuation">{</span><span class="punctuation">}</span> | 48 | <span class="keyword unsafe">unsafe</span> <span class="keyword">fn</span> <span class="function declaration unsafe">unsafe_method</span><span class="punctuation">(</span><span class="operator">&</span><span class="self_keyword">self</span><span class="punctuation">)</span> <span class="punctuation">{</span><span class="punctuation">}</span> |
44 | <span class="punctuation">}</span> | 49 | <span class="punctuation">}</span> |
45 | 50 | ||
51 | <span class="keyword">struct</span> <span class="struct declaration">TypeForStaticMut</span> <span class="punctuation">{</span> | ||
52 | <span class="field declaration">a</span><span class="punctuation">:</span> <span class="builtin_type">u8</span> | ||
53 | <span class="punctuation">}</span> | ||
54 | |||
55 | <span class="keyword">static</span> <span class="keyword">mut</span> <span class="static declaration mutable unsafe">global_mut</span><span class="punctuation">:</span> <span class="struct">TypeForStaticMut</span> <span class="operator">=</span> <span class="struct">TypeForStaticMut</span> <span class="punctuation">{</span> <span class="field">a</span><span class="punctuation">:</span> <span class="numeric_literal">0</span> <span class="punctuation">}</span><span class="punctuation">;</span> | ||
56 | |||
46 | <span class="keyword">fn</span> <span class="function declaration">main</span><span class="punctuation">(</span><span class="punctuation">)</span> <span class="punctuation">{</span> | 57 | <span class="keyword">fn</span> <span class="function declaration">main</span><span class="punctuation">(</span><span class="punctuation">)</span> <span class="punctuation">{</span> |
47 | <span class="keyword">let</span> <span class="variable declaration">x</span> <span class="operator">=</span> <span class="operator">&</span><span class="numeric_literal">5</span> <span class="keyword">as</span> <span class="keyword">*</span><span class="keyword">const</span> <span class="builtin_type">usize</span><span class="punctuation">;</span> | 58 | <span class="keyword">let</span> <span class="variable declaration">x</span> <span class="operator">=</span> <span class="operator">&</span><span class="numeric_literal">5</span> <span class="keyword">as</span> <span class="keyword">*</span><span class="keyword">const</span> <span class="builtin_type">usize</span><span class="punctuation">;</span> |
59 | <span class="keyword">let</span> <span class="variable declaration">u</span> <span class="operator">=</span> <span class="union">Union</span> <span class="punctuation">{</span> <span class="field">b</span><span class="punctuation">:</span> <span class="numeric_literal">0</span> <span class="punctuation">}</span><span class="punctuation">;</span> | ||
48 | <span class="keyword unsafe">unsafe</span> <span class="punctuation">{</span> | 60 | <span class="keyword unsafe">unsafe</span> <span class="punctuation">{</span> |
49 | <span class="function unsafe">unsafe_fn</span><span class="punctuation">(</span><span class="punctuation">)</span><span class="punctuation">;</span> | 61 | <span class="function unsafe">unsafe_fn</span><span class="punctuation">(</span><span class="punctuation">)</span><span class="punctuation">;</span> |
62 | <span class="keyword">let</span> <span class="variable declaration">b</span> <span class="operator">=</span> <span class="variable">u</span><span class="punctuation">.</span><span class="field unsafe">b</span><span class="punctuation">;</span> | ||
63 | <span class="keyword control">match</span> <span class="variable">u</span> <span class="punctuation">{</span> | ||
64 | <span class="union">Union</span> <span class="punctuation">{</span> <span class="field unsafe">b</span><span class="punctuation">:</span> <span class="numeric_literal">0</span> <span class="punctuation">}</span> <span class="operator">=></span> <span class="punctuation">(</span><span class="punctuation">)</span><span class="punctuation">,</span> | ||
65 | <span class="union">Union</span> <span class="punctuation">{</span> <span class="field unsafe">a</span> <span class="punctuation">}</span> <span class="operator">=></span> <span class="punctuation">(</span><span class="punctuation">)</span><span class="punctuation">,</span> | ||
66 | <span class="punctuation">}</span> | ||
50 | <span class="struct">HasUnsafeFn</span><span class="punctuation">.</span><span class="function unsafe">unsafe_method</span><span class="punctuation">(</span><span class="punctuation">)</span><span class="punctuation">;</span> | 67 | <span class="struct">HasUnsafeFn</span><span class="punctuation">.</span><span class="function unsafe">unsafe_method</span><span class="punctuation">(</span><span class="punctuation">)</span><span class="punctuation">;</span> |
51 | <span class="keyword">let</span> <span class="variable declaration">y</span> <span class="operator">=</span> <span class="operator unsafe">*</span><span class="punctuation">(</span><span class="variable">x</span><span class="punctuation">)</span><span class="punctuation">;</span> | 68 | <span class="keyword">let</span> <span class="variable declaration">y</span> <span class="operator">=</span> <span class="operator unsafe">*</span><span class="punctuation">(</span><span class="variable">x</span><span class="punctuation">)</span><span class="punctuation">;</span> |
52 | <span class="keyword">let</span> <span class="variable declaration">z</span> <span class="operator">=</span> <span class="numeric_literal">-</span><span class="variable">x</span><span class="punctuation">;</span> | 69 | <span class="keyword">let</span> <span class="variable declaration">z</span> <span class="operator">=</span> <span class="numeric_literal">-</span><span class="variable">x</span><span class="punctuation">;</span> |
70 | <span class="keyword">let</span> <span class="variable declaration">a</span> <span class="operator">=</span> <span class="static mutable unsafe">global_mut</span><span class="punctuation">.</span><span class="field">a</span><span class="punctuation">;</span> | ||
53 | <span class="punctuation">}</span> | 71 | <span class="punctuation">}</span> |
54 | <span class="punctuation">}</span></code></pre> \ No newline at end of file | 72 | <span class="punctuation">}</span></code></pre> \ No newline at end of file |
diff --git a/crates/ra_ide/test_data/highlighting.html b/crates/ra_ide/test_data/highlighting.html index 345a2f023..8e0160eee 100644 --- a/crates/ra_ide/test_data/highlighting.html +++ b/crates/ra_ide/test_data/highlighting.html | |||
@@ -35,7 +35,10 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd | |||
35 | 35 | ||
36 | .unresolved_reference { color: #FC5555; text-decoration: wavy underline; } | 36 | .unresolved_reference { color: #FC5555; text-decoration: wavy underline; } |
37 | </style> | 37 | </style> |
38 | <pre><code><span class="attribute">#</span><span class="attribute">[</span><span class="function attribute">derive</span><span class="punctuation">(</span><span class="attribute">Clone</span><span class="punctuation">,</span><span class="attribute"> Debug</span><span class="punctuation">)</span><span class="attribute">]</span> | 38 | <pre><code><span class="keyword">use</span> <span class="module">inner</span><span class="operator">::</span><span class="punctuation">{</span><span class="self_keyword">self</span> <span class="keyword">as</span> <span class="module declaration">inner_mod</span><span class="punctuation">}</span><span class="punctuation">;</span> |
39 | <span class="keyword">mod</span> <span class="module declaration">inner</span> <span class="punctuation">{</span><span class="punctuation">}</span> | ||
40 | |||
41 | <span class="attribute">#</span><span class="attribute">[</span><span class="function attribute">derive</span><span class="punctuation">(</span><span class="attribute">Clone</span><span class="punctuation">,</span><span class="attribute"> Debug</span><span class="punctuation">)</span><span class="attribute">]</span> | ||
39 | <span class="keyword">struct</span> <span class="struct declaration">Foo</span> <span class="punctuation">{</span> | 42 | <span class="keyword">struct</span> <span class="struct declaration">Foo</span> <span class="punctuation">{</span> |
40 | <span class="keyword">pub</span> <span class="field declaration">x</span><span class="punctuation">:</span> <span class="builtin_type">i32</span><span class="punctuation">,</span> | 43 | <span class="keyword">pub</span> <span class="field declaration">x</span><span class="punctuation">:</span> <span class="builtin_type">i32</span><span class="punctuation">,</span> |
41 | <span class="keyword">pub</span> <span class="field declaration">y</span><span class="punctuation">:</span> <span class="builtin_type">i32</span><span class="punctuation">,</span> | 44 | <span class="keyword">pub</span> <span class="field declaration">y</span><span class="punctuation">:</span> <span class="builtin_type">i32</span><span class="punctuation">,</span> |
@@ -61,7 +64,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd | |||
61 | <span class="punctuation">}</span> | 64 | <span class="punctuation">}</span> |
62 | <span class="punctuation">}</span> | 65 | <span class="punctuation">}</span> |
63 | 66 | ||
64 | <span class="keyword">static</span> <span class="keyword">mut</span> <span class="static declaration mutable">STATIC_MUT</span><span class="punctuation">:</span> <span class="builtin_type">i32</span> <span class="operator">=</span> <span class="numeric_literal">0</span><span class="punctuation">;</span> | 67 | <span class="keyword">static</span> <span class="keyword">mut</span> <span class="static declaration mutable unsafe">STATIC_MUT</span><span class="punctuation">:</span> <span class="builtin_type">i32</span> <span class="operator">=</span> <span class="numeric_literal">0</span><span class="punctuation">;</span> |
65 | 68 | ||
66 | <span class="keyword">fn</span> <span class="function declaration">foo</span><span class="punctuation"><</span><span class="lifetime declaration">'a</span><span class="punctuation">,</span> <span class="type_param declaration">T</span><span class="punctuation">></span><span class="punctuation">(</span><span class="punctuation">)</span> <span class="operator">-></span> <span class="type_param">T</span> <span class="punctuation">{</span> | 69 | <span class="keyword">fn</span> <span class="function declaration">foo</span><span class="punctuation"><</span><span class="lifetime declaration">'a</span><span class="punctuation">,</span> <span class="type_param declaration">T</span><span class="punctuation">></span><span class="punctuation">(</span><span class="punctuation">)</span> <span class="operator">-></span> <span class="type_param">T</span> <span class="punctuation">{</span> |
67 | <span class="function">foo</span><span class="operator">::</span><span class="punctuation"><</span><span class="lifetime">'a</span><span class="punctuation">,</span> <span class="builtin_type">i32</span><span class="punctuation">></span><span class="punctuation">(</span><span class="punctuation">)</span> | 70 | <span class="function">foo</span><span class="operator">::</span><span class="punctuation"><</span><span class="lifetime">'a</span><span class="punctuation">,</span> <span class="builtin_type">i32</span><span class="punctuation">></span><span class="punctuation">(</span><span class="punctuation">)</span> |
@@ -94,7 +97,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd | |||
94 | <span class="punctuation">}</span> | 97 | <span class="punctuation">}</span> |
95 | <span class="keyword unsafe">unsafe</span> <span class="punctuation">{</span> | 98 | <span class="keyword unsafe">unsafe</span> <span class="punctuation">{</span> |
96 | <span class="variable mutable">vec</span><span class="punctuation">.</span><span class="unresolved_reference">set_len</span><span class="punctuation">(</span><span class="numeric_literal">0</span><span class="punctuation">)</span><span class="punctuation">;</span> | 99 | <span class="variable mutable">vec</span><span class="punctuation">.</span><span class="unresolved_reference">set_len</span><span class="punctuation">(</span><span class="numeric_literal">0</span><span class="punctuation">)</span><span class="punctuation">;</span> |
97 | <span class="static mutable">STATIC_MUT</span> <span class="operator">=</span> <span class="numeric_literal">1</span><span class="punctuation">;</span> | 100 | <span class="static mutable unsafe">STATIC_MUT</span> <span class="operator">=</span> <span class="numeric_literal">1</span><span class="punctuation">;</span> |
98 | <span class="punctuation">}</span> | 101 | <span class="punctuation">}</span> |
99 | 102 | ||
100 | <span class="keyword control">for</span> <span class="variable declaration">e</span> <span class="keyword control">in</span> <span class="variable mutable">vec</span> <span class="punctuation">{</span> | 103 | <span class="keyword control">for</span> <span class="variable declaration">e</span> <span class="keyword control">in</span> <span class="variable mutable">vec</span> <span class="punctuation">{</span> |
diff --git a/crates/ra_ide_db/src/change.rs b/crates/ra_ide_db/src/change.rs index 32d9a8d1f..b13df8b85 100644 --- a/crates/ra_ide_db/src/change.rs +++ b/crates/ra_ide_db/src/change.rs | |||
@@ -190,11 +190,24 @@ impl RootDatabase { | |||
190 | let q: $q = Default::default(); | 190 | let q: $q = Default::default(); |
191 | let name = format!("{:?} (deps)", q); | 191 | let name = format!("{:?} (deps)", q); |
192 | acc.push((name, before - after)); | 192 | acc.push((name, before - after)); |
193 | |||
194 | let before = memory_usage().allocated; | ||
195 | $q.in_db(self).purge(); | ||
196 | let after = memory_usage().allocated; | ||
197 | let q: $q = Default::default(); | ||
198 | let name = format!("{:?} (purge)", q); | ||
199 | acc.push((name, before - after)); | ||
193 | )*} | 200 | )*} |
194 | } | 201 | } |
195 | sweep_each_query![ | 202 | sweep_each_query![ |
196 | // SourceDatabase | 203 | // SourceDatabase |
197 | ra_db::ParseQuery | 204 | ra_db::ParseQuery |
205 | ra_db::CrateGraphQuery | ||
206 | |||
207 | // SourceDatabaseExt | ||
208 | ra_db::FileTextQuery | ||
209 | ra_db::FileSourceRootQuery | ||
210 | ra_db::SourceRootQuery | ||
198 | ra_db::SourceRootCratesQuery | 211 | ra_db::SourceRootCratesQuery |
199 | 212 | ||
200 | // AstDatabase | 213 | // AstDatabase |
@@ -242,15 +255,24 @@ impl RootDatabase { | |||
242 | hir::db::TraitImplsInCrateQuery | 255 | hir::db::TraitImplsInCrateQuery |
243 | hir::db::TraitImplsInDepsQuery | 256 | hir::db::TraitImplsInDepsQuery |
244 | hir::db::AssociatedTyDataQuery | 257 | hir::db::AssociatedTyDataQuery |
258 | hir::db::AssociatedTyDataQuery | ||
245 | hir::db::TraitDatumQuery | 259 | hir::db::TraitDatumQuery |
246 | hir::db::StructDatumQuery | 260 | hir::db::StructDatumQuery |
247 | hir::db::ImplDatumQuery | 261 | hir::db::ImplDatumQuery |
262 | hir::db::FnDefDatumQuery | ||
263 | hir::db::ReturnTypeImplTraitsQuery | ||
264 | hir::db::InternCallableDefQuery | ||
265 | hir::db::InternTypeParamIdQuery | ||
266 | hir::db::InternImplTraitIdQuery | ||
267 | hir::db::InternClosureQuery | ||
248 | hir::db::AssociatedTyValueQuery | 268 | hir::db::AssociatedTyValueQuery |
249 | hir::db::TraitSolveQuery | 269 | hir::db::TraitSolveQuery |
250 | hir::db::ReturnTypeImplTraitsQuery | ||
251 | 270 | ||
252 | // SymbolsDatabase | 271 | // SymbolsDatabase |
253 | crate::symbol_index::FileSymbolsQuery | 272 | crate::symbol_index::FileSymbolsQuery |
273 | crate::symbol_index::LibrarySymbolsQuery | ||
274 | crate::symbol_index::LocalRootsQuery | ||
275 | crate::symbol_index::LibraryRootsQuery | ||
254 | 276 | ||
255 | // LineIndexDatabase | 277 | // LineIndexDatabase |
256 | crate::LineIndexQuery | 278 | crate::LineIndexQuery |
diff --git a/crates/ra_ide_db/src/defs.rs b/crates/ra_ide_db/src/defs.rs index 66c048714..b51000b03 100644 --- a/crates/ra_ide_db/src/defs.rs +++ b/crates/ra_ide_db/src/defs.rs | |||
@@ -12,7 +12,7 @@ use hir::{ | |||
12 | use ra_prof::profile; | 12 | use ra_prof::profile; |
13 | use ra_syntax::{ | 13 | use ra_syntax::{ |
14 | ast::{self, AstNode}, | 14 | ast::{self, AstNode}, |
15 | match_ast, | 15 | match_ast, SyntaxNode, |
16 | }; | 16 | }; |
17 | 17 | ||
18 | use crate::RootDatabase; | 18 | use crate::RootDatabase; |
@@ -123,8 +123,27 @@ pub fn classify_name(sema: &Semantics<RootDatabase>, name: &ast::Name) -> Option | |||
123 | let use_tree = it.syntax().parent().and_then(ast::UseTree::cast)?; | 123 | let use_tree = it.syntax().parent().and_then(ast::UseTree::cast)?; |
124 | let path = use_tree.path()?; | 124 | let path = use_tree.path()?; |
125 | let path_segment = path.segment()?; | 125 | let path_segment = path.segment()?; |
126 | let name_ref = path_segment.name_ref()?; | 126 | let name_ref_class = path_segment |
127 | let name_ref_class = classify_name_ref(sema, &name_ref)?; | 127 | .name_ref() |
128 | // The rename might be from a `self` token, so fallback to the name higher | ||
129 | // in the use tree. | ||
130 | .or_else(||{ | ||
131 | if path_segment.self_token().is_none() { | ||
132 | return None; | ||
133 | } | ||
134 | |||
135 | let use_tree = use_tree | ||
136 | .syntax() | ||
137 | .parent() | ||
138 | .as_ref() | ||
139 | // Skip over UseTreeList | ||
140 | .and_then(SyntaxNode::parent) | ||
141 | .and_then(ast::UseTree::cast)?; | ||
142 | let path = use_tree.path()?; | ||
143 | let path_segment = path.segment()?; | ||
144 | path_segment.name_ref() | ||
145 | }) | ||
146 | .and_then(|name_ref| classify_name_ref(sema, &name_ref))?; | ||
128 | 147 | ||
129 | Some(NameClass::Definition(name_ref_class.definition())) | 148 | Some(NameClass::Definition(name_ref_class.definition())) |
130 | }, | 149 | }, |
diff --git a/crates/ra_ssr/src/lib.rs b/crates/ra_ssr/src/lib.rs index 73abfecb2..c780b460a 100644 --- a/crates/ra_ssr/src/lib.rs +++ b/crates/ra_ssr/src/lib.rs | |||
@@ -66,12 +66,7 @@ impl<'db> MatchFinder<'db> { | |||
66 | restrict_ranges.retain(|range| !range.range.is_empty()); | 66 | restrict_ranges.retain(|range| !range.range.is_empty()); |
67 | let sema = Semantics::new(db); | 67 | let sema = Semantics::new(db); |
68 | let resolution_scope = resolving::ResolutionScope::new(&sema, lookup_context); | 68 | let resolution_scope = resolving::ResolutionScope::new(&sema, lookup_context); |
69 | MatchFinder { | 69 | MatchFinder { sema, rules: Vec::new(), resolution_scope, restrict_ranges } |
70 | sema: Semantics::new(db), | ||
71 | rules: Vec::new(), | ||
72 | resolution_scope, | ||
73 | restrict_ranges, | ||
74 | } | ||
75 | } | 70 | } |
76 | 71 | ||
77 | /// Constructs an instance using the start of the first file in `db` as the lookup context. | 72 | /// Constructs an instance using the start of the first file in `db` as the lookup context. |
diff --git a/crates/ra_ssr/src/resolving.rs b/crates/ra_ssr/src/resolving.rs index 6f62000f4..df60048eb 100644 --- a/crates/ra_ssr/src/resolving.rs +++ b/crates/ra_ssr/src/resolving.rs | |||
@@ -11,6 +11,7 @@ use test_utils::mark; | |||
11 | pub(crate) struct ResolutionScope<'db> { | 11 | pub(crate) struct ResolutionScope<'db> { |
12 | scope: hir::SemanticsScope<'db>, | 12 | scope: hir::SemanticsScope<'db>, |
13 | hygiene: hir::Hygiene, | 13 | hygiene: hir::Hygiene, |
14 | node: SyntaxNode, | ||
14 | } | 15 | } |
15 | 16 | ||
16 | pub(crate) struct ResolvedRule { | 17 | pub(crate) struct ResolvedRule { |
@@ -25,6 +26,7 @@ pub(crate) struct ResolvedPattern { | |||
25 | // Paths in `node` that we've resolved. | 26 | // Paths in `node` that we've resolved. |
26 | pub(crate) resolved_paths: FxHashMap<SyntaxNode, ResolvedPath>, | 27 | pub(crate) resolved_paths: FxHashMap<SyntaxNode, ResolvedPath>, |
27 | pub(crate) ufcs_function_calls: FxHashMap<SyntaxNode, hir::Function>, | 28 | pub(crate) ufcs_function_calls: FxHashMap<SyntaxNode, hir::Function>, |
29 | pub(crate) contains_self: bool, | ||
28 | } | 30 | } |
29 | 31 | ||
30 | pub(crate) struct ResolvedPath { | 32 | pub(crate) struct ResolvedPath { |
@@ -68,6 +70,7 @@ struct Resolver<'a, 'db> { | |||
68 | 70 | ||
69 | impl Resolver<'_, '_> { | 71 | impl Resolver<'_, '_> { |
70 | fn resolve_pattern_tree(&self, pattern: SyntaxNode) -> Result<ResolvedPattern, SsrError> { | 72 | fn resolve_pattern_tree(&self, pattern: SyntaxNode) -> Result<ResolvedPattern, SsrError> { |
73 | use ra_syntax::{SyntaxElement, T}; | ||
71 | let mut resolved_paths = FxHashMap::default(); | 74 | let mut resolved_paths = FxHashMap::default(); |
72 | self.resolve(pattern.clone(), 0, &mut resolved_paths)?; | 75 | self.resolve(pattern.clone(), 0, &mut resolved_paths)?; |
73 | let ufcs_function_calls = resolved_paths | 76 | let ufcs_function_calls = resolved_paths |
@@ -85,11 +88,17 @@ impl Resolver<'_, '_> { | |||
85 | None | 88 | None |
86 | }) | 89 | }) |
87 | .collect(); | 90 | .collect(); |
91 | let contains_self = | ||
92 | pattern.descendants_with_tokens().any(|node_or_token| match node_or_token { | ||
93 | SyntaxElement::Token(t) => t.kind() == T![self], | ||
94 | _ => false, | ||
95 | }); | ||
88 | Ok(ResolvedPattern { | 96 | Ok(ResolvedPattern { |
89 | node: pattern, | 97 | node: pattern, |
90 | resolved_paths, | 98 | resolved_paths, |
91 | placeholders_by_stand_in: self.placeholders_by_stand_in.clone(), | 99 | placeholders_by_stand_in: self.placeholders_by_stand_in.clone(), |
92 | ufcs_function_calls, | 100 | ufcs_function_calls, |
101 | contains_self, | ||
93 | }) | 102 | }) |
94 | } | 103 | } |
95 | 104 | ||
@@ -101,6 +110,10 @@ impl Resolver<'_, '_> { | |||
101 | ) -> Result<(), SsrError> { | 110 | ) -> Result<(), SsrError> { |
102 | use ra_syntax::ast::AstNode; | 111 | use ra_syntax::ast::AstNode; |
103 | if let Some(path) = ast::Path::cast(node.clone()) { | 112 | if let Some(path) = ast::Path::cast(node.clone()) { |
113 | if is_self(&path) { | ||
114 | // Self cannot be resolved like other paths. | ||
115 | return Ok(()); | ||
116 | } | ||
104 | // Check if this is an appropriate place in the path to resolve. If the path is | 117 | // Check if this is an appropriate place in the path to resolve. If the path is |
105 | // something like `a::B::<i32>::c` then we want to resolve `a::B`. If the path contains | 118 | // something like `a::B::<i32>::c` then we want to resolve `a::B`. If the path contains |
106 | // a placeholder. e.g. `a::$b::c` then we want to resolve `a`. | 119 | // a placeholder. e.g. `a::$b::c` then we want to resolve `a`. |
@@ -157,9 +170,15 @@ impl<'db> ResolutionScope<'db> { | |||
157 | ResolutionScope { | 170 | ResolutionScope { |
158 | scope, | 171 | scope, |
159 | hygiene: hir::Hygiene::new(sema.db, resolve_context.file_id.into()), | 172 | hygiene: hir::Hygiene::new(sema.db, resolve_context.file_id.into()), |
173 | node, | ||
160 | } | 174 | } |
161 | } | 175 | } |
162 | 176 | ||
177 | /// Returns the function in which SSR was invoked, if any. | ||
178 | pub(crate) fn current_function(&self) -> Option<SyntaxNode> { | ||
179 | self.node.ancestors().find(|node| node.kind() == SyntaxKind::FN).map(|node| node.clone()) | ||
180 | } | ||
181 | |||
163 | fn resolve_path(&self, path: &ast::Path) -> Option<hir::PathResolution> { | 182 | fn resolve_path(&self, path: &ast::Path) -> Option<hir::PathResolution> { |
164 | let hir_path = hir::Path::from_src(path.clone(), &self.hygiene)?; | 183 | let hir_path = hir::Path::from_src(path.clone(), &self.hygiene)?; |
165 | // First try resolving the whole path. This will work for things like | 184 | // First try resolving the whole path. This will work for things like |
@@ -186,6 +205,10 @@ impl<'db> ResolutionScope<'db> { | |||
186 | } | 205 | } |
187 | } | 206 | } |
188 | 207 | ||
208 | fn is_self(path: &ast::Path) -> bool { | ||
209 | path.segment().map(|segment| segment.self_token().is_some()).unwrap_or(false) | ||
210 | } | ||
211 | |||
189 | /// Returns a suitable node for resolving paths in the current scope. If we create a scope based on | 212 | /// Returns a suitable node for resolving paths in the current scope. If we create a scope based on |
190 | /// a statement node, then we can't resolve local variables that were defined in the current scope | 213 | /// a statement node, then we can't resolve local variables that were defined in the current scope |
191 | /// (only in parent scopes). So we find another node, ideally a child of the statement where local | 214 | /// (only in parent scopes). So we find another node, ideally a child of the statement where local |
diff --git a/crates/ra_ssr/src/search.rs b/crates/ra_ssr/src/search.rs index 213dc494f..85ffa2ac2 100644 --- a/crates/ra_ssr/src/search.rs +++ b/crates/ra_ssr/src/search.rs | |||
@@ -33,6 +33,15 @@ impl<'db> MatchFinder<'db> { | |||
33 | usage_cache: &mut UsageCache, | 33 | usage_cache: &mut UsageCache, |
34 | matches_out: &mut Vec<Match>, | 34 | matches_out: &mut Vec<Match>, |
35 | ) { | 35 | ) { |
36 | if rule.pattern.contains_self { | ||
37 | // If the pattern contains `self` we restrict the scope of the search to just the | ||
38 | // current method. No other method can reference the same `self`. This makes the | ||
39 | // behavior of `self` consistent with other variables. | ||
40 | if let Some(current_function) = self.resolution_scope.current_function() { | ||
41 | self.slow_scan_node(¤t_function, rule, &None, matches_out); | ||
42 | } | ||
43 | return; | ||
44 | } | ||
36 | if pick_path_for_usages(&rule.pattern).is_none() { | 45 | if pick_path_for_usages(&rule.pattern).is_none() { |
37 | self.slow_scan(rule, matches_out); | 46 | self.slow_scan(rule, matches_out); |
38 | return; | 47 | return; |
diff --git a/crates/ra_ssr/src/tests.rs b/crates/ra_ssr/src/tests.rs index 2ae03c64c..d483640df 100644 --- a/crates/ra_ssr/src/tests.rs +++ b/crates/ra_ssr/src/tests.rs | |||
@@ -1044,3 +1044,38 @@ fn replace_nonpath_within_selection() { | |||
1044 | }"#]], | 1044 | }"#]], |
1045 | ); | 1045 | ); |
1046 | } | 1046 | } |
1047 | |||
1048 | #[test] | ||
1049 | fn replace_self() { | ||
1050 | // `foo(self)` occurs twice in the code, however only the first occurrence is the `self` that's | ||
1051 | // in scope where the rule is invoked. | ||
1052 | assert_ssr_transform( | ||
1053 | "foo(self) ==>> bar(self)", | ||
1054 | r#" | ||
1055 | struct S1 {} | ||
1056 | fn foo(_: &S1) {} | ||
1057 | fn bar(_: &S1) {} | ||
1058 | impl S1 { | ||
1059 | fn f1(&self) { | ||
1060 | foo(self)<|> | ||
1061 | } | ||
1062 | fn f2(&self) { | ||
1063 | foo(self) | ||
1064 | } | ||
1065 | } | ||
1066 | "#, | ||
1067 | expect![[r#" | ||
1068 | struct S1 {} | ||
1069 | fn foo(_: &S1) {} | ||
1070 | fn bar(_: &S1) {} | ||
1071 | impl S1 { | ||
1072 | fn f1(&self) { | ||
1073 | bar(self) | ||
1074 | } | ||
1075 | fn f2(&self) { | ||
1076 | foo(self) | ||
1077 | } | ||
1078 | } | ||
1079 | "#]], | ||
1080 | ); | ||
1081 | } | ||
diff --git a/crates/ra_syntax/src/ast/edit.rs b/crates/ra_syntax/src/ast/edit.rs index 667a9294f..5ed123f91 100644 --- a/crates/ra_syntax/src/ast/edit.rs +++ b/crates/ra_syntax/src/ast/edit.rs | |||
@@ -621,7 +621,7 @@ fn single_node(element: impl Into<SyntaxElement>) -> RangeInclusive<SyntaxElemen | |||
621 | #[test] | 621 | #[test] |
622 | fn test_increase_indent() { | 622 | fn test_increase_indent() { |
623 | let arm_list = { | 623 | let arm_list = { |
624 | let arm = make::match_arm(iter::once(make::placeholder_pat().into()), make::expr_unit()); | 624 | let arm = make::match_arm(iter::once(make::wildcard_pat().into()), make::expr_unit()); |
625 | make::match_arm_list(vec![arm.clone(), arm]) | 625 | make::match_arm_list(vec![arm.clone(), arm]) |
626 | }; | 626 | }; |
627 | assert_eq!( | 627 | assert_eq!( |
diff --git a/crates/ra_syntax/src/ast/make.rs b/crates/ra_syntax/src/ast/make.rs index 673777015..254a37fe3 100644 --- a/crates/ra_syntax/src/ast/make.rs +++ b/crates/ra_syntax/src/ast/make.rs | |||
@@ -17,7 +17,7 @@ pub fn name_ref(text: &str) -> ast::NameRef { | |||
17 | ast_from_text(&format!("fn f() {{ {}; }}", text)) | 17 | ast_from_text(&format!("fn f() {{ {}; }}", text)) |
18 | } | 18 | } |
19 | 19 | ||
20 | pub fn type_ref(text: &str) -> ast::Type { | 20 | pub fn ty(text: &str) -> ast::Type { |
21 | ast_from_text(&format!("impl {} for D {{}};", text)) | 21 | ast_from_text(&format!("impl {} for D {{}};", text)) |
22 | } | 22 | } |
23 | 23 | ||
@@ -30,7 +30,7 @@ pub fn path_unqualified(segment: ast::PathSegment) -> ast::Path { | |||
30 | pub fn path_qualified(qual: ast::Path, segment: ast::PathSegment) -> ast::Path { | 30 | pub fn path_qualified(qual: ast::Path, segment: ast::PathSegment) -> ast::Path { |
31 | path_from_text(&format!("{}::{}", qual, segment)) | 31 | path_from_text(&format!("{}::{}", qual, segment)) |
32 | } | 32 | } |
33 | fn path_from_text(text: &str) -> ast::Path { | 33 | pub fn path_from_text(text: &str) -> ast::Path { |
34 | ast_from_text(text) | 34 | ast_from_text(text) |
35 | } | 35 | } |
36 | 36 | ||
@@ -60,11 +60,11 @@ pub fn use_tree_list(use_trees: impl IntoIterator<Item = ast::UseTree>) -> ast:: | |||
60 | ast_from_text(&format!("use {{{}}};", use_trees)) | 60 | ast_from_text(&format!("use {{{}}};", use_trees)) |
61 | } | 61 | } |
62 | 62 | ||
63 | pub fn use_item(use_tree: ast::UseTree) -> ast::Use { | 63 | pub fn use_(use_tree: ast::UseTree) -> ast::Use { |
64 | ast_from_text(&format!("use {};", use_tree)) | 64 | ast_from_text(&format!("use {};", use_tree)) |
65 | } | 65 | } |
66 | 66 | ||
67 | pub fn record_field(name: ast::NameRef, expr: Option<ast::Expr>) -> ast::RecordExprField { | 67 | pub fn record_expr_field(name: ast::NameRef, expr: Option<ast::Expr>) -> ast::RecordExprField { |
68 | return match expr { | 68 | return match expr { |
69 | Some(expr) => from_text(&format!("{}: {}", name, expr)), | 69 | Some(expr) => from_text(&format!("{}: {}", name, expr)), |
70 | None => from_text(&name.to_string()), | 70 | None => from_text(&name.to_string()), |
@@ -75,7 +75,7 @@ pub fn record_field(name: ast::NameRef, expr: Option<ast::Expr>) -> ast::RecordE | |||
75 | } | 75 | } |
76 | } | 76 | } |
77 | 77 | ||
78 | pub fn record_field_def(name: ast::NameRef, ty: ast::Type) -> ast::RecordField { | 78 | pub fn record_field(name: ast::NameRef, ty: ast::Type) -> ast::RecordField { |
79 | ast_from_text(&format!("struct S {{ {}: {}, }}", name, ty)) | 79 | ast_from_text(&format!("struct S {{ {}: {}, }}", name, ty)) |
80 | } | 80 | } |
81 | 81 | ||
@@ -148,7 +148,7 @@ pub fn condition(expr: ast::Expr, pattern: Option<ast::Pat>) -> ast::Condition { | |||
148 | } | 148 | } |
149 | } | 149 | } |
150 | 150 | ||
151 | pub fn bind_pat(name: ast::Name) -> ast::IdentPat { | 151 | pub fn ident_pat(name: ast::Name) -> ast::IdentPat { |
152 | return from_text(name.text()); | 152 | return from_text(name.text()); |
153 | 153 | ||
154 | fn from_text(text: &str) -> ast::IdentPat { | 154 | fn from_text(text: &str) -> ast::IdentPat { |
@@ -156,7 +156,7 @@ pub fn bind_pat(name: ast::Name) -> ast::IdentPat { | |||
156 | } | 156 | } |
157 | } | 157 | } |
158 | 158 | ||
159 | pub fn placeholder_pat() -> ast::WildcardPat { | 159 | pub fn wildcard_pat() -> ast::WildcardPat { |
160 | return from_text("_"); | 160 | return from_text("_"); |
161 | 161 | ||
162 | fn from_text(text: &str) -> ast::WildcardPat { | 162 | fn from_text(text: &str) -> ast::WildcardPat { |
@@ -288,7 +288,7 @@ pub fn visibility_pub_crate() -> ast::Visibility { | |||
288 | ast_from_text("pub(crate) struct S") | 288 | ast_from_text("pub(crate) struct S") |
289 | } | 289 | } |
290 | 290 | ||
291 | pub fn fn_def( | 291 | pub fn fn_( |
292 | visibility: Option<ast::Visibility>, | 292 | visibility: Option<ast::Visibility>, |
293 | fn_name: ast::Name, | 293 | fn_name: ast::Name, |
294 | type_params: Option<ast::GenericParamList>, | 294 | type_params: Option<ast::GenericParamList>, |
diff --git a/crates/rust-analyzer/src/caps.rs b/crates/rust-analyzer/src/caps.rs index 37d695448..92a743fd8 100644 --- a/crates/rust-analyzer/src/caps.rs +++ b/crates/rust-analyzer/src/caps.rs | |||
@@ -76,7 +76,9 @@ pub fn server_capabilities(client_caps: &ClientCapabilities) -> ServerCapabiliti | |||
76 | token_modifiers: semantic_tokens::SUPPORTED_MODIFIERS.to_vec(), | 76 | token_modifiers: semantic_tokens::SUPPORTED_MODIFIERS.to_vec(), |
77 | }, | 77 | }, |
78 | 78 | ||
79 | document_provider: Some(SemanticTokensDocumentProvider::Bool(true)), | 79 | document_provider: Some(SemanticTokensDocumentProvider::Edits { |
80 | edits: Some(true), | ||
81 | }), | ||
80 | range_provider: Some(true), | 82 | range_provider: Some(true), |
81 | work_done_progress_options: Default::default(), | 83 | work_done_progress_options: Default::default(), |
82 | } | 84 | } |
diff --git a/crates/rust-analyzer/src/document.rs b/crates/rust-analyzer/src/document.rs index 43219e633..e882c9865 100644 --- a/crates/rust-analyzer/src/document.rs +++ b/crates/rust-analyzer/src/document.rs | |||
@@ -1,9 +1,9 @@ | |||
1 | //! In-memory document information. | 1 | //! In-memory document information. |
2 | 2 | ||
3 | /// Information about a document that the Language Client | 3 | /// Information about a document that the Language Client |
4 | // knows about. | 4 | /// knows about. |
5 | // Its lifetime is driven by the textDocument/didOpen and textDocument/didClose | 5 | /// Its lifetime is driven by the textDocument/didOpen and textDocument/didClose |
6 | // client notifications. | 6 | /// client notifications. |
7 | #[derive(Debug, Clone)] | 7 | #[derive(Debug, Clone)] |
8 | pub(crate) struct DocumentData { | 8 | pub(crate) struct DocumentData { |
9 | pub version: Option<i64>, | 9 | pub version: Option<i64>, |
diff --git a/crates/rust-analyzer/src/global_state.rs b/crates/rust-analyzer/src/global_state.rs index b2d65a6d1..0e592ac1b 100644 --- a/crates/rust-analyzer/src/global_state.rs +++ b/crates/rust-analyzer/src/global_state.rs | |||
@@ -7,8 +7,8 @@ use std::{sync::Arc, time::Instant}; | |||
7 | 7 | ||
8 | use crossbeam_channel::{unbounded, Receiver, Sender}; | 8 | use crossbeam_channel::{unbounded, Receiver, Sender}; |
9 | use flycheck::FlycheckHandle; | 9 | use flycheck::FlycheckHandle; |
10 | use lsp_types::Url; | 10 | use lsp_types::{SemanticTokens, Url}; |
11 | use parking_lot::RwLock; | 11 | use parking_lot::{Mutex, RwLock}; |
12 | use ra_db::{CrateId, VfsPath}; | 12 | use ra_db::{CrateId, VfsPath}; |
13 | use ra_ide::{Analysis, AnalysisChange, AnalysisHost, FileId}; | 13 | use ra_ide::{Analysis, AnalysisChange, AnalysisHost, FileId}; |
14 | use ra_project_model::{CargoWorkspace, ProcMacroClient, ProjectWorkspace, Target}; | 14 | use ra_project_model::{CargoWorkspace, ProcMacroClient, ProjectWorkspace, Target}; |
@@ -71,6 +71,7 @@ pub(crate) struct GlobalState { | |||
71 | pub(crate) analysis_host: AnalysisHost, | 71 | pub(crate) analysis_host: AnalysisHost, |
72 | pub(crate) diagnostics: DiagnosticCollection, | 72 | pub(crate) diagnostics: DiagnosticCollection, |
73 | pub(crate) mem_docs: FxHashMap<VfsPath, DocumentData>, | 73 | pub(crate) mem_docs: FxHashMap<VfsPath, DocumentData>, |
74 | pub(crate) semantic_tokens_cache: Arc<Mutex<FxHashMap<Url, SemanticTokens>>>, | ||
74 | pub(crate) vfs: Arc<RwLock<(vfs::Vfs, FxHashMap<FileId, LineEndings>)>>, | 75 | pub(crate) vfs: Arc<RwLock<(vfs::Vfs, FxHashMap<FileId, LineEndings>)>>, |
75 | pub(crate) status: Status, | 76 | pub(crate) status: Status, |
76 | pub(crate) source_root_config: SourceRootConfig, | 77 | pub(crate) source_root_config: SourceRootConfig, |
@@ -86,6 +87,7 @@ pub(crate) struct GlobalStateSnapshot { | |||
86 | pub(crate) check_fixes: CheckFixes, | 87 | pub(crate) check_fixes: CheckFixes, |
87 | pub(crate) latest_requests: Arc<RwLock<LatestRequests>>, | 88 | pub(crate) latest_requests: Arc<RwLock<LatestRequests>>, |
88 | mem_docs: FxHashMap<VfsPath, DocumentData>, | 89 | mem_docs: FxHashMap<VfsPath, DocumentData>, |
90 | pub semantic_tokens_cache: Arc<Mutex<FxHashMap<Url, SemanticTokens>>>, | ||
89 | vfs: Arc<RwLock<(vfs::Vfs, FxHashMap<FileId, LineEndings>)>>, | 91 | vfs: Arc<RwLock<(vfs::Vfs, FxHashMap<FileId, LineEndings>)>>, |
90 | pub(crate) workspaces: Arc<Vec<ProjectWorkspace>>, | 92 | pub(crate) workspaces: Arc<Vec<ProjectWorkspace>>, |
91 | } | 93 | } |
@@ -120,6 +122,7 @@ impl GlobalState { | |||
120 | analysis_host, | 122 | analysis_host, |
121 | diagnostics: Default::default(), | 123 | diagnostics: Default::default(), |
122 | mem_docs: FxHashMap::default(), | 124 | mem_docs: FxHashMap::default(), |
125 | semantic_tokens_cache: Arc::new(Default::default()), | ||
123 | vfs: Arc::new(RwLock::new((vfs::Vfs::default(), FxHashMap::default()))), | 126 | vfs: Arc::new(RwLock::new((vfs::Vfs::default(), FxHashMap::default()))), |
124 | status: Status::default(), | 127 | status: Status::default(), |
125 | source_root_config: SourceRootConfig::default(), | 128 | source_root_config: SourceRootConfig::default(), |
@@ -186,6 +189,7 @@ impl GlobalState { | |||
186 | latest_requests: Arc::clone(&self.latest_requests), | 189 | latest_requests: Arc::clone(&self.latest_requests), |
187 | check_fixes: Arc::clone(&self.diagnostics.check_fixes), | 190 | check_fixes: Arc::clone(&self.diagnostics.check_fixes), |
188 | mem_docs: self.mem_docs.clone(), | 191 | mem_docs: self.mem_docs.clone(), |
192 | semantic_tokens_cache: Arc::clone(&self.semantic_tokens_cache), | ||
189 | } | 193 | } |
190 | } | 194 | } |
191 | 195 | ||
diff --git a/crates/rust-analyzer/src/handlers.rs b/crates/rust-analyzer/src/handlers.rs index 6994e611b..895af1dd7 100644 --- a/crates/rust-analyzer/src/handlers.rs +++ b/crates/rust-analyzer/src/handlers.rs | |||
@@ -13,9 +13,10 @@ use lsp_types::{ | |||
13 | CallHierarchyOutgoingCall, CallHierarchyOutgoingCallsParams, CallHierarchyPrepareParams, | 13 | CallHierarchyOutgoingCall, CallHierarchyOutgoingCallsParams, CallHierarchyPrepareParams, |
14 | CodeActionKind, CodeLens, Command, CompletionItem, Diagnostic, DocumentFormattingParams, | 14 | CodeActionKind, CodeLens, Command, CompletionItem, Diagnostic, DocumentFormattingParams, |
15 | DocumentHighlight, DocumentSymbol, FoldingRange, FoldingRangeParams, HoverContents, Location, | 15 | DocumentHighlight, DocumentSymbol, FoldingRange, FoldingRangeParams, HoverContents, Location, |
16 | Position, PrepareRenameResponse, Range, RenameParams, SemanticTokensParams, | 16 | Position, PrepareRenameResponse, Range, RenameParams, SemanticTokensEditResult, |
17 | SemanticTokensRangeParams, SemanticTokensRangeResult, SemanticTokensResult, SymbolInformation, | 17 | SemanticTokensEditsParams, SemanticTokensParams, SemanticTokensRangeParams, |
18 | SymbolTag, TextDocumentIdentifier, Url, WorkspaceEdit, | 18 | SemanticTokensRangeResult, SemanticTokensResult, SymbolInformation, SymbolTag, |
19 | TextDocumentIdentifier, Url, WorkspaceEdit, | ||
19 | }; | 20 | }; |
20 | use ra_ide::{ | 21 | use ra_ide::{ |
21 | FileId, FilePosition, FileRange, HoverAction, HoverGotoTypeData, NavigationTarget, Query, | 22 | FileId, FilePosition, FileRange, HoverAction, HoverGotoTypeData, NavigationTarget, Query, |
@@ -709,11 +710,6 @@ pub(crate) fn handle_formatting( | |||
709 | } | 710 | } |
710 | }; | 711 | }; |
711 | 712 | ||
712 | if let Ok(path) = params.text_document.uri.to_file_path() { | ||
713 | if let Some(parent) = path.parent() { | ||
714 | rustfmt.current_dir(parent); | ||
715 | } | ||
716 | } | ||
717 | let mut rustfmt = rustfmt.stdin(Stdio::piped()).stdout(Stdio::piped()).spawn()?; | 713 | let mut rustfmt = rustfmt.stdin(Stdio::piped()).stdout(Stdio::piped()).spawn()?; |
718 | 714 | ||
719 | rustfmt.stdin.as_mut().unwrap().write_all(file.as_bytes())?; | 715 | rustfmt.stdin.as_mut().unwrap().write_all(file.as_bytes())?; |
@@ -1184,6 +1180,40 @@ pub(crate) fn handle_semantic_tokens( | |||
1184 | 1180 | ||
1185 | let highlights = snap.analysis.highlight(file_id)?; | 1181 | let highlights = snap.analysis.highlight(file_id)?; |
1186 | let semantic_tokens = to_proto::semantic_tokens(&text, &line_index, highlights); | 1182 | let semantic_tokens = to_proto::semantic_tokens(&text, &line_index, highlights); |
1183 | |||
1184 | // Unconditionally cache the tokens | ||
1185 | snap.semantic_tokens_cache.lock().insert(params.text_document.uri, semantic_tokens.clone()); | ||
1186 | |||
1187 | Ok(Some(semantic_tokens.into())) | ||
1188 | } | ||
1189 | |||
1190 | pub(crate) fn handle_semantic_tokens_edits( | ||
1191 | snap: GlobalStateSnapshot, | ||
1192 | params: SemanticTokensEditsParams, | ||
1193 | ) -> Result<Option<SemanticTokensEditResult>> { | ||
1194 | let _p = profile("handle_semantic_tokens_edits"); | ||
1195 | |||
1196 | let file_id = from_proto::file_id(&snap, ¶ms.text_document.uri)?; | ||
1197 | let text = snap.analysis.file_text(file_id)?; | ||
1198 | let line_index = snap.analysis.file_line_index(file_id)?; | ||
1199 | |||
1200 | let highlights = snap.analysis.highlight(file_id)?; | ||
1201 | |||
1202 | let semantic_tokens = to_proto::semantic_tokens(&text, &line_index, highlights); | ||
1203 | |||
1204 | let mut cache = snap.semantic_tokens_cache.lock(); | ||
1205 | let cached_tokens = cache.entry(params.text_document.uri).or_default(); | ||
1206 | |||
1207 | if let Some(prev_id) = &cached_tokens.result_id { | ||
1208 | if *prev_id == params.previous_result_id { | ||
1209 | let edits = to_proto::semantic_token_edits(&cached_tokens, &semantic_tokens); | ||
1210 | *cached_tokens = semantic_tokens; | ||
1211 | return Ok(Some(edits.into())); | ||
1212 | } | ||
1213 | } | ||
1214 | |||
1215 | *cached_tokens = semantic_tokens.clone(); | ||
1216 | |||
1187 | Ok(Some(semantic_tokens.into())) | 1217 | Ok(Some(semantic_tokens.into())) |
1188 | } | 1218 | } |
1189 | 1219 | ||
diff --git a/crates/rust-analyzer/src/lsp_utils.rs b/crates/rust-analyzer/src/lsp_utils.rs index d4cc9dd04..0bc3ff115 100644 --- a/crates/rust-analyzer/src/lsp_utils.rs +++ b/crates/rust-analyzer/src/lsp_utils.rs | |||
@@ -1,5 +1,5 @@ | |||
1 | //! Utilities for LSP-related boilerplate code. | 1 | //! Utilities for LSP-related boilerplate code. |
2 | use std::{borrow::Cow, error::Error, ops::Range}; | 2 | use std::{error::Error, ops::Range}; |
3 | 3 | ||
4 | use lsp_server::Notification; | 4 | use lsp_server::Notification; |
5 | use ra_db::Canceled; | 5 | use ra_db::Canceled; |
@@ -84,8 +84,8 @@ impl GlobalState { | |||
84 | pub(crate) fn apply_document_changes( | 84 | pub(crate) fn apply_document_changes( |
85 | old_text: &mut String, | 85 | old_text: &mut String, |
86 | content_changes: Vec<lsp_types::TextDocumentContentChangeEvent>, | 86 | content_changes: Vec<lsp_types::TextDocumentContentChangeEvent>, |
87 | mut line_index: Cow<'_, LineIndex>, | ||
88 | ) { | 87 | ) { |
88 | let mut line_index = LineIndex::new(old_text); | ||
89 | // The changes we got must be applied sequentially, but can cross lines so we | 89 | // The changes we got must be applied sequentially, but can cross lines so we |
90 | // have to keep our line index updated. | 90 | // have to keep our line index updated. |
91 | // Some clients (e.g. Code) sort the ranges in reverse. As an optimization, we | 91 | // Some clients (e.g. Code) sort the ranges in reverse. As an optimization, we |
@@ -110,7 +110,7 @@ pub(crate) fn apply_document_changes( | |||
110 | match change.range { | 110 | match change.range { |
111 | Some(range) => { | 111 | Some(range) => { |
112 | if !index_valid.covers(range.end.line) { | 112 | if !index_valid.covers(range.end.line) { |
113 | line_index = Cow::Owned(LineIndex::new(old_text)); | 113 | line_index = LineIndex::new(&old_text); |
114 | } | 114 | } |
115 | index_valid = IndexValid::UpToLineExclusive(range.start.line); | 115 | index_valid = IndexValid::UpToLineExclusive(range.start.line); |
116 | let range = from_proto::text_range(&line_index, range); | 116 | let range = from_proto::text_range(&line_index, range); |
@@ -145,15 +145,10 @@ mod tests { | |||
145 | }; | 145 | }; |
146 | } | 146 | } |
147 | 147 | ||
148 | fn run(text: &mut String, changes: Vec<TextDocumentContentChangeEvent>) { | ||
149 | let line_index = Cow::Owned(LineIndex::new(&text)); | ||
150 | super::apply_document_changes(text, changes, line_index); | ||
151 | } | ||
152 | |||
153 | let mut text = String::new(); | 148 | let mut text = String::new(); |
154 | run(&mut text, vec![]); | 149 | apply_document_changes(&mut text, vec![]); |
155 | assert_eq!(text, ""); | 150 | assert_eq!(text, ""); |
156 | run( | 151 | apply_document_changes( |
157 | &mut text, | 152 | &mut text, |
158 | vec![TextDocumentContentChangeEvent { | 153 | vec![TextDocumentContentChangeEvent { |
159 | range: None, | 154 | range: None, |
@@ -162,36 +157,39 @@ mod tests { | |||
162 | }], | 157 | }], |
163 | ); | 158 | ); |
164 | assert_eq!(text, "the"); | 159 | assert_eq!(text, "the"); |
165 | run(&mut text, c![0, 3; 0, 3 => " quick"]); | 160 | apply_document_changes(&mut text, c![0, 3; 0, 3 => " quick"]); |
166 | assert_eq!(text, "the quick"); | 161 | assert_eq!(text, "the quick"); |
167 | run(&mut text, c![0, 0; 0, 4 => "", 0, 5; 0, 5 => " foxes"]); | 162 | apply_document_changes(&mut text, c![0, 0; 0, 4 => "", 0, 5; 0, 5 => " foxes"]); |
168 | assert_eq!(text, "quick foxes"); | 163 | assert_eq!(text, "quick foxes"); |
169 | run(&mut text, c![0, 11; 0, 11 => "\ndream"]); | 164 | apply_document_changes(&mut text, c![0, 11; 0, 11 => "\ndream"]); |
170 | assert_eq!(text, "quick foxes\ndream"); | 165 | assert_eq!(text, "quick foxes\ndream"); |
171 | run(&mut text, c![1, 0; 1, 0 => "have "]); | 166 | apply_document_changes(&mut text, c![1, 0; 1, 0 => "have "]); |
172 | assert_eq!(text, "quick foxes\nhave dream"); | 167 | assert_eq!(text, "quick foxes\nhave dream"); |
173 | run(&mut text, c![0, 0; 0, 0 => "the ", 1, 4; 1, 4 => " quiet", 1, 16; 1, 16 => "s\n"]); | 168 | apply_document_changes( |
169 | &mut text, | ||
170 | c![0, 0; 0, 0 => "the ", 1, 4; 1, 4 => " quiet", 1, 16; 1, 16 => "s\n"], | ||
171 | ); | ||
174 | assert_eq!(text, "the quick foxes\nhave quiet dreams\n"); | 172 | assert_eq!(text, "the quick foxes\nhave quiet dreams\n"); |
175 | run(&mut text, c![0, 15; 0, 15 => "\n", 2, 17; 2, 17 => "\n"]); | 173 | apply_document_changes(&mut text, c![0, 15; 0, 15 => "\n", 2, 17; 2, 17 => "\n"]); |
176 | assert_eq!(text, "the quick foxes\n\nhave quiet dreams\n\n"); | 174 | assert_eq!(text, "the quick foxes\n\nhave quiet dreams\n\n"); |
177 | run( | 175 | apply_document_changes( |
178 | &mut text, | 176 | &mut text, |
179 | c![1, 0; 1, 0 => "DREAM", 2, 0; 2, 0 => "they ", 3, 0; 3, 0 => "DON'T THEY?"], | 177 | c![1, 0; 1, 0 => "DREAM", 2, 0; 2, 0 => "they ", 3, 0; 3, 0 => "DON'T THEY?"], |
180 | ); | 178 | ); |
181 | assert_eq!(text, "the quick foxes\nDREAM\nthey have quiet dreams\nDON'T THEY?\n"); | 179 | assert_eq!(text, "the quick foxes\nDREAM\nthey have quiet dreams\nDON'T THEY?\n"); |
182 | run(&mut text, c![0, 10; 1, 5 => "", 2, 0; 2, 12 => ""]); | 180 | apply_document_changes(&mut text, c![0, 10; 1, 5 => "", 2, 0; 2, 12 => ""]); |
183 | assert_eq!(text, "the quick \nthey have quiet dreams\n"); | 181 | assert_eq!(text, "the quick \nthey have quiet dreams\n"); |
184 | 182 | ||
185 | text = String::from("❤️"); | 183 | text = String::from("❤️"); |
186 | run(&mut text, c![0, 0; 0, 0 => "a"]); | 184 | apply_document_changes(&mut text, c![0, 0; 0, 0 => "a"]); |
187 | assert_eq!(text, "a❤️"); | 185 | assert_eq!(text, "a❤️"); |
188 | 186 | ||
189 | text = String::from("a\nb"); | 187 | text = String::from("a\nb"); |
190 | run(&mut text, c![0, 1; 1, 0 => "\nțc", 0, 1; 1, 1 => "d"]); | 188 | apply_document_changes(&mut text, c![0, 1; 1, 0 => "\nțc", 0, 1; 1, 1 => "d"]); |
191 | assert_eq!(text, "adcb"); | 189 | assert_eq!(text, "adcb"); |
192 | 190 | ||
193 | text = String::from("a\nb"); | 191 | text = String::from("a\nb"); |
194 | run(&mut text, c![0, 1; 1, 0 => "ț\nc", 0, 2; 0, 2 => "c"]); | 192 | apply_document_changes(&mut text, c![0, 1; 1, 0 => "ț\nc", 0, 2; 0, 2 => "c"]); |
195 | assert_eq!(text, "ațc\ncb"); | 193 | assert_eq!(text, "ațc\ncb"); |
196 | } | 194 | } |
197 | } | 195 | } |
diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs index 0ace4cb45..438e965e0 100644 --- a/crates/rust-analyzer/src/main_loop.rs +++ b/crates/rust-analyzer/src/main_loop.rs | |||
@@ -1,14 +1,13 @@ | |||
1 | //! The main loop of `rust-analyzer` responsible for dispatching LSP | 1 | //! The main loop of `rust-analyzer` responsible for dispatching LSP |
2 | //! requests/replies and notifications back to the client. | 2 | //! requests/replies and notifications back to the client. |
3 | use std::{ | 3 | use std::{ |
4 | borrow::Cow, | ||
5 | env, fmt, panic, | 4 | env, fmt, panic, |
6 | time::{Duration, Instant}, | 5 | time::{Duration, Instant}, |
7 | }; | 6 | }; |
8 | 7 | ||
9 | use crossbeam_channel::{select, Receiver}; | 8 | use crossbeam_channel::{select, Receiver}; |
10 | use lsp_server::{Connection, Notification, Request, Response}; | 9 | use lsp_server::{Connection, Notification, Request, Response}; |
11 | use lsp_types::{notification::Notification as _, DidChangeTextDocumentParams}; | 10 | use lsp_types::notification::Notification as _; |
12 | use ra_db::VfsPath; | 11 | use ra_db::VfsPath; |
13 | use ra_ide::{Canceled, FileId}; | 12 | use ra_ide::{Canceled, FileId}; |
14 | use ra_prof::profile; | 13 | use ra_prof::profile; |
@@ -48,7 +47,7 @@ pub fn main_loop(config: Config, connection: Connection) -> Result<()> { | |||
48 | SetThreadPriority(thread, thread_priority_above_normal); | 47 | SetThreadPriority(thread, thread_priority_above_normal); |
49 | } | 48 | } |
50 | 49 | ||
51 | GlobalState::new(connection.sender.clone(), config).run(connection.receiver) | 50 | GlobalState::new(connection.sender, config).run(connection.receiver) |
52 | } | 51 | } |
53 | 52 | ||
54 | enum Event { | 53 | enum Event { |
@@ -387,6 +386,9 @@ impl GlobalState { | |||
387 | handlers::handle_call_hierarchy_outgoing, | 386 | handlers::handle_call_hierarchy_outgoing, |
388 | )? | 387 | )? |
389 | .on::<lsp_types::request::SemanticTokensRequest>(handlers::handle_semantic_tokens)? | 388 | .on::<lsp_types::request::SemanticTokensRequest>(handlers::handle_semantic_tokens)? |
389 | .on::<lsp_types::request::SemanticTokensEditsRequest>( | ||
390 | handlers::handle_semantic_tokens_edits, | ||
391 | )? | ||
390 | .on::<lsp_types::request::SemanticTokensRangeRequest>( | 392 | .on::<lsp_types::request::SemanticTokensRangeRequest>( |
391 | handlers::handle_semantic_tokens_range, | 393 | handlers::handle_semantic_tokens_range, |
392 | )? | 394 | )? |
@@ -422,20 +424,15 @@ impl GlobalState { | |||
422 | })? | 424 | })? |
423 | .on::<lsp_types::notification::DidChangeTextDocument>(|this, params| { | 425 | .on::<lsp_types::notification::DidChangeTextDocument>(|this, params| { |
424 | if let Ok(path) = from_proto::vfs_path(¶ms.text_document.uri) { | 426 | if let Ok(path) = from_proto::vfs_path(¶ms.text_document.uri) { |
425 | let DidChangeTextDocumentParams { text_document, content_changes } = params; | 427 | let doc = this.mem_docs.get_mut(&path).unwrap(); |
426 | let vfs = &mut this.vfs.write().0; | 428 | let vfs = &mut this.vfs.write().0; |
427 | let world = this.snapshot(); | ||
428 | let file_id = vfs.file_id(&path).unwrap(); | 429 | let file_id = vfs.file_id(&path).unwrap(); |
429 | |||
430 | // let file_id = vfs.file_id(&path).unwrap(); | ||
431 | let mut text = String::from_utf8(vfs.file_contents(file_id).to_vec()).unwrap(); | 430 | let mut text = String::from_utf8(vfs.file_contents(file_id).to_vec()).unwrap(); |
432 | let line_index = world.analysis.file_line_index(file_id)?; | 431 | apply_document_changes(&mut text, params.content_changes); |
433 | apply_document_changes(&mut text, content_changes, Cow::Borrowed(&line_index)); | ||
434 | 432 | ||
435 | // The version passed in DidChangeTextDocument is the version after all edits are applied | 433 | // The version passed in DidChangeTextDocument is the version after all edits are applied |
436 | // so we should apply it before the vfs is notified. | 434 | // so we should apply it before the vfs is notified. |
437 | let doc = this.mem_docs.get_mut(&path).unwrap(); | 435 | doc.version = params.text_document.version; |
438 | doc.version = text_document.version; | ||
439 | 436 | ||
440 | vfs.set_file_contents(path.clone(), Some(text.into_bytes())); | 437 | vfs.set_file_contents(path.clone(), Some(text.into_bytes())); |
441 | } | 438 | } |
@@ -449,6 +446,8 @@ impl GlobalState { | |||
449 | None => log::error!("orphan DidCloseTextDocument: {}", path), | 446 | None => log::error!("orphan DidCloseTextDocument: {}", path), |
450 | } | 447 | } |
451 | 448 | ||
449 | this.semantic_tokens_cache.lock().remove(¶ms.text_document.uri); | ||
450 | |||
452 | if let Some(path) = path.as_path() { | 451 | if let Some(path) = path.as_path() { |
453 | this.loader.handle.invalidate(path.to_path_buf()); | 452 | this.loader.handle.invalidate(path.to_path_buf()); |
454 | } | 453 | } |
diff --git a/crates/rust-analyzer/src/semantic_tokens.rs b/crates/rust-analyzer/src/semantic_tokens.rs index 576bd8adc..afc38fb4e 100644 --- a/crates/rust-analyzer/src/semantic_tokens.rs +++ b/crates/rust-analyzer/src/semantic_tokens.rs | |||
@@ -2,7 +2,10 @@ | |||
2 | 2 | ||
3 | use std::ops; | 3 | use std::ops; |
4 | 4 | ||
5 | use lsp_types::{Range, SemanticToken, SemanticTokenModifier, SemanticTokenType, SemanticTokens}; | 5 | use lsp_types::{ |
6 | Range, SemanticToken, SemanticTokenModifier, SemanticTokenType, SemanticTokens, | ||
7 | SemanticTokensEdit, | ||
8 | }; | ||
6 | 9 | ||
7 | macro_rules! define_semantic_token_types { | 10 | macro_rules! define_semantic_token_types { |
8 | ($(($ident:ident, $string:literal)),*$(,)?) => { | 11 | ($(($ident:ident, $string:literal)),*$(,)?) => { |
@@ -89,14 +92,18 @@ impl ops::BitOrAssign<SemanticTokenModifier> for ModifierSet { | |||
89 | /// Tokens are encoded relative to each other. | 92 | /// Tokens are encoded relative to each other. |
90 | /// | 93 | /// |
91 | /// This is a direct port of https://github.com/microsoft/vscode-languageserver-node/blob/f425af9de46a0187adb78ec8a46b9b2ce80c5412/server/src/sematicTokens.proposed.ts#L45 | 94 | /// This is a direct port of https://github.com/microsoft/vscode-languageserver-node/blob/f425af9de46a0187adb78ec8a46b9b2ce80c5412/server/src/sematicTokens.proposed.ts#L45 |
92 | #[derive(Default)] | ||
93 | pub(crate) struct SemanticTokensBuilder { | 95 | pub(crate) struct SemanticTokensBuilder { |
96 | id: String, | ||
94 | prev_line: u32, | 97 | prev_line: u32, |
95 | prev_char: u32, | 98 | prev_char: u32, |
96 | data: Vec<SemanticToken>, | 99 | data: Vec<SemanticToken>, |
97 | } | 100 | } |
98 | 101 | ||
99 | impl SemanticTokensBuilder { | 102 | impl SemanticTokensBuilder { |
103 | pub fn new(id: String) -> Self { | ||
104 | SemanticTokensBuilder { id, prev_line: 0, prev_char: 0, data: Default::default() } | ||
105 | } | ||
106 | |||
100 | /// Push a new token onto the builder | 107 | /// Push a new token onto the builder |
101 | pub fn push(&mut self, range: Range, token_index: u32, modifier_bitset: u32) { | 108 | pub fn push(&mut self, range: Range, token_index: u32, modifier_bitset: u32) { |
102 | let mut push_line = range.start.line as u32; | 109 | let mut push_line = range.start.line as u32; |
@@ -127,10 +134,136 @@ impl SemanticTokensBuilder { | |||
127 | } | 134 | } |
128 | 135 | ||
129 | pub fn build(self) -> SemanticTokens { | 136 | pub fn build(self) -> SemanticTokens { |
130 | SemanticTokens { result_id: None, data: self.data } | 137 | SemanticTokens { result_id: Some(self.id), data: self.data } |
138 | } | ||
139 | } | ||
140 | |||
141 | pub fn diff_tokens(old: &[SemanticToken], new: &[SemanticToken]) -> Vec<SemanticTokensEdit> { | ||
142 | let offset = new.iter().zip(old.iter()).take_while(|&(n, p)| n == p).count(); | ||
143 | |||
144 | let (_, old) = old.split_at(offset); | ||
145 | let (_, new) = new.split_at(offset); | ||
146 | |||
147 | let offset_from_end = | ||
148 | new.iter().rev().zip(old.iter().rev()).take_while(|&(n, p)| n == p).count(); | ||
149 | |||
150 | let (old, _) = old.split_at(old.len() - offset_from_end); | ||
151 | let (new, _) = new.split_at(new.len() - offset_from_end); | ||
152 | |||
153 | if old.is_empty() && new.is_empty() { | ||
154 | vec![] | ||
155 | } else { | ||
156 | // The lsp data field is actually a byte-diff but we | ||
157 | // travel in tokens so `start` and `delete_count` are in multiples of the | ||
158 | // serialized size of `SemanticToken`. | ||
159 | vec![SemanticTokensEdit { | ||
160 | start: 5 * offset as u32, | ||
161 | delete_count: 5 * old.len() as u32, | ||
162 | data: Some(new.into()), | ||
163 | }] | ||
131 | } | 164 | } |
132 | } | 165 | } |
133 | 166 | ||
134 | pub fn type_index(type_: SemanticTokenType) -> u32 { | 167 | pub fn type_index(type_: SemanticTokenType) -> u32 { |
135 | SUPPORTED_TYPES.iter().position(|it| *it == type_).unwrap() as u32 | 168 | SUPPORTED_TYPES.iter().position(|it| *it == type_).unwrap() as u32 |
136 | } | 169 | } |
170 | |||
171 | #[cfg(test)] | ||
172 | mod tests { | ||
173 | use super::*; | ||
174 | |||
175 | fn from(t: (u32, u32, u32, u32, u32)) -> SemanticToken { | ||
176 | SemanticToken { | ||
177 | delta_line: t.0, | ||
178 | delta_start: t.1, | ||
179 | length: t.2, | ||
180 | token_type: t.3, | ||
181 | token_modifiers_bitset: t.4, | ||
182 | } | ||
183 | } | ||
184 | |||
185 | #[test] | ||
186 | fn test_diff_insert_at_end() { | ||
187 | let before = [from((1, 2, 3, 4, 5)), from((6, 7, 8, 9, 10))]; | ||
188 | let after = [from((1, 2, 3, 4, 5)), from((6, 7, 8, 9, 10)), from((11, 12, 13, 14, 15))]; | ||
189 | |||
190 | let edits = diff_tokens(&before, &after); | ||
191 | assert_eq!( | ||
192 | edits[0], | ||
193 | SemanticTokensEdit { | ||
194 | start: 10, | ||
195 | delete_count: 0, | ||
196 | data: Some(vec![from((11, 12, 13, 14, 15))]) | ||
197 | } | ||
198 | ); | ||
199 | } | ||
200 | |||
201 | #[test] | ||
202 | fn test_diff_insert_at_beginning() { | ||
203 | let before = [from((1, 2, 3, 4, 5)), from((6, 7, 8, 9, 10))]; | ||
204 | let after = [from((11, 12, 13, 14, 15)), from((1, 2, 3, 4, 5)), from((6, 7, 8, 9, 10))]; | ||
205 | |||
206 | let edits = diff_tokens(&before, &after); | ||
207 | assert_eq!( | ||
208 | edits[0], | ||
209 | SemanticTokensEdit { | ||
210 | start: 0, | ||
211 | delete_count: 0, | ||
212 | data: Some(vec![from((11, 12, 13, 14, 15))]) | ||
213 | } | ||
214 | ); | ||
215 | } | ||
216 | |||
217 | #[test] | ||
218 | fn test_diff_insert_in_middle() { | ||
219 | let before = [from((1, 2, 3, 4, 5)), from((6, 7, 8, 9, 10))]; | ||
220 | let after = [ | ||
221 | from((1, 2, 3, 4, 5)), | ||
222 | from((10, 20, 30, 40, 50)), | ||
223 | from((60, 70, 80, 90, 100)), | ||
224 | from((6, 7, 8, 9, 10)), | ||
225 | ]; | ||
226 | |||
227 | let edits = diff_tokens(&before, &after); | ||
228 | assert_eq!( | ||
229 | edits[0], | ||
230 | SemanticTokensEdit { | ||
231 | start: 5, | ||
232 | delete_count: 0, | ||
233 | data: Some(vec![from((10, 20, 30, 40, 50)), from((60, 70, 80, 90, 100))]) | ||
234 | } | ||
235 | ); | ||
236 | } | ||
237 | |||
238 | #[test] | ||
239 | fn test_diff_remove_from_end() { | ||
240 | let before = [from((1, 2, 3, 4, 5)), from((6, 7, 8, 9, 10)), from((11, 12, 13, 14, 15))]; | ||
241 | let after = [from((1, 2, 3, 4, 5)), from((6, 7, 8, 9, 10))]; | ||
242 | |||
243 | let edits = diff_tokens(&before, &after); | ||
244 | assert_eq!(edits[0], SemanticTokensEdit { start: 10, delete_count: 5, data: Some(vec![]) }); | ||
245 | } | ||
246 | |||
247 | #[test] | ||
248 | fn test_diff_remove_from_beginning() { | ||
249 | let before = [from((11, 12, 13, 14, 15)), from((1, 2, 3, 4, 5)), from((6, 7, 8, 9, 10))]; | ||
250 | let after = [from((1, 2, 3, 4, 5)), from((6, 7, 8, 9, 10))]; | ||
251 | |||
252 | let edits = diff_tokens(&before, &after); | ||
253 | assert_eq!(edits[0], SemanticTokensEdit { start: 0, delete_count: 5, data: Some(vec![]) }); | ||
254 | } | ||
255 | |||
256 | #[test] | ||
257 | fn test_diff_remove_from_middle() { | ||
258 | let before = [ | ||
259 | from((1, 2, 3, 4, 5)), | ||
260 | from((10, 20, 30, 40, 50)), | ||
261 | from((60, 70, 80, 90, 100)), | ||
262 | from((6, 7, 8, 9, 10)), | ||
263 | ]; | ||
264 | let after = [from((1, 2, 3, 4, 5)), from((6, 7, 8, 9, 10))]; | ||
265 | |||
266 | let edits = diff_tokens(&before, &after); | ||
267 | assert_eq!(edits[0], SemanticTokensEdit { start: 5, delete_count: 10, data: Some(vec![]) }); | ||
268 | } | ||
269 | } | ||
diff --git a/crates/rust-analyzer/src/to_proto.rs b/crates/rust-analyzer/src/to_proto.rs index fadcc5853..27460db78 100644 --- a/crates/rust-analyzer/src/to_proto.rs +++ b/crates/rust-analyzer/src/to_proto.rs | |||
@@ -1,5 +1,8 @@ | |||
1 | //! Conversion of rust-analyzer specific types to lsp_types equivalents. | 1 | //! Conversion of rust-analyzer specific types to lsp_types equivalents. |
2 | use std::path::{self, Path}; | 2 | use std::{ |
3 | path::{self, Path}, | ||
4 | sync::atomic::{AtomicU32, Ordering}, | ||
5 | }; | ||
3 | 6 | ||
4 | use itertools::Itertools; | 7 | use itertools::Itertools; |
5 | use ra_db::{FileId, FileRange}; | 8 | use ra_db::{FileId, FileRange}; |
@@ -303,12 +306,15 @@ pub(crate) fn inlay_int(line_index: &LineIndex, inlay_hint: InlayHint) -> lsp_ex | |||
303 | } | 306 | } |
304 | } | 307 | } |
305 | 308 | ||
309 | static TOKEN_RESULT_COUNTER: AtomicU32 = AtomicU32::new(1); | ||
310 | |||
306 | pub(crate) fn semantic_tokens( | 311 | pub(crate) fn semantic_tokens( |
307 | text: &str, | 312 | text: &str, |
308 | line_index: &LineIndex, | 313 | line_index: &LineIndex, |
309 | highlights: Vec<HighlightedRange>, | 314 | highlights: Vec<HighlightedRange>, |
310 | ) -> lsp_types::SemanticTokens { | 315 | ) -> lsp_types::SemanticTokens { |
311 | let mut builder = semantic_tokens::SemanticTokensBuilder::default(); | 316 | let id = TOKEN_RESULT_COUNTER.fetch_add(1, Ordering::SeqCst).to_string(); |
317 | let mut builder = semantic_tokens::SemanticTokensBuilder::new(id); | ||
312 | 318 | ||
313 | for highlight_range in highlights { | 319 | for highlight_range in highlights { |
314 | let (type_, mods) = semantic_token_type_and_modifiers(highlight_range.highlight); | 320 | let (type_, mods) = semantic_token_type_and_modifiers(highlight_range.highlight); |
@@ -328,6 +334,15 @@ pub(crate) fn semantic_tokens( | |||
328 | builder.build() | 334 | builder.build() |
329 | } | 335 | } |
330 | 336 | ||
337 | pub(crate) fn semantic_token_edits( | ||
338 | previous: &lsp_types::SemanticTokens, | ||
339 | current: &lsp_types::SemanticTokens, | ||
340 | ) -> lsp_types::SemanticTokensEdits { | ||
341 | let result_id = current.result_id.clone(); | ||
342 | let edits = semantic_tokens::diff_tokens(&previous.data, ¤t.data); | ||
343 | lsp_types::SemanticTokensEdits { result_id, edits } | ||
344 | } | ||
345 | |||
331 | fn semantic_token_type_and_modifiers( | 346 | fn semantic_token_type_and_modifiers( |
332 | highlight: Highlight, | 347 | highlight: Highlight, |
333 | ) -> (lsp_types::SemanticTokenType, semantic_tokens::ModifierSet) { | 348 | ) -> (lsp_types::SemanticTokenType, semantic_tokens::ModifierSet) { |
@@ -740,7 +755,8 @@ pub(crate) fn runnable( | |||
740 | } | 755 | } |
741 | 756 | ||
742 | pub(crate) fn markup_content(markup: Markup) -> lsp_types::MarkupContent { | 757 | pub(crate) fn markup_content(markup: Markup) -> lsp_types::MarkupContent { |
743 | lsp_types::MarkupContent { kind: lsp_types::MarkupKind::Markdown, value: markup.into() } | 758 | let value = crate::markdown::format_docs(markup.as_str()); |
759 | lsp_types::MarkupContent { kind: lsp_types::MarkupKind::Markdown, value } | ||
744 | } | 760 | } |
745 | 761 | ||
746 | #[cfg(test)] | 762 | #[cfg(test)] |