aboutsummaryrefslogtreecommitdiff
path: root/crates
diff options
context:
space:
mode:
Diffstat (limited to 'crates')
-rw-r--r--crates/ra_hir/src/code_model.rs8
-rw-r--r--crates/ra_hir_def/src/data.rs6
-rw-r--r--crates/ra_ide/src/display/navigation_target.rs66
-rw-r--r--crates/ra_ide/src/runnables.rs250
-rw-r--r--crates/ra_ide/src/snapshots/highlight_injection.html1
-rw-r--r--crates/ra_ide/src/snapshots/highlight_strings.html2
-rw-r--r--crates/ra_ide/src/snapshots/highlight_unsafe.html48
-rw-r--r--crates/ra_ide/src/snapshots/highlighting.html1
-rw-r--r--crates/ra_ide/src/snapshots/rainbow_highlighting.html1
-rw-r--r--crates/ra_ide/src/syntax_highlighting.rs25
-rw-r--r--crates/ra_ide/src/syntax_highlighting/html.rs1
-rw-r--r--crates/ra_ide/src/syntax_highlighting/tags.rs8
-rw-r--r--crates/ra_ide/src/syntax_highlighting/tests.rs32
-rw-r--r--crates/ra_proc_macro_srv/Cargo.toml1
-rw-r--r--crates/ra_proc_macro_srv/src/tests/utils.rs3
-rw-r--r--crates/ra_project_model/src/cargo_workspace.rs2
-rw-r--r--crates/ra_syntax/src/ast/tokens.rs5
-rw-r--r--crates/rust-analyzer/src/caps.rs3
-rw-r--r--crates/rust-analyzer/src/cargo_target_spec.rs85
-rw-r--r--crates/rust-analyzer/src/config.rs2
-rw-r--r--crates/rust-analyzer/src/diagnostics/snapshots/rust_analyzer__diagnostics__to_proto__tests__snap_rustc_unused_variable.snap2
-rw-r--r--crates/rust-analyzer/src/diagnostics/to_proto.rs3
-rw-r--r--crates/rust-analyzer/src/lsp_ext.rs30
-rw-r--r--crates/rust-analyzer/src/main_loop/handlers.rs162
-rw-r--r--crates/rust-analyzer/src/to_proto.rs40
-rw-r--r--crates/rust-analyzer/tests/heavy_tests/main.rs113
26 files changed, 607 insertions, 293 deletions
diff --git a/crates/ra_hir/src/code_model.rs b/crates/ra_hir/src/code_model.rs
index e40aeffbc..4a06f3bcd 100644
--- a/crates/ra_hir/src/code_model.rs
+++ b/crates/ra_hir/src/code_model.rs
@@ -637,6 +637,10 @@ impl Function {
637 db.function_data(self.id).params.clone() 637 db.function_data(self.id).params.clone()
638 } 638 }
639 639
640 pub fn is_unsafe(self, db: &dyn HirDatabase) -> bool {
641 db.function_data(self.id).is_unsafe
642 }
643
640 pub fn diagnostics(self, db: &dyn HirDatabase, sink: &mut DiagnosticSink) { 644 pub fn diagnostics(self, db: &dyn HirDatabase, sink: &mut DiagnosticSink) {
641 let _p = profile("Function::diagnostics"); 645 let _p = profile("Function::diagnostics");
642 let infer = db.infer(self.id.into()); 646 let infer = db.infer(self.id.into());
@@ -1190,6 +1194,10 @@ impl Type {
1190 ) 1194 )
1191 } 1195 }
1192 1196
1197 pub fn is_raw_ptr(&self) -> bool {
1198 matches!(&self.ty.value, Ty::Apply(ApplicationTy { ctor: TypeCtor::RawPtr(..), .. }))
1199 }
1200
1193 pub fn contains_unknown(&self) -> bool { 1201 pub fn contains_unknown(&self) -> bool {
1194 return go(&self.ty.value); 1202 return go(&self.ty.value);
1195 1203
diff --git a/crates/ra_hir_def/src/data.rs b/crates/ra_hir_def/src/data.rs
index e2130d931..807195d25 100644
--- a/crates/ra_hir_def/src/data.rs
+++ b/crates/ra_hir_def/src/data.rs
@@ -34,6 +34,7 @@ pub struct FunctionData {
34 /// True if the first param is `self`. This is relevant to decide whether this 34 /// True if the first param is `self`. This is relevant to decide whether this
35 /// can be called as a method. 35 /// can be called as a method.
36 pub has_self_param: bool, 36 pub has_self_param: bool,
37 pub is_unsafe: bool,
37 pub visibility: RawVisibility, 38 pub visibility: RawVisibility,
38} 39}
39 40
@@ -85,11 +86,14 @@ impl FunctionData {
85 ret_type 86 ret_type
86 }; 87 };
87 88
89 let is_unsafe = src.value.unsafe_token().is_some();
90
88 let vis_default = RawVisibility::default_for_container(loc.container); 91 let vis_default = RawVisibility::default_for_container(loc.container);
89 let visibility = 92 let visibility =
90 RawVisibility::from_ast_with_default(db, vis_default, src.map(|s| s.visibility())); 93 RawVisibility::from_ast_with_default(db, vis_default, src.map(|s| s.visibility()));
91 94
92 let sig = FunctionData { name, params, ret_type, has_self_param, visibility, attrs }; 95 let sig =
96 FunctionData { name, params, ret_type, has_self_param, is_unsafe, visibility, attrs };
93 Arc::new(sig) 97 Arc::new(sig)
94 } 98 }
95} 99}
diff --git a/crates/ra_ide/src/display/navigation_target.rs b/crates/ra_ide/src/display/navigation_target.rs
index 5da28edd2..c7bb1e69f 100644
--- a/crates/ra_ide/src/display/navigation_target.rs
+++ b/crates/ra_ide/src/display/navigation_target.rs
@@ -92,15 +92,16 @@ impl NavigationTarget {
92 let name = module.name(db).map(|it| it.to_string().into()).unwrap_or_default(); 92 let name = module.name(db).map(|it| it.to_string().into()).unwrap_or_default();
93 if let Some(src) = module.declaration_source(db) { 93 if let Some(src) = module.declaration_source(db) {
94 let frange = original_range(db, src.as_ref().map(|it| it.syntax())); 94 let frange = original_range(db, src.as_ref().map(|it| it.syntax()));
95 return NavigationTarget::from_syntax( 95 let mut res = NavigationTarget::from_syntax(
96 frange.file_id, 96 frange.file_id,
97 name, 97 name,
98 None, 98 None,
99 frange.range, 99 frange.range,
100 src.value.syntax().kind(), 100 src.value.syntax().kind(),
101 src.value.doc_comment_text(),
102 src.value.short_label(),
103 ); 101 );
102 res.docs = src.value.doc_comment_text();
103 res.description = src.value.short_label();
104 return res;
104 } 105 }
105 module.to_nav(db) 106 module.to_nav(db)
106 } 107 }
@@ -130,11 +131,9 @@ impl NavigationTarget {
130 } 131 }
131 132
132 /// Allows `NavigationTarget` to be created from a `NameOwner` 133 /// Allows `NavigationTarget` to be created from a `NameOwner`
133 fn from_named( 134 pub(crate) fn from_named(
134 db: &RootDatabase, 135 db: &RootDatabase,
135 node: InFile<&dyn ast::NameOwner>, 136 node: InFile<&dyn ast::NameOwner>,
136 docs: Option<String>,
137 description: Option<String>,
138 ) -> NavigationTarget { 137 ) -> NavigationTarget {
139 //FIXME: use `_` instead of empty string 138 //FIXME: use `_` instead of empty string
140 let name = node.value.name().map(|it| it.text().clone()).unwrap_or_default(); 139 let name = node.value.name().map(|it| it.text().clone()).unwrap_or_default();
@@ -148,8 +147,6 @@ impl NavigationTarget {
148 focus_range, 147 focus_range,
149 frange.range, 148 frange.range,
150 node.value.syntax().kind(), 149 node.value.syntax().kind(),
151 docs,
152 description,
153 ) 150 )
154 } 151 }
155 152
@@ -159,8 +156,6 @@ impl NavigationTarget {
159 focus_range: Option<TextRange>, 156 focus_range: Option<TextRange>,
160 full_range: TextRange, 157 full_range: TextRange,
161 kind: SyntaxKind, 158 kind: SyntaxKind,
162 docs: Option<String>,
163 description: Option<String>,
164 ) -> NavigationTarget { 159 ) -> NavigationTarget {
165 NavigationTarget { 160 NavigationTarget {
166 file_id, 161 file_id,
@@ -169,8 +164,8 @@ impl NavigationTarget {
169 full_range, 164 full_range,
170 focus_range, 165 focus_range,
171 container_name: None, 166 container_name: None,
172 description, 167 description: None,
173 docs, 168 docs: None,
174 } 169 }
175 } 170 }
176} 171}
@@ -238,12 +233,11 @@ where
238{ 233{
239 fn to_nav(&self, db: &RootDatabase) -> NavigationTarget { 234 fn to_nav(&self, db: &RootDatabase) -> NavigationTarget {
240 let src = self.source(db); 235 let src = self.source(db);
241 NavigationTarget::from_named( 236 let mut res =
242 db, 237 NavigationTarget::from_named(db, src.as_ref().map(|it| it as &dyn ast::NameOwner));
243 src.as_ref().map(|it| it as &dyn ast::NameOwner), 238 res.docs = src.value.doc_comment_text();
244 src.value.doc_comment_text(), 239 res.description = src.value.short_label();
245 src.value.short_label(), 240 res
246 )
247 } 241 }
248} 242}
249 243
@@ -258,15 +252,7 @@ impl ToNav for hir::Module {
258 } 252 }
259 }; 253 };
260 let frange = original_range(db, src.with_value(syntax)); 254 let frange = original_range(db, src.with_value(syntax));
261 NavigationTarget::from_syntax( 255 NavigationTarget::from_syntax(frange.file_id, name, focus, frange.range, syntax.kind())
262 frange.file_id,
263 name,
264 focus,
265 frange.range,
266 syntax.kind(),
267 None,
268 None,
269 )
270 } 256 }
271} 257}
272 258
@@ -285,8 +271,6 @@ impl ToNav for hir::ImplDef {
285 None, 271 None,
286 frange.range, 272 frange.range,
287 src.value.syntax().kind(), 273 src.value.syntax().kind(),
288 None,
289 None,
290 ) 274 )
291 } 275 }
292} 276}
@@ -296,12 +280,12 @@ impl ToNav for hir::Field {
296 let src = self.source(db); 280 let src = self.source(db);
297 281
298 match &src.value { 282 match &src.value {
299 FieldSource::Named(it) => NavigationTarget::from_named( 283 FieldSource::Named(it) => {
300 db, 284 let mut res = NavigationTarget::from_named(db, src.with_value(it));
301 src.with_value(it), 285 res.docs = it.doc_comment_text();
302 it.doc_comment_text(), 286 res.description = it.short_label();
303 it.short_label(), 287 res
304 ), 288 }
305 FieldSource::Pos(it) => { 289 FieldSource::Pos(it) => {
306 let frange = original_range(db, src.with_value(it.syntax())); 290 let frange = original_range(db, src.with_value(it.syntax()));
307 NavigationTarget::from_syntax( 291 NavigationTarget::from_syntax(
@@ -310,8 +294,6 @@ impl ToNav for hir::Field {
310 None, 294 None,
311 frange.range, 295 frange.range,
312 it.syntax().kind(), 296 it.syntax().kind(),
313 None,
314 None,
315 ) 297 )
316 } 298 }
317 } 299 }
@@ -322,12 +304,10 @@ impl ToNav for hir::MacroDef {
322 fn to_nav(&self, db: &RootDatabase) -> NavigationTarget { 304 fn to_nav(&self, db: &RootDatabase) -> NavigationTarget {
323 let src = self.source(db); 305 let src = self.source(db);
324 log::debug!("nav target {:#?}", src.value.syntax()); 306 log::debug!("nav target {:#?}", src.value.syntax());
325 NavigationTarget::from_named( 307 let mut res =
326 db, 308 NavigationTarget::from_named(db, src.as_ref().map(|it| it as &dyn ast::NameOwner));
327 src.as_ref().map(|it| it as &dyn ast::NameOwner), 309 res.docs = src.value.doc_comment_text();
328 src.value.doc_comment_text(), 310 res
329 None,
330 )
331 } 311 }
332} 312}
333 313
diff --git a/crates/ra_ide/src/runnables.rs b/crates/ra_ide/src/runnables.rs
index 286d45eee..f32ce0d22 100644
--- a/crates/ra_ide/src/runnables.rs
+++ b/crates/ra_ide/src/runnables.rs
@@ -1,19 +1,19 @@
1use std::fmt;
2
1use hir::{AsAssocItem, Attrs, HirFileId, InFile, Semantics}; 3use hir::{AsAssocItem, Attrs, HirFileId, InFile, Semantics};
2use itertools::Itertools; 4use itertools::Itertools;
5use ra_cfg::CfgExpr;
3use ra_ide_db::RootDatabase; 6use ra_ide_db::RootDatabase;
4use ra_syntax::{ 7use ra_syntax::{
5 ast::{self, AstNode, AttrsOwner, ModuleItemOwner, NameOwner}, 8 ast::{self, AstNode, AttrsOwner, DocCommentsOwner, ModuleItemOwner, NameOwner},
6 match_ast, SyntaxNode, TextRange, 9 match_ast, SyntaxNode,
7}; 10};
8 11
9use crate::FileId; 12use crate::{display::ToNav, FileId, NavigationTarget};
10use ast::DocCommentsOwner;
11use ra_cfg::CfgExpr;
12use std::fmt::Display;
13 13
14#[derive(Debug)] 14#[derive(Debug)]
15pub struct Runnable { 15pub struct Runnable {
16 pub range: TextRange, 16 pub nav: NavigationTarget,
17 pub kind: RunnableKind, 17 pub kind: RunnableKind,
18 pub cfg_exprs: Vec<CfgExpr>, 18 pub cfg_exprs: Vec<CfgExpr>,
19} 19}
@@ -24,8 +24,8 @@ pub enum TestId {
24 Path(String), 24 Path(String),
25} 25}
26 26
27impl Display for TestId { 27impl fmt::Display for TestId {
28 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 28 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
29 match self { 29 match self {
30 TestId::Name(name) => write!(f, "{}", name), 30 TestId::Name(name) => write!(f, "{}", name),
31 TestId::Path(path) => write!(f, "{}", path), 31 TestId::Path(path) => write!(f, "{}", path),
@@ -131,7 +131,8 @@ fn runnable_fn(
131 let cfg_exprs = 131 let cfg_exprs =
132 attrs.by_key("cfg").tt_values().map(|subtree| ra_cfg::parse_cfg(subtree)).collect(); 132 attrs.by_key("cfg").tt_values().map(|subtree| ra_cfg::parse_cfg(subtree)).collect();
133 133
134 Some(Runnable { range: fn_def.syntax().text_range(), kind, cfg_exprs }) 134 let nav = NavigationTarget::from_named(sema.db, InFile::new(file_id.into(), &fn_def));
135 Some(Runnable { nav, kind, cfg_exprs })
135} 136}
136 137
137#[derive(Debug)] 138#[derive(Debug)]
@@ -183,7 +184,6 @@ fn runnable_mod(
183 if !has_test_function { 184 if !has_test_function {
184 return None; 185 return None;
185 } 186 }
186 let range = module.syntax().text_range();
187 let module_def = sema.to_def(&module)?; 187 let module_def = sema.to_def(&module)?;
188 188
189 let path = module_def 189 let path = module_def
@@ -197,7 +197,8 @@ fn runnable_mod(
197 let cfg_exprs = 197 let cfg_exprs =
198 attrs.by_key("cfg").tt_values().map(|subtree| ra_cfg::parse_cfg(subtree)).collect(); 198 attrs.by_key("cfg").tt_values().map(|subtree| ra_cfg::parse_cfg(subtree)).collect();
199 199
200 Some(Runnable { range, kind: RunnableKind::TestMod { path }, cfg_exprs }) 200 let nav = module_def.to_nav(sema.db);
201 Some(Runnable { nav, kind: RunnableKind::TestMod { path }, cfg_exprs })
201} 202}
202 203
203#[cfg(test)] 204#[cfg(test)]
@@ -227,12 +228,38 @@ mod tests {
227 @r###" 228 @r###"
228 [ 229 [
229 Runnable { 230 Runnable {
230 range: 1..21, 231 nav: NavigationTarget {
232 file_id: FileId(
233 1,
234 ),
235 full_range: 1..21,
236 name: "main",
237 kind: FN_DEF,
238 focus_range: Some(
239 12..16,
240 ),
241 container_name: None,
242 description: None,
243 docs: None,
244 },
231 kind: Bin, 245 kind: Bin,
232 cfg_exprs: [], 246 cfg_exprs: [],
233 }, 247 },
234 Runnable { 248 Runnable {
235 range: 22..46, 249 nav: NavigationTarget {
250 file_id: FileId(
251 1,
252 ),
253 full_range: 22..46,
254 name: "test_foo",
255 kind: FN_DEF,
256 focus_range: Some(
257 33..41,
258 ),
259 container_name: None,
260 description: None,
261 docs: None,
262 },
236 kind: Test { 263 kind: Test {
237 test_id: Path( 264 test_id: Path(
238 "test_foo", 265 "test_foo",
@@ -244,7 +271,20 @@ mod tests {
244 cfg_exprs: [], 271 cfg_exprs: [],
245 }, 272 },
246 Runnable { 273 Runnable {
247 range: 47..81, 274 nav: NavigationTarget {
275 file_id: FileId(
276 1,
277 ),
278 full_range: 47..81,
279 name: "test_foo",
280 kind: FN_DEF,
281 focus_range: Some(
282 68..76,
283 ),
284 container_name: None,
285 description: None,
286 docs: None,
287 },
248 kind: Test { 288 kind: Test {
249 test_id: Path( 289 test_id: Path(
250 "test_foo", 290 "test_foo",
@@ -279,12 +319,38 @@ mod tests {
279 @r###" 319 @r###"
280 [ 320 [
281 Runnable { 321 Runnable {
282 range: 1..21, 322 nav: NavigationTarget {
323 file_id: FileId(
324 1,
325 ),
326 full_range: 1..21,
327 name: "main",
328 kind: FN_DEF,
329 focus_range: Some(
330 12..16,
331 ),
332 container_name: None,
333 description: None,
334 docs: None,
335 },
283 kind: Bin, 336 kind: Bin,
284 cfg_exprs: [], 337 cfg_exprs: [],
285 }, 338 },
286 Runnable { 339 Runnable {
287 range: 22..64, 340 nav: NavigationTarget {
341 file_id: FileId(
342 1,
343 ),
344 full_range: 22..64,
345 name: "foo",
346 kind: FN_DEF,
347 focus_range: Some(
348 56..59,
349 ),
350 container_name: None,
351 description: None,
352 docs: None,
353 },
288 kind: DocTest { 354 kind: DocTest {
289 test_id: Path( 355 test_id: Path(
290 "foo", 356 "foo",
@@ -319,12 +385,38 @@ mod tests {
319 @r###" 385 @r###"
320 [ 386 [
321 Runnable { 387 Runnable {
322 range: 1..21, 388 nav: NavigationTarget {
389 file_id: FileId(
390 1,
391 ),
392 full_range: 1..21,
393 name: "main",
394 kind: FN_DEF,
395 focus_range: Some(
396 12..16,
397 ),
398 container_name: None,
399 description: None,
400 docs: None,
401 },
323 kind: Bin, 402 kind: Bin,
324 cfg_exprs: [], 403 cfg_exprs: [],
325 }, 404 },
326 Runnable { 405 Runnable {
327 range: 51..105, 406 nav: NavigationTarget {
407 file_id: FileId(
408 1,
409 ),
410 full_range: 51..105,
411 name: "foo",
412 kind: FN_DEF,
413 focus_range: Some(
414 97..100,
415 ),
416 container_name: None,
417 description: None,
418 docs: None,
419 },
328 kind: DocTest { 420 kind: DocTest {
329 test_id: Path( 421 test_id: Path(
330 "Data::foo", 422 "Data::foo",
@@ -354,14 +446,40 @@ mod tests {
354 @r###" 446 @r###"
355 [ 447 [
356 Runnable { 448 Runnable {
357 range: 1..59, 449 nav: NavigationTarget {
450 file_id: FileId(
451 1,
452 ),
453 full_range: 1..59,
454 name: "test_mod",
455 kind: MODULE,
456 focus_range: Some(
457 13..21,
458 ),
459 container_name: None,
460 description: None,
461 docs: None,
462 },
358 kind: TestMod { 463 kind: TestMod {
359 path: "test_mod", 464 path: "test_mod",
360 }, 465 },
361 cfg_exprs: [], 466 cfg_exprs: [],
362 }, 467 },
363 Runnable { 468 Runnable {
364 range: 28..57, 469 nav: NavigationTarget {
470 file_id: FileId(
471 1,
472 ),
473 full_range: 28..57,
474 name: "test_foo1",
475 kind: FN_DEF,
476 focus_range: Some(
477 43..52,
478 ),
479 container_name: None,
480 description: None,
481 docs: None,
482 },
365 kind: Test { 483 kind: Test {
366 test_id: Path( 484 test_id: Path(
367 "test_mod::test_foo1", 485 "test_mod::test_foo1",
@@ -396,14 +514,40 @@ mod tests {
396 @r###" 514 @r###"
397 [ 515 [
398 Runnable { 516 Runnable {
399 range: 23..85, 517 nav: NavigationTarget {
518 file_id: FileId(
519 1,
520 ),
521 full_range: 23..85,
522 name: "test_mod",
523 kind: MODULE,
524 focus_range: Some(
525 27..35,
526 ),
527 container_name: None,
528 description: None,
529 docs: None,
530 },
400 kind: TestMod { 531 kind: TestMod {
401 path: "foo::test_mod", 532 path: "foo::test_mod",
402 }, 533 },
403 cfg_exprs: [], 534 cfg_exprs: [],
404 }, 535 },
405 Runnable { 536 Runnable {
406 range: 46..79, 537 nav: NavigationTarget {
538 file_id: FileId(
539 1,
540 ),
541 full_range: 46..79,
542 name: "test_foo1",
543 kind: FN_DEF,
544 focus_range: Some(
545 65..74,
546 ),
547 container_name: None,
548 description: None,
549 docs: None,
550 },
407 kind: Test { 551 kind: Test {
408 test_id: Path( 552 test_id: Path(
409 "foo::test_mod::test_foo1", 553 "foo::test_mod::test_foo1",
@@ -440,14 +584,40 @@ mod tests {
440 @r###" 584 @r###"
441 [ 585 [
442 Runnable { 586 Runnable {
443 range: 41..115, 587 nav: NavigationTarget {
588 file_id: FileId(
589 1,
590 ),
591 full_range: 41..115,
592 name: "test_mod",
593 kind: MODULE,
594 focus_range: Some(
595 45..53,
596 ),
597 container_name: None,
598 description: None,
599 docs: None,
600 },
444 kind: TestMod { 601 kind: TestMod {
445 path: "foo::bar::test_mod", 602 path: "foo::bar::test_mod",
446 }, 603 },
447 cfg_exprs: [], 604 cfg_exprs: [],
448 }, 605 },
449 Runnable { 606 Runnable {
450 range: 68..105, 607 nav: NavigationTarget {
608 file_id: FileId(
609 1,
610 ),
611 full_range: 68..105,
612 name: "test_foo1",
613 kind: FN_DEF,
614 focus_range: Some(
615 91..100,
616 ),
617 container_name: None,
618 description: None,
619 docs: None,
620 },
451 kind: Test { 621 kind: Test {
452 test_id: Path( 622 test_id: Path(
453 "foo::bar::test_mod::test_foo1", 623 "foo::bar::test_mod::test_foo1",
@@ -479,7 +649,20 @@ mod tests {
479 @r###" 649 @r###"
480 [ 650 [
481 Runnable { 651 Runnable {
482 range: 1..58, 652 nav: NavigationTarget {
653 file_id: FileId(
654 1,
655 ),
656 full_range: 1..58,
657 name: "test_foo1",
658 kind: FN_DEF,
659 focus_range: Some(
660 44..53,
661 ),
662 container_name: None,
663 description: None,
664 docs: None,
665 },
483 kind: Test { 666 kind: Test {
484 test_id: Path( 667 test_id: Path(
485 "test_foo1", 668 "test_foo1",
@@ -516,7 +699,20 @@ mod tests {
516 @r###" 699 @r###"
517 [ 700 [
518 Runnable { 701 Runnable {
519 range: 1..80, 702 nav: NavigationTarget {
703 file_id: FileId(
704 1,
705 ),
706 full_range: 1..80,
707 name: "test_foo1",
708 kind: FN_DEF,
709 focus_range: Some(
710 66..75,
711 ),
712 container_name: None,
713 description: None,
714 docs: None,
715 },
520 kind: Test { 716 kind: Test {
521 test_id: Path( 717 test_id: Path(
522 "test_foo1", 718 "test_foo1",
diff --git a/crates/ra_ide/src/snapshots/highlight_injection.html b/crates/ra_ide/src/snapshots/highlight_injection.html
index 68fc589bc..fcdc98201 100644
--- a/crates/ra_ide/src/snapshots/highlight_injection.html
+++ b/crates/ra_ide/src/snapshots/highlight_injection.html
@@ -10,6 +10,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
10.string_literal { color: #CC9393; } 10.string_literal { color: #CC9393; }
11.field { color: #94BFF3; } 11.field { color: #94BFF3; }
12.function { color: #93E0E3; } 12.function { color: #93E0E3; }
13.operator.unsafe { color: #E28C14; }
13.parameter { color: #94BFF3; } 14.parameter { color: #94BFF3; }
14.text { color: #DCDCCC; } 15.text { color: #DCDCCC; }
15.type { color: #7CB8BB; } 16.type { color: #7CB8BB; }
diff --git a/crates/ra_ide/src/snapshots/highlight_strings.html b/crates/ra_ide/src/snapshots/highlight_strings.html
index 326744361..e97192b61 100644
--- a/crates/ra_ide/src/snapshots/highlight_strings.html
+++ b/crates/ra_ide/src/snapshots/highlight_strings.html
@@ -10,6 +10,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
10.string_literal { color: #CC9393; } 10.string_literal { color: #CC9393; }
11.field { color: #94BFF3; } 11.field { color: #94BFF3; }
12.function { color: #93E0E3; } 12.function { color: #93E0E3; }
13.operator.unsafe { color: #E28C14; }
13.parameter { color: #94BFF3; } 14.parameter { color: #94BFF3; }
14.text { color: #DCDCCC; } 15.text { color: #DCDCCC; }
15.type { color: #7CB8BB; } 16.type { color: #7CB8BB; }
@@ -52,6 +53,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
52 <span class="macro">println!</span>(<span class="string_literal">"</span><span class="format_specifier">{</span><span class="variable">argument</span><span class="format_specifier">}</span><span class="string_literal">"</span>, argument = <span class="string_literal">"test"</span>); <span class="comment">// =&gt; "test"</span> 53 <span class="macro">println!</span>(<span class="string_literal">"</span><span class="format_specifier">{</span><span class="variable">argument</span><span class="format_specifier">}</span><span class="string_literal">"</span>, argument = <span class="string_literal">"test"</span>); <span class="comment">// =&gt; "test"</span>
53 <span class="macro">println!</span>(<span class="string_literal">"</span><span class="format_specifier">{</span><span class="variable">name</span><span class="format_specifier">}</span><span class="string_literal"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal">"</span>, <span class="numeric_literal">1</span>, name = <span class="numeric_literal">2</span>); <span class="comment">// =&gt; "2 1"</span> 54 <span class="macro">println!</span>(<span class="string_literal">"</span><span class="format_specifier">{</span><span class="variable">name</span><span class="format_specifier">}</span><span class="string_literal"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal">"</span>, <span class="numeric_literal">1</span>, name = <span class="numeric_literal">2</span>); <span class="comment">// =&gt; "2 1"</span>
54 <span class="macro">println!</span>(<span class="string_literal">"</span><span class="format_specifier">{</span><span class="variable">a</span><span class="format_specifier">}</span><span class="string_literal"> </span><span class="format_specifier">{</span><span class="variable">c</span><span class="format_specifier">}</span><span class="string_literal"> </span><span class="format_specifier">{</span><span class="variable">b</span><span class="format_specifier">}</span><span class="string_literal">"</span>, a=<span class="string_literal">"a"</span>, b=<span class="char_literal">'b'</span>, c=<span class="numeric_literal">3</span>); <span class="comment">// =&gt; "a 3 b"</span> 55 <span class="macro">println!</span>(<span class="string_literal">"</span><span class="format_specifier">{</span><span class="variable">a</span><span class="format_specifier">}</span><span class="string_literal"> </span><span class="format_specifier">{</span><span class="variable">c</span><span class="format_specifier">}</span><span class="string_literal"> </span><span class="format_specifier">{</span><span class="variable">b</span><span class="format_specifier">}</span><span class="string_literal">"</span>, a=<span class="string_literal">"a"</span>, b=<span class="char_literal">'b'</span>, c=<span class="numeric_literal">3</span>); <span class="comment">// =&gt; "a 3 b"</span>
56 <span class="macro">println!</span>(<span class="string_literal">"{{</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal">}}"</span>, <span class="numeric_literal">2</span>); <span class="comment">// =&gt; "{2}"</span>
55 <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal">!"</span>, <span class="string_literal">"x"</span>); 57 <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal">!"</span>, <span class="string_literal">"x"</span>);
56 <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="numeric_literal">1</span><span class="format_specifier">$</span><span class="format_specifier">}</span><span class="string_literal">!"</span>, <span class="string_literal">"x"</span>, <span class="numeric_literal">5</span>); 58 <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="numeric_literal">1</span><span class="format_specifier">$</span><span class="format_specifier">}</span><span class="string_literal">!"</span>, <span class="string_literal">"x"</span>, <span class="numeric_literal">5</span>);
57 <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="numeric_literal">1</span><span class="format_specifier">:</span><span class="numeric_literal">0</span><span class="format_specifier">$</span><span class="format_specifier">}</span><span class="string_literal">!"</span>, <span class="numeric_literal">5</span>, <span class="string_literal">"x"</span>); 59 <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="numeric_literal">1</span><span class="format_specifier">:</span><span class="numeric_literal">0</span><span class="format_specifier">$</span><span class="format_specifier">}</span><span class="string_literal">!"</span>, <span class="numeric_literal">5</span>, <span class="string_literal">"x"</span>);
diff --git a/crates/ra_ide/src/snapshots/highlight_unsafe.html b/crates/ra_ide/src/snapshots/highlight_unsafe.html
new file mode 100644
index 000000000..17ffc727c
--- /dev/null
+++ b/crates/ra_ide/src/snapshots/highlight_unsafe.html
@@ -0,0 +1,48 @@
1
2<style>
3body { margin: 0; }
4pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padding: 0.4em; }
5
6.lifetime { color: #DFAF8F; font-style: italic; }
7.comment { color: #7F9F7F; }
8.struct, .enum { color: #7CB8BB; }
9.enum_variant { color: #BDE0F3; }
10.string_literal { color: #CC9393; }
11.field { color: #94BFF3; }
12.function { color: #93E0E3; }
13.operator.unsafe { color: #E28C14; }
14.parameter { color: #94BFF3; }
15.text { color: #DCDCCC; }
16.type { color: #7CB8BB; }
17.builtin_type { color: #8CD0D3; }
18.type_param { color: #DFAF8F; }
19.attribute { color: #94BFF3; }
20.numeric_literal { color: #BFEBBF; }
21.bool_literal { color: #BFE6EB; }
22.macro { color: #94BFF3; }
23.module { color: #AFD8AF; }
24.variable { color: #DCDCCC; }
25.format_specifier { color: #CC696B; }
26.mutable { text-decoration: underline; }
27
28.keyword { color: #F0DFAF; font-weight: bold; }
29.keyword.unsafe { color: #BC8383; font-weight: bold; }
30.control { font-style: italic; }
31</style>
32<pre><code><span class="keyword unsafe">unsafe</span> <span class="keyword">fn</span> <span class="function declaration unsafe">unsafe_fn</span>() {}
33
34<span class="keyword">struct</span> <span class="struct declaration">HasUnsafeFn</span>;
35
36<span class="keyword">impl</span> <span class="struct">HasUnsafeFn</span> {
37 <span class="keyword unsafe">unsafe</span> <span class="keyword">fn</span> <span class="function declaration unsafe">unsafe_method</span>(&<span class="self_keyword">self</span>) {}
38}
39
40<span class="keyword">fn</span> <span class="function declaration">main</span>() {
41 <span class="keyword">let</span> <span class="variable declaration">x</span> = &<span class="numeric_literal">5</span> <span class="keyword">as</span> *<span class="keyword">const</span> <span class="builtin_type">usize</span>;
42 <span class="keyword unsafe">unsafe</span> {
43 <span class="function unsafe">unsafe_fn</span>();
44 <span class="struct">HasUnsafeFn</span>.<span class="function unsafe">unsafe_method</span>();
45 <span class="keyword">let</span> <span class="variable declaration">y</span> = <span class="operator unsafe">*</span><span class="variable">x</span>;
46 <span class="keyword">let</span> <span class="variable declaration">z</span> = -<span class="variable">x</span>;
47 }
48}</code></pre> \ No newline at end of file
diff --git a/crates/ra_ide/src/snapshots/highlighting.html b/crates/ra_ide/src/snapshots/highlighting.html
index 352e35095..42c5f3e55 100644
--- a/crates/ra_ide/src/snapshots/highlighting.html
+++ b/crates/ra_ide/src/snapshots/highlighting.html
@@ -10,6 +10,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
10.string_literal { color: #CC9393; } 10.string_literal { color: #CC9393; }
11.field { color: #94BFF3; } 11.field { color: #94BFF3; }
12.function { color: #93E0E3; } 12.function { color: #93E0E3; }
13.operator.unsafe { color: #E28C14; }
13.parameter { color: #94BFF3; } 14.parameter { color: #94BFF3; }
14.text { color: #DCDCCC; } 15.text { color: #DCDCCC; }
15.type { color: #7CB8BB; } 16.type { color: #7CB8BB; }
diff --git a/crates/ra_ide/src/snapshots/rainbow_highlighting.html b/crates/ra_ide/src/snapshots/rainbow_highlighting.html
index 2a0294f71..2dd61d20d 100644
--- a/crates/ra_ide/src/snapshots/rainbow_highlighting.html
+++ b/crates/ra_ide/src/snapshots/rainbow_highlighting.html
@@ -10,6 +10,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
10.string_literal { color: #CC9393; } 10.string_literal { color: #CC9393; }
11.field { color: #94BFF3; } 11.field { color: #94BFF3; }
12.function { color: #93E0E3; } 12.function { color: #93E0E3; }
13.operator.unsafe { color: #E28C14; }
13.parameter { color: #94BFF3; } 14.parameter { color: #94BFF3; }
14.text { color: #DCDCCC; } 15.text { color: #DCDCCC; }
15.type { color: #7CB8BB; } 16.type { color: #7CB8BB; }
diff --git a/crates/ra_ide/src/syntax_highlighting.rs b/crates/ra_ide/src/syntax_highlighting.rs
index 0b53ebe69..19ecd54d6 100644
--- a/crates/ra_ide/src/syntax_highlighting.rs
+++ b/crates/ra_ide/src/syntax_highlighting.rs
@@ -406,6 +406,23 @@ fn highlight_element(
406 _ => h, 406 _ => h,
407 } 407 }
408 } 408 }
409 PREFIX_EXPR => {
410 let prefix_expr = element.into_node().and_then(ast::PrefixExpr::cast)?;
411 match prefix_expr.op_kind() {
412 Some(ast::PrefixOp::Deref) => {}
413 _ => return None,
414 }
415
416 let expr = prefix_expr.expr()?;
417 let ty = sema.type_of_expr(&expr)?;
418 if !ty.is_raw_ptr() {
419 return None;
420 }
421
422 let mut h = Highlight::new(HighlightTag::Operator);
423 h |= HighlightModifier::Unsafe;
424 h
425 }
409 426
410 k if k.is_keyword() => { 427 k if k.is_keyword() => {
411 let h = Highlight::new(HighlightTag::Keyword); 428 let h = Highlight::new(HighlightTag::Keyword);
@@ -458,7 +475,13 @@ fn highlight_name(db: &RootDatabase, def: Definition) -> Highlight {
458 Definition::Field(_) => HighlightTag::Field, 475 Definition::Field(_) => HighlightTag::Field,
459 Definition::ModuleDef(def) => match def { 476 Definition::ModuleDef(def) => match def {
460 hir::ModuleDef::Module(_) => HighlightTag::Module, 477 hir::ModuleDef::Module(_) => HighlightTag::Module,
461 hir::ModuleDef::Function(_) => HighlightTag::Function, 478 hir::ModuleDef::Function(func) => {
479 let mut h = HighlightTag::Function.into();
480 if func.is_unsafe(db) {
481 h |= HighlightModifier::Unsafe;
482 }
483 return h;
484 }
462 hir::ModuleDef::Adt(hir::Adt::Struct(_)) => HighlightTag::Struct, 485 hir::ModuleDef::Adt(hir::Adt::Struct(_)) => HighlightTag::Struct,
463 hir::ModuleDef::Adt(hir::Adt::Enum(_)) => HighlightTag::Enum, 486 hir::ModuleDef::Adt(hir::Adt::Enum(_)) => HighlightTag::Enum,
464 hir::ModuleDef::Adt(hir::Adt::Union(_)) => HighlightTag::Union, 487 hir::ModuleDef::Adt(hir::Adt::Union(_)) => HighlightTag::Union,
diff --git a/crates/ra_ide/src/syntax_highlighting/html.rs b/crates/ra_ide/src/syntax_highlighting/html.rs
index edfe61f39..7d946c98d 100644
--- a/crates/ra_ide/src/syntax_highlighting/html.rs
+++ b/crates/ra_ide/src/syntax_highlighting/html.rs
@@ -69,6 +69,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
69.string_literal { color: #CC9393; } 69.string_literal { color: #CC9393; }
70.field { color: #94BFF3; } 70.field { color: #94BFF3; }
71.function { color: #93E0E3; } 71.function { color: #93E0E3; }
72.operator.unsafe { color: #E28C14; }
72.parameter { color: #94BFF3; } 73.parameter { color: #94BFF3; }
73.text { color: #DCDCCC; } 74.text { color: #DCDCCC; }
74.type { color: #7CB8BB; } 75.type { color: #7CB8BB; }
diff --git a/crates/ra_ide/src/syntax_highlighting/tags.rs b/crates/ra_ide/src/syntax_highlighting/tags.rs
index 1514531de..94f466966 100644
--- a/crates/ra_ide/src/syntax_highlighting/tags.rs
+++ b/crates/ra_ide/src/syntax_highlighting/tags.rs
@@ -24,12 +24,14 @@ pub enum HighlightTag {
24 Enum, 24 Enum,
25 EnumVariant, 25 EnumVariant,
26 Field, 26 Field,
27 FormatSpecifier,
27 Function, 28 Function,
28 Keyword, 29 Keyword,
29 Lifetime, 30 Lifetime,
30 Macro, 31 Macro,
31 Module, 32 Module,
32 NumericLiteral, 33 NumericLiteral,
34 Operator,
33 SelfKeyword, 35 SelfKeyword,
34 SelfType, 36 SelfType,
35 Static, 37 Static,
@@ -41,8 +43,6 @@ pub enum HighlightTag {
41 Union, 43 Union,
42 Local, 44 Local,
43 UnresolvedReference, 45 UnresolvedReference,
44 FormatSpecifier,
45 Operator,
46} 46}
47 47
48#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] 48#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
@@ -72,12 +72,14 @@ impl HighlightTag {
72 HighlightTag::Enum => "enum", 72 HighlightTag::Enum => "enum",
73 HighlightTag::EnumVariant => "enum_variant", 73 HighlightTag::EnumVariant => "enum_variant",
74 HighlightTag::Field => "field", 74 HighlightTag::Field => "field",
75 HighlightTag::FormatSpecifier => "format_specifier",
75 HighlightTag::Function => "function", 76 HighlightTag::Function => "function",
76 HighlightTag::Keyword => "keyword", 77 HighlightTag::Keyword => "keyword",
77 HighlightTag::Lifetime => "lifetime", 78 HighlightTag::Lifetime => "lifetime",
78 HighlightTag::Macro => "macro", 79 HighlightTag::Macro => "macro",
79 HighlightTag::Module => "module", 80 HighlightTag::Module => "module",
80 HighlightTag::NumericLiteral => "numeric_literal", 81 HighlightTag::NumericLiteral => "numeric_literal",
82 HighlightTag::Operator => "operator",
81 HighlightTag::SelfKeyword => "self_keyword", 83 HighlightTag::SelfKeyword => "self_keyword",
82 HighlightTag::SelfType => "self_type", 84 HighlightTag::SelfType => "self_type",
83 HighlightTag::Static => "static", 85 HighlightTag::Static => "static",
@@ -89,8 +91,6 @@ impl HighlightTag {
89 HighlightTag::Union => "union", 91 HighlightTag::Union => "union",
90 HighlightTag::Local => "variable", 92 HighlightTag::Local => "variable",
91 HighlightTag::UnresolvedReference => "unresolved_reference", 93 HighlightTag::UnresolvedReference => "unresolved_reference",
92 HighlightTag::FormatSpecifier => "format_specifier",
93 HighlightTag::Operator => "operator",
94 } 94 }
95 } 95 }
96} 96}
diff --git a/crates/ra_ide/src/syntax_highlighting/tests.rs b/crates/ra_ide/src/syntax_highlighting/tests.rs
index eb43a23da..36a1aa419 100644
--- a/crates/ra_ide/src/syntax_highlighting/tests.rs
+++ b/crates/ra_ide/src/syntax_highlighting/tests.rs
@@ -218,6 +218,7 @@ fn main() {
218 println!("{argument}", argument = "test"); // => "test" 218 println!("{argument}", argument = "test"); // => "test"
219 println!("{name} {}", 1, name = 2); // => "2 1" 219 println!("{name} {}", 1, name = 2); // => "2 1"
220 println!("{a} {c} {b}", a="a", b='b', c=3); // => "a 3 b" 220 println!("{a} {c} {b}", a="a", b='b', c=3); // => "a 3 b"
221 println!("{{{}}}", 2); // => "{2}"
221 println!("Hello {:5}!", "x"); 222 println!("Hello {:5}!", "x");
222 println!("Hello {:1$}!", "x", 5); 223 println!("Hello {:1$}!", "x", 5);
223 println!("Hello {1:0$}!", 5, "x"); 224 println!("Hello {1:0$}!", 5, "x");
@@ -257,3 +258,34 @@ fn main() {
257 fs::write(dst_file, &actual_html).unwrap(); 258 fs::write(dst_file, &actual_html).unwrap();
258 assert_eq_text!(expected_html, actual_html); 259 assert_eq_text!(expected_html, actual_html);
259} 260}
261
262#[test]
263fn test_unsafe_highlighting() {
264 let (analysis, file_id) = single_file(
265 r#"
266unsafe fn unsafe_fn() {}
267
268struct HasUnsafeFn;
269
270impl HasUnsafeFn {
271 unsafe fn unsafe_method(&self) {}
272}
273
274fn main() {
275 let x = &5 as *const usize;
276 unsafe {
277 unsafe_fn();
278 HasUnsafeFn.unsafe_method();
279 let y = *x;
280 let z = -x;
281 }
282}
283"#
284 .trim(),
285 );
286 let dst_file = project_dir().join("crates/ra_ide/src/snapshots/highlight_unsafe.html");
287 let actual_html = &analysis.highlight_as_html(file_id, false).unwrap();
288 let expected_html = &read_text(&dst_file);
289 fs::write(dst_file, &actual_html).unwrap();
290 assert_eq_text!(expected_html, actual_html);
291}
diff --git a/crates/ra_proc_macro_srv/Cargo.toml b/crates/ra_proc_macro_srv/Cargo.toml
index bb3003278..582102945 100644
--- a/crates/ra_proc_macro_srv/Cargo.toml
+++ b/crates/ra_proc_macro_srv/Cargo.toml
@@ -22,3 +22,4 @@ cargo_metadata = "0.10.0"
22difference = "2.0.0" 22difference = "2.0.0"
23# used as proc macro test target 23# used as proc macro test target
24serde_derive = "1.0.106" 24serde_derive = "1.0.106"
25ra_toolchain = { path = "../ra_toolchain" }
diff --git a/crates/ra_proc_macro_srv/src/tests/utils.rs b/crates/ra_proc_macro_srv/src/tests/utils.rs
index 84348b5de..8d85f2d8a 100644
--- a/crates/ra_proc_macro_srv/src/tests/utils.rs
+++ b/crates/ra_proc_macro_srv/src/tests/utils.rs
@@ -2,7 +2,6 @@
2 2
3use crate::dylib; 3use crate::dylib;
4use crate::ProcMacroSrv; 4use crate::ProcMacroSrv;
5pub use difference::Changeset as __Changeset;
6use ra_proc_macro::ListMacrosTask; 5use ra_proc_macro::ListMacrosTask;
7use std::str::FromStr; 6use std::str::FromStr;
8use test_utils::assert_eq_text; 7use test_utils::assert_eq_text;
@@ -13,7 +12,7 @@ mod fixtures {
13 12
14 // Use current project metadata to get the proc-macro dylib path 13 // Use current project metadata to get the proc-macro dylib path
15 pub fn dylib_path(crate_name: &str, version: &str) -> std::path::PathBuf { 14 pub fn dylib_path(crate_name: &str, version: &str) -> std::path::PathBuf {
16 let command = Command::new("cargo") 15 let command = Command::new(ra_toolchain::cargo())
17 .args(&["check", "--message-format", "json"]) 16 .args(&["check", "--message-format", "json"])
18 .output() 17 .output()
19 .unwrap() 18 .unwrap()
diff --git a/crates/ra_project_model/src/cargo_workspace.rs b/crates/ra_project_model/src/cargo_workspace.rs
index a306ce95f..4b7444039 100644
--- a/crates/ra_project_model/src/cargo_workspace.rs
+++ b/crates/ra_project_model/src/cargo_workspace.rs
@@ -64,7 +64,7 @@ impl Default for CargoConfig {
64 fn default() -> Self { 64 fn default() -> Self {
65 CargoConfig { 65 CargoConfig {
66 no_default_features: false, 66 no_default_features: false,
67 all_features: true, 67 all_features: false,
68 features: Vec::new(), 68 features: Vec::new(),
69 load_out_dirs_from_check: false, 69 load_out_dirs_from_check: false,
70 target: None, 70 target: None,
diff --git a/crates/ra_syntax/src/ast/tokens.rs b/crates/ra_syntax/src/ast/tokens.rs
index 3cd6d99c3..04b0a4480 100644
--- a/crates/ra_syntax/src/ast/tokens.rs
+++ b/crates/ra_syntax/src/ast/tokens.rs
@@ -418,14 +418,9 @@ pub trait HasFormatSpecifier: AstToken {
418 418
419 let mut cloned = chars.clone().take(2); 419 let mut cloned = chars.clone().take(2);
420 let first = cloned.next().and_then(|next| next.1.as_ref().ok()).copied(); 420 let first = cloned.next().and_then(|next| next.1.as_ref().ok()).copied();
421 let second = cloned.next().and_then(|next| next.1.as_ref().ok()).copied();
422 if first != Some('}') { 421 if first != Some('}') {
423 continue; 422 continue;
424 } 423 }
425 if second == Some('}') {
426 // Escaped format end specifier, `}}`
427 continue;
428 }
429 skip_char_and_emit(&mut chars, FormatSpecifier::Close, &mut callback); 424 skip_char_and_emit(&mut chars, FormatSpecifier::Close, &mut callback);
430 } 425 }
431 _ => { 426 _ => {
diff --git a/crates/rust-analyzer/src/caps.rs b/crates/rust-analyzer/src/caps.rs
index 345693524..673795e78 100644
--- a/crates/rust-analyzer/src/caps.rs
+++ b/crates/rust-analyzer/src/caps.rs
@@ -87,6 +87,9 @@ pub fn server_capabilities(client_caps: &ClientCapabilities) -> ServerCapabiliti
87 "ssr": true, 87 "ssr": true,
88 "onEnter": true, 88 "onEnter": true,
89 "parentModule": true, 89 "parentModule": true,
90 "runnables": {
91 "kinds": [ "cargo" ],
92 },
90 })), 93 })),
91 } 94 }
92} 95}
diff --git a/crates/rust-analyzer/src/cargo_target_spec.rs b/crates/rust-analyzer/src/cargo_target_spec.rs
index 441fb61df..008518a08 100644
--- a/crates/rust-analyzer/src/cargo_target_spec.rs
+++ b/crates/rust-analyzer/src/cargo_target_spec.rs
@@ -1,10 +1,10 @@
1//! See `CargoTargetSpec` 1//! See `CargoTargetSpec`
2 2
3use ra_cfg::CfgExpr;
3use ra_ide::{FileId, RunnableKind, TestId}; 4use ra_ide::{FileId, RunnableKind, TestId};
4use ra_project_model::{self, ProjectWorkspace, TargetKind}; 5use ra_project_model::{self, ProjectWorkspace, TargetKind};
5 6
6use crate::{world::WorldSnapshot, Result}; 7use crate::{world::WorldSnapshot, Result};
7use ra_syntax::SmolStr;
8 8
9/// Abstract representation of Cargo target. 9/// Abstract representation of Cargo target.
10/// 10///
@@ -21,7 +21,7 @@ impl CargoTargetSpec {
21 pub(crate) fn runnable_args( 21 pub(crate) fn runnable_args(
22 spec: Option<CargoTargetSpec>, 22 spec: Option<CargoTargetSpec>,
23 kind: &RunnableKind, 23 kind: &RunnableKind,
24 features_needed: &Vec<SmolStr>, 24 cfgs: &[CfgExpr],
25 ) -> Result<(Vec<String>, Vec<String>)> { 25 ) -> Result<(Vec<String>, Vec<String>)> {
26 let mut args = Vec::new(); 26 let mut args = Vec::new();
27 let mut extra_args = Vec::new(); 27 let mut extra_args = Vec::new();
@@ -76,10 +76,14 @@ impl CargoTargetSpec {
76 } 76 }
77 } 77 }
78 78
79 features_needed.iter().for_each(|feature| { 79 let mut features = Vec::new();
80 for cfg in cfgs {
81 required_features(cfg, &mut features);
82 }
83 for feature in features {
80 args.push("--features".to_string()); 84 args.push("--features".to_string());
81 args.push(feature.to_string()); 85 args.push(feature);
82 }); 86 }
83 87
84 Ok((args, extra_args)) 88 Ok((args, extra_args))
85 } 89 }
@@ -140,3 +144,74 @@ impl CargoTargetSpec {
140 } 144 }
141 } 145 }
142} 146}
147
148/// Fill minimal features needed
149fn required_features(cfg_expr: &CfgExpr, features: &mut Vec<String>) {
150 match cfg_expr {
151 CfgExpr::KeyValue { key, value } if key == "feature" => features.push(value.to_string()),
152 CfgExpr::All(preds) => {
153 preds.iter().for_each(|cfg| required_features(cfg, features));
154 }
155 CfgExpr::Any(preds) => {
156 for cfg in preds {
157 let len_features = features.len();
158 required_features(cfg, features);
159 if len_features != features.len() {
160 break;
161 }
162 }
163 }
164 _ => {}
165 }
166}
167
168#[cfg(test)]
169mod tests {
170 use super::*;
171
172 use mbe::{ast_to_token_tree, TokenMap};
173 use ra_cfg::parse_cfg;
174 use ra_syntax::{
175 ast::{self, AstNode},
176 SmolStr,
177 };
178
179 fn get_token_tree_generated(input: &str) -> (tt::Subtree, TokenMap) {
180 let source_file = ast::SourceFile::parse(input).ok().unwrap();
181 let tt = source_file.syntax().descendants().find_map(ast::TokenTree::cast).unwrap();
182 ast_to_token_tree(&tt).unwrap()
183 }
184
185 #[test]
186 fn test_cfg_expr_minimal_features_needed() {
187 let (subtree, _) = get_token_tree_generated(r#"#![cfg(feature = "baz")]"#);
188 let cfg_expr = parse_cfg(&subtree);
189 let mut min_features = vec![];
190 required_features(&cfg_expr, &mut min_features);
191
192 assert_eq!(min_features, vec![SmolStr::new("baz")]);
193
194 let (subtree, _) =
195 get_token_tree_generated(r#"#![cfg(all(feature = "baz", feature = "foo"))]"#);
196 let cfg_expr = parse_cfg(&subtree);
197
198 let mut min_features = vec![];
199 required_features(&cfg_expr, &mut min_features);
200 assert_eq!(min_features, vec![SmolStr::new("baz"), SmolStr::new("foo")]);
201
202 let (subtree, _) =
203 get_token_tree_generated(r#"#![cfg(any(feature = "baz", feature = "foo", unix))]"#);
204 let cfg_expr = parse_cfg(&subtree);
205
206 let mut min_features = vec![];
207 required_features(&cfg_expr, &mut min_features);
208 assert_eq!(min_features, vec![SmolStr::new("baz")]);
209
210 let (subtree, _) = get_token_tree_generated(r#"#![cfg(foo)]"#);
211 let cfg_expr = parse_cfg(&subtree);
212
213 let mut min_features = vec![];
214 required_features(&cfg_expr, &mut min_features);
215 assert!(min_features.is_empty());
216 }
217}
diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs
index c0f7c2c0c..9c6e369d2 100644
--- a/crates/rust-analyzer/src/config.rs
+++ b/crates/rust-analyzer/src/config.rs
@@ -122,7 +122,7 @@ impl Default for Config {
122 check: Some(FlycheckConfig::CargoCommand { 122 check: Some(FlycheckConfig::CargoCommand {
123 command: "check".to_string(), 123 command: "check".to_string(),
124 all_targets: true, 124 all_targets: true,
125 all_features: true, 125 all_features: false,
126 extra_args: Vec::new(), 126 extra_args: Vec::new(),
127 }), 127 }),
128 128
diff --git a/crates/rust-analyzer/src/diagnostics/snapshots/rust_analyzer__diagnostics__to_proto__tests__snap_rustc_unused_variable.snap b/crates/rust-analyzer/src/diagnostics/snapshots/rust_analyzer__diagnostics__to_proto__tests__snap_rustc_unused_variable.snap
index 6dd3fcb2e..33b516e26 100644
--- a/crates/rust-analyzer/src/diagnostics/snapshots/rust_analyzer__diagnostics__to_proto__tests__snap_rustc_unused_variable.snap
+++ b/crates/rust-analyzer/src/diagnostics/snapshots/rust_analyzer__diagnostics__to_proto__tests__snap_rustc_unused_variable.snap
@@ -29,7 +29,7 @@ expression: diag
29 }, 29 },
30 }, 30 },
31 severity: Some( 31 severity: Some(
32 Warning, 32 Hint,
33 ), 33 ),
34 code: Some( 34 code: Some(
35 String( 35 String(
diff --git a/crates/rust-analyzer/src/diagnostics/to_proto.rs b/crates/rust-analyzer/src/diagnostics/to_proto.rs
index a500d670a..6a6e7b457 100644
--- a/crates/rust-analyzer/src/diagnostics/to_proto.rs
+++ b/crates/rust-analyzer/src/diagnostics/to_proto.rs
@@ -183,7 +183,7 @@ pub(crate) fn map_rust_diagnostic_to_lsp(
183 return Vec::new(); 183 return Vec::new();
184 } 184 }
185 185
186 let severity = map_level_to_severity(rd.level); 186 let mut severity = map_level_to_severity(rd.level);
187 187
188 let mut source = String::from("rustc"); 188 let mut source = String::from("rustc");
189 let mut code = rd.code.as_ref().map(|c| c.code.clone()); 189 let mut code = rd.code.as_ref().map(|c| c.code.clone());
@@ -225,6 +225,7 @@ pub(crate) fn map_rust_diagnostic_to_lsp(
225 } 225 }
226 226
227 if is_unused_or_unnecessary(rd) { 227 if is_unused_or_unnecessary(rd) {
228 severity = Some(DiagnosticSeverity::Hint);
228 tags.push(DiagnosticTag::Unnecessary); 229 tags.push(DiagnosticTag::Unnecessary);
229 } 230 }
230 231
diff --git a/crates/rust-analyzer/src/lsp_ext.rs b/crates/rust-analyzer/src/lsp_ext.rs
index acb1dacb6..ec24ce5e0 100644
--- a/crates/rust-analyzer/src/lsp_ext.rs
+++ b/crates/rust-analyzer/src/lsp_ext.rs
@@ -4,7 +4,6 @@ use std::{collections::HashMap, path::PathBuf};
4 4
5use lsp_types::request::Request; 5use lsp_types::request::Request;
6use lsp_types::{Position, Range, TextDocumentIdentifier}; 6use lsp_types::{Position, Range, TextDocumentIdentifier};
7use rustc_hash::FxHashMap;
8use serde::{Deserialize, Serialize}; 7use serde::{Deserialize, Serialize};
9 8
10pub enum AnalyzerStatus {} 9pub enum AnalyzerStatus {}
@@ -111,7 +110,7 @@ pub enum Runnables {}
111impl Request for Runnables { 110impl Request for Runnables {
112 type Params = RunnablesParams; 111 type Params = RunnablesParams;
113 type Result = Vec<Runnable>; 112 type Result = Vec<Runnable>;
114 const METHOD: &'static str = "rust-analyzer/runnables"; 113 const METHOD: &'static str = "experimental/runnables";
115} 114}
116 115
117#[derive(Serialize, Deserialize, Debug)] 116#[derive(Serialize, Deserialize, Debug)]
@@ -124,13 +123,28 @@ pub struct RunnablesParams {
124#[derive(Deserialize, Serialize, Debug)] 123#[derive(Deserialize, Serialize, Debug)]
125#[serde(rename_all = "camelCase")] 124#[serde(rename_all = "camelCase")]
126pub struct Runnable { 125pub struct Runnable {
127 pub range: Range,
128 pub label: String, 126 pub label: String,
129 pub bin: String, 127 #[serde(skip_serializing_if = "Option::is_none")]
130 pub args: Vec<String>, 128 pub location: Option<lsp_types::LocationLink>,
131 pub extra_args: Vec<String>, 129 pub kind: RunnableKind,
132 pub env: FxHashMap<String, String>, 130 pub args: CargoRunnable,
133 pub cwd: Option<PathBuf>, 131}
132
133#[derive(Serialize, Deserialize, Debug)]
134#[serde(rename_all = "lowercase")]
135pub enum RunnableKind {
136 Cargo,
137}
138
139#[derive(Deserialize, Serialize, Debug)]
140#[serde(rename_all = "camelCase")]
141pub struct CargoRunnable {
142 #[serde(skip_serializing_if = "Option::is_none")]
143 pub workspace_root: Option<PathBuf>,
144 // command, --package and --lib stuff
145 pub cargo_args: Vec<String>,
146 // stuff after --
147 pub executable_args: Vec<String>,
134} 148}
135 149
136pub enum InlayHints {} 150pub enum InlayHints {}
diff --git a/crates/rust-analyzer/src/main_loop/handlers.rs b/crates/rust-analyzer/src/main_loop/handlers.rs
index 1f910ff82..7fd691764 100644
--- a/crates/rust-analyzer/src/main_loop/handlers.rs
+++ b/crates/rust-analyzer/src/main_loop/handlers.rs
@@ -17,15 +17,12 @@ use lsp_types::{
17 SemanticTokensParams, SemanticTokensRangeParams, SemanticTokensRangeResult, 17 SemanticTokensParams, SemanticTokensRangeParams, SemanticTokensRangeResult,
18 SemanticTokensResult, SymbolInformation, TextDocumentIdentifier, Url, WorkspaceEdit, 18 SemanticTokensResult, SymbolInformation, TextDocumentIdentifier, Url, WorkspaceEdit,
19}; 19};
20use ra_cfg::CfgExpr;
21use ra_ide::{ 20use ra_ide::{
22 FileId, FilePosition, FileRange, Query, RangeInfo, Runnable, RunnableKind, SearchScope, 21 FileId, FilePosition, FileRange, Query, RangeInfo, RunnableKind, SearchScope, TextEdit,
23 TextEdit,
24}; 22};
25use ra_prof::profile; 23use ra_prof::profile;
26use ra_project_model::TargetKind; 24use ra_project_model::TargetKind;
27use ra_syntax::{AstNode, SmolStr, SyntaxKind, TextRange, TextSize}; 25use ra_syntax::{AstNode, SyntaxKind, TextRange, TextSize};
28use rustc_hash::FxHashMap;
29use serde::{Deserialize, Serialize}; 26use serde::{Deserialize, Serialize};
30use serde_json::to_value; 27use serde_json::to_value;
31use stdx::format_to; 28use stdx::format_to;
@@ -403,7 +400,7 @@ pub fn handle_runnables(
403 let cargo_spec = CargoTargetSpec::for_file(&world, file_id)?; 400 let cargo_spec = CargoTargetSpec::for_file(&world, file_id)?;
404 for runnable in world.analysis().runnables(file_id)? { 401 for runnable in world.analysis().runnables(file_id)? {
405 if let Some(offset) = offset { 402 if let Some(offset) = offset {
406 if !runnable.range.contains_inclusive(offset) { 403 if !runnable.nav.full_range().contains_inclusive(offset) {
407 continue; 404 continue;
408 } 405 }
409 } 406 }
@@ -416,7 +413,7 @@ pub fn handle_runnables(
416 } 413 }
417 } 414 }
418 } 415 }
419 res.push(to_lsp_runnable(&world, file_id, runnable)?); 416 res.push(to_proto::runnable(&world, file_id, runnable)?);
420 } 417 }
421 418
422 // Add `cargo check` and `cargo test` for the whole package 419 // Add `cargo check` and `cargo test` for the whole package
@@ -424,25 +421,31 @@ pub fn handle_runnables(
424 Some(spec) => { 421 Some(spec) => {
425 for &cmd in ["check", "test"].iter() { 422 for &cmd in ["check", "test"].iter() {
426 res.push(lsp_ext::Runnable { 423 res.push(lsp_ext::Runnable {
427 range: Default::default(),
428 label: format!("cargo {} -p {}", cmd, spec.package), 424 label: format!("cargo {} -p {}", cmd, spec.package),
429 bin: "cargo".to_string(), 425 location: None,
430 args: vec![cmd.to_string(), "--package".to_string(), spec.package.clone()], 426 kind: lsp_ext::RunnableKind::Cargo,
431 extra_args: Vec::new(), 427 args: lsp_ext::CargoRunnable {
432 env: FxHashMap::default(), 428 workspace_root: workspace_root.map(|root| root.to_owned()),
433 cwd: workspace_root.map(|root| root.to_owned()), 429 cargo_args: vec![
430 cmd.to_string(),
431 "--package".to_string(),
432 spec.package.clone(),
433 ],
434 executable_args: Vec::new(),
435 },
434 }) 436 })
435 } 437 }
436 } 438 }
437 None => { 439 None => {
438 res.push(lsp_ext::Runnable { 440 res.push(lsp_ext::Runnable {
439 range: Default::default(),
440 label: "cargo check --workspace".to_string(), 441 label: "cargo check --workspace".to_string(),
441 bin: "cargo".to_string(), 442 location: None,
442 args: vec!["check".to_string(), "--workspace".to_string()], 443 kind: lsp_ext::RunnableKind::Cargo,
443 extra_args: Vec::new(), 444 args: lsp_ext::CargoRunnable {
444 env: FxHashMap::default(), 445 workspace_root: workspace_root.map(|root| root.to_owned()),
445 cwd: workspace_root.map(|root| root.to_owned()), 446 cargo_args: vec!["check".to_string(), "--workspace".to_string()],
447 executable_args: Vec::new(),
448 },
446 }); 449 });
447 } 450 }
448 } 451 }
@@ -784,10 +787,11 @@ pub fn handle_code_lens(
784 } 787 }
785 }; 788 };
786 789
787 let mut r = to_lsp_runnable(&world, file_id, runnable)?; 790 let range = to_proto::range(&line_index, runnable.nav.range());
791 let r = to_proto::runnable(&world, file_id, runnable)?;
788 if world.config.lens.run { 792 if world.config.lens.run {
789 let lens = CodeLens { 793 let lens = CodeLens {
790 range: r.range, 794 range,
791 command: Some(Command { 795 command: Some(Command {
792 title: run_title.to_string(), 796 title: run_title.to_string(),
793 command: "rust-analyzer.runSingle".into(), 797 command: "rust-analyzer.runSingle".into(),
@@ -799,13 +803,8 @@ pub fn handle_code_lens(
799 } 803 }
800 804
801 if debugee && world.config.lens.debug { 805 if debugee && world.config.lens.debug {
802 if r.args[0] == "run" {
803 r.args[0] = "build".into();
804 } else {
805 r.args.push("--no-run".into());
806 }
807 let debug_lens = CodeLens { 806 let debug_lens = CodeLens {
808 range: r.range, 807 range,
809 command: Some(Command { 808 command: Some(Command {
810 title: "Debug".into(), 809 title: "Debug".into(),
811 command: "rust-analyzer.debugSingle".into(), 810 command: "rust-analyzer.debugSingle".into(),
@@ -959,64 +958,6 @@ pub fn publish_diagnostics(world: &WorldSnapshot, file_id: FileId) -> Result<Dia
959 Ok(DiagnosticTask::SetNative(file_id, diagnostics)) 958 Ok(DiagnosticTask::SetNative(file_id, diagnostics))
960} 959}
961 960
962fn to_lsp_runnable(
963 world: &WorldSnapshot,
964 file_id: FileId,
965 runnable: Runnable,
966) -> Result<lsp_ext::Runnable> {
967 let spec = CargoTargetSpec::for_file(world, file_id)?;
968 let target = spec.as_ref().map(|s| s.target.clone());
969 let mut features_needed = vec![];
970 for cfg_expr in &runnable.cfg_exprs {
971 collect_minimal_features_needed(cfg_expr, &mut features_needed);
972 }
973 let (args, extra_args) =
974 CargoTargetSpec::runnable_args(spec, &runnable.kind, &features_needed)?;
975 let line_index = world.analysis().file_line_index(file_id)?;
976 let label = match &runnable.kind {
977 RunnableKind::Test { test_id, .. } => format!("test {}", test_id),
978 RunnableKind::TestMod { path } => format!("test-mod {}", path),
979 RunnableKind::Bench { test_id } => format!("bench {}", test_id),
980 RunnableKind::DocTest { test_id, .. } => format!("doctest {}", test_id),
981 RunnableKind::Bin => {
982 target.map_or_else(|| "run binary".to_string(), |t| format!("run {}", t))
983 }
984 };
985 Ok(lsp_ext::Runnable {
986 range: to_proto::range(&line_index, runnable.range),
987 label,
988 bin: "cargo".to_string(),
989 args,
990 extra_args,
991 env: {
992 let mut m = FxHashMap::default();
993 m.insert("RUST_BACKTRACE".to_string(), "short".to_string());
994 m
995 },
996 cwd: world.workspace_root_for(file_id).map(|root| root.to_owned()),
997 })
998}
999
1000/// Fill minimal features needed
1001fn collect_minimal_features_needed(cfg_expr: &CfgExpr, features: &mut Vec<SmolStr>) {
1002 match cfg_expr {
1003 CfgExpr::KeyValue { key, value } if key == "feature" => features.push(value.clone()),
1004 CfgExpr::All(preds) => {
1005 preds.iter().for_each(|cfg| collect_minimal_features_needed(cfg, features));
1006 }
1007 CfgExpr::Any(preds) => {
1008 for cfg in preds {
1009 let len_features = features.len();
1010 collect_minimal_features_needed(cfg, features);
1011 if len_features != features.len() {
1012 break;
1013 }
1014 }
1015 }
1016 _ => {}
1017 }
1018}
1019
1020pub fn handle_inlay_hints( 961pub fn handle_inlay_hints(
1021 world: WorldSnapshot, 962 world: WorldSnapshot,
1022 params: InlayHintsParams, 963 params: InlayHintsParams,
@@ -1153,54 +1094,3 @@ pub fn handle_semantic_tokens_range(
1153 let semantic_tokens = to_proto::semantic_tokens(&text, &line_index, highlights); 1094 let semantic_tokens = to_proto::semantic_tokens(&text, &line_index, highlights);
1154 Ok(Some(semantic_tokens.into())) 1095 Ok(Some(semantic_tokens.into()))
1155} 1096}
1156
1157#[cfg(test)]
1158mod tests {
1159 use super::*;
1160
1161 use mbe::{ast_to_token_tree, TokenMap};
1162 use ra_cfg::parse_cfg;
1163 use ra_syntax::{
1164 ast::{self, AstNode},
1165 SmolStr,
1166 };
1167
1168 fn get_token_tree_generated(input: &str) -> (tt::Subtree, TokenMap) {
1169 let source_file = ast::SourceFile::parse(input).ok().unwrap();
1170 let tt = source_file.syntax().descendants().find_map(ast::TokenTree::cast).unwrap();
1171 ast_to_token_tree(&tt).unwrap()
1172 }
1173
1174 #[test]
1175 fn test_cfg_expr_minimal_features_needed() {
1176 let (subtree, _) = get_token_tree_generated(r#"#![cfg(feature = "baz")]"#);
1177 let cfg_expr = parse_cfg(&subtree);
1178 let mut min_features = vec![];
1179 collect_minimal_features_needed(&cfg_expr, &mut min_features);
1180
1181 assert_eq!(min_features, vec![SmolStr::new("baz")]);
1182
1183 let (subtree, _) =
1184 get_token_tree_generated(r#"#![cfg(all(feature = "baz", feature = "foo"))]"#);
1185 let cfg_expr = parse_cfg(&subtree);
1186
1187 let mut min_features = vec![];
1188 collect_minimal_features_needed(&cfg_expr, &mut min_features);
1189 assert_eq!(min_features, vec![SmolStr::new("baz"), SmolStr::new("foo")]);
1190
1191 let (subtree, _) =
1192 get_token_tree_generated(r#"#![cfg(any(feature = "baz", feature = "foo", unix))]"#);
1193 let cfg_expr = parse_cfg(&subtree);
1194
1195 let mut min_features = vec![];
1196 collect_minimal_features_needed(&cfg_expr, &mut min_features);
1197 assert_eq!(min_features, vec![SmolStr::new("baz")]);
1198
1199 let (subtree, _) = get_token_tree_generated(r#"#![cfg(foo)]"#);
1200 let cfg_expr = parse_cfg(&subtree);
1201
1202 let mut min_features = vec![];
1203 collect_minimal_features_needed(&cfg_expr, &mut min_features);
1204 assert!(min_features.is_empty());
1205 }
1206}
diff --git a/crates/rust-analyzer/src/to_proto.rs b/crates/rust-analyzer/src/to_proto.rs
index 2fbbb4e63..85304aa87 100644
--- a/crates/rust-analyzer/src/to_proto.rs
+++ b/crates/rust-analyzer/src/to_proto.rs
@@ -3,13 +3,15 @@ use ra_db::{FileId, FileRange};
3use ra_ide::{ 3use ra_ide::{
4 Assist, CompletionItem, CompletionItemKind, Documentation, FileSystemEdit, Fold, FoldKind, 4 Assist, CompletionItem, CompletionItemKind, Documentation, FileSystemEdit, Fold, FoldKind,
5 FunctionSignature, Highlight, HighlightModifier, HighlightTag, HighlightedRange, Indel, 5 FunctionSignature, Highlight, HighlightModifier, HighlightTag, HighlightedRange, Indel,
6 InlayHint, InlayKind, InsertTextFormat, LineIndex, NavigationTarget, ReferenceAccess, Severity, 6 InlayHint, InlayKind, InsertTextFormat, LineIndex, NavigationTarget, ReferenceAccess, Runnable,
7 SourceChange, SourceFileEdit, TextEdit, 7 RunnableKind, Severity, SourceChange, SourceFileEdit, TextEdit,
8}; 8};
9use ra_syntax::{SyntaxKind, TextRange, TextSize}; 9use ra_syntax::{SyntaxKind, TextRange, TextSize};
10use ra_vfs::LineEndings; 10use ra_vfs::LineEndings;
11 11
12use crate::{lsp_ext, semantic_tokens, world::WorldSnapshot, Result}; 12use crate::{
13 cargo_target_spec::CargoTargetSpec, lsp_ext, semantic_tokens, world::WorldSnapshot, Result,
14};
13 15
14pub(crate) fn position(line_index: &LineIndex, offset: TextSize) -> lsp_types::Position { 16pub(crate) fn position(line_index: &LineIndex, offset: TextSize) -> lsp_types::Position {
15 let line_col = line_index.line_col(offset); 17 let line_col = line_index.line_col(offset);
@@ -627,3 +629,35 @@ pub(crate) fn code_action(world: &WorldSnapshot, assist: Assist) -> Result<lsp_e
627 }; 629 };
628 Ok(res) 630 Ok(res)
629} 631}
632
633pub(crate) fn runnable(
634 world: &WorldSnapshot,
635 file_id: FileId,
636 runnable: Runnable,
637) -> Result<lsp_ext::Runnable> {
638 let spec = CargoTargetSpec::for_file(world, file_id)?;
639 let target = spec.as_ref().map(|s| s.target.clone());
640 let (cargo_args, executable_args) =
641 CargoTargetSpec::runnable_args(spec, &runnable.kind, &runnable.cfg_exprs)?;
642 let label = match &runnable.kind {
643 RunnableKind::Test { test_id, .. } => format!("test {}", test_id),
644 RunnableKind::TestMod { path } => format!("test-mod {}", path),
645 RunnableKind::Bench { test_id } => format!("bench {}", test_id),
646 RunnableKind::DocTest { test_id, .. } => format!("doctest {}", test_id),
647 RunnableKind::Bin => {
648 target.map_or_else(|| "run binary".to_string(), |t| format!("run {}", t))
649 }
650 };
651 let location = location_link(world, None, runnable.nav)?;
652
653 Ok(lsp_ext::Runnable {
654 label,
655 location: Some(location),
656 kind: lsp_ext::RunnableKind::Cargo,
657 args: lsp_ext::CargoRunnable {
658 workspace_root: world.workspace_root_for(file_id).map(|root| root.to_owned()),
659 cargo_args,
660 executable_args,
661 },
662 })
663}
diff --git a/crates/rust-analyzer/tests/heavy_tests/main.rs b/crates/rust-analyzer/tests/heavy_tests/main.rs
index 405ddb362..e18f973b8 100644
--- a/crates/rust-analyzer/tests/heavy_tests/main.rs
+++ b/crates/rust-analyzer/tests/heavy_tests/main.rs
@@ -76,30 +76,33 @@ fn foo() {
76 server.request::<Runnables>( 76 server.request::<Runnables>(
77 RunnablesParams { text_document: server.doc_id("lib.rs"), position: None }, 77 RunnablesParams { text_document: server.doc_id("lib.rs"), position: None },
78 json!([ 78 json!([
79 { 79 {
80 "args": [ "test" ], 80 "args": {
81 "extraArgs": [ "foo", "--nocapture" ], 81 "cargoArgs": ["test"],
82 "bin": "cargo", 82 "executableArgs": ["foo", "--nocapture"],
83 "env": { "RUST_BACKTRACE": "short" }, 83 },
84 "cwd": null, 84 "kind": "cargo",
85 "label": "test foo", 85 "label": "test foo",
86 "range": { 86 "location": {
87 "end": { "character": 1, "line": 2 }, 87 "targetRange": {
88 "start": { "character": 0, "line": 0 } 88 "end": { "character": 1, "line": 2 },
89 } 89 "start": { "character": 0, "line": 0 }
90 }, 90 },
91 { 91 "targetSelectionRange": {
92 "args": ["check", "--workspace"], 92 "end": { "character": 6, "line": 1 },
93 "extraArgs": [], 93 "start": { "character": 3, "line": 1 }
94 "bin": "cargo", 94 },
95 "env": {}, 95 "targetUri": "file:///[..]/lib.rs"
96 "cwd": null, 96 }
97 "label": "cargo check --workspace", 97 },
98 "range": { 98 {
99 "end": { "character": 0, "line": 0 }, 99 "args": {
100 "start": { "character": 0, "line": 0 } 100 "cargoArgs": ["check", "--workspace"],
101 "executableArgs": [],
102 },
103 "kind": "cargo",
104 "label": "cargo check --workspace"
101 } 105 }
102 }
103 ]), 106 ]),
104 ); 107 );
105} 108}
@@ -138,42 +141,44 @@ fn main() {}
138 server.request::<Runnables>( 141 server.request::<Runnables>(
139 RunnablesParams { text_document: server.doc_id("foo/tests/spam.rs"), position: None }, 142 RunnablesParams { text_document: server.doc_id("foo/tests/spam.rs"), position: None },
140 json!([ 143 json!([
141 { 144 {
142 "args": [ "test", "--package", "foo", "--test", "spam" ], 145 "args": {
143 "extraArgs": [ "test_eggs", "--exact", "--nocapture" ], 146 "cargoArgs": ["test", "--package", "foo", "--test", "spam"],
144 "bin": "cargo", 147 "executableArgs": ["test_eggs", "--exact", "--nocapture"],
145 "env": { "RUST_BACKTRACE": "short" }, 148 "workspaceRoot": server.path().join("foo")
146 "label": "test test_eggs",
147 "range": {
148 "end": { "character": 17, "line": 1 },
149 "start": { "character": 0, "line": 0 }
150 },
151 "cwd": server.path().join("foo")
152 }, 149 },
153 { 150 "kind": "cargo",
154 "args": [ "check", "--package", "foo" ], 151 "label": "test test_eggs",
155 "extraArgs": [], 152 "location": {
156 "bin": "cargo", 153 "targetRange": {
157 "env": {}, 154 "end": { "character": 17, "line": 1 },
158 "label": "cargo check -p foo",
159 "range": {
160 "end": { "character": 0, "line": 0 },
161 "start": { "character": 0, "line": 0 } 155 "start": { "character": 0, "line": 0 }
162 }, 156 },
163 "cwd": server.path().join("foo") 157 "targetSelectionRange": {
164 }, 158 "end": { "character": 12, "line": 1 },
165 { 159 "start": { "character": 3, "line": 1 }
166 "args": [ "test", "--package", "foo" ],
167 "extraArgs": [],
168 "bin": "cargo",
169 "env": {},
170 "label": "cargo test -p foo",
171 "range": {
172 "end": { "character": 0, "line": 0 },
173 "start": { "character": 0, "line": 0 }
174 }, 160 },
175 "cwd": server.path().join("foo") 161 "targetUri": "file:///[..]/tests/spam.rs"
176 } 162 }
163 },
164 {
165 "args": {
166 "cargoArgs": ["check", "--package", "foo"],
167 "executableArgs": [],
168 "workspaceRoot": server.path().join("foo")
169 },
170 "kind": "cargo",
171 "label": "cargo check -p foo"
172 },
173 {
174 "args": {
175 "cargoArgs": ["test", "--package", "foo"],
176 "executableArgs": [],
177 "workspaceRoot": server.path().join("foo")
178 },
179 "kind": "cargo",
180 "label": "cargo test -p foo"
181 }
177 ]), 182 ]),
178 ); 183 );
179} 184}