aboutsummaryrefslogtreecommitdiff
path: root/crates
diff options
context:
space:
mode:
Diffstat (limited to 'crates')
-rw-r--r--crates/ra_assists/src/assist_context.rs4
-rw-r--r--crates/ra_assists/src/ast_transform.rs2
-rw-r--r--crates/ra_assists/src/handlers/early_return.rs6
-rw-r--r--crates/ra_assists/src/handlers/expand_glob_import.rs391
-rw-r--r--crates/ra_assists/src/handlers/fill_match_arms.rs5
-rw-r--r--crates/ra_assists/src/handlers/generate_function.rs2
-rw-r--r--crates/ra_assists/src/handlers/raw_string.rs2
-rw-r--r--crates/ra_assists/src/handlers/replace_if_let_with_match.rs2
-rw-r--r--crates/ra_assists/src/handlers/replace_let_with_if_let.rs4
-rw-r--r--crates/ra_assists/src/handlers/replace_qualified_name_with_use.rs42
-rw-r--r--crates/ra_assists/src/handlers/replace_unwrap_with_match.rs4
-rw-r--r--crates/ra_assists/src/lib.rs2
-rw-r--r--crates/ra_assists/src/tests/generated.rs27
-rw-r--r--crates/ra_assists/src/utils.rs4
-rw-r--r--crates/ra_assists/src/utils/insert_use.rs3
-rw-r--r--crates/ra_db/Cargo.toml2
-rw-r--r--crates/ra_hir/src/db.rs9
-rw-r--r--crates/ra_hir_expand/src/proc_macro.rs2
-rw-r--r--crates/ra_hir_ty/Cargo.toml6
-rw-r--r--crates/ra_hir_ty/src/diagnostics/unsafe_check.rs38
-rw-r--r--crates/ra_hir_ty/src/infer.rs11
-rw-r--r--crates/ra_hir_ty/src/lower.rs5
-rw-r--r--crates/ra_hir_ty/src/tests/simple.rs38
-rw-r--r--crates/ra_ide/src/diagnostics.rs10
-rw-r--r--crates/ra_ide/src/folding_ranges.rs23
-rw-r--r--crates/ra_ide/src/hover.rs31
-rw-r--r--crates/ra_ide/src/syntax_highlighting.rs67
-rw-r--r--crates/ra_ide/src/syntax_highlighting/tests.rs21
-rw-r--r--crates/ra_ide/test_data/highlight_unsafe.html18
-rw-r--r--crates/ra_ide/test_data/highlighting.html9
-rw-r--r--crates/ra_ide_db/src/change.rs24
-rw-r--r--crates/ra_ide_db/src/defs.rs25
-rw-r--r--crates/ra_ssr/src/lib.rs7
-rw-r--r--crates/ra_ssr/src/resolving.rs23
-rw-r--r--crates/ra_ssr/src/search.rs9
-rw-r--r--crates/ra_ssr/src/tests.rs35
-rw-r--r--crates/ra_syntax/src/ast/edit.rs2
-rw-r--r--crates/ra_syntax/src/ast/make.rs16
-rw-r--r--crates/rust-analyzer/src/caps.rs4
-rw-r--r--crates/rust-analyzer/src/document.rs6
-rw-r--r--crates/rust-analyzer/src/global_state.rs8
-rw-r--r--crates/rust-analyzer/src/handlers.rs46
-rw-r--r--crates/rust-analyzer/src/lsp_utils.rs40
-rw-r--r--crates/rust-analyzer/src/main_loop.rs21
-rw-r--r--crates/rust-analyzer/src/semantic_tokens.rs139
-rw-r--r--crates/rust-analyzer/src/to_proto.rs22
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 @@
1use hir::{AssocItem, MacroDef, ModuleDef, Name, PathResolution, ScopeDef, SemanticsScope};
2use ra_ide_db::{
3 defs::{classify_name_ref, Definition, NameRefClass},
4 RootDatabase,
5};
6use ra_syntax::{algo, ast, match_ast, AstNode, SyntaxNode, SyntaxToken, T};
7
8use crate::{
9 assist_context::{AssistBuilder, AssistContext, Assists},
10 AssistId, AssistKind,
11};
12
13use 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// ```
40pub(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
63fn 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)]
68enum Def {
69 ModuleDef(ModuleDef),
70 MacroDef(MacroDef),
71}
72
73impl 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
82fn 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
110fn 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
144fn 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)]
184mod 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"
194mod foo {
195 pub struct Bar;
196 pub struct Baz;
197 pub struct Qux;
198
199 pub fn f() {}
200}
201
202use foo::*<|>;
203
204fn qux(bar: Bar, baz: Baz) {
205 f();
206}
207",
208 r"
209mod foo {
210 pub struct Bar;
211 pub struct Baz;
212 pub struct Qux;
213
214 pub fn f() {}
215}
216
217use foo::{Baz, Bar, f};
218
219fn 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"
231mod foo {
232 pub struct Bar;
233 pub struct Baz;
234 pub struct Qux;
235
236 pub fn f() {}
237}
238
239use foo::{*<|>, f};
240
241fn qux(bar: Bar, baz: Baz) {
242 f();
243}
244",
245 r"
246mod foo {
247 pub struct Bar;
248 pub struct Baz;
249 pub struct Qux;
250
251 pub fn f() {}
252}
253
254use foo::{Baz, Bar, f};
255
256fn 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"
268mod 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
282use foo::{bar::{*<|>, f}, baz::*};
283
284fn qux(bar: Bar, baz: Baz) {
285 f();
286 g();
287}
288",
289 r"
290mod 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
304use foo::{bar::{Baz, Bar, f}, baz::*};
305
306fn 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]
321macro_rules! bar {
322 () => ()
323}
324
325pub fn baz() {}
326
327//- /main.rs crate:main deps:foo
328use foo::*<|>;
329
330fn main() {
331 bar!();
332 baz();
333}
334",
335 r"
336use foo::{bar, baz};
337
338fn 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
352pub trait Tr {
353 fn method(&self) {}
354}
355impl Tr for () {}
356
357//- /main.rs crate:main deps:foo
358use foo::*<|>;
359
360fn main() {
361 ().method();
362}
363",
364 r"
365use foo::Tr;
366
367fn 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)]
176mod test { 176mod 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"
652pub use std::fmt;
653
654impl std::io<|> for Foo {
655}
656 ",
657 r"
658use std::io;
659
660pub use std::fmt;
661
662impl 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"
673pub(crate) use std::fmt;
674
675impl std::io<|> for Foo {
676}
677 ",
678 r"
679use std::io;
680
681pub(crate) use std::fmt;
682
683impl 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]
232fn doctest_expand_glob_import() {
233 check_doc_test(
234 "expand_glob_import",
235 r#####"
236mod foo {
237 pub struct Bar;
238 pub struct Baz;
239}
240
241use foo::*<|>;
242
243fn qux(bar: Bar, baz: Baz) {}
244"#####,
245 r#####"
246mod foo {
247 pub struct Bar;
248 pub struct Baz;
249}
250
251use foo::{Baz, Bar};
252
253fn qux(bar: Bar, baz: Baz) {}
254"#####,
255 )
256}
257
258#[test]
232fn doctest_extract_struct_from_enum_variant() { 259fn 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
5use hir::{self, ModPath}; 5use hir::{self, ModPath};
6use ra_syntax::{ 6use 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"
9doctest = false 9doctest = false
10 10
11[dependencies] 11[dependencies]
12salsa = "0.15.0" 12salsa = "0.15.2"
13rustc-hash = "1.1.0" 13rustc-hash = "1.1.0"
14 14
15ra_syntax = { path = "../ra_syntax" } 15ra_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};
16pub use hir_ty::db::{ 16pub 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]
26fn hir_database_is_object_safe() { 19fn 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)]
104mod test { 104mod 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
29scoped-tls = "1" 29scoped-tls = "1"
30 30
31chalk-solve = { version = "0.19.0" } 31chalk-solve = { version = "0.21.0" }
32chalk-ir = { version = "0.19.0" } 32chalk-ir = { version = "0.21.0" }
33chalk-recursive = { version = "0.19.0" } 33chalk-recursive = { version = "0.21.0" }
34 34
35[dev-dependencies] 35[dev-dependencies]
36expect = { path = "../expect" } 36expect = { 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;
6use hir_def::{ 6use 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};
11use hir_expand::diagnostics::DiagnosticSink; 12use 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#"
189struct Ty {
190 a: u8,
191}
192
193static mut static_mut: Ty = Ty { a: 0 };
194
195fn 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
1148pub enum ValueTyDefId { 1149pub 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}
1155impl_from!(FunctionId, StructId, EnumVariantId, ConstId, StaticId for ValueTyDefId); 1157impl_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#"
344struct Foo <fold block>{
345}</fold>
346"#,
347 );
348 }
349
350 #[test]
351 fn test_folds_traits() {
352 check(
353 r#"
354trait 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/// ```
521pub fn foo<|>(_: &Path) {}
522
523fn 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)]
5mod tests; 5mod tests;
6 6
7use hir::{Name, Semantics}; 7use hir::{Name, Semantics, VariantDef};
8use ra_ide_db::{ 8use 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
458fn 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
458fn highlight_element( 470fn 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
655fn highlight_name(db: &RootDatabase, def: Definition) -> Highlight { 679fn 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
727fn highlight_name_ref_by_syntax(name: ast::NameRef) -> Highlight { 761fn 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};
9fn test_highlighting() { 9fn test_highlighting() {
10 check_highlighting( 10 check_highlighting(
11 r#" 11 r#"
12use inner::{self as inner_mod};
13mod inner {}
14
12#[derive(Clone, Debug)] 15#[derive(Clone, Debug)]
13struct Foo { 16struct Foo {
14 pub x: i32, 17 pub x: i32,
@@ -272,19 +275,37 @@ fn test_unsafe_highlighting() {
272 r#" 275 r#"
273unsafe fn unsafe_fn() {} 276unsafe fn unsafe_fn() {}
274 277
278union Union {
279 a: u32,
280 b: f32,
281}
282
275struct HasUnsafeFn; 283struct HasUnsafeFn;
276 284
277impl HasUnsafeFn { 285impl HasUnsafeFn {
278 unsafe fn unsafe_method(&self) {} 286 unsafe fn unsafe_method(&self) {}
279} 287}
280 288
289struct TypeForStaticMut {
290 a: u8
291}
292
293static mut global_mut: TypeForStaticMut = TypeForStaticMut { a: 0 };
294
281fn main() { 295fn 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">=&gt;</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">=&gt;</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">&lt;</span><span class="lifetime declaration">'a</span><span class="punctuation">,</span> <span class="type_param declaration">T</span><span class="punctuation">&gt;</span><span class="punctuation">(</span><span class="punctuation">)</span> <span class="operator">-&gt;</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">&lt;</span><span class="lifetime declaration">'a</span><span class="punctuation">,</span> <span class="type_param declaration">T</span><span class="punctuation">&gt;</span><span class="punctuation">(</span><span class="punctuation">)</span> <span class="operator">-&gt;</span> <span class="type_param">T</span> <span class="punctuation">{</span>
67 <span class="function">foo</span><span class="operator">::</span><span class="punctuation">&lt;</span><span class="lifetime">'a</span><span class="punctuation">,</span> <span class="builtin_type">i32</span><span class="punctuation">&gt;</span><span class="punctuation">(</span><span class="punctuation">)</span> 70 <span class="function">foo</span><span class="operator">::</span><span class="punctuation">&lt;</span><span class="lifetime">'a</span><span class="punctuation">,</span> <span class="builtin_type">i32</span><span class="punctuation">&gt;</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::{
12use ra_prof::profile; 12use ra_prof::profile;
13use ra_syntax::{ 13use ra_syntax::{
14 ast::{self, AstNode}, 14 ast::{self, AstNode},
15 match_ast, 15 match_ast, SyntaxNode,
16}; 16};
17 17
18use crate::RootDatabase; 18use 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;
11pub(crate) struct ResolutionScope<'db> { 11pub(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
16pub(crate) struct ResolvedRule { 17pub(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
30pub(crate) struct ResolvedPath { 32pub(crate) struct ResolvedPath {
@@ -68,6 +70,7 @@ struct Resolver<'a, 'db> {
68 70
69impl Resolver<'_, '_> { 71impl 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
208fn 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(&current_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]
1049fn 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]
622fn test_increase_indent() { 622fn 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
20pub fn type_ref(text: &str) -> ast::Type { 20pub 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 {
30pub fn path_qualified(qual: ast::Path, segment: ast::PathSegment) -> ast::Path { 30pub 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}
33fn path_from_text(text: &str) -> ast::Path { 33pub 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
63pub fn use_item(use_tree: ast::UseTree) -> ast::Use { 63pub 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
67pub fn record_field(name: ast::NameRef, expr: Option<ast::Expr>) -> ast::RecordExprField { 67pub 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
78pub fn record_field_def(name: ast::NameRef, ty: ast::Type) -> ast::RecordField { 78pub 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
151pub fn bind_pat(name: ast::Name) -> ast::IdentPat { 151pub 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
159pub fn placeholder_pat() -> ast::WildcardPat { 159pub 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
291pub fn fn_def( 291pub 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)]
8pub(crate) struct DocumentData { 8pub(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
8use crossbeam_channel::{unbounded, Receiver, Sender}; 8use crossbeam_channel::{unbounded, Receiver, Sender};
9use flycheck::FlycheckHandle; 9use flycheck::FlycheckHandle;
10use lsp_types::Url; 10use lsp_types::{SemanticTokens, Url};
11use parking_lot::RwLock; 11use parking_lot::{Mutex, RwLock};
12use ra_db::{CrateId, VfsPath}; 12use ra_db::{CrateId, VfsPath};
13use ra_ide::{Analysis, AnalysisChange, AnalysisHost, FileId}; 13use ra_ide::{Analysis, AnalysisChange, AnalysisHost, FileId};
14use ra_project_model::{CargoWorkspace, ProcMacroClient, ProjectWorkspace, Target}; 14use 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};
20use ra_ide::{ 21use 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
1190pub(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, &params.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.
2use std::{borrow::Cow, error::Error, ops::Range}; 2use std::{error::Error, ops::Range};
3 3
4use lsp_server::Notification; 4use lsp_server::Notification;
5use ra_db::Canceled; 5use ra_db::Canceled;
@@ -84,8 +84,8 @@ impl GlobalState {
84pub(crate) fn apply_document_changes( 84pub(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.
3use std::{ 3use std::{
4 borrow::Cow,
5 env, fmt, panic, 4 env, fmt, panic,
6 time::{Duration, Instant}, 5 time::{Duration, Instant},
7}; 6};
8 7
9use crossbeam_channel::{select, Receiver}; 8use crossbeam_channel::{select, Receiver};
10use lsp_server::{Connection, Notification, Request, Response}; 9use lsp_server::{Connection, Notification, Request, Response};
11use lsp_types::{notification::Notification as _, DidChangeTextDocumentParams}; 10use lsp_types::notification::Notification as _;
12use ra_db::VfsPath; 11use ra_db::VfsPath;
13use ra_ide::{Canceled, FileId}; 12use ra_ide::{Canceled, FileId};
14use ra_prof::profile; 13use 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
54enum Event { 53enum 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(&params.text_document.uri) { 426 if let Ok(path) = from_proto::vfs_path(&params.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(&params.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
3use std::ops; 3use std::ops;
4 4
5use lsp_types::{Range, SemanticToken, SemanticTokenModifier, SemanticTokenType, SemanticTokens}; 5use lsp_types::{
6 Range, SemanticToken, SemanticTokenModifier, SemanticTokenType, SemanticTokens,
7 SemanticTokensEdit,
8};
6 9
7macro_rules! define_semantic_token_types { 10macro_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)]
93pub(crate) struct SemanticTokensBuilder { 95pub(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
99impl SemanticTokensBuilder { 102impl 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
141pub 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
134pub fn type_index(type_: SemanticTokenType) -> u32 { 167pub 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)]
172mod 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.
2use std::path::{self, Path}; 2use std::{
3 path::{self, Path},
4 sync::atomic::{AtomicU32, Ordering},
5};
3 6
4use itertools::Itertools; 7use itertools::Itertools;
5use ra_db::{FileId, FileRange}; 8use 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
309static TOKEN_RESULT_COUNTER: AtomicU32 = AtomicU32::new(1);
310
306pub(crate) fn semantic_tokens( 311pub(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
337pub(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, &current.data);
343 lsp_types::SemanticTokensEdits { result_id, edits }
344}
345
331fn semantic_token_type_and_modifiers( 346fn 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
742pub(crate) fn markup_content(markup: Markup) -> lsp_types::MarkupContent { 757pub(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)]