aboutsummaryrefslogtreecommitdiff
path: root/crates
diff options
context:
space:
mode:
Diffstat (limited to 'crates')
-rw-r--r--crates/assists/src/handlers/extract_struct_from_enum_variant.rs3
-rw-r--r--crates/assists/src/handlers/inline_local_variable.rs31
-rw-r--r--crates/project_model/src/lib.rs245
-rw-r--r--crates/project_model/src/project_json.rs19
-rw-r--r--crates/project_model/src/sysroot.rs20
-rw-r--r--crates/rust-analyzer/src/bin/args.rs492
-rw-r--r--crates/rust-analyzer/src/bin/logger.rs73
-rw-r--r--crates/rust-analyzer/src/bin/main.rs29
-rw-r--r--crates/rust-analyzer/src/cli/diagnostics.rs7
-rw-r--r--crates/rust-analyzer/tests/rust-analyzer/main.rs74
-rw-r--r--crates/rust-analyzer/tests/rust-analyzer/support.rs5
-rw-r--r--crates/text_edit/src/lib.rs12
12 files changed, 483 insertions, 527 deletions
diff --git a/crates/assists/src/handlers/extract_struct_from_enum_variant.rs b/crates/assists/src/handlers/extract_struct_from_enum_variant.rs
index c1124b9e2..8ac20210a 100644
--- a/crates/assists/src/handlers/extract_struct_from_enum_variant.rs
+++ b/crates/assists/src/handlers/extract_struct_from_enum_variant.rs
@@ -1,6 +1,7 @@
1use base_db::FileId; 1use base_db::FileId;
2use hir::{EnumVariant, Module, ModuleDef, Name}; 2use hir::{EnumVariant, Module, ModuleDef, Name};
3use ide_db::{defs::Definition, search::Reference, RootDatabase}; 3use ide_db::{defs::Definition, search::Reference, RootDatabase};
4use itertools::Itertools;
4use rustc_hash::FxHashSet; 5use rustc_hash::FxHashSet;
5use syntax::{ 6use syntax::{
6 algo::find_node_at_offset, 7 algo::find_node_at_offset,
@@ -203,13 +204,11 @@ fn list_with_visibility(list: &str) -> String {
203 mod_part.insert_str(index, "pub "); 204 mod_part.insert_str(index, "pub ");
204 mod_part 205 mod_part
205 }) 206 })
206 .collect::<Vec<String>>()
207 .join(", ") 207 .join(", ")
208} 208}
209 209
210#[cfg(test)] 210#[cfg(test)]
211mod tests { 211mod tests {
212
213 use crate::{ 212 use crate::{
214 tests::{check_assist, check_assist_not_applicable}, 213 tests::{check_assist, check_assist_not_applicable},
215 utils::FamousDefs, 214 utils::FamousDefs,
diff --git a/crates/assists/src/handlers/inline_local_variable.rs b/crates/assists/src/handlers/inline_local_variable.rs
index 164bbce86..587eb5feb 100644
--- a/crates/assists/src/handlers/inline_local_variable.rs
+++ b/crates/assists/src/handlers/inline_local_variable.rs
@@ -1,4 +1,4 @@
1use ide_db::defs::Definition; 1use ide_db::{defs::Definition, search::ReferenceKind};
2use syntax::{ 2use syntax::{
3 ast::{self, AstNode, AstToken}, 3 ast::{self, AstNode, AstToken},
4 TextRange, 4 TextRange,
@@ -119,7 +119,13 @@ pub(crate) fn inline_local_variable(acc: &mut Assists, ctx: &AssistContext) -> O
119 for (desc, should_wrap) in refs.iter().zip(wrap_in_parens) { 119 for (desc, should_wrap) in refs.iter().zip(wrap_in_parens) {
120 let replacement = 120 let replacement =
121 if should_wrap { init_in_paren.clone() } else { init_str.clone() }; 121 if should_wrap { init_in_paren.clone() } else { init_str.clone() };
122 builder.replace(desc.file_range.range, replacement) 122 match desc.kind {
123 ReferenceKind::FieldShorthandForLocal => {
124 mark::hit!(inline_field_shorthand);
125 builder.insert(desc.file_range.range.end(), format!(": {}", replacement))
126 }
127 _ => builder.replace(desc.file_range.range, replacement),
128 }
123 } 129 }
124 }, 130 },
125 ) 131 )
@@ -667,6 +673,27 @@ fn foo() {
667 } 673 }
668 674
669 #[test] 675 #[test]
676 fn inline_field_shorthand() {
677 mark::check!(inline_field_shorthand);
678 check_assist(
679 inline_local_variable,
680 r"
681struct S { foo: i32}
682fn main() {
683 let <|>foo = 92;
684 S { foo }
685}
686",
687 r"
688struct S { foo: i32}
689fn main() {
690 S { foo: 92 }
691}
692",
693 );
694 }
695
696 #[test]
670 fn test_not_applicable_if_variable_unused() { 697 fn test_not_applicable_if_variable_unused() {
671 mark::check!(test_not_applicable_if_variable_unused); 698 mark::check!(test_not_applicable_if_variable_unused);
672 check_assist_not_applicable( 699 check_assist_not_applicable(
diff --git a/crates/project_model/src/lib.rs b/crates/project_model/src/lib.rs
index 2d65fc076..2d91939ce 100644
--- a/crates/project_model/src/lib.rs
+++ b/crates/project_model/src/lib.rs
@@ -39,11 +39,18 @@ pub enum ProjectWorkspace {
39impl fmt::Debug for ProjectWorkspace { 39impl fmt::Debug for ProjectWorkspace {
40 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 40 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
41 match self { 41 match self {
42 ProjectWorkspace::Cargo { cargo, .. } => { 42 ProjectWorkspace::Cargo { cargo, sysroot } => f
43 f.debug_struct("Cargo").field("n_packages", &cargo.packages().len()).finish() 43 .debug_struct("Cargo")
44 } 44 .field("n_packages", &cargo.packages().len())
45 .field("n_sysroot_crates", &sysroot.crates().len())
46 .finish(),
45 ProjectWorkspace::Json { project } => { 47 ProjectWorkspace::Json { project } => {
46 f.debug_struct("Json").field("n_crates", &project.crates.len()).finish() 48 let mut debug_struct = f.debug_struct("Json");
49 debug_struct.field("n_crates", &project.n_crates());
50 if let Some(sysroot) = &project.sysroot {
51 debug_struct.field("n_sysroot_crates", &sysroot.crates().len());
52 }
53 debug_struct.finish()
47 } 54 }
48 } 55 }
49 } 56 }
@@ -202,15 +209,21 @@ impl ProjectWorkspace {
202 pub fn to_roots(&self) -> Vec<PackageRoot> { 209 pub fn to_roots(&self) -> Vec<PackageRoot> {
203 match self { 210 match self {
204 ProjectWorkspace::Json { project } => project 211 ProjectWorkspace::Json { project } => project
205 .crates 212 .crates()
206 .iter() 213 .map(|(_, krate)| PackageRoot {
207 .map(|krate| PackageRoot {
208 is_member: krate.is_workspace_member, 214 is_member: krate.is_workspace_member,
209 include: krate.include.clone(), 215 include: krate.include.clone(),
210 exclude: krate.exclude.clone(), 216 exclude: krate.exclude.clone(),
211 }) 217 })
212 .collect::<FxHashSet<_>>() 218 .collect::<FxHashSet<_>>()
213 .into_iter() 219 .into_iter()
220 .chain(project.sysroot.as_ref().into_iter().flat_map(|sysroot| {
221 sysroot.crates().map(move |krate| PackageRoot {
222 is_member: false,
223 include: vec![sysroot[krate].root_dir().to_path_buf()],
224 exclude: Vec::new(),
225 })
226 }))
214 .collect::<Vec<_>>(), 227 .collect::<Vec<_>>(),
215 ProjectWorkspace::Cargo { cargo, sysroot } => cargo 228 ProjectWorkspace::Cargo { cargo, sysroot } => cargo
216 .packages() 229 .packages()
@@ -243,9 +256,8 @@ impl ProjectWorkspace {
243 pub fn proc_macro_dylib_paths(&self) -> Vec<AbsPathBuf> { 256 pub fn proc_macro_dylib_paths(&self) -> Vec<AbsPathBuf> {
244 match self { 257 match self {
245 ProjectWorkspace::Json { project } => project 258 ProjectWorkspace::Json { project } => project
246 .crates 259 .crates()
247 .iter() 260 .filter_map(|(_, krate)| krate.proc_macro_dylib_path.as_ref())
248 .filter_map(|krate| krate.proc_macro_dylib_path.as_ref())
249 .cloned() 261 .cloned()
250 .collect(), 262 .collect(),
251 ProjectWorkspace::Cargo { cargo, sysroot: _sysroot } => cargo 263 ProjectWorkspace::Cargo { cargo, sysroot: _sysroot } => cargo
@@ -258,7 +270,7 @@ impl ProjectWorkspace {
258 270
259 pub fn n_packages(&self) -> usize { 271 pub fn n_packages(&self) -> usize {
260 match self { 272 match self {
261 ProjectWorkspace::Json { project, .. } => project.crates.len(), 273 ProjectWorkspace::Json { project, .. } => project.n_crates(),
262 ProjectWorkspace::Cargo { cargo, sysroot } => { 274 ProjectWorkspace::Cargo { cargo, sysroot } => {
263 cargo.packages().len() + sysroot.crates().len() 275 cargo.packages().len() + sysroot.crates().len()
264 } 276 }
@@ -274,12 +286,15 @@ impl ProjectWorkspace {
274 let mut crate_graph = CrateGraph::default(); 286 let mut crate_graph = CrateGraph::default();
275 match self { 287 match self {
276 ProjectWorkspace::Json { project } => { 288 ProjectWorkspace::Json { project } => {
289 let sysroot_dps = project
290 .sysroot
291 .as_ref()
292 .map(|sysroot| sysroot_to_crate_graph(&mut crate_graph, sysroot, target, load));
293
277 let mut cfg_cache: FxHashMap<Option<&str>, Vec<CfgFlag>> = FxHashMap::default(); 294 let mut cfg_cache: FxHashMap<Option<&str>, Vec<CfgFlag>> = FxHashMap::default();
278 let crates: FxHashMap<_, _> = project 295 let crates: FxHashMap<_, _> = project
279 .crates 296 .crates()
280 .iter() 297 .filter_map(|(crate_id, krate)| {
281 .enumerate()
282 .filter_map(|(seq_index, krate)| {
283 let file_path = &krate.root_module; 298 let file_path = &krate.root_module;
284 let file_id = load(&file_path)?; 299 let file_id = load(&file_path)?;
285 300
@@ -297,9 +312,8 @@ impl ProjectWorkspace {
297 let mut cfg_options = CfgOptions::default(); 312 let mut cfg_options = CfgOptions::default();
298 cfg_options.extend(target_cfgs.iter().chain(krate.cfg.iter()).cloned()); 313 cfg_options.extend(target_cfgs.iter().chain(krate.cfg.iter()).cloned());
299 314
300 // FIXME: No crate name in json definition such that we cannot add OUT_DIR to env
301 Some(( 315 Some((
302 CrateId(seq_index as u32), 316 crate_id,
303 crate_graph.add_crate_root( 317 crate_graph.add_crate_root(
304 file_id, 318 file_id,
305 krate.edition, 319 krate.edition,
@@ -313,67 +327,33 @@ impl ProjectWorkspace {
313 }) 327 })
314 .collect(); 328 .collect();
315 329
316 for (id, krate) in project.crates.iter().enumerate() { 330 for (from, krate) in project.crates() {
317 for dep in &krate.deps { 331 if let Some(&from) = crates.get(&from) {
318 let from_crate_id = CrateId(id as u32); 332 if let Some((public_deps, _proc_macro)) = &sysroot_dps {
319 let to_crate_id = dep.crate_id; 333 for (name, to) in public_deps.iter() {
320 if let (Some(&from), Some(&to)) = 334 if let Err(_) = crate_graph.add_dep(from, name.clone(), *to) {
321 (crates.get(&from_crate_id), crates.get(&to_crate_id)) 335 log::error!("cyclic dependency on {} for {:?}", name, from)
322 { 336 }
323 if crate_graph.add_dep(from, dep.name.clone(), to).is_err() {
324 log::error!(
325 "cyclic dependency {:?} -> {:?}",
326 from_crate_id,
327 to_crate_id
328 );
329 } 337 }
330 } 338 }
331 }
332 }
333 }
334 ProjectWorkspace::Cargo { cargo, sysroot } => {
335 let mut cfg_options = CfgOptions::default();
336 cfg_options.extend(get_rustc_cfg_options(target));
337
338 let sysroot_crates: FxHashMap<_, _> = sysroot
339 .crates()
340 .filter_map(|krate| {
341 let file_id = load(&sysroot[krate].root)?;
342
343 let env = Env::default();
344 let proc_macro = vec![];
345 let name = sysroot[krate].name.clone();
346 let crate_id = crate_graph.add_crate_root(
347 file_id,
348 Edition::Edition2018,
349 Some(name),
350 cfg_options.clone(),
351 env,
352 proc_macro,
353 );
354 Some((krate, crate_id))
355 })
356 .collect();
357 339
358 for from in sysroot.crates() { 340 for dep in &krate.deps {
359 for &to in sysroot[from].deps.iter() { 341 let to_crate_id = dep.crate_id;
360 let name = &sysroot[to].name; 342 if let Some(&to) = crates.get(&to_crate_id) {
361 if let (Some(&from), Some(&to)) = 343 if let Err(_) = crate_graph.add_dep(from, dep.name.clone(), to) {
362 (sysroot_crates.get(&from), sysroot_crates.get(&to)) 344 log::error!("cyclic dependency {:?} -> {:?}", from, to);
363 { 345 }
364 if crate_graph.add_dep(from, CrateName::new(name).unwrap(), to).is_err()
365 {
366 log::error!("cyclic dependency between sysroot crates")
367 } 346 }
368 } 347 }
369 } 348 }
370 } 349 }
350 }
351 ProjectWorkspace::Cargo { cargo, sysroot } => {
352 let (public_deps, libproc_macro) =
353 sysroot_to_crate_graph(&mut crate_graph, sysroot, target, load);
371 354
372 let libcore = sysroot.core().and_then(|it| sysroot_crates.get(&it).copied()); 355 let mut cfg_options = CfgOptions::default();
373 let liballoc = sysroot.alloc().and_then(|it| sysroot_crates.get(&it).copied()); 356 cfg_options.extend(get_rustc_cfg_options(target));
374 let libstd = sysroot.std().and_then(|it| sysroot_crates.get(&it).copied());
375 let libproc_macro =
376 sysroot.proc_macro().and_then(|it| sysroot_crates.get(&it).copied());
377 357
378 let mut pkg_to_lib_crate = FxHashMap::default(); 358 let mut pkg_to_lib_crate = FxHashMap::default();
379 let mut pkg_crates = FxHashMap::default(); 359 let mut pkg_crates = FxHashMap::default();
@@ -424,14 +404,11 @@ impl ProjectWorkspace {
424 } 404 }
425 if cargo[tgt].is_proc_macro { 405 if cargo[tgt].is_proc_macro {
426 if let Some(proc_macro) = libproc_macro { 406 if let Some(proc_macro) = libproc_macro {
427 if crate_graph 407 if let Err(_) = crate_graph.add_dep(
428 .add_dep( 408 crate_id,
429 crate_id, 409 CrateName::new("proc_macro").unwrap(),
430 CrateName::new("proc_macro").unwrap(), 410 proc_macro,
431 proc_macro, 411 ) {
432 )
433 .is_err()
434 {
435 log::error!( 412 log::error!(
436 "cyclic dependency on proc_macro for {}", 413 "cyclic dependency on proc_macro for {}",
437 &cargo[pkg].name 414 &cargo[pkg].name
@@ -447,50 +424,24 @@ impl ProjectWorkspace {
447 // Set deps to the core, std and to the lib target of the current package 424 // Set deps to the core, std and to the lib target of the current package
448 for &from in pkg_crates.get(&pkg).into_iter().flatten() { 425 for &from in pkg_crates.get(&pkg).into_iter().flatten() {
449 if let Some((to, name)) = lib_tgt.clone() { 426 if let Some((to, name)) = lib_tgt.clone() {
450 if to != from 427 // For root projects with dashes in their name,
451 && crate_graph 428 // cargo metadata does not do any normalization,
452 .add_dep( 429 // so we do it ourselves currently
453 from, 430 let name = CrateName::normalize_dashes(&name);
454 // For root projects with dashes in their name, 431 if to != from && crate_graph.add_dep(from, name, to).is_err() {
455 // cargo metadata does not do any normalization, 432 log::error!(
456 // so we do it ourselves currently 433 "cyclic dependency between targets of {}",
457 CrateName::normalize_dashes(&name), 434 &cargo[pkg].name
458 to, 435 )
459 )
460 .is_err()
461 {
462 {
463 log::error!(
464 "cyclic dependency between targets of {}",
465 &cargo[pkg].name
466 )
467 }
468 }
469 }
470 // core is added as a dependency before std in order to
471 // mimic rustcs dependency order
472 if let Some(core) = libcore {
473 if crate_graph
474 .add_dep(from, CrateName::new("core").unwrap(), core)
475 .is_err()
476 {
477 log::error!("cyclic dependency on core for {}", &cargo[pkg].name)
478 }
479 }
480 if let Some(alloc) = liballoc {
481 if crate_graph
482 .add_dep(from, CrateName::new("alloc").unwrap(), alloc)
483 .is_err()
484 {
485 log::error!("cyclic dependency on alloc for {}", &cargo[pkg].name)
486 } 436 }
487 } 437 }
488 if let Some(std) = libstd { 438 for (name, krate) in public_deps.iter() {
489 if crate_graph 439 if let Err(_) = crate_graph.add_dep(from, name.clone(), *krate) {
490 .add_dep(from, CrateName::new("std").unwrap(), std) 440 log::error!(
491 .is_err() 441 "cyclic dependency on {} for {}",
492 { 442 name,
493 log::error!("cyclic dependency on std for {}", &cargo[pkg].name) 443 &cargo[pkg].name
444 )
494 } 445 }
495 } 446 }
496 } 447 }
@@ -500,12 +451,10 @@ impl ProjectWorkspace {
500 // target of downstream. 451 // target of downstream.
501 for pkg in cargo.packages() { 452 for pkg in cargo.packages() {
502 for dep in cargo[pkg].dependencies.iter() { 453 for dep in cargo[pkg].dependencies.iter() {
454 let name = CrateName::new(&dep.name).unwrap();
503 if let Some(&to) = pkg_to_lib_crate.get(&dep.pkg) { 455 if let Some(&to) = pkg_to_lib_crate.get(&dep.pkg) {
504 for &from in pkg_crates.get(&pkg).into_iter().flatten() { 456 for &from in pkg_crates.get(&pkg).into_iter().flatten() {
505 if crate_graph 457 if let Err(_) = crate_graph.add_dep(from, name.clone(), to) {
506 .add_dep(from, CrateName::new(&dep.name).unwrap(), to)
507 .is_err()
508 {
509 log::error!( 458 log::error!(
510 "cyclic dependency {} -> {}", 459 "cyclic dependency {} -> {}",
511 &cargo[pkg].name, 460 &cargo[pkg].name,
@@ -563,3 +512,51 @@ fn utf8_stdout(mut cmd: Command) -> Result<String> {
563 let stdout = String::from_utf8(output.stdout)?; 512 let stdout = String::from_utf8(output.stdout)?;
564 Ok(stdout.trim().to_string()) 513 Ok(stdout.trim().to_string())
565} 514}
515
516fn sysroot_to_crate_graph(
517 crate_graph: &mut CrateGraph,
518 sysroot: &Sysroot,
519 target: Option<&str>,
520 load: &mut dyn FnMut(&AbsPath) -> Option<FileId>,
521) -> (Vec<(CrateName, CrateId)>, Option<CrateId>) {
522 let mut cfg_options = CfgOptions::default();
523 cfg_options.extend(get_rustc_cfg_options(target));
524 let sysroot_crates: FxHashMap<_, _> = sysroot
525 .crates()
526 .filter_map(|krate| {
527 let file_id = load(&sysroot[krate].root)?;
528
529 let env = Env::default();
530 let proc_macro = vec![];
531 let name = sysroot[krate].name.clone();
532 let crate_id = crate_graph.add_crate_root(
533 file_id,
534 Edition::Edition2018,
535 Some(name),
536 cfg_options.clone(),
537 env,
538 proc_macro,
539 );
540 Some((krate, crate_id))
541 })
542 .collect();
543
544 for from in sysroot.crates() {
545 for &to in sysroot[from].deps.iter() {
546 let name = CrateName::new(&sysroot[to].name).unwrap();
547 if let (Some(&from), Some(&to)) = (sysroot_crates.get(&from), sysroot_crates.get(&to)) {
548 if let Err(_) = crate_graph.add_dep(from, name, to) {
549 log::error!("cyclic dependency between sysroot crates")
550 }
551 }
552 }
553 }
554
555 let public_deps = sysroot
556 .public_deps()
557 .map(|(name, idx)| (CrateName::new(name).unwrap(), sysroot_crates[&idx]))
558 .collect::<Vec<_>>();
559
560 let libproc_macro = sysroot.proc_macro().and_then(|it| sysroot_crates.get(&it).copied());
561 (public_deps, libproc_macro)
562}
diff --git a/crates/project_model/src/project_json.rs b/crates/project_model/src/project_json.rs
index 060ea5b7d..5a0fe749a 100644
--- a/crates/project_model/src/project_json.rs
+++ b/crates/project_model/src/project_json.rs
@@ -7,12 +7,13 @@ use paths::{AbsPath, AbsPathBuf};
7use rustc_hash::FxHashMap; 7use rustc_hash::FxHashMap;
8use serde::{de, Deserialize}; 8use serde::{de, Deserialize};
9 9
10use crate::cfg_flag::CfgFlag; 10use crate::{cfg_flag::CfgFlag, Sysroot};
11 11
12/// Roots and crates that compose this Rust project. 12/// Roots and crates that compose this Rust project.
13#[derive(Clone, Debug, Eq, PartialEq)] 13#[derive(Clone, Debug, Eq, PartialEq)]
14pub struct ProjectJson { 14pub struct ProjectJson {
15 pub(crate) crates: Vec<Crate>, 15 pub(crate) sysroot: Option<Sysroot>,
16 crates: Vec<Crate>,
16} 17}
17 18
18/// A crate points to the root module of a crate and lists the dependencies of the crate. This is 19/// A crate points to the root module of a crate and lists the dependencies of the crate. This is
@@ -34,6 +35,7 @@ pub struct Crate {
34impl ProjectJson { 35impl ProjectJson {
35 pub fn new(base: &AbsPath, data: ProjectJsonData) -> ProjectJson { 36 pub fn new(base: &AbsPath, data: ProjectJsonData) -> ProjectJson {
36 ProjectJson { 37 ProjectJson {
38 sysroot: data.sysroot_src.map(|it| base.join(it)).map(|it| Sysroot::load(&it)),
37 crates: data 39 crates: data
38 .crates 40 .crates
39 .into_iter() 41 .into_iter()
@@ -43,11 +45,13 @@ impl ProjectJson {
43 && !crate_data.root_module.starts_with("..") 45 && !crate_data.root_module.starts_with("..")
44 || crate_data.root_module.starts_with(base) 46 || crate_data.root_module.starts_with(base)
45 }); 47 });
46 let root_module = base.join(crate_data.root_module); 48 let root_module = base.join(crate_data.root_module).normalize();
47 let (include, exclude) = match crate_data.source { 49 let (include, exclude) = match crate_data.source {
48 Some(src) => { 50 Some(src) => {
49 let absolutize = |dirs: Vec<PathBuf>| { 51 let absolutize = |dirs: Vec<PathBuf>| {
50 dirs.into_iter().map(|it| base.join(it)).collect::<Vec<_>>() 52 dirs.into_iter()
53 .map(|it| base.join(it).normalize())
54 .collect::<Vec<_>>()
51 }; 55 };
52 (absolutize(src.include_dirs), absolutize(src.exclude_dirs)) 56 (absolutize(src.include_dirs), absolutize(src.exclude_dirs))
53 } 57 }
@@ -79,10 +83,17 @@ impl ProjectJson {
79 .collect::<Vec<_>>(), 83 .collect::<Vec<_>>(),
80 } 84 }
81 } 85 }
86 pub fn n_crates(&self) -> usize {
87 self.crates.len()
88 }
89 pub fn crates(&self) -> impl Iterator<Item = (CrateId, &Crate)> + '_ {
90 self.crates.iter().enumerate().map(|(idx, krate)| (CrateId(idx as u32), krate))
91 }
82} 92}
83 93
84#[derive(Deserialize)] 94#[derive(Deserialize)]
85pub struct ProjectJsonData { 95pub struct ProjectJsonData {
96 sysroot_src: Option<PathBuf>,
86 crates: Vec<CrateData>, 97 crates: Vec<CrateData>,
87} 98}
88 99
diff --git a/crates/project_model/src/sysroot.rs b/crates/project_model/src/sysroot.rs
index 687e15337..74c0eda9a 100644
--- a/crates/project_model/src/sysroot.rs
+++ b/crates/project_model/src/sysroot.rs
@@ -34,16 +34,10 @@ impl ops::Index<SysrootCrate> for Sysroot {
34} 34}
35 35
36impl Sysroot { 36impl Sysroot {
37 pub fn core(&self) -> Option<SysrootCrate> { 37 pub fn public_deps(&self) -> impl Iterator<Item = (&'static str, SysrootCrate)> + '_ {
38 self.by_name("core") 38 // core is added as a dependency before std in order to
39 } 39 // mimic rustcs dependency order
40 40 vec!["core", "alloc", "std"].into_iter().filter_map(move |it| Some((it, self.by_name(it)?)))
41 pub fn alloc(&self) -> Option<SysrootCrate> {
42 self.by_name("alloc")
43 }
44
45 pub fn std(&self) -> Option<SysrootCrate> {
46 self.by_name("std")
47 } 41 }
48 42
49 pub fn proc_macro(&self) -> Option<SysrootCrate> { 43 pub fn proc_macro(&self) -> Option<SysrootCrate> {
@@ -81,7 +75,7 @@ impl Sysroot {
81 } 75 }
82 } 76 }
83 77
84 if let Some(std) = sysroot.std() { 78 if let Some(std) = sysroot.by_name("std") {
85 for dep in STD_DEPS.trim().lines() { 79 for dep in STD_DEPS.trim().lines() {
86 if let Some(dep) = sysroot.by_name(dep) { 80 if let Some(dep) = sysroot.by_name(dep) {
87 sysroot.crates[std].deps.push(dep) 81 sysroot.crates[std].deps.push(dep)
@@ -89,8 +83,8 @@ impl Sysroot {
89 } 83 }
90 } 84 }
91 85
92 if let Some(alloc) = sysroot.alloc() { 86 if let Some(alloc) = sysroot.by_name("alloc") {
93 if let Some(core) = sysroot.core() { 87 if let Some(core) = sysroot.by_name("core") {
94 sysroot.crates[alloc].deps.push(core); 88 sysroot.crates[alloc].deps.push(core);
95 } 89 }
96 } 90 }
diff --git a/crates/rust-analyzer/src/bin/args.rs b/crates/rust-analyzer/src/bin/args.rs
index 0bc92431a..8ddf1e031 100644
--- a/crates/rust-analyzer/src/bin/args.rs
+++ b/crates/rust-analyzer/src/bin/args.rs
@@ -3,9 +3,9 @@
3//! If run started args, we run the LSP server loop. With a subcommand, we do a 3//! If run started args, we run the LSP server loop. With a subcommand, we do a
4//! one-time batch processing. 4//! one-time batch processing.
5 5
6use std::{env, fmt::Write, path::PathBuf}; 6use std::{env, path::PathBuf};
7 7
8use anyhow::{bail, Result}; 8use anyhow::{bail, format_err, Result};
9use pico_args::Arguments; 9use pico_args::Arguments;
10use rust_analyzer::cli::{AnalysisStatsCmd, BenchCmd, BenchWhat, Position, Verbosity}; 10use rust_analyzer::cli::{AnalysisStatsCmd, BenchCmd, BenchWhat, Position, Verbosity};
11use ssr::{SsrPattern, SsrRule}; 11use ssr::{SsrPattern, SsrRule};
@@ -13,47 +13,107 @@ use vfs::AbsPathBuf;
13 13
14pub(crate) struct Args { 14pub(crate) struct Args {
15 pub(crate) verbosity: Verbosity, 15 pub(crate) verbosity: Verbosity,
16 pub(crate) log_file: Option<PathBuf>,
16 pub(crate) command: Command, 17 pub(crate) command: Command,
17} 18}
18 19
19pub(crate) enum Command { 20pub(crate) enum Command {
20 Parse { 21 Parse { no_dump: bool },
21 no_dump: bool,
22 },
23 Symbols, 22 Symbols,
24 Highlight { 23 Highlight { rainbow: bool },
25 rainbow: bool,
26 },
27 AnalysisStats(AnalysisStatsCmd), 24 AnalysisStats(AnalysisStatsCmd),
28 Bench(BenchCmd), 25 Bench(BenchCmd),
29 Diagnostics { 26 Diagnostics { path: PathBuf, load_output_dirs: bool, with_proc_macro: bool },
30 path: PathBuf, 27 Ssr { rules: Vec<SsrRule> },
31 load_output_dirs: bool, 28 StructuredSearch { debug_snippet: Option<String>, patterns: Vec<SsrPattern> },
32 with_proc_macro: bool,
33 /// Include files which are not modules. In rust-analyzer
34 /// this would include the parser test files.
35 all: bool,
36 },
37 Ssr {
38 rules: Vec<SsrRule>,
39 },
40 StructuredSearch {
41 debug_snippet: Option<String>,
42 patterns: Vec<SsrPattern>,
43 },
44 ProcMacro, 29 ProcMacro,
45 RunServer, 30 RunServer,
46 Version, 31 Version,
47 Help, 32 Help,
48} 33}
49 34
35const HELP: &str = "\
36rust-analyzer
37
38USAGE:
39 rust-analyzer [FLAGS] [COMMAND] [COMMAND_OPTIONS]
40
41FLAGS:
42 --version Print version
43 -h, --help Print this help
44
45 -v, --verbose
46 -vv, --spammy
47 -q, --quiet Set verbosity
48
49 --log-file <PATH> Log to the specified filed instead of stderr
50
51ENVIRONMENTAL VARIABLES:
52 RA_LOG Set log filter in env_logger format
53 RA_PROFILE Enable hierarchical profiler
54
55COMMANDS:
56
57not specified Launch LSP server
58
59parse < main.rs Parse tree
60 --no-dump Suppress printing
61
62symbols < main.rs Parse input an print the list of symbols
63
64highlight < main.rs Highlight input as html
65 --rainbow Enable rainbow highlighting of identifiers
66
67analysis-stats <PATH> Batch typecheck project and print summary statistics
68 <PATH> Directory with Cargo.toml
69 --randomize Randomize order in which crates, modules, and items are processed
70 --parallel Run type inference in parallel
71 --memory-usage Collect memory usage statistics
72 -o, --only <PATH> Only analyze items matching this path
73 --with-deps Also analyze all dependencies
74 --load-output-dirs
75 Load OUT_DIR values by running `cargo check` before analysis
76 --with-proc-macro Use proc-macro-srv for proc-macro expanding
77
78analysis-bench <PATH> Benchmark specific analysis operation
79 <PATH> Directory with Cargo.toml
80 --highlight <PATH>
81 Compute syntax highlighting for this file
82 --complete <PATH:LINE:COLUMN>
83 Compute completions at this location
84 --goto-def <PATH:LINE:COLUMN>
85 Compute goto definition at this location
86 --memory-usage Collect memory usage statistics
87 --load-output-dirs
88 Load OUT_DIR values by running `cargo check` before analysis
89 --with-proc-macro Use proc-macro-srv for proc-macro expanding
90
91diagnostics <PATH>
92 <PATH> Directory with Cargo.toml
93 --load-output-dirs
94 Load OUT_DIR values by running `cargo check` before analysis
95 --with-proc-macro Use proc-macro-srv for proc-macro expanding
96
97ssr [RULE...]
98 <RULE> A structured search replace rule (`$a.foo($b) ==> bar($a, $b)`)
99
100search [PATTERN..]
101 <PATTERN> A structured search replace pattern (`$a.foo($b)`)
102 --debug <snippet> Prints debug information for any nodes with source exactly
103 equal to <snippet>
104";
105
50impl Args { 106impl Args {
51 pub(crate) fn parse() -> Result<Args> { 107 pub(crate) fn parse() -> Result<Args> {
52 let mut matches = Arguments::from_env(); 108 let mut matches = Arguments::from_env();
53 109
54 if matches.contains("--version") { 110 if matches.contains("--version") {
55 matches.finish().or_else(handle_extra_flags)?; 111 matches.finish()?;
56 return Ok(Args { verbosity: Verbosity::Normal, command: Command::Version }); 112 return Ok(Args {
113 verbosity: Verbosity::Normal,
114 log_file: None,
115 command: Command::Version,
116 });
57 } 117 }
58 118
59 let verbosity = match ( 119 let verbosity = match (
@@ -68,320 +128,96 @@ impl Args {
68 (false, true, false) => Verbosity::Verbose, 128 (false, true, false) => Verbosity::Verbose,
69 (false, true, true) => bail!("Invalid flags: -q conflicts with -v"), 129 (false, true, true) => bail!("Invalid flags: -q conflicts with -v"),
70 }; 130 };
131 let log_file = matches.opt_value_from_str("--log-file")?;
132
133 if matches.contains(["-h", "--help"]) {
134 eprintln!("{}", HELP);
135 return Ok(Args { verbosity, log_file: None, command: Command::Help });
136 }
71 137
72 let help = Ok(Args { verbosity, command: Command::Help });
73 let subcommand = match matches.subcommand()? { 138 let subcommand = match matches.subcommand()? {
74 Some(it) => it, 139 Some(it) => it,
75 None => { 140 None => {
76 if matches.contains(["-h", "--help"]) { 141 matches.finish()?;
77 print_subcommands(); 142 return Ok(Args { verbosity, log_file, command: Command::RunServer });
78 return help;
79 }
80 matches.finish().or_else(handle_extra_flags)?;
81 return Ok(Args { verbosity, command: Command::RunServer });
82 } 143 }
83 }; 144 };
84 let command = match subcommand.as_str() { 145 let command = match subcommand.as_str() {
85 "parse" => { 146 "parse" => Command::Parse { no_dump: matches.contains("--no-dump") },
86 if matches.contains(["-h", "--help"]) { 147 "symbols" => Command::Symbols,
87 eprintln!( 148 "highlight" => Command::Highlight { rainbow: matches.contains("--rainbow") },
88 "\ 149 "analysis-stats" => Command::AnalysisStats(AnalysisStatsCmd {
89rust-analyzer parse 150 randomize: matches.contains("--randomize"),
90 151 parallel: matches.contains("--parallel"),
91USAGE: 152 memory_usage: matches.contains("--memory-usage"),
92 rust-analyzer parse [FLAGS] 153 only: matches.opt_value_from_str(["-o", "--only"])?,
93 154 with_deps: matches.contains("--with-deps"),
94FLAGS: 155 load_output_dirs: matches.contains("--load-output-dirs"),
95 -h, --help Prints help information 156 with_proc_macro: matches.contains("--with-proc-macro"),
96 --no-dump" 157 path: matches
97 ); 158 .free_from_str()?
98 return help; 159 .ok_or_else(|| format_err!("expected positional argument"))?,
99 } 160 }),
100 161 "analysis-bench" => Command::Bench(BenchCmd {
101 let no_dump = matches.contains("--no-dump"); 162 what: {
102 matches.finish().or_else(handle_extra_flags)?; 163 let highlight_path: Option<String> =
103 Command::Parse { no_dump } 164 matches.opt_value_from_str("--highlight")?;
104 } 165 let complete_path: Option<Position> =
105 "symbols" => { 166 matches.opt_value_from_str("--complete")?;
106 if matches.contains(["-h", "--help"]) { 167 let goto_def_path: Option<Position> =
107 eprintln!( 168 matches.opt_value_from_str("--goto-def")?;
108 "\ 169 match (highlight_path, complete_path, goto_def_path) {
109rust-analyzer symbols 170 (Some(path), None, None) => {
110 171 let path = env::current_dir().unwrap().join(path);
111USAGE: 172 BenchWhat::Highlight { path: AbsPathBuf::assert(path) }
112 rust-analyzer highlight [FLAGS] 173 }
113 174 (None, Some(position), None) => BenchWhat::Complete(position),
114FLAGS: 175 (None, None, Some(position)) => BenchWhat::GotoDef(position),
115 -h, --help Prints help inforamtion" 176 _ => panic!(
116 ); 177 "exactly one of `--highlight`, `--complete` or `--goto-def` must be set"
117 return help; 178 ),
118 } 179 }
119 180 },
120 matches.finish().or_else(handle_extra_flags)?; 181 memory_usage: matches.contains("--memory-usage"),
121 182 load_output_dirs: matches.contains("--load-output-dirs"),
122 Command::Symbols 183 with_proc_macro: matches.contains("--with-proc-macro"),
123 } 184 path: matches
124 "highlight" => { 185 .free_from_str()?
125 if matches.contains(["-h", "--help"]) { 186 .ok_or_else(|| format_err!("expected positional argument"))?,
126 eprintln!( 187 }),
127 "\ 188 "diagnostics" => Command::Diagnostics {
128rust-analyzer highlight 189 load_output_dirs: matches.contains("--load-output-dirs"),
129 190 with_proc_macro: matches.contains("--with-proc-macro"),
130USAGE: 191 path: matches
131 rust-analyzer highlight [FLAGS] 192 .free_from_str()?
132 193 .ok_or_else(|| format_err!("expected positional argument"))?,
133FLAGS: 194 },
134 -h, --help Prints help information 195 "proc-macro" => Command::ProcMacro,
135 -r, --rainbow" 196 "ssr" => Command::Ssr {
136 ); 197 rules: {
137 return help; 198 let mut acc = Vec::new();
138 } 199 while let Some(rule) = matches.free_from_str()? {
139 200 acc.push(rule);
140 let rainbow = matches.contains(["-r", "--rainbow"]);
141 matches.finish().or_else(handle_extra_flags)?;
142 Command::Highlight { rainbow }
143 }
144 "analysis-stats" => {
145 if matches.contains(["-h", "--help"]) {
146 eprintln!(
147 "\
148rust-analyzer analysis-stats
149
150USAGE:
151 rust-analyzer analysis-stats [FLAGS] [OPTIONS] [PATH]
152
153FLAGS:
154 -o, --only Only analyze items matching this path
155 -h, --help Prints help information
156 --memory-usage Collect memory usage statistics
157 --randomize Randomize order in which crates, modules, and items are processed
158 --parallel Run type inference in parallel
159 --load-output-dirs Load OUT_DIR values by running `cargo check` before analysis
160 --with-proc-macro Use ra-proc-macro-srv for proc-macro expanding
161 --with-deps Also analyze all dependencies
162 -v, --verbose
163 -q, --quiet
164
165OPTIONS:
166 -o <ONLY>
167
168ARGS:
169 <PATH>"
170 );
171 return help;
172 }
173
174 let randomize = matches.contains("--randomize");
175 let parallel = matches.contains("--parallel");
176 let memory_usage = matches.contains("--memory-usage");
177 let only: Option<String> = matches.opt_value_from_str(["-o", "--only"])?;
178 let with_deps: bool = matches.contains("--with-deps");
179 let load_output_dirs = matches.contains("--load-output-dirs");
180 let with_proc_macro = matches.contains("--with-proc-macro");
181 let path = {
182 let mut trailing = matches.free()?;
183 if trailing.len() != 1 {
184 bail!("Invalid flags");
185 }
186 trailing.pop().unwrap().into()
187 };
188
189 Command::AnalysisStats(AnalysisStatsCmd {
190 randomize,
191 parallel,
192 memory_usage,
193 only,
194 with_deps,
195 path,
196 load_output_dirs,
197 with_proc_macro,
198 })
199 }
200 "analysis-bench" => {
201 if matches.contains(["-h", "--help"]) {
202 eprintln!(
203 "\
204rust-analyzer analysis-bench
205
206USAGE:
207 rust-analyzer analysis-bench [FLAGS] [OPTIONS]
208
209FLAGS:
210 -h, --help Prints help information
211 --memory-usage Collect memory usage statistics
212 --load-output-dirs Load OUT_DIR values by running `cargo check` before analysis
213 --with-proc-macro Use ra-proc-macro-srv for proc-macro expanding
214 -v, --verbose
215
216OPTIONS:
217 --project <PATH> Path to directory with Cargo.toml
218 --complete <PATH:LINE:COLUMN> Compute completions at this location
219 --goto-def <PATH:LINE:COLUMN> Compute goto definition at this location
220 --highlight <PATH> Hightlight this file
221
222ARGS:
223 <PATH> Project to analyse"
224 );
225 return help;
226 }
227
228 let path: PathBuf = matches.opt_value_from_str("--project")?.unwrap_or_default();
229 let highlight_path: Option<String> = matches.opt_value_from_str("--highlight")?;
230 let complete_path: Option<Position> = matches.opt_value_from_str("--complete")?;
231 let goto_def_path: Option<Position> = matches.opt_value_from_str("--goto-def")?;
232 let what = match (highlight_path, complete_path, goto_def_path) {
233 (Some(path), None, None) => {
234 let path = env::current_dir().unwrap().join(path);
235 BenchWhat::Highlight { path: AbsPathBuf::assert(path) }
236 } 201 }
237 (None, Some(position), None) => BenchWhat::Complete(position), 202 acc
238 (None, None, Some(position)) => BenchWhat::GotoDef(position), 203 },
239 _ => panic!( 204 },
240 "exactly one of `--highlight`, `--complete` or `--goto-def` must be set" 205 "search" => Command::StructuredSearch {
241 ), 206 debug_snippet: matches.opt_value_from_str("--debug")?,
242 }; 207 patterns: {
243 let memory_usage = matches.contains("--memory-usage"); 208 let mut acc = Vec::new();
244 let load_output_dirs = matches.contains("--load-output-dirs"); 209 while let Some(rule) = matches.free_from_str()? {
245 let with_proc_macro = matches.contains("--with-proc-macro"); 210 acc.push(rule);
246 Command::Bench(BenchCmd {
247 memory_usage,
248 path,
249 what,
250 load_output_dirs,
251 with_proc_macro,
252 })
253 }
254 "diagnostics" => {
255 if matches.contains(["-h", "--help"]) {
256 eprintln!(
257 "\
258rust-analyzer diagnostics
259
260USAGE:
261 rust-analyzer diagnostics [FLAGS] [PATH]
262
263FLAGS:
264 -h, --help Prints help information
265 --load-output-dirs Load OUT_DIR values by running `cargo check` before analysis
266 --all Include all files rather than only modules
267
268ARGS:
269 <PATH>"
270 );
271 return help;
272 }
273
274 let load_output_dirs = matches.contains("--load-output-dirs");
275 let with_proc_macro = matches.contains("--with-proc-macro");
276 let all = matches.contains("--all");
277 let path = {
278 let mut trailing = matches.free()?;
279 if trailing.len() != 1 {
280 bail!("Invalid flags");
281 } 211 }
282 trailing.pop().unwrap().into() 212 acc
283 }; 213 },
284 214 },
285 Command::Diagnostics { path, load_output_dirs, with_proc_macro, all }
286 }
287 "proc-macro" => Command::ProcMacro,
288 "ssr" => {
289 if matches.contains(["-h", "--help"]) {
290 eprintln!(
291 "\
292rust-analyzer ssr
293
294USAGE:
295 rust-analyzer ssr [FLAGS] [RULE...]
296
297EXAMPLE:
298 rust-analyzer ssr '$a.foo($b) ==> bar($a, $b)'
299
300FLAGS:
301 --debug <snippet> Prints debug information for any nodes with source exactly equal to <snippet>
302 -h, --help Prints help information
303
304ARGS:
305 <RULE> A structured search replace rule"
306 );
307 return help;
308 }
309 let mut rules = Vec::new();
310 while let Some(rule) = matches.free_from_str()? {
311 rules.push(rule);
312 }
313 Command::Ssr { rules }
314 }
315 "search" => {
316 if matches.contains(["-h", "--help"]) {
317 eprintln!(
318 "\
319rust-analyzer search
320
321USAGE:
322 rust-analyzer search [FLAGS] [PATTERN...]
323
324EXAMPLE:
325 rust-analyzer search '$a.foo($b)'
326
327FLAGS:
328 --debug <snippet> Prints debug information for any nodes with source exactly equal to <snippet>
329 -h, --help Prints help information
330
331ARGS:
332 <PATTERN> A structured search pattern"
333 );
334 return help;
335 }
336 let debug_snippet = matches.opt_value_from_str("--debug")?;
337 let mut patterns = Vec::new();
338 while let Some(rule) = matches.free_from_str()? {
339 patterns.push(rule);
340 }
341 Command::StructuredSearch { patterns, debug_snippet }
342 }
343 _ => { 215 _ => {
344 print_subcommands(); 216 eprintln!("{}", HELP);
345 return help; 217 return Ok(Args { verbosity, log_file: None, command: Command::Help });
346 } 218 }
347 }; 219 };
348 Ok(Args { verbosity, command }) 220 matches.finish()?;
349 } 221 Ok(Args { verbosity, log_file, command })
350}
351
352fn print_subcommands() {
353 eprintln!(
354 "\
355rust-analyzer
356
357USAGE:
358 rust-analyzer <SUBCOMMAND>
359
360FLAGS:
361 -h, --help Prints help information
362
363SUBCOMMANDS:
364 analysis-bench
365 analysis-stats
366 highlight
367 diagnostics
368 proc-macro
369 parse
370 search
371 ssr
372 symbols"
373 )
374}
375
376fn handle_extra_flags(e: pico_args::Error) -> Result<()> {
377 if let pico_args::Error::UnusedArgsLeft(flags) = e {
378 let mut invalid_flags = String::new();
379 for flag in flags {
380 write!(&mut invalid_flags, "{}, ", flag)?;
381 }
382 let (invalid_flags, _) = invalid_flags.split_at(invalid_flags.len() - 2);
383 bail!("Invalid flags: {}", invalid_flags);
384 } else {
385 bail!(e);
386 } 222 }
387} 223}
diff --git a/crates/rust-analyzer/src/bin/logger.rs b/crates/rust-analyzer/src/bin/logger.rs
new file mode 100644
index 000000000..3bcb1ae37
--- /dev/null
+++ b/crates/rust-analyzer/src/bin/logger.rs
@@ -0,0 +1,73 @@
1//! Simple logger that logs either to stderr or to a file, using `env_logger`
2//! filter syntax. Amusingly, there's no crates.io crate that can do this and
3//! only this.
4
5use std::{
6 fs::File,
7 io::{BufWriter, Write},
8};
9
10use env_logger::filter::{Builder, Filter};
11use log::{Log, Metadata, Record};
12use parking_lot::Mutex;
13
14pub(crate) struct Logger {
15 filter: Filter,
16 file: Option<Mutex<BufWriter<File>>>,
17}
18
19impl Logger {
20 pub(crate) fn new(log_file: Option<File>, filter: Option<&str>) -> Logger {
21 let filter = {
22 let mut builder = Builder::new();
23 if let Some(filter) = filter {
24 builder.parse(filter);
25 }
26 builder.build()
27 };
28
29 let file = log_file.map(|it| Mutex::new(BufWriter::new(it)));
30
31 Logger { filter, file }
32 }
33
34 pub(crate) fn install(self) {
35 let max_level = self.filter.filter();
36 let _ = log::set_boxed_logger(Box::new(self)).map(|()| log::set_max_level(max_level));
37 }
38}
39
40impl Log for Logger {
41 fn enabled(&self, metadata: &Metadata) -> bool {
42 self.filter.enabled(metadata)
43 }
44
45 fn log(&self, record: &Record) {
46 if !self.filter.matches(record) {
47 return;
48 }
49 match &self.file {
50 Some(w) => {
51 let _ = writeln!(
52 w.lock(),
53 "[{} {}] {}",
54 record.level(),
55 record.module_path().unwrap_or_default(),
56 record.args(),
57 );
58 }
59 None => eprintln!(
60 "[{} {}] {}",
61 record.level(),
62 record.module_path().unwrap_or_default(),
63 record.args(),
64 ),
65 }
66 }
67
68 fn flush(&self) {
69 if let Some(w) = &self.file {
70 let _ = w.lock().flush();
71 }
72 }
73}
diff --git a/crates/rust-analyzer/src/bin/main.rs b/crates/rust-analyzer/src/bin/main.rs
index 0e03a0ca8..ba4402ade 100644
--- a/crates/rust-analyzer/src/bin/main.rs
+++ b/crates/rust-analyzer/src/bin/main.rs
@@ -2,8 +2,9 @@
2//! 2//!
3//! Based on cli flags, either spawns an LSP server, or runs a batch analysis 3//! Based on cli flags, either spawns an LSP server, or runs a batch analysis
4mod args; 4mod args;
5mod logger;
5 6
6use std::{convert::TryFrom, process}; 7use std::{convert::TryFrom, env, fs, path::PathBuf, process};
7 8
8use lsp_server::Connection; 9use lsp_server::Connection;
9use project_model::ProjectManifest; 10use project_model::ProjectManifest;
@@ -26,8 +27,8 @@ fn main() {
26} 27}
27 28
28fn try_main() -> Result<()> { 29fn try_main() -> Result<()> {
29 setup_logging()?;
30 let args = args::Args::parse()?; 30 let args = args::Args::parse()?;
31 setup_logging(args.log_file)?;
31 match args.command { 32 match args.command {
32 args::Command::RunServer => run_server()?, 33 args::Command::RunServer => run_server()?,
33 args::Command::ProcMacro => proc_macro_srv::cli::run()?, 34 args::Command::ProcMacro => proc_macro_srv::cli::run()?,
@@ -37,8 +38,8 @@ fn try_main() -> Result<()> {
37 args::Command::Highlight { rainbow } => cli::highlight(rainbow)?, 38 args::Command::Highlight { rainbow } => cli::highlight(rainbow)?,
38 args::Command::AnalysisStats(cmd) => cmd.run(args.verbosity)?, 39 args::Command::AnalysisStats(cmd) => cmd.run(args.verbosity)?,
39 args::Command::Bench(cmd) => cmd.run(args.verbosity)?, 40 args::Command::Bench(cmd) => cmd.run(args.verbosity)?,
40 args::Command::Diagnostics { path, load_output_dirs, with_proc_macro, all } => { 41 args::Command::Diagnostics { path, load_output_dirs, with_proc_macro } => {
41 cli::diagnostics(path.as_ref(), load_output_dirs, with_proc_macro, all)? 42 cli::diagnostics(path.as_ref(), load_output_dirs, with_proc_macro)?
42 } 43 }
43 args::Command::Ssr { rules } => { 44 args::Command::Ssr { rules } => {
44 cli::apply_ssr_rules(rules)?; 45 cli::apply_ssr_rules(rules)?;
@@ -52,9 +53,21 @@ fn try_main() -> Result<()> {
52 Ok(()) 53 Ok(())
53} 54}
54 55
55fn setup_logging() -> Result<()> { 56fn setup_logging(log_file: Option<PathBuf>) -> Result<()> {
56 std::env::set_var("RUST_BACKTRACE", "short"); 57 env::set_var("RUST_BACKTRACE", "short");
57 env_logger::try_init_from_env("RA_LOG")?; 58
59 let log_file = match log_file {
60 Some(path) => {
61 if let Some(parent) = path.parent() {
62 let _ = fs::create_dir_all(parent);
63 }
64 Some(fs::File::create(path)?)
65 }
66 None => None,
67 };
68 let filter = env::var("RA_LOG").ok();
69 logger::Logger::new(log_file, filter.as_deref()).install();
70
58 profile::init(); 71 profile::init();
59 Ok(()) 72 Ok(())
60} 73}
@@ -95,7 +108,7 @@ fn run_server() -> Result<()> {
95 { 108 {
96 Some(it) => it, 109 Some(it) => it,
97 None => { 110 None => {
98 let cwd = std::env::current_dir()?; 111 let cwd = env::current_dir()?;
99 AbsPathBuf::assert(cwd) 112 AbsPathBuf::assert(cwd)
100 } 113 }
101 }; 114 };
diff --git a/crates/rust-analyzer/src/cli/diagnostics.rs b/crates/rust-analyzer/src/cli/diagnostics.rs
index c424aa6e2..f3b6c900e 100644
--- a/crates/rust-analyzer/src/cli/diagnostics.rs
+++ b/crates/rust-analyzer/src/cli/diagnostics.rs
@@ -12,12 +12,7 @@ use ide::{DiagnosticsConfig, Severity};
12 12
13use crate::cli::{load_cargo::load_cargo, Result}; 13use crate::cli::{load_cargo::load_cargo, Result};
14 14
15pub fn diagnostics( 15pub fn diagnostics(path: &Path, load_output_dirs: bool, with_proc_macro: bool) -> Result<()> {
16 path: &Path,
17 load_output_dirs: bool,
18 with_proc_macro: bool,
19 _all: bool,
20) -> Result<()> {
21 let (host, _vfs) = load_cargo(path, load_output_dirs, with_proc_macro)?; 16 let (host, _vfs) = load_cargo(path, load_output_dirs, with_proc_macro)?;
22 let db = host.raw_database(); 17 let db = host.raw_database();
23 let analysis = host.analysis(); 18 let analysis = host.analysis();
diff --git a/crates/rust-analyzer/tests/rust-analyzer/main.rs b/crates/rust-analyzer/tests/rust-analyzer/main.rs
index e97784c47..0880d0425 100644
--- a/crates/rust-analyzer/tests/rust-analyzer/main.rs
+++ b/crates/rust-analyzer/tests/rust-analyzer/main.rs
@@ -39,7 +39,6 @@ fn completes_items_from_standard_library() {
39 return; 39 return;
40 } 40 }
41 41
42 let project_start = Instant::now();
43 let server = Project::with_fixture( 42 let server = Project::with_fixture(
44 r#" 43 r#"
45//- /Cargo.toml 44//- /Cargo.toml
@@ -52,10 +51,9 @@ use std::collections::Spam;
52"#, 51"#,
53 ) 52 )
54 .with_sysroot(true) 53 .with_sysroot(true)
55 .server(); 54 .server()
56 server.wait_until_workspace_is_loaded(); 55 .wait_until_workspace_is_loaded();
57 eprintln!("loading took {:?}", project_start.elapsed()); 56
58 let completion_start = Instant::now();
59 let res = server.send_request::<Completion>(CompletionParams { 57 let res = server.send_request::<Completion>(CompletionParams {
60 text_document_position: TextDocumentPositionParams::new( 58 text_document_position: TextDocumentPositionParams::new(
61 server.doc_id("src/lib.rs"), 59 server.doc_id("src/lib.rs"),
@@ -66,7 +64,6 @@ use std::collections::Spam;
66 work_done_progress_params: WorkDoneProgressParams::default(), 64 work_done_progress_params: WorkDoneProgressParams::default(),
67 }); 65 });
68 assert!(res.to_string().contains("HashMap")); 66 assert!(res.to_string().contains("HashMap"));
69 eprintln!("completion took {:?}", completion_start.elapsed());
70} 67}
71 68
72#[test] 69#[test]
@@ -75,7 +72,8 @@ fn test_runnables_project() {
75 return; 72 return;
76 } 73 }
77 74
78 let code = r#" 75 let server = Project::with_fixture(
76 r#"
79//- /foo/Cargo.toml 77//- /foo/Cargo.toml
80[package] 78[package]
81name = "foo" 79name = "foo"
@@ -95,11 +93,13 @@ version = "0.0.0"
95 93
96//- /bar/src/main.rs 94//- /bar/src/main.rs
97fn main() {} 95fn main() {}
98"#; 96"#,
99 97 )
100 let server = Project::with_fixture(code).root("foo").root("bar").server(); 98 .root("foo")
99 .root("bar")
100 .server()
101 .wait_until_workspace_is_loaded();
101 102
102 server.wait_until_workspace_is_loaded();
103 server.request::<Runnables>( 103 server.request::<Runnables>(
104 RunnablesParams { text_document: server.doc_id("foo/tests/spam.rs"), position: None }, 104 RunnablesParams { text_document: server.doc_id("foo/tests/spam.rs"), position: None },
105 json!([ 105 json!([
@@ -166,8 +166,8 @@ fn main() {
166 166
167pub use std::collections::HashMap; 167pub use std::collections::HashMap;
168"#, 168"#,
169 ); 169 )
170 server.wait_until_workspace_is_loaded(); 170 .wait_until_workspace_is_loaded();
171 171
172 server.request::<Formatting>( 172 server.request::<Formatting>(
173 DocumentFormattingParams { 173 DocumentFormattingParams {
@@ -224,8 +224,8 @@ fn main() {
224 224
225pub use std::collections::HashMap; 225pub use std::collections::HashMap;
226"#, 226"#,
227 ); 227 )
228 server.wait_until_workspace_is_loaded(); 228 .wait_until_workspace_is_loaded();
229 229
230 server.request::<Formatting>( 230 server.request::<Formatting>(
231 DocumentFormattingParams { 231 DocumentFormattingParams {
@@ -277,14 +277,14 @@ mod bar;
277 277
278fn main() {} 278fn main() {}
279"#, 279"#,
280 ); 280 )
281 server.wait_until_workspace_is_loaded(); 281 .wait_until_workspace_is_loaded();
282 let empty_context = || CodeActionContext { diagnostics: Vec::new(), only: None }; 282
283 server.request::<CodeActionRequest>( 283 server.request::<CodeActionRequest>(
284 CodeActionParams { 284 CodeActionParams {
285 text_document: server.doc_id("src/lib.rs"), 285 text_document: server.doc_id("src/lib.rs"),
286 range: Range::new(Position::new(0, 4), Position::new(0, 7)), 286 range: Range::new(Position::new(0, 4), Position::new(0, 7)),
287 context: empty_context(), 287 context: CodeActionContext::default(),
288 partial_result_params: PartialResultParams::default(), 288 partial_result_params: PartialResultParams::default(),
289 work_done_progress_params: WorkDoneProgressParams::default(), 289 work_done_progress_params: WorkDoneProgressParams::default(),
290 }, 290 },
@@ -307,7 +307,7 @@ fn main() {}
307 CodeActionParams { 307 CodeActionParams {
308 text_document: server.doc_id("src/lib.rs"), 308 text_document: server.doc_id("src/lib.rs"),
309 range: Range::new(Position::new(2, 4), Position::new(2, 7)), 309 range: Range::new(Position::new(2, 4), Position::new(2, 7)),
310 context: empty_context(), 310 context: CodeActionContext::default(),
311 partial_result_params: PartialResultParams::default(), 311 partial_result_params: PartialResultParams::default(),
312 work_done_progress_params: WorkDoneProgressParams::default(), 312 work_done_progress_params: WorkDoneProgressParams::default(),
313 }, 313 },
@@ -348,15 +348,14 @@ fn main() {{}}
348 PROJECT = project.to_string(), 348 PROJECT = project.to_string(),
349 ); 349 );
350 350
351 let server = Project::with_fixture(&code).tmp_dir(tmp_dir).server(); 351 let server =
352 Project::with_fixture(&code).tmp_dir(tmp_dir).server().wait_until_workspace_is_loaded();
352 353
353 server.wait_until_workspace_is_loaded();
354 let empty_context = || CodeActionContext { diagnostics: Vec::new(), only: None };
355 server.request::<CodeActionRequest>( 354 server.request::<CodeActionRequest>(
356 CodeActionParams { 355 CodeActionParams {
357 text_document: server.doc_id("src/lib.rs"), 356 text_document: server.doc_id("src/lib.rs"),
358 range: Range::new(Position::new(0, 4), Position::new(0, 7)), 357 range: Range::new(Position::new(0, 4), Position::new(0, 7)),
359 context: empty_context(), 358 context: CodeActionContext::default(),
360 partial_result_params: PartialResultParams::default(), 359 partial_result_params: PartialResultParams::default(),
361 work_done_progress_params: WorkDoneProgressParams::default(), 360 work_done_progress_params: WorkDoneProgressParams::default(),
362 }, 361 },
@@ -379,7 +378,7 @@ fn main() {{}}
379 CodeActionParams { 378 CodeActionParams {
380 text_document: server.doc_id("src/lib.rs"), 379 text_document: server.doc_id("src/lib.rs"),
381 range: Range::new(Position::new(2, 4), Position::new(2, 7)), 380 range: Range::new(Position::new(2, 4), Position::new(2, 7)),
382 context: empty_context(), 381 context: CodeActionContext::default(),
383 partial_result_params: PartialResultParams::default(), 382 partial_result_params: PartialResultParams::default(),
384 work_done_progress_params: WorkDoneProgressParams::default(), 383 work_done_progress_params: WorkDoneProgressParams::default(),
385 }, 384 },
@@ -412,9 +411,9 @@ fn main() {{}}
412 librs, libs 411 librs, libs
413 )) 412 ))
414 .with_sysroot(true) 413 .with_sysroot(true)
415 .server(); 414 .server()
415 .wait_until_workspace_is_loaded();
416 416
417 server.wait_until_workspace_is_loaded();
418 for i in 0..10 { 417 for i in 0..10 {
419 server.notification::<DidOpenTextDocument>(DidOpenTextDocumentParams { 418 server.notification::<DidOpenTextDocument>(DidOpenTextDocumentParams {
420 text_document: TextDocumentItem { 419 text_document: TextDocumentItem {
@@ -425,7 +424,7 @@ fn main() {{}}
425 }, 424 },
426 }); 425 });
427 } 426 }
428 let start = std::time::Instant::now(); 427 let start = Instant::now();
429 server.request::<OnEnter>( 428 server.request::<OnEnter>(
430 TextDocumentPositionParams { 429 TextDocumentPositionParams {
431 text_document: server.doc_id("src/m0.rs"), 430 text_document: server.doc_id("src/m0.rs"),
@@ -461,8 +460,8 @@ version = \"0.0.0\"
461/// Some Docs\r\nfn main() {} 460/// Some Docs\r\nfn main() {}
462", 461",
463 ) 462 )
464 .server(); 463 .server()
465 server.wait_until_workspace_is_loaded(); 464 .wait_until_workspace_is_loaded();
466 465
467 server.request::<OnEnter>( 466 server.request::<OnEnter>(
468 TextDocumentPositionParams { 467 TextDocumentPositionParams {
@@ -536,8 +535,9 @@ fn main() {
536 .with_config(|config| { 535 .with_config(|config| {
537 config.cargo.load_out_dirs_from_check = true; 536 config.cargo.load_out_dirs_from_check = true;
538 }) 537 })
539 .server(); 538 .server()
540 server.wait_until_workspace_is_loaded(); 539 .wait_until_workspace_is_loaded();
540
541 let res = server.send_request::<HoverRequest>(HoverParams { 541 let res = server.send_request::<HoverRequest>(HoverParams {
542 text_document_position_params: TextDocumentPositionParams::new( 542 text_document_position_params: TextDocumentPositionParams::new(
543 server.doc_id("src/main.rs"), 543 server.doc_id("src/main.rs"),
@@ -546,6 +546,7 @@ fn main() {
546 work_done_progress_params: Default::default(), 546 work_done_progress_params: Default::default(),
547 }); 547 });
548 assert!(res.to_string().contains("&str")); 548 assert!(res.to_string().contains("&str"));
549
549 let res = server.send_request::<HoverRequest>(HoverParams { 550 let res = server.send_request::<HoverRequest>(HoverParams {
550 text_document_position_params: TextDocumentPositionParams::new( 551 text_document_position_params: TextDocumentPositionParams::new(
551 server.doc_id("src/main.rs"), 552 server.doc_id("src/main.rs"),
@@ -554,6 +555,7 @@ fn main() {
554 work_done_progress_params: Default::default(), 555 work_done_progress_params: Default::default(),
555 }); 556 });
556 assert!(res.to_string().contains("&str")); 557 assert!(res.to_string().contains("&str"));
558
557 server.request::<GotoTypeDefinition>( 559 server.request::<GotoTypeDefinition>(
558 GotoDefinitionParams { 560 GotoDefinitionParams {
559 text_document_position_params: TextDocumentPositionParams::new( 561 text_document_position_params: TextDocumentPositionParams::new(
@@ -579,6 +581,7 @@ fn main() {
579 "targetUri": "file:///[..]src/main.rs" 581 "targetUri": "file:///[..]src/main.rs"
580 }]), 582 }]),
581 ); 583 );
584
582 server.request::<GotoTypeDefinition>( 585 server.request::<GotoTypeDefinition>(
583 GotoDefinitionParams { 586 GotoDefinitionParams {
584 text_document_position_params: TextDocumentPositionParams::new( 587 text_document_position_params: TextDocumentPositionParams::new(
@@ -611,6 +614,7 @@ fn resolve_proc_macro() {
611 if skip_slow_tests() { 614 if skip_slow_tests() {
612 return; 615 return;
613 } 616 }
617
614 let server = Project::with_fixture( 618 let server = Project::with_fixture(
615 r###" 619 r###"
616//- /foo/Cargo.toml 620//- /foo/Cargo.toml
@@ -679,8 +683,9 @@ pub fn foo(_input: TokenStream) -> TokenStream {
679 }) 683 })
680 .root("foo") 684 .root("foo")
681 .root("bar") 685 .root("bar")
682 .server(); 686 .server()
683 server.wait_until_workspace_is_loaded(); 687 .wait_until_workspace_is_loaded();
688
684 let res = server.send_request::<HoverRequest>(HoverParams { 689 let res = server.send_request::<HoverRequest>(HoverParams {
685 text_document_position_params: TextDocumentPositionParams::new( 690 text_document_position_params: TextDocumentPositionParams::new(
686 server.doc_id("foo/src/main.rs"), 691 server.doc_id("foo/src/main.rs"),
@@ -688,7 +693,6 @@ pub fn foo(_input: TokenStream) -> TokenStream {
688 ), 693 ),
689 work_done_progress_params: Default::default(), 694 work_done_progress_params: Default::default(),
690 }); 695 });
691
692 let value = res.get("contents").unwrap().get("value").unwrap().to_string(); 696 let value = res.get("contents").unwrap().get("value").unwrap().to_string();
693 assert_eq!(value, r#""\n```rust\nfoo::Bar\n```\n\n```rust\nfn bar()\n```""#) 697 assert_eq!(value, r#""\n```rust\nfoo::Bar\n```\n\n```rust\nfn bar()\n```""#)
694} 698}
diff --git a/crates/rust-analyzer/tests/rust-analyzer/support.rs b/crates/rust-analyzer/tests/rust-analyzer/support.rs
index 5bafeba79..784cbda79 100644
--- a/crates/rust-analyzer/tests/rust-analyzer/support.rs
+++ b/crates/rust-analyzer/tests/rust-analyzer/support.rs
@@ -202,7 +202,7 @@ impl Server {
202 } 202 }
203 panic!("no response"); 203 panic!("no response");
204 } 204 }
205 pub fn wait_until_workspace_is_loaded(&self) { 205 pub fn wait_until_workspace_is_loaded(self) -> Server {
206 self.wait_for_message_cond(1, &|msg: &Message| match msg { 206 self.wait_for_message_cond(1, &|msg: &Message| match msg {
207 Message::Notification(n) if n.method == "$/progress" => { 207 Message::Notification(n) if n.method == "$/progress" => {
208 match n.clone().extract::<ProgressParams>("$/progress").unwrap() { 208 match n.clone().extract::<ProgressParams>("$/progress").unwrap() {
@@ -214,7 +214,8 @@ impl Server {
214 } 214 }
215 } 215 }
216 _ => false, 216 _ => false,
217 }) 217 });
218 self
218 } 219 }
219 fn wait_for_message_cond(&self, n: usize, cond: &dyn Fn(&Message) -> bool) { 220 fn wait_for_message_cond(&self, n: usize, cond: &dyn Fn(&Message) -> bool) {
220 let mut total = 0; 221 let mut total = 0;
diff --git a/crates/text_edit/src/lib.rs b/crates/text_edit/src/lib.rs
index ab8cd7fd1..e417e8ea6 100644
--- a/crates/text_edit/src/lib.rs
+++ b/crates/text_edit/src/lib.rs
@@ -159,13 +159,13 @@ impl<'a> IntoIterator for &'a TextEdit {
159 159
160impl TextEditBuilder { 160impl TextEditBuilder {
161 pub fn replace(&mut self, range: TextRange, replace_with: String) { 161 pub fn replace(&mut self, range: TextRange, replace_with: String) {
162 self.indels.push(Indel::replace(range, replace_with)) 162 self.indel(Indel::replace(range, replace_with))
163 } 163 }
164 pub fn delete(&mut self, range: TextRange) { 164 pub fn delete(&mut self, range: TextRange) {
165 self.indels.push(Indel::delete(range)) 165 self.indel(Indel::delete(range))
166 } 166 }
167 pub fn insert(&mut self, offset: TextSize, text: String) { 167 pub fn insert(&mut self, offset: TextSize, text: String) {
168 self.indels.push(Indel::insert(offset, text)) 168 self.indel(Indel::insert(offset, text))
169 } 169 }
170 pub fn finish(self) -> TextEdit { 170 pub fn finish(self) -> TextEdit {
171 let mut indels = self.indels; 171 let mut indels = self.indels;
@@ -175,6 +175,12 @@ impl TextEditBuilder {
175 pub fn invalidates_offset(&self, offset: TextSize) -> bool { 175 pub fn invalidates_offset(&self, offset: TextSize) -> bool {
176 self.indels.iter().any(|indel| indel.delete.contains_inclusive(offset)) 176 self.indels.iter().any(|indel| indel.delete.contains_inclusive(offset))
177 } 177 }
178 fn indel(&mut self, indel: Indel) {
179 self.indels.push(indel);
180 if self.indels.len() <= 16 {
181 check_disjoint(&mut self.indels);
182 }
183 }
178} 184}
179 185
180fn check_disjoint(indels: &mut [impl std::borrow::Borrow<Indel>]) -> bool { 186fn check_disjoint(indels: &mut [impl std::borrow::Borrow<Indel>]) -> bool {