From beb79ed104c686d8704eb7042318eefea78770df Mon Sep 17 00:00:00 2001 From: Aaron Wood Date: Fri, 8 May 2020 16:59:52 -0700 Subject: [PATCH 001/172] Begin transition to new fields for JsonProject crate cfgs This starts the transition to a new method of documenting the cfgs that are enabled for a given crate in the json file. This is changing from a list of atoms and a dict of key:value pairs, to a list of strings that is equivalent to that returned by `rustc --print cfg ..`, and parsed in the same manner by rust-analyzer. This is the first of two changes, which adds the new field that contains the list of strings. Next change will complete the transition and remove the previous fields. --- crates/ra_project_model/src/json_project.rs | 79 +++++++++++++++++++ crates/ra_project_model/src/lib.rs | 10 +++ .../rust-analyzer/tests/heavy_tests/main.rs | 5 +- 3 files changed, 92 insertions(+), 2 deletions(-) diff --git a/crates/ra_project_model/src/json_project.rs b/crates/ra_project_model/src/json_project.rs index b030c8a6a18f..bd2bae15e690 100644 --- a/crates/ra_project_model/src/json_project.rs +++ b/crates/ra_project_model/src/json_project.rs @@ -20,8 +20,17 @@ pub struct Crate { pub(crate) root_module: PathBuf, pub(crate) edition: Edition, pub(crate) deps: Vec, + + // This is the preferred method of providing cfg options. + #[serde(default)] + pub(crate) cfg: FxHashSet, + + // These two are here for transition only. + #[serde(default)] pub(crate) atom_cfgs: FxHashSet, + #[serde(default)] pub(crate) key_value_cfgs: FxHashMap, + pub(crate) out_dir: Option, pub(crate) proc_macro_dylib_path: Option, } @@ -54,3 +63,73 @@ pub struct JsonProject { pub(crate) roots: Vec, pub(crate) crates: Vec, } + +#[cfg(test)] +mod tests { + use super::*; + use serde_json::json; + + #[test] + fn test_crate_deserialization() { + let raw_json = json!( { + "crate_id": 2, + "root_module": "this/is/a/file/path.rs", + "deps": [ + { + "crate": 1, + "name": "some_dep_crate" + }, + ], + "edition": "2015", + "cfg": [ + "atom_1", + "atom_2", + "feature=feature_1", + "feature=feature_2", + "other=value", + ], + + }); + + let krate: Crate = serde_json::from_value(raw_json).unwrap(); + + assert!(krate.cfg.contains(&"atom_1".to_string())); + assert!(krate.cfg.contains(&"atom_2".to_string())); + assert!(krate.cfg.contains(&"feature=feature_1".to_string())); + assert!(krate.cfg.contains(&"feature=feature_2".to_string())); + assert!(krate.cfg.contains(&"other=value".to_string())); + } + + #[test] + fn test_crate_deserialization_old_json() { + let raw_json = json!( { + "crate_id": 2, + "root_module": "this/is/a/file/path.rs", + "deps": [ + { + "crate": 1, + "name": "some_dep_crate" + }, + ], + "edition": "2015", + "atom_cfgs": [ + "atom_1", + "atom_2", + ], + "key_value_cfgs": { + "feature": "feature_1", + "feature": "feature_2", + "other": "value", + }, + }); + + let krate: Crate = serde_json::from_value(raw_json).unwrap(); + + assert!(krate.atom_cfgs.contains(&"atom_1".to_string())); + assert!(krate.atom_cfgs.contains(&"atom_2".to_string())); + assert!(krate.key_value_cfgs.contains_key(&"feature".to_string())); + assert_eq!(krate.key_value_cfgs.get("feature"), Some(&"feature_2".to_string())); + assert!(krate.key_value_cfgs.contains_key(&"other".to_string())); + assert_eq!(krate.key_value_cfgs.get("other"), Some(&"value".to_string())); + } +} diff --git a/crates/ra_project_model/src/lib.rs b/crates/ra_project_model/src/lib.rs index 731cbd291818..e7da683d6edf 100644 --- a/crates/ra_project_model/src/lib.rs +++ b/crates/ra_project_model/src/lib.rs @@ -273,6 +273,16 @@ impl ProjectWorkspace { }; let cfg_options = { let mut opts = default_cfg_options.clone(); + for cfg in &krate.cfg { + match cfg.find('=') { + None => opts.insert_atom(cfg.into()), + Some(pos) => { + let key = &cfg[..pos]; + let value = cfg[pos + 1..].trim_matches('"'); + opts.insert_key_value(key.into(), value.into()); + } + } + } for name in &krate.atom_cfgs { opts.insert_atom(name.into()); } diff --git a/crates/rust-analyzer/tests/heavy_tests/main.rs b/crates/rust-analyzer/tests/heavy_tests/main.rs index 07b8114c6139..3d5574c7ff9a 100644 --- a/crates/rust-analyzer/tests/heavy_tests/main.rs +++ b/crates/rust-analyzer/tests/heavy_tests/main.rs @@ -384,8 +384,9 @@ fn test_missing_module_code_action_in_json_project() { "root_module": path.join("src/lib.rs"), "deps": [], "edition": "2015", - "atom_cfgs": [], - "key_value_cfgs": {} + "cfg": [ "cfg_atom_1", "feature=cfg_1"], + "atom_cfgs": ["atom_2"], + "key_value_cfgs": { "feature": "key_value_feature", "other": "value"} } ] }); From 5cd4eb6dd6d8c733077a6aeea5d2cc0812ded096 Mon Sep 17 00:00:00 2001 From: Mikhail Rakhmanov Date: Fri, 22 May 2020 22:28:30 +0200 Subject: [PATCH 002/172] Add preliminary implementation of extract struct from enum variant --- Cargo.lock | 1 + crates/ra_assists/Cargo.toml | 1 + crates/ra_assists/src/assist_context.rs | 62 +++- .../extract_struct_from_enum_variant.rs | 338 ++++++++++++++++++ crates/ra_assists/src/lib.rs | 2 + 5 files changed, 403 insertions(+), 1 deletion(-) create mode 100644 crates/ra_assists/src/handlers/extract_struct_from_enum_variant.rs diff --git a/Cargo.lock b/Cargo.lock index 007f05b4d03e..c604298ec1e3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -921,6 +921,7 @@ dependencies = [ "ra_db", "ra_fmt", "ra_hir", + "ra_hir_expand", "ra_ide_db", "ra_prof", "ra_syntax", diff --git a/crates/ra_assists/Cargo.toml b/crates/ra_assists/Cargo.toml index 3bcf58ba4b09..f3481bdeb1a2 100644 --- a/crates/ra_assists/Cargo.toml +++ b/crates/ra_assists/Cargo.toml @@ -20,5 +20,6 @@ ra_fmt = { path = "../ra_fmt" } ra_prof = { path = "../ra_prof" } ra_db = { path = "../ra_db" } ra_ide_db = { path = "../ra_ide_db" } +hir_expand = { path = "../ra_hir_expand", package = "ra_hir_expand" } hir = { path = "../ra_hir", package = "ra_hir" } test_utils = { path = "../test_utils" } diff --git a/crates/ra_assists/src/assist_context.rs b/crates/ra_assists/src/assist_context.rs index 5b1a4680b439..6291c68def18 100644 --- a/crates/ra_assists/src/assist_context.rs +++ b/crates/ra_assists/src/assist_context.rs @@ -2,7 +2,7 @@ use algo::find_covering_element; use hir::Semantics; -use ra_db::{FileId, FileRange}; +use ra_db::{FileId, FileRange, FilePosition}; use ra_fmt::{leading_indent, reindent}; use ra_ide_db::{ source_change::{SourceChange, SourceFileEdit}, @@ -19,6 +19,7 @@ use crate::{ assist_config::{AssistConfig, SnippetCap}, Assist, AssistId, GroupLabel, ResolvedAssist, }; +use rustc_hash::FxHashMap; /// `AssistContext` allows to apply an assist or check if it could be applied. /// @@ -138,6 +139,16 @@ impl Assists { let label = Assist::new(id, label.into(), None, target); self.add_impl(label, f) } + pub(crate) fn add_in_multiple_files( + &mut self, + id: AssistId, + label: impl Into, + target: TextRange, + f: impl FnOnce(&mut AssistDirector), + ) -> Option<()> { + let label = Assist::new(id, label.into(), None, target); + self.add_impl_multiple_files(label, f) + } pub(crate) fn add_group( &mut self, group: &GroupLabel, @@ -162,6 +173,27 @@ impl Assists { Some(()) } + fn add_impl_multiple_files(&mut self, label: Assist, f: impl FnOnce(&mut AssistDirector)) -> Option<()> { + let change_label = label.label.clone(); + if !self.resolve { + return None + } + let mut director = AssistDirector::new(change_label.clone()); + f(&mut director); + let changes = director.finish(); + let file_edits: Vec = changes.into_iter() + .map(|mut change| change.source_file_edits.pop().unwrap()).collect(); + + let source_change = SourceChange { + source_file_edits: file_edits, + file_system_edits: vec![], + is_snippet: false, + }; + + self.buf.push((label, Some(source_change))); + Some(()) + } + fn finish(mut self) -> Vec<(Assist, Option)> { self.buf.sort_by_key(|(label, _edit)| label.target.len()); self.buf @@ -255,3 +287,31 @@ impl AssistBuilder { res } } + +pub(crate) struct AssistDirector { + source_changes: Vec, + builders: FxHashMap, + change_label: String +} + +impl AssistDirector { + fn new(change_label: String) -> AssistDirector { + AssistDirector { + source_changes: vec![], + builders: FxHashMap::default(), + change_label + } + } + + pub(crate) fn perform(&mut self, file_id: FileId, f: impl FnOnce(&mut AssistBuilder)) { + let mut builder = self.builders.entry(file_id).or_insert(AssistBuilder::new(file_id)); + f(&mut builder); + } + + fn finish(mut self) -> Vec { + for (file_id, builder) in self.builders.into_iter().collect::>() { + self.source_changes.push(builder.finish()); + } + self.source_changes + } +} diff --git a/crates/ra_assists/src/handlers/extract_struct_from_enum_variant.rs b/crates/ra_assists/src/handlers/extract_struct_from_enum_variant.rs new file mode 100644 index 000000000000..6e19a6febba2 --- /dev/null +++ b/crates/ra_assists/src/handlers/extract_struct_from_enum_variant.rs @@ -0,0 +1,338 @@ +use hir_expand::name::AsName; +use ra_ide_db::{ + defs::Definition, imports_locator::ImportsLocator, search::Reference, RootDatabase, +}; +use ra_syntax::{ + algo::find_node_at_offset, + ast::{self, AstNode, NameOwner}, + SourceFile, SyntaxNode, TextRange, TextSize, +}; +use stdx::format_to; + +use crate::{ + assist_context::{AssistBuilder, AssistDirector}, + utils::insert_use_statement, + AssistContext, AssistId, Assists, +}; +use ast::{ArgListOwner, VisibilityOwner}; +use hir::{EnumVariant, Module, ModuleDef}; +use ra_fmt::leading_indent; +use rustc_hash::FxHashSet; +use ra_db::FileId; + +// Assist extract_struct_from_enum +// +// Extracts a from struct from enum variant +// +// ``` +// enum A { <|>One(u32, u32) } +// ``` +// -> +// ``` +// struct One(pub u32, pub u32); +// +// enum A { One(One) }" +// ``` +pub(crate) fn extract_struct_from_enum(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { + let variant = ctx.find_node_at_offset::()?; + let field_list = match variant.kind() { + ast::StructKind::Tuple(field_list) => field_list, + _ => return None, + }; + let variant_name = variant.name()?.to_string(); + let enum_ast = variant.parent_enum(); + let enum_name = enum_ast.name().unwrap().to_string(); + let visibility = enum_ast.visibility(); + let variant_hir = ctx.sema.to_def(&variant)?; + + if existing_struct_def(ctx.db, &variant_name, &variant_hir) { + return None; + } + + let target = variant.syntax().text_range(); + return acc.add_in_multiple_files( + AssistId("extract_struct_from_enum_variant"), + "Extract struct from enum variant", + target, + |edit| { + let definition = Definition::ModuleDef(ModuleDef::EnumVariant(variant_hir)); + let res = definition.find_usages(&ctx.db, None); + let module_def = mod_def_for_target_module(ctx, &enum_name); + let start_offset = variant.parent_enum().syntax().text_range().start(); + let mut seen_files_map: FxHashSet = FxHashSet::default(); + seen_files_map.insert(module_def.module(ctx.db).unwrap()); + for reference in res { + let source_file = ctx.sema.parse(reference.file_range.file_id); + update_reference( + ctx, + edit, + reference, + &source_file, + &module_def, + &mut seen_files_map, + ); + } + extract_struct_def( + edit, + enum_ast.syntax(), + &variant_name, + &field_list.to_string(), + start_offset, + ctx.frange.file_id, + &visibility, + ); + let list_range = field_list.syntax().text_range(); + update_variant(edit, &variant_name, ctx.frange.file_id, list_range); + }, + ); +} + +fn existing_struct_def(db: &RootDatabase, variant_name: &str, variant: &EnumVariant) -> bool { + let module_defs = variant.parent_enum(db).module(db).scope(db, None); + for (name, _) in module_defs { + if name.to_string() == variant_name.to_string() { + return true; + } + } + false +} + +fn mod_def_for_target_module(ctx: &AssistContext, enum_name: &str) -> ModuleDef { + ImportsLocator::new(ctx.db).find_imports(enum_name).first().unwrap().left().unwrap() +} + +fn insert_use_import( + ctx: &AssistContext, + builder: &mut AssistBuilder, + path: &ast::PathExpr, + module: &Module, + module_def: &ModuleDef, + path_segment: ast::NameRef, +) -> Option<()> { + let db = ctx.db; + let mod_path = module.find_use_path(db, module_def.clone()); + if let Some(mut mod_path) = mod_path { + mod_path.segments.pop(); + mod_path.segments.push(path_segment.as_name()); + insert_use_statement(path.syntax(), &mod_path, ctx, builder.text_edit_builder()); + } + Some(()) +} + +fn extract_struct_def( + edit: &mut AssistDirector, + enum_ast: &SyntaxNode, + variant_name: &str, + variant_list: &str, + start_offset: TextSize, + file_id: FileId, + visibility: &Option, +) -> Option<()> { + let visibility_string = if let Some(visibility) = visibility { + format!("{} ", visibility.to_string()) + } else { + "".to_string() + }; + let mut buf = String::new(); + let indent = if let Some(indent) = leading_indent(enum_ast) { + indent.to_string() + } else { + "".to_string() + }; + + format_to!( + buf, + r#"{}struct {}{}; + +{}"#, + visibility_string, + variant_name, + list_with_visibility(variant_list), + indent + ); + edit.perform(file_id, |builder| { + builder.insert(start_offset, buf); + }); + Some(()) +} + +fn update_variant( + edit: &mut AssistDirector, + variant_name: &str, + file_id: FileId, + list_range: TextRange, +) -> Option<()> { + let inside_variant_range = TextRange::new( + list_range.start().checked_add(TextSize::from(1))?, + list_range.end().checked_sub(TextSize::from(1))?, + ); + edit.perform(file_id, |builder| { + builder.set_file(file_id); + builder.replace(inside_variant_range, variant_name); + }); + Some(()) +} + +fn update_reference( + ctx: &AssistContext, + edit: &mut AssistDirector, + reference: Reference, + source_file: &SourceFile, + module_def: &ModuleDef, + seen_files_map: &mut FxHashSet, +) -> Option<()> { + let path_expr: ast::PathExpr = find_node_at_offset::( + source_file.syntax(), + reference.file_range.range.start(), + )?; + let call = path_expr.syntax().parent().and_then(ast::CallExpr::cast)?; + let list = call.arg_list()?; + let segment = path_expr.path()?.segment()?; + let list_range = list.syntax().text_range(); + let inside_list_range = TextRange::new( + list_range.start().checked_add(TextSize::from(1))?, + list_range.end().checked_sub(TextSize::from(1))?, + ); + edit.perform(reference.file_range.file_id, |builder| { + let module = ctx.sema.scope(&path_expr.syntax()).module().unwrap(); + if !seen_files_map.contains(&module) { + if insert_use_import( + ctx, + builder, + &path_expr, + &module, + module_def, + segment.name_ref().unwrap(), + ) + .is_some() + { + seen_files_map.insert(module); + } + } + builder.replace(inside_list_range, format!("{}{}", segment, list)); + }); + Some(()) +} + +fn list_with_visibility(list: &str) -> String { + list.split(',') + .map(|part| { + let index = if part.chars().next().unwrap() == '(' { 1usize } else { 0 }; + let mut mod_part = part.trim().to_string(); + mod_part.insert_str(index, "pub "); + mod_part + }) + .collect::>() + .join(", ") +} + +#[cfg(test)] +mod tests { + + use crate::{utils::FamousDefs, tests::{check_assist, check_assist_not_applicable}}; + + use super::*; + + #[test] + fn test_extract_struct_several_fields() { + check_assist( + extract_struct_from_enum, + "enum A { <|>One(u32, u32) }", + r#"struct One(pub u32, pub u32); + +enum A { One(One) }"#, + ); + } + + #[test] + fn test_extract_struct_one_field() { + check_assist( + extract_struct_from_enum, + "enum A { <|>One(u32) }", + r#"struct One(pub u32); + +enum A { One(One) }"#, + ); + } + + #[test] + fn test_extract_struct_pub_visibility() { + check_assist( + extract_struct_from_enum, + "pub enum A { <|>One(u32, u32) }", + r#"pub struct One(pub u32, pub u32); + +pub enum A { One(One) }"#, + ); + } + + #[test] + fn test_extract_struct_with_complex_imports() { + check_assist( + extract_struct_from_enum, + r#"mod my_mod { + fn another_fn() { + let m = my_other_mod::MyEnum::MyField(1, 1); + } + + pub mod my_other_mod { + fn another_fn() { + let m = MyEnum::MyField(1, 1); + } + + pub enum MyEnum { + <|>MyField(u8, u8), + } + } +} + +fn another_fn() { + let m = my_mod::my_other_mod::MyEnum::MyField(1, 1); +}"#, + r#"use my_mod::my_other_mod::MyField; + +mod my_mod { + use my_other_mod::MyField; + + fn another_fn() { + let m = my_other_mod::MyEnum::MyField(MyField(1, 1)); + } + + pub mod my_other_mod { + fn another_fn() { + let m = MyEnum::MyField(MyField(1, 1)); + } + + pub struct MyField(pub u8, pub u8); + + pub enum MyEnum { + MyField(MyField), + } + } +} + +fn another_fn() { + let m = my_mod::my_other_mod::MyEnum::MyField(MyField(1, 1)); +}"#, + ); + } + + fn check_not_applicable(ra_fixture: &str) { + let fixture = + format!("//- main.rs crate:main deps:core\n{}\n{}", ra_fixture, FamousDefs::FIXTURE); + check_assist_not_applicable(extract_struct_from_enum, &fixture) + } + + #[test] + fn test_extract_enum_not_applicable_for_element_with_no_fields() { + check_not_applicable("enum A { <|>One }"); + } + + #[test] + fn test_extract_enum_not_applicable_if_struct_exists() { + check_not_applicable( + r#"struct One; + enum A { <|>One(u8) }"#, + ); + } +} diff --git a/crates/ra_assists/src/lib.rs b/crates/ra_assists/src/lib.rs index 464bc03dde9e..9933f7a50546 100644 --- a/crates/ra_assists/src/lib.rs +++ b/crates/ra_assists/src/lib.rs @@ -115,6 +115,7 @@ mod handlers { mod change_return_type_to_result; mod change_visibility; mod early_return; + mod extract_struct_from_enum_variant; mod fill_match_arms; mod fix_visibility; mod flip_binexpr; @@ -154,6 +155,7 @@ mod handlers { change_return_type_to_result::change_return_type_to_result, change_visibility::change_visibility, early_return::convert_to_guarded_return, + extract_struct_from_enum_variant::extract_struct_from_enum, fill_match_arms::fill_match_arms, fix_visibility::fix_visibility, flip_binexpr::flip_binexpr, From 04a35784df4cf267a8bbce6d5542869ed1a52fcb Mon Sep 17 00:00:00 2001 From: Mikhail Rakhmanov Date: Fri, 22 May 2020 22:43:52 +0200 Subject: [PATCH 003/172] Formatting and remove unused imports --- crates/ra_assists/src/assist_context.rs | 30 ++++++++++--------- .../extract_struct_from_enum_variant.rs | 7 +++-- 2 files changed, 21 insertions(+), 16 deletions(-) diff --git a/crates/ra_assists/src/assist_context.rs b/crates/ra_assists/src/assist_context.rs index 6291c68def18..52bc7820e9d1 100644 --- a/crates/ra_assists/src/assist_context.rs +++ b/crates/ra_assists/src/assist_context.rs @@ -2,7 +2,7 @@ use algo::find_covering_element; use hir::Semantics; -use ra_db::{FileId, FileRange, FilePosition}; +use ra_db::{FileId, FileRange}; use ra_fmt::{leading_indent, reindent}; use ra_ide_db::{ source_change::{SourceChange, SourceFileEdit}, @@ -173,16 +173,20 @@ impl Assists { Some(()) } - fn add_impl_multiple_files(&mut self, label: Assist, f: impl FnOnce(&mut AssistDirector)) -> Option<()> { + fn add_impl_multiple_files( + &mut self, + label: Assist, + f: impl FnOnce(&mut AssistDirector), + ) -> Option<()> { let change_label = label.label.clone(); if !self.resolve { - return None + return None; } let mut director = AssistDirector::new(change_label.clone()); f(&mut director); let changes = director.finish(); - let file_edits: Vec = changes.into_iter() - .map(|mut change| change.source_file_edits.pop().unwrap()).collect(); + let file_edits: Vec = + changes.into_iter().map(|mut change| change.source_file_edits.pop().unwrap()).collect(); let source_change = SourceChange { source_file_edits: file_edits, @@ -291,16 +295,12 @@ impl AssistBuilder { pub(crate) struct AssistDirector { source_changes: Vec, builders: FxHashMap, - change_label: String -} + change_label: String, +} impl AssistDirector { fn new(change_label: String) -> AssistDirector { - AssistDirector { - source_changes: vec![], - builders: FxHashMap::default(), - change_label - } + AssistDirector { source_changes: vec![], builders: FxHashMap::default(), change_label } } pub(crate) fn perform(&mut self, file_id: FileId, f: impl FnOnce(&mut AssistBuilder)) { @@ -309,8 +309,10 @@ impl AssistDirector { } fn finish(mut self) -> Vec { - for (file_id, builder) in self.builders.into_iter().collect::>() { - self.source_changes.push(builder.finish()); + for (file_id, builder) in + self.builders.into_iter().collect::>() + { + self.source_changes.push(builder.finish()); } self.source_changes } diff --git a/crates/ra_assists/src/handlers/extract_struct_from_enum_variant.rs b/crates/ra_assists/src/handlers/extract_struct_from_enum_variant.rs index 6e19a6febba2..57907a503799 100644 --- a/crates/ra_assists/src/handlers/extract_struct_from_enum_variant.rs +++ b/crates/ra_assists/src/handlers/extract_struct_from_enum_variant.rs @@ -16,9 +16,9 @@ use crate::{ }; use ast::{ArgListOwner, VisibilityOwner}; use hir::{EnumVariant, Module, ModuleDef}; +use ra_db::FileId; use ra_fmt::leading_indent; use rustc_hash::FxHashSet; -use ra_db::FileId; // Assist extract_struct_from_enum // @@ -229,7 +229,10 @@ fn list_with_visibility(list: &str) -> String { #[cfg(test)] mod tests { - use crate::{utils::FamousDefs, tests::{check_assist, check_assist_not_applicable}}; + use crate::{ + tests::{check_assist, check_assist_not_applicable}, + utils::FamousDefs, + }; use super::*; From 97ffe3c6e8289553e3b3bd22392a22eaa8d61f42 Mon Sep 17 00:00:00 2001 From: Mikhail Rakhmanov Date: Fri, 22 May 2020 22:47:25 +0200 Subject: [PATCH 004/172] Refactor AssistDirector --- crates/ra_assists/src/assist_context.rs | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/crates/ra_assists/src/assist_context.rs b/crates/ra_assists/src/assist_context.rs index 52bc7820e9d1..37de4f590bc1 100644 --- a/crates/ra_assists/src/assist_context.rs +++ b/crates/ra_assists/src/assist_context.rs @@ -178,11 +178,10 @@ impl Assists { label: Assist, f: impl FnOnce(&mut AssistDirector), ) -> Option<()> { - let change_label = label.label.clone(); if !self.resolve { return None; } - let mut director = AssistDirector::new(change_label.clone()); + let mut director = AssistDirector::new(); f(&mut director); let changes = director.finish(); let file_edits: Vec = @@ -295,12 +294,11 @@ impl AssistBuilder { pub(crate) struct AssistDirector { source_changes: Vec, builders: FxHashMap, - change_label: String, } impl AssistDirector { - fn new(change_label: String) -> AssistDirector { - AssistDirector { source_changes: vec![], builders: FxHashMap::default(), change_label } + fn new() -> AssistDirector { + AssistDirector { source_changes: vec![], builders: FxHashMap::default() } } pub(crate) fn perform(&mut self, file_id: FileId, f: impl FnOnce(&mut AssistBuilder)) { @@ -309,7 +307,7 @@ impl AssistDirector { } fn finish(mut self) -> Vec { - for (file_id, builder) in + for (_, builder) in self.builders.into_iter().collect::>() { self.source_changes.push(builder.finish()); From ef1aaeb59516b16f1b83eb7cdb22f1bcdcc46446 Mon Sep 17 00:00:00 2001 From: Mikhail Rakhmanov Date: Fri, 22 May 2020 22:59:17 +0200 Subject: [PATCH 005/172] More formatting --- crates/ra_assists/src/assist_context.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/crates/ra_assists/src/assist_context.rs b/crates/ra_assists/src/assist_context.rs index 37de4f590bc1..e7220eea9cf7 100644 --- a/crates/ra_assists/src/assist_context.rs +++ b/crates/ra_assists/src/assist_context.rs @@ -307,9 +307,7 @@ impl AssistDirector { } fn finish(mut self) -> Vec { - for (_, builder) in - self.builders.into_iter().collect::>() - { + for (_, builder) in self.builders.into_iter().collect::>() { self.source_changes.push(builder.finish()); } self.source_changes From fce10200a0d666fbd2e2faa84b0526f586485bb3 Mon Sep 17 00:00:00 2001 From: Mikhail Rakhmanov Date: Sat, 23 May 2020 01:23:40 +0200 Subject: [PATCH 006/172] Better naming and fix some review comments --- crates/ra_assists/src/assist_context.rs | 13 ++++++------- .../handlers/extract_struct_from_enum_variant.rs | 16 ++++++++-------- 2 files changed, 14 insertions(+), 15 deletions(-) diff --git a/crates/ra_assists/src/assist_context.rs b/crates/ra_assists/src/assist_context.rs index e7220eea9cf7..94286b49720c 100644 --- a/crates/ra_assists/src/assist_context.rs +++ b/crates/ra_assists/src/assist_context.rs @@ -292,13 +292,12 @@ impl AssistBuilder { } pub(crate) struct AssistDirector { - source_changes: Vec, builders: FxHashMap, } impl AssistDirector { fn new() -> AssistDirector { - AssistDirector { source_changes: vec![], builders: FxHashMap::default() } + AssistDirector { builders: FxHashMap::default() } } pub(crate) fn perform(&mut self, file_id: FileId, f: impl FnOnce(&mut AssistBuilder)) { @@ -306,10 +305,10 @@ impl AssistDirector { f(&mut builder); } - fn finish(mut self) -> Vec { - for (_, builder) in self.builders.into_iter().collect::>() { - self.source_changes.push(builder.finish()); - } - self.source_changes + fn finish(self) -> Vec { + self.builders + .into_iter() + .map(|(_, builder)| builder.finish()) + .collect::>() } } diff --git a/crates/ra_assists/src/handlers/extract_struct_from_enum_variant.rs b/crates/ra_assists/src/handlers/extract_struct_from_enum_variant.rs index 57907a503799..3250eed5bd77 100644 --- a/crates/ra_assists/src/handlers/extract_struct_from_enum_variant.rs +++ b/crates/ra_assists/src/handlers/extract_struct_from_enum_variant.rs @@ -59,8 +59,8 @@ pub(crate) fn extract_struct_from_enum(acc: &mut Assists, ctx: &AssistContext) - let res = definition.find_usages(&ctx.db, None); let module_def = mod_def_for_target_module(ctx, &enum_name); let start_offset = variant.parent_enum().syntax().text_range().start(); - let mut seen_files_map: FxHashSet = FxHashSet::default(); - seen_files_map.insert(module_def.module(ctx.db).unwrap()); + let mut visited_modules_set: FxHashSet = FxHashSet::default(); + visited_modules_set.insert(module_def.module(ctx.db).unwrap()); for reference in res { let source_file = ctx.sema.parse(reference.file_range.file_id); update_reference( @@ -69,7 +69,7 @@ pub(crate) fn extract_struct_from_enum(acc: &mut Assists, ctx: &AssistContext) - reference, &source_file, &module_def, - &mut seen_files_map, + &mut visited_modules_set, ); } extract_struct_def( @@ -101,7 +101,7 @@ fn mod_def_for_target_module(ctx: &AssistContext, enum_name: &str) -> ModuleDef ImportsLocator::new(ctx.db).find_imports(enum_name).first().unwrap().left().unwrap() } -fn insert_use_import( +fn insert_import( ctx: &AssistContext, builder: &mut AssistBuilder, path: &ast::PathExpr, @@ -179,7 +179,7 @@ fn update_reference( reference: Reference, source_file: &SourceFile, module_def: &ModuleDef, - seen_files_map: &mut FxHashSet, + visited_modules_set: &mut FxHashSet, ) -> Option<()> { let path_expr: ast::PathExpr = find_node_at_offset::( source_file.syntax(), @@ -195,8 +195,8 @@ fn update_reference( ); edit.perform(reference.file_range.file_id, |builder| { let module = ctx.sema.scope(&path_expr.syntax()).module().unwrap(); - if !seen_files_map.contains(&module) { - if insert_use_import( + if !visited_modules_set.contains(&module) { + if insert_import( ctx, builder, &path_expr, @@ -206,7 +206,7 @@ fn update_reference( ) .is_some() { - seen_files_map.insert(module); + visited_modules_set.insert(module); } } builder.replace(inside_list_range, format!("{}{}", segment, list)); From 4984520ef565e926ba08c6512715ed631e4527e4 Mon Sep 17 00:00:00 2001 From: Mikhail Rakhmanov Date: Sat, 23 May 2020 01:27:11 +0200 Subject: [PATCH 007/172] Use default instead of new in AssistDirector --- crates/ra_assists/src/assist_context.rs | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/crates/ra_assists/src/assist_context.rs b/crates/ra_assists/src/assist_context.rs index 94286b49720c..bc54814948de 100644 --- a/crates/ra_assists/src/assist_context.rs +++ b/crates/ra_assists/src/assist_context.rs @@ -181,7 +181,7 @@ impl Assists { if !self.resolve { return None; } - let mut director = AssistDirector::new(); + let mut director = AssistDirector::default(); f(&mut director); let changes = director.finish(); let file_edits: Vec = @@ -296,10 +296,6 @@ pub(crate) struct AssistDirector { } impl AssistDirector { - fn new() -> AssistDirector { - AssistDirector { builders: FxHashMap::default() } - } - pub(crate) fn perform(&mut self, file_id: FileId, f: impl FnOnce(&mut AssistBuilder)) { let mut builder = self.builders.entry(file_id).or_insert(AssistBuilder::new(file_id)); f(&mut builder); @@ -312,3 +308,9 @@ impl AssistDirector { .collect::>() } } + +impl Default for AssistDirector { + fn default() -> Self { + AssistDirector { builders: FxHashMap::default() } + } +} From 6ee1c60c9cff781e10d6379f68fc951378403f6b Mon Sep 17 00:00:00 2001 From: Mikhail Rakhmanov Date: Sat, 23 May 2020 01:41:08 +0200 Subject: [PATCH 008/172] Further review fixes --- Cargo.lock | 1 - crates/ra_assists/Cargo.toml | 1 - .../extract_struct_from_enum_variant.rs | 20 +++++++++---------- crates/ra_hir/src/lib.rs | 5 +++-- 4 files changed, 12 insertions(+), 15 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c604298ec1e3..007f05b4d03e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -921,7 +921,6 @@ dependencies = [ "ra_db", "ra_fmt", "ra_hir", - "ra_hir_expand", "ra_ide_db", "ra_prof", "ra_syntax", diff --git a/crates/ra_assists/Cargo.toml b/crates/ra_assists/Cargo.toml index f3481bdeb1a2..3bcf58ba4b09 100644 --- a/crates/ra_assists/Cargo.toml +++ b/crates/ra_assists/Cargo.toml @@ -20,6 +20,5 @@ ra_fmt = { path = "../ra_fmt" } ra_prof = { path = "../ra_prof" } ra_db = { path = "../ra_db" } ra_ide_db = { path = "../ra_ide_db" } -hir_expand = { path = "../ra_hir_expand", package = "ra_hir_expand" } hir = { path = "../ra_hir", package = "ra_hir" } test_utils = { path = "../test_utils" } diff --git a/crates/ra_assists/src/handlers/extract_struct_from_enum_variant.rs b/crates/ra_assists/src/handlers/extract_struct_from_enum_variant.rs index 3250eed5bd77..3592838029a1 100644 --- a/crates/ra_assists/src/handlers/extract_struct_from_enum_variant.rs +++ b/crates/ra_assists/src/handlers/extract_struct_from_enum_variant.rs @@ -1,4 +1,3 @@ -use hir_expand::name::AsName; use ra_ide_db::{ defs::Definition, imports_locator::ImportsLocator, search::Reference, RootDatabase, }; @@ -15,14 +14,14 @@ use crate::{ AssistContext, AssistId, Assists, }; use ast::{ArgListOwner, VisibilityOwner}; -use hir::{EnumVariant, Module, ModuleDef}; +use hir::{AsName, EnumVariant, Module, ModuleDef}; use ra_db::FileId; use ra_fmt::leading_indent; use rustc_hash::FxHashSet; // Assist extract_struct_from_enum // -// Extracts a from struct from enum variant +// Extracts a struct from enum variant // // ``` // enum A { <|>One(u32, u32) } @@ -41,7 +40,7 @@ pub(crate) fn extract_struct_from_enum(acc: &mut Assists, ctx: &AssistContext) - }; let variant_name = variant.name()?.to_string(); let enum_ast = variant.parent_enum(); - let enum_name = enum_ast.name().unwrap().to_string(); + let enum_name = enum_ast.name()?.to_string(); let visibility = enum_ast.visibility(); let variant_hir = ctx.sema.to_def(&variant)?; @@ -88,13 +87,12 @@ pub(crate) fn extract_struct_from_enum(acc: &mut Assists, ctx: &AssistContext) - } fn existing_struct_def(db: &RootDatabase, variant_name: &str, variant: &EnumVariant) -> bool { - let module_defs = variant.parent_enum(db).module(db).scope(db, None); - for (name, _) in module_defs { - if name.to_string() == variant_name.to_string() { - return true; - } - } - false + variant + .parent_enum(db) + .module(db) + .scope(db, None) + .into_iter() + .any(|(name, _)| name.to_string() == variant_name.to_string()) } fn mod_def_for_target_module(ctx: &AssistContext, enum_name: &str) -> ModuleDef { diff --git a/crates/ra_hir/src/lib.rs b/crates/ra_hir/src/lib.rs index c5df4ac24da4..4af0f046a720 100644 --- a/crates/ra_hir/src/lib.rs +++ b/crates/ra_hir/src/lib.rs @@ -70,7 +70,8 @@ pub use hir_def::{ type_ref::Mutability, }; pub use hir_expand::{ - hygiene::Hygiene, name::Name, HirFileId, InFile, MacroCallId, MacroCallLoc, MacroDefId, - MacroFile, Origin, + hygiene::Hygiene, + name::{AsName, Name}, + HirFileId, InFile, MacroCallId, MacroCallLoc, MacroDefId, MacroFile, Origin, }; pub use hir_ty::{display::HirDisplay, CallableDef}; From 3a244e02b53687161d4cc39254cb9a432756017f Mon Sep 17 00:00:00 2001 From: Mikhail Rakhmanov Date: Sat, 23 May 2020 11:53:02 +0200 Subject: [PATCH 009/172] Remove unwraps where possible --- .../extract_struct_from_enum_variant.rs | 33 +++++++------------ 1 file changed, 11 insertions(+), 22 deletions(-) diff --git a/crates/ra_assists/src/handlers/extract_struct_from_enum_variant.rs b/crates/ra_assists/src/handlers/extract_struct_from_enum_variant.rs index 3592838029a1..d5397bf2134b 100644 --- a/crates/ra_assists/src/handlers/extract_struct_from_enum_variant.rs +++ b/crates/ra_assists/src/handlers/extract_struct_from_enum_variant.rs @@ -39,15 +39,16 @@ pub(crate) fn extract_struct_from_enum(acc: &mut Assists, ctx: &AssistContext) - _ => return None, }; let variant_name = variant.name()?.to_string(); - let enum_ast = variant.parent_enum(); - let enum_name = enum_ast.name()?.to_string(); - let visibility = enum_ast.visibility(); let variant_hir = ctx.sema.to_def(&variant)?; - if existing_struct_def(ctx.db, &variant_name, &variant_hir) { return None; } - + let enum_ast = variant.parent_enum(); + let enum_name = enum_ast.name()?.to_string(); + let visibility = enum_ast.visibility(); + let current_module_def = + ImportsLocator::new(ctx.db).find_imports(&enum_name).first()?.left()?; + let current_module = current_module_def.module(ctx.db)?; let target = variant.syntax().text_range(); return acc.add_in_multiple_files( AssistId("extract_struct_from_enum_variant"), @@ -56,10 +57,9 @@ pub(crate) fn extract_struct_from_enum(acc: &mut Assists, ctx: &AssistContext) - |edit| { let definition = Definition::ModuleDef(ModuleDef::EnumVariant(variant_hir)); let res = definition.find_usages(&ctx.db, None); - let module_def = mod_def_for_target_module(ctx, &enum_name); let start_offset = variant.parent_enum().syntax().text_range().start(); let mut visited_modules_set: FxHashSet = FxHashSet::default(); - visited_modules_set.insert(module_def.module(ctx.db).unwrap()); + visited_modules_set.insert(current_module); for reference in res { let source_file = ctx.sema.parse(reference.file_range.file_id); update_reference( @@ -67,7 +67,7 @@ pub(crate) fn extract_struct_from_enum(acc: &mut Assists, ctx: &AssistContext) - edit, reference, &source_file, - &module_def, + ¤t_module_def, &mut visited_modules_set, ); } @@ -95,10 +95,6 @@ fn existing_struct_def(db: &RootDatabase, variant_name: &str, variant: &EnumVari .any(|(name, _)| name.to_string() == variant_name.to_string()) } -fn mod_def_for_target_module(ctx: &AssistContext, enum_name: &str) -> ModuleDef { - ImportsLocator::new(ctx.db).find_imports(enum_name).first().unwrap().left().unwrap() -} - fn insert_import( ctx: &AssistContext, builder: &mut AssistBuilder, @@ -186,23 +182,16 @@ fn update_reference( let call = path_expr.syntax().parent().and_then(ast::CallExpr::cast)?; let list = call.arg_list()?; let segment = path_expr.path()?.segment()?; + let segment_name = segment.name_ref()?; + let module = ctx.sema.scope(&path_expr.syntax()).module()?; let list_range = list.syntax().text_range(); let inside_list_range = TextRange::new( list_range.start().checked_add(TextSize::from(1))?, list_range.end().checked_sub(TextSize::from(1))?, ); edit.perform(reference.file_range.file_id, |builder| { - let module = ctx.sema.scope(&path_expr.syntax()).module().unwrap(); if !visited_modules_set.contains(&module) { - if insert_import( - ctx, - builder, - &path_expr, - &module, - module_def, - segment.name_ref().unwrap(), - ) - .is_some() + if insert_import(ctx, builder, &path_expr, &module, module_def, segment_name).is_some() { visited_modules_set.insert(module); } From e2974ba8f7d862eac084ada2fdf327bbde803bed Mon Sep 17 00:00:00 2001 From: Mikhail Rakhmanov Date: Sat, 23 May 2020 11:57:12 +0200 Subject: [PATCH 010/172] Remove unnecessary set_file and change variable positions for better readability --- .../src/handlers/extract_struct_from_enum_variant.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/crates/ra_assists/src/handlers/extract_struct_from_enum_variant.rs b/crates/ra_assists/src/handlers/extract_struct_from_enum_variant.rs index d5397bf2134b..dd2fd57a7a2f 100644 --- a/crates/ra_assists/src/handlers/extract_struct_from_enum_variant.rs +++ b/crates/ra_assists/src/handlers/extract_struct_from_enum_variant.rs @@ -127,12 +127,12 @@ fn extract_struct_def( } else { "".to_string() }; - let mut buf = String::new(); let indent = if let Some(indent) = leading_indent(enum_ast) { indent.to_string() } else { "".to_string() }; + let mut buf = String::new(); format_to!( buf, @@ -161,7 +161,6 @@ fn update_variant( list_range.end().checked_sub(TextSize::from(1))?, ); edit.perform(file_id, |builder| { - builder.set_file(file_id); builder.replace(inside_variant_range, variant_name); }); Some(()) From 08aa8e1de717425ebf49796df1515d9fbd8b2e3e Mon Sep 17 00:00:00 2001 From: Mikhail Rakhmanov Date: Sun, 24 May 2020 14:53:12 +0200 Subject: [PATCH 011/172] Further refactoring under review comments --- .../src/handlers/extract_struct_from_enum_variant.rs | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/crates/ra_assists/src/handlers/extract_struct_from_enum_variant.rs b/crates/ra_assists/src/handlers/extract_struct_from_enum_variant.rs index dd2fd57a7a2f..a36587633b16 100644 --- a/crates/ra_assists/src/handlers/extract_struct_from_enum_variant.rs +++ b/crates/ra_assists/src/handlers/extract_struct_from_enum_variant.rs @@ -6,7 +6,6 @@ use ra_syntax::{ ast::{self, AstNode, NameOwner}, SourceFile, SyntaxNode, TextRange, TextSize, }; -use stdx::format_to; use crate::{ assist_context::{AssistBuilder, AssistDirector}, @@ -58,7 +57,7 @@ pub(crate) fn extract_struct_from_enum(acc: &mut Assists, ctx: &AssistContext) - let definition = Definition::ModuleDef(ModuleDef::EnumVariant(variant_hir)); let res = definition.find_usages(&ctx.db, None); let start_offset = variant.parent_enum().syntax().text_range().start(); - let mut visited_modules_set: FxHashSet = FxHashSet::default(); + let mut visited_modules_set = FxHashSet::default(); visited_modules_set.insert(current_module); for reference in res { let source_file = ctx.sema.parse(reference.file_range.file_id); @@ -132,10 +131,7 @@ fn extract_struct_def( } else { "".to_string() }; - let mut buf = String::new(); - - format_to!( - buf, + let struct_def = format!( r#"{}struct {}{}; {}"#, @@ -145,7 +141,7 @@ fn extract_struct_def( indent ); edit.perform(file_id, |builder| { - builder.insert(start_offset, buf); + builder.insert(start_offset, struct_def); }); Some(()) } From a27ede88a3a9063a3a2265b668b1c41b589852f5 Mon Sep 17 00:00:00 2001 From: Roland Ruckerbauer Date: Sat, 30 May 2020 14:09:10 +0200 Subject: [PATCH 012/172] Fix problem with format string tokenization Fixed by just not handling closing curlybrace escaping. --- crates/ra_syntax/src/ast/tokens.rs | 5 ----- 1 file changed, 5 deletions(-) diff --git a/crates/ra_syntax/src/ast/tokens.rs b/crates/ra_syntax/src/ast/tokens.rs index 3cd6d99c3889..04b0a4480375 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 { let mut cloned = chars.clone().take(2); let first = cloned.next().and_then(|next| next.1.as_ref().ok()).copied(); - let second = cloned.next().and_then(|next| next.1.as_ref().ok()).copied(); if first != Some('}') { continue; } - if second == Some('}') { - // Escaped format end specifier, `}}` - continue; - } skip_char_and_emit(&mut chars, FormatSpecifier::Close, &mut callback); } _ => { From 780c89959a2f7362eb51508d83863eeff9a49e4c Mon Sep 17 00:00:00 2001 From: Roland Ruckerbauer Date: Sat, 30 May 2020 18:35:11 +0200 Subject: [PATCH 013/172] Test case for format string highlighting of closing curlybrace --- crates/ra_ide/src/snapshots/highlight_strings.html | 1 + crates/ra_ide/src/syntax_highlighting/tests.rs | 1 + 2 files changed, 2 insertions(+) diff --git a/crates/ra_ide/src/snapshots/highlight_strings.html b/crates/ra_ide/src/snapshots/highlight_strings.html index 326744361c68..41cddd0ff261 100644 --- a/crates/ra_ide/src/snapshots/highlight_strings.html +++ b/crates/ra_ide/src/snapshots/highlight_strings.html @@ -52,6 +52,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd println!("{argument}", argument = "test"); // => "test" println!("{name} {}", 1, name = 2); // => "2 1" println!("{a} {c} {b}", a="a", b='b', c=3); // => "a 3 b" + println!("{{{}}}", 2); // => "{2}" println!("Hello {:5}!", "x"); println!("Hello {:1$}!", "x", 5); println!("Hello {1:0$}!", 5, "x"); diff --git a/crates/ra_ide/src/syntax_highlighting/tests.rs b/crates/ra_ide/src/syntax_highlighting/tests.rs index eb43a23da682..7dc229cab73d 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() { println!("{argument}", argument = "test"); // => "test" println!("{name} {}", 1, name = 2); // => "2 1" println!("{a} {c} {b}", a="a", b='b', c=3); // => "a 3 b" + println!("{{{}}}", 2); // => "{2}" println!("Hello {:5}!", "x"); println!("Hello {:1$}!", "x", 5); println!("Hello {1:0$}!", 5, "x"); From 030d78345fa79af07f8ebd89a9d244576fac992b Mon Sep 17 00:00:00 2001 From: veetaha Date: Sat, 23 May 2020 04:58:22 +0300 Subject: [PATCH 014/172] Fix invoking cargo without consulting CARGO or standard installation paths --- Cargo.lock | 1 + crates/rust-analyzer/Cargo.toml | 1 + crates/rust-analyzer/src/main_loop/handlers.rs | 8 +++++++- editors/code/src/cargo.ts | 4 ++-- editors/code/src/tasks.ts | 7 +++++-- 5 files changed, 16 insertions(+), 5 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index def4ed45e04e..7de784c1cde3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1364,6 +1364,7 @@ dependencies = [ "ra_syntax", "ra_text_edit", "ra_tt", + "ra_toolchain", "ra_vfs", "rand", "relative-path", diff --git a/crates/rust-analyzer/Cargo.toml b/crates/rust-analyzer/Cargo.toml index 65b487db3b98..2e49448cc6ca 100644 --- a/crates/rust-analyzer/Cargo.toml +++ b/crates/rust-analyzer/Cargo.toml @@ -48,6 +48,7 @@ hir = { path = "../ra_hir", package = "ra_hir" } hir_def = { path = "../ra_hir_def", package = "ra_hir_def" } hir_ty = { path = "../ra_hir_ty", package = "ra_hir_ty" } ra_proc_macro_srv = { path = "../ra_proc_macro_srv" } +ra_toolchain = { path = "../ra_toolchain" } [target.'cfg(windows)'.dependencies] winapi = "0.3.8" diff --git a/crates/rust-analyzer/src/main_loop/handlers.rs b/crates/rust-analyzer/src/main_loop/handlers.rs index 1f910ff82b93..1b5b3325c04f 100644 --- a/crates/rust-analyzer/src/main_loop/handlers.rs +++ b/crates/rust-analyzer/src/main_loop/handlers.rs @@ -40,6 +40,7 @@ use crate::{ world::WorldSnapshot, LspError, Result, }; +use anyhow::Context; pub fn handle_analyzer_status(world: WorldSnapshot, _: ()) -> Result { let _p = profile("handle_analyzer_status"); @@ -982,10 +983,15 @@ fn to_lsp_runnable( target.map_or_else(|| "run binary".to_string(), |t| format!("run {}", t)) } }; + let cargo_path = ra_toolchain::cargo() + .to_str() + .context("Path to cargo executable contains invalid UTF8 characters")? + .to_owned(); + Ok(lsp_ext::Runnable { range: to_proto::range(&line_index, runnable.range), label, - bin: "cargo".to_string(), + bin: cargo_path, args, extra_args, env: { diff --git a/editors/code/src/cargo.ts b/editors/code/src/cargo.ts index a55b2f860f68..46cd3d777847 100644 --- a/editors/code/src/cargo.ts +++ b/editors/code/src/cargo.ts @@ -126,8 +126,8 @@ export class Cargo { } } -// Mirrors `ra_env::get_path_for_executable` implementation -function getCargoPathOrFail(): string { +// Mirrors `ra_toolchain::cargo()` implementation +export function getCargoPathOrFail(): string { const envVar = process.env.CARGO; const executableName = "cargo"; diff --git a/editors/code/src/tasks.ts b/editors/code/src/tasks.ts index 1366c76d6bda..c22d693623f8 100644 --- a/editors/code/src/tasks.ts +++ b/editors/code/src/tasks.ts @@ -1,4 +1,5 @@ import * as vscode from 'vscode'; +import { getCargoPathOrFail } from "./cargo"; // This ends up as the `type` key in tasks.json. RLS also uses `cargo` and // our configuration should be compatible with it so use the same key. @@ -24,6 +25,8 @@ class CargoTaskProvider implements vscode.TaskProvider { // set of tasks that always exist. These tasks cannot be removed in // tasks.json - only tweaked. + const cargoPath = getCargoPathOrFail(); + return [ { command: 'build', group: vscode.TaskGroup.Build }, { command: 'check', group: vscode.TaskGroup.Build }, @@ -46,7 +49,7 @@ class CargoTaskProvider implements vscode.TaskProvider { `cargo ${command}`, 'rust', // What to do when this command is executed. - new vscode.ShellExecution('cargo', [command]), + new vscode.ShellExecution(cargoPath, [command]), // Problem matchers. ['$rustc'], ); @@ -80,4 +83,4 @@ class CargoTaskProvider implements vscode.TaskProvider { export function activateTaskProvider(target: vscode.WorkspaceFolder): vscode.Disposable { const provider = new CargoTaskProvider(target); return vscode.tasks.registerTaskProvider(TASK_TYPE, provider); -} \ No newline at end of file +} From a419cedb1cc661349a022262c8b03993e063252f Mon Sep 17 00:00:00 2001 From: veetaha Date: Sat, 23 May 2020 16:31:56 +0300 Subject: [PATCH 015/172] Fix tests, apply code review proposals --- Cargo.lock | 1 + crates/ra_proc_macro_srv/Cargo.toml | 1 + crates/ra_proc_macro_srv/src/tests/utils.rs | 3 +-- crates/rust-analyzer/src/main_loop/handlers.rs | 17 ++++++++++------- crates/rust-analyzer/tests/heavy_tests/main.rs | 14 +++++++++----- 5 files changed, 22 insertions(+), 14 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 7de784c1cde3..e1da335f5f6e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1119,6 +1119,7 @@ dependencies = [ "memmap", "ra_mbe", "ra_proc_macro", + "ra_toolchain", "ra_tt", "serde_derive", "test_utils", diff --git a/crates/ra_proc_macro_srv/Cargo.toml b/crates/ra_proc_macro_srv/Cargo.toml index bb3003278516..5821029450f5 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" difference = "2.0.0" # used as proc macro test target serde_derive = "1.0.106" +ra_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 84348b5defce..8d85f2d8a775 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 @@ use crate::dylib; use crate::ProcMacroSrv; -pub use difference::Changeset as __Changeset; use ra_proc_macro::ListMacrosTask; use std::str::FromStr; use test_utils::assert_eq_text; @@ -13,7 +12,7 @@ mod fixtures { // Use current project metadata to get the proc-macro dylib path pub fn dylib_path(crate_name: &str, version: &str) -> std::path::PathBuf { - let command = Command::new("cargo") + let command = Command::new(ra_toolchain::cargo()) .args(&["check", "--message-format", "json"]) .output() .unwrap() diff --git a/crates/rust-analyzer/src/main_loop/handlers.rs b/crates/rust-analyzer/src/main_loop/handlers.rs index 1b5b3325c04f..d42cfa30004e 100644 --- a/crates/rust-analyzer/src/main_loop/handlers.rs +++ b/crates/rust-analyzer/src/main_loop/handlers.rs @@ -427,7 +427,7 @@ pub fn handle_runnables( res.push(lsp_ext::Runnable { range: Default::default(), label: format!("cargo {} -p {}", cmd, spec.package), - bin: "cargo".to_string(), + bin: cargo_path()?, args: vec![cmd.to_string(), "--package".to_string(), spec.package.clone()], extra_args: Vec::new(), env: FxHashMap::default(), @@ -439,7 +439,7 @@ pub fn handle_runnables( res.push(lsp_ext::Runnable { range: Default::default(), label: "cargo check --workspace".to_string(), - bin: "cargo".to_string(), + bin: cargo_path()?, args: vec!["check".to_string(), "--workspace".to_string()], extra_args: Vec::new(), env: FxHashMap::default(), @@ -450,6 +450,13 @@ pub fn handle_runnables( Ok(res) } +fn cargo_path() -> Result { + Ok(ra_toolchain::cargo() + .to_str() + .context("Path to `cargo` executable contains invalid UTF8 characters")? + .to_owned()) +} + pub fn handle_completion( world: WorldSnapshot, params: lsp_types::CompletionParams, @@ -983,15 +990,11 @@ fn to_lsp_runnable( target.map_or_else(|| "run binary".to_string(), |t| format!("run {}", t)) } }; - let cargo_path = ra_toolchain::cargo() - .to_str() - .context("Path to cargo executable contains invalid UTF8 characters")? - .to_owned(); Ok(lsp_ext::Runnable { range: to_proto::range(&line_index, runnable.range), label, - bin: cargo_path, + bin: cargo_path()?, args, extra_args, env: { diff --git a/crates/rust-analyzer/tests/heavy_tests/main.rs b/crates/rust-analyzer/tests/heavy_tests/main.rs index 405ddb362969..a31580c867c3 100644 --- a/crates/rust-analyzer/tests/heavy_tests/main.rs +++ b/crates/rust-analyzer/tests/heavy_tests/main.rs @@ -58,6 +58,10 @@ use std::collections::Spam; eprintln!("completion took {:?}", completion_start.elapsed()); } +fn cargo_path() -> String { + ra_toolchain::cargo().to_str().unwrap().to_owned() +} + #[test] fn test_runnables_no_project() { if skip_slow_tests() { @@ -79,7 +83,7 @@ fn foo() { { "args": [ "test" ], "extraArgs": [ "foo", "--nocapture" ], - "bin": "cargo", + "bin": cargo_path(), "env": { "RUST_BACKTRACE": "short" }, "cwd": null, "label": "test foo", @@ -91,7 +95,7 @@ fn foo() { { "args": ["check", "--workspace"], "extraArgs": [], - "bin": "cargo", + "bin": cargo_path(), "env": {}, "cwd": null, "label": "cargo check --workspace", @@ -141,7 +145,7 @@ fn main() {} { "args": [ "test", "--package", "foo", "--test", "spam" ], "extraArgs": [ "test_eggs", "--exact", "--nocapture" ], - "bin": "cargo", + "bin": cargo_path(), "env": { "RUST_BACKTRACE": "short" }, "label": "test test_eggs", "range": { @@ -153,7 +157,7 @@ fn main() {} { "args": [ "check", "--package", "foo" ], "extraArgs": [], - "bin": "cargo", + "bin": cargo_path(), "env": {}, "label": "cargo check -p foo", "range": { @@ -165,7 +169,7 @@ fn main() {} { "args": [ "test", "--package", "foo" ], "extraArgs": [], - "bin": "cargo", + "bin": cargo_path(), "env": {}, "label": "cargo test -p foo", "range": { From d605ec9c321392d9c7ee4b440c560e1e405d92e6 Mon Sep 17 00:00:00 2001 From: veetaha Date: Sun, 31 May 2020 05:13:08 +0300 Subject: [PATCH 016/172] Change Runnable.bin -> Runnable.kind As per matklad, we now pass the responsibility for finding the binary to the frontend. Also, added caching for finding the binary path to reduce the amount of filesystem interactions. --- Cargo.lock | 1 - crates/rust-analyzer/Cargo.toml | 1 - crates/rust-analyzer/src/lsp_ext.rs | 11 +- .../rust-analyzer/src/main_loop/handlers.rs | 14 +- .../rust-analyzer/tests/heavy_tests/main.rs | 14 +- docs/dev/lsp-extensions.md | 2 +- editors/code/src/debug.ts | 2 +- editors/code/src/lsp_ext.ts | 5 +- editors/code/src/run.ts | 3 +- editors/code/src/tasks.ts | 4 +- editors/code/src/{cargo.ts => toolchain.ts} | 137 ++++++++++-------- editors/code/src/util.ts | 18 +++ editors/code/tests/unit/launch_config.test.ts | 14 +- 13 files changed, 133 insertions(+), 93 deletions(-) rename editors/code/src/{cargo.ts => toolchain.ts} (53%) diff --git a/Cargo.lock b/Cargo.lock index e1da335f5f6e..07f18c7607ee 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1365,7 +1365,6 @@ dependencies = [ "ra_syntax", "ra_text_edit", "ra_tt", - "ra_toolchain", "ra_vfs", "rand", "relative-path", diff --git a/crates/rust-analyzer/Cargo.toml b/crates/rust-analyzer/Cargo.toml index 2e49448cc6ca..65b487db3b98 100644 --- a/crates/rust-analyzer/Cargo.toml +++ b/crates/rust-analyzer/Cargo.toml @@ -48,7 +48,6 @@ hir = { path = "../ra_hir", package = "ra_hir" } hir_def = { path = "../ra_hir_def", package = "ra_hir_def" } hir_ty = { path = "../ra_hir_ty", package = "ra_hir_ty" } ra_proc_macro_srv = { path = "../ra_proc_macro_srv" } -ra_toolchain = { path = "../ra_toolchain" } [target.'cfg(windows)'.dependencies] winapi = "0.3.8" diff --git a/crates/rust-analyzer/src/lsp_ext.rs b/crates/rust-analyzer/src/lsp_ext.rs index acb1dacb6b66..173c23b9e5d0 100644 --- a/crates/rust-analyzer/src/lsp_ext.rs +++ b/crates/rust-analyzer/src/lsp_ext.rs @@ -121,12 +121,21 @@ pub struct RunnablesParams { pub position: Option, } +// Must strictly correspond to the executable name +#[derive(Serialize, Deserialize, Debug)] +#[serde(rename_all = "lowercase")] +pub enum RunnableKind { + Cargo, + Rustc, + Rustup, +} + #[derive(Deserialize, Serialize, Debug)] #[serde(rename_all = "camelCase")] pub struct Runnable { pub range: Range, pub label: String, - pub bin: String, + pub kind: RunnableKind, pub args: Vec, pub extra_args: Vec, pub env: FxHashMap, diff --git a/crates/rust-analyzer/src/main_loop/handlers.rs b/crates/rust-analyzer/src/main_loop/handlers.rs index d42cfa30004e..bc7c7f1ef5d5 100644 --- a/crates/rust-analyzer/src/main_loop/handlers.rs +++ b/crates/rust-analyzer/src/main_loop/handlers.rs @@ -40,7 +40,6 @@ use crate::{ world::WorldSnapshot, LspError, Result, }; -use anyhow::Context; pub fn handle_analyzer_status(world: WorldSnapshot, _: ()) -> Result { let _p = profile("handle_analyzer_status"); @@ -427,7 +426,7 @@ pub fn handle_runnables( res.push(lsp_ext::Runnable { range: Default::default(), label: format!("cargo {} -p {}", cmd, spec.package), - bin: cargo_path()?, + kind: lsp_ext::RunnableKind::Cargo, args: vec![cmd.to_string(), "--package".to_string(), spec.package.clone()], extra_args: Vec::new(), env: FxHashMap::default(), @@ -439,7 +438,7 @@ pub fn handle_runnables( res.push(lsp_ext::Runnable { range: Default::default(), label: "cargo check --workspace".to_string(), - bin: cargo_path()?, + kind: lsp_ext::RunnableKind::Cargo, args: vec!["check".to_string(), "--workspace".to_string()], extra_args: Vec::new(), env: FxHashMap::default(), @@ -450,13 +449,6 @@ pub fn handle_runnables( Ok(res) } -fn cargo_path() -> Result { - Ok(ra_toolchain::cargo() - .to_str() - .context("Path to `cargo` executable contains invalid UTF8 characters")? - .to_owned()) -} - pub fn handle_completion( world: WorldSnapshot, params: lsp_types::CompletionParams, @@ -994,7 +986,7 @@ fn to_lsp_runnable( Ok(lsp_ext::Runnable { range: to_proto::range(&line_index, runnable.range), label, - bin: cargo_path()?, + kind: lsp_ext::RunnableKind::Cargo, args, extra_args, env: { diff --git a/crates/rust-analyzer/tests/heavy_tests/main.rs b/crates/rust-analyzer/tests/heavy_tests/main.rs index a31580c867c3..8b473ff74cd1 100644 --- a/crates/rust-analyzer/tests/heavy_tests/main.rs +++ b/crates/rust-analyzer/tests/heavy_tests/main.rs @@ -58,10 +58,6 @@ use std::collections::Spam; eprintln!("completion took {:?}", completion_start.elapsed()); } -fn cargo_path() -> String { - ra_toolchain::cargo().to_str().unwrap().to_owned() -} - #[test] fn test_runnables_no_project() { if skip_slow_tests() { @@ -83,7 +79,7 @@ fn foo() { { "args": [ "test" ], "extraArgs": [ "foo", "--nocapture" ], - "bin": cargo_path(), + "kind": "cargo", "env": { "RUST_BACKTRACE": "short" }, "cwd": null, "label": "test foo", @@ -95,7 +91,7 @@ fn foo() { { "args": ["check", "--workspace"], "extraArgs": [], - "bin": cargo_path(), + "kind": "cargo", "env": {}, "cwd": null, "label": "cargo check --workspace", @@ -145,7 +141,7 @@ fn main() {} { "args": [ "test", "--package", "foo", "--test", "spam" ], "extraArgs": [ "test_eggs", "--exact", "--nocapture" ], - "bin": cargo_path(), + "kind": "cargo", "env": { "RUST_BACKTRACE": "short" }, "label": "test test_eggs", "range": { @@ -157,7 +153,7 @@ fn main() {} { "args": [ "check", "--package", "foo" ], "extraArgs": [], - "bin": cargo_path(), + "kind": "cargo", "env": {}, "label": "cargo check -p foo", "range": { @@ -169,7 +165,7 @@ fn main() {} { "args": [ "test", "--package", "foo" ], "extraArgs": [], - "bin": cargo_path(), + "kind": "cargo", "env": {}, "label": "cargo test -p foo", "range": { diff --git a/docs/dev/lsp-extensions.md b/docs/dev/lsp-extensions.md index dbc95be387c5..d06da355d39d 100644 --- a/docs/dev/lsp-extensions.md +++ b/docs/dev/lsp-extensions.md @@ -417,7 +417,7 @@ interface Runnable { /// The label to show in the UI. label: string; /// The following fields describe a process to spawn. - bin: string; + kind: "cargo" | "rustc" | "rustup"; args: string[]; /// Args for cargo after `--`. extraArgs: string[]; diff --git a/editors/code/src/debug.ts b/editors/code/src/debug.ts index 027504ecd2a8..bdec5b7357fa 100644 --- a/editors/code/src/debug.ts +++ b/editors/code/src/debug.ts @@ -3,7 +3,7 @@ import * as vscode from 'vscode'; import * as path from 'path'; import * as ra from './lsp_ext'; -import { Cargo } from './cargo'; +import { Cargo } from './toolchain'; import { Ctx } from "./ctx"; const debugOutput = vscode.window.createOutputChannel("Debug"); diff --git a/editors/code/src/lsp_ext.ts b/editors/code/src/lsp_ext.ts index 4da12eb3092a..3e0b606997fb 100644 --- a/editors/code/src/lsp_ext.ts +++ b/editors/code/src/lsp_ext.ts @@ -45,10 +45,13 @@ export interface RunnablesParams { textDocument: lc.TextDocumentIdentifier; position: lc.Position | null; } + +export type RunnableKind = "cargo" | "rustc" | "rustup"; + export interface Runnable { range: lc.Range; label: string; - bin: string; + kind: RunnableKind; args: string[]; extraArgs: string[]; env: { [key: string]: string }; diff --git a/editors/code/src/run.ts b/editors/code/src/run.ts index 2a7a429cfa66..401cb76af67d 100644 --- a/editors/code/src/run.ts +++ b/editors/code/src/run.ts @@ -1,6 +1,7 @@ import * as vscode from 'vscode'; import * as lc from 'vscode-languageclient'; import * as ra from './lsp_ext'; +import * as toolchain from "./toolchain"; import { Ctx, Cmd } from './ctx'; import { startDebugSession, getDebugConfiguration } from './debug'; @@ -175,7 +176,7 @@ export function createTask(spec: ra.Runnable): vscode.Task { const definition: CargoTaskDefinition = { type: 'cargo', label: spec.label, - command: spec.bin, + command: toolchain.getPathForExecutable(spec.kind), args: spec.extraArgs ? [...spec.args, '--', ...spec.extraArgs] : spec.args, env: spec.env, }; diff --git a/editors/code/src/tasks.ts b/editors/code/src/tasks.ts index c22d693623f8..9748824df38f 100644 --- a/editors/code/src/tasks.ts +++ b/editors/code/src/tasks.ts @@ -1,5 +1,5 @@ import * as vscode from 'vscode'; -import { getCargoPathOrFail } from "./cargo"; +import * as toolchain from "./toolchain"; // This ends up as the `type` key in tasks.json. RLS also uses `cargo` and // our configuration should be compatible with it so use the same key. @@ -25,7 +25,7 @@ class CargoTaskProvider implements vscode.TaskProvider { // set of tasks that always exist. These tasks cannot be removed in // tasks.json - only tweaked. - const cargoPath = getCargoPathOrFail(); + const cargoPath = toolchain.cargoPath(); return [ { command: 'build', group: vscode.TaskGroup.Build }, diff --git a/editors/code/src/cargo.ts b/editors/code/src/toolchain.ts similarity index 53% rename from editors/code/src/cargo.ts rename to editors/code/src/toolchain.ts index 46cd3d777847..80a7915e90e8 100644 --- a/editors/code/src/cargo.ts +++ b/editors/code/src/toolchain.ts @@ -1,9 +1,10 @@ import * as cp from 'child_process'; import * as os from 'os'; import * as path from 'path'; +import * as fs from 'fs'; import * as readline from 'readline'; import { OutputChannel } from 'vscode'; -import { isValidExecutable } from './util'; +import { log, memoize } from './util'; interface CompilationArtifact { fileName: string; @@ -17,34 +18,35 @@ export interface ArtifactSpec { filter?: (artifacts: CompilationArtifact[]) => CompilationArtifact[]; } -export function artifactSpec(args: readonly string[]): ArtifactSpec { - const cargoArgs = [...args, "--message-format=json"]; - - // arguments for a runnable from the quick pick should be updated. - // see crates\rust-analyzer\src\main_loop\handlers.rs, handle_code_lens - switch (cargoArgs[0]) { - case "run": cargoArgs[0] = "build"; break; - case "test": { - if (!cargoArgs.includes("--no-run")) { - cargoArgs.push("--no-run"); - } - break; - } - } - - const result: ArtifactSpec = { cargoArgs: cargoArgs }; - if (cargoArgs[0] === "test") { - // for instance, `crates\rust-analyzer\tests\heavy_tests\main.rs` tests - // produce 2 artifacts: {"kind": "bin"} and {"kind": "test"} - result.filter = (artifacts) => artifacts.filter(it => it.isTest); - } - - return result; -} - export class Cargo { constructor(readonly rootFolder: string, readonly output: OutputChannel) { } + // Made public for testing purposes + static artifactSpec(args: readonly string[]): ArtifactSpec { + const cargoArgs = [...args, "--message-format=json"]; + + // arguments for a runnable from the quick pick should be updated. + // see crates\rust-analyzer\src\main_loop\handlers.rs, handle_code_lens + switch (cargoArgs[0]) { + case "run": cargoArgs[0] = "build"; break; + case "test": { + if (!cargoArgs.includes("--no-run")) { + cargoArgs.push("--no-run"); + } + break; + } + } + + const result: ArtifactSpec = { cargoArgs: cargoArgs }; + if (cargoArgs[0] === "test") { + // for instance, `crates\rust-analyzer\tests\heavy_tests\main.rs` tests + // produce 2 artifacts: {"kind": "bin"} and {"kind": "test"} + result.filter = (artifacts) => artifacts.filter(it => it.isTest); + } + + return result; + } + private async getArtifacts(spec: ArtifactSpec): Promise { const artifacts: CompilationArtifact[] = []; @@ -77,7 +79,7 @@ export class Cargo { } async executableFromArgs(args: readonly string[]): Promise { - const artifacts = await this.getArtifacts(artifactSpec(args)); + const artifacts = await this.getArtifacts(Cargo.artifactSpec(args)); if (artifacts.length === 0) { throw new Error('No compilation artifacts'); @@ -94,14 +96,7 @@ export class Cargo { onStderrString: (data: string) => void ): Promise { return new Promise((resolve, reject) => { - let cargoPath; - try { - cargoPath = getCargoPathOrFail(); - } catch (err) { - return reject(err); - } - - const cargo = cp.spawn(cargoPath, cargoArgs, { + const cargo = cp.spawn(cargoPath(), cargoArgs, { stdio: ['ignore', 'pipe', 'pipe'], cwd: this.rootFolder }); @@ -126,26 +121,54 @@ export class Cargo { } } -// Mirrors `ra_toolchain::cargo()` implementation -export function getCargoPathOrFail(): string { - const envVar = process.env.CARGO; - const executableName = "cargo"; - - if (envVar) { - if (isValidExecutable(envVar)) return envVar; - - throw new Error(`\`${envVar}\` environment variable points to something that's not a valid executable`); - } - - if (isValidExecutable(executableName)) return executableName; - - const standardLocation = path.join(os.homedir(), '.cargo', 'bin', executableName); - - if (isValidExecutable(standardLocation)) return standardLocation; - - throw new Error( - `Failed to find \`${executableName}\` executable. ` + - `Make sure \`${executableName}\` is in \`$PATH\`, ` + - `or set \`${envVar}\` to point to a valid executable.` - ); +/** Mirrors `ra_toolchain::cargo()` implementation */ +export function cargoPath(): string { + return getPathForExecutable("cargo"); +} + +/** Mirrors `ra_toolchain::get_path_for_executable()` implementation */ +export const getPathForExecutable = memoize( + // We apply caching to decrease file-system interactions + (executableName: "cargo" | "rustc" | "rustup"): string => { + { + const envVar = process.env[executableName.toUpperCase()]; + if (envVar) return envVar; + } + + if (lookupInPath(executableName)) return executableName; + + try { + // hmm, `os.homedir()` seems to be infallible + // it is not mentioned in docs and cannot be infered by the type signature... + const standardPath = path.join(os.homedir(), ".cargo", "bin", executableName); + + if (isFile(standardPath)) return standardPath; + } catch (err) { + log.error("Failed to read the fs info", err); + } + return executableName; + } +); + +function lookupInPath(exec: string): boolean { + const paths = process.env.PATH ?? "";; + + const candidates = paths.split(path.delimiter).flatMap(dirInPath => { + const candidate = path.join(dirInPath, exec); + return os.type() === "Windows_NT" + ? [candidate, `${candidate}.exe`] + : [candidate]; + }); + + return candidates.some(isFile); +} + +function isFile(suspectPath: string): boolean { + // It is not mentionned in docs, but `statSync()` throws an error when + // the path doesn't exist + try { + return fs.statSync(suspectPath).isFile(); + } catch { + return false; + } } diff --git a/editors/code/src/util.ts b/editors/code/src/util.ts index 352ef9162f3d..fe3fb71cd7dc 100644 --- a/editors/code/src/util.ts +++ b/editors/code/src/util.ts @@ -99,3 +99,21 @@ export function isValidExecutable(path: string): boolean { export function setContextValue(key: string, value: any): Thenable { return vscode.commands.executeCommand('setContext', key, value); } + +/** + * Returns a higher-order function that caches the results of invoking the + * underlying function. + */ +export function memoize(func: (this: TThis, arg: Param) => Ret) { + const cache = new Map(); + + return function(this: TThis, arg: Param) { + const cached = cache.get(arg); + if (cached) return cached; + + const result = func.call(this, arg); + cache.set(arg, result); + + return result; + }; +} diff --git a/editors/code/tests/unit/launch_config.test.ts b/editors/code/tests/unit/launch_config.test.ts index d5cf1b74eadd..68794d53ede1 100644 --- a/editors/code/tests/unit/launch_config.test.ts +++ b/editors/code/tests/unit/launch_config.test.ts @@ -1,25 +1,25 @@ import * as assert from 'assert'; -import * as cargo from '../../src/cargo'; +import { Cargo } from '../../src/toolchain'; suite('Launch configuration', () => { suite('Lens', () => { test('A binary', async () => { - const args = cargo.artifactSpec(["build", "--package", "pkg_name", "--bin", "pkg_name"]); + const args = Cargo.artifactSpec(["build", "--package", "pkg_name", "--bin", "pkg_name"]); assert.deepEqual(args.cargoArgs, ["build", "--package", "pkg_name", "--bin", "pkg_name", "--message-format=json"]); assert.deepEqual(args.filter, undefined); }); test('One of Multiple Binaries', async () => { - const args = cargo.artifactSpec(["build", "--package", "pkg_name", "--bin", "bin1"]); + const args = Cargo.artifactSpec(["build", "--package", "pkg_name", "--bin", "bin1"]); assert.deepEqual(args.cargoArgs, ["build", "--package", "pkg_name", "--bin", "bin1", "--message-format=json"]); assert.deepEqual(args.filter, undefined); }); test('A test', async () => { - const args = cargo.artifactSpec(["test", "--package", "pkg_name", "--lib", "--no-run"]); + const args = Cargo.artifactSpec(["test", "--package", "pkg_name", "--lib", "--no-run"]); assert.deepEqual(args.cargoArgs, ["test", "--package", "pkg_name", "--lib", "--no-run", "--message-format=json"]); assert.notDeepEqual(args.filter, undefined); @@ -28,7 +28,7 @@ suite('Launch configuration', () => { suite('QuickPick', () => { test('A binary', async () => { - const args = cargo.artifactSpec(["run", "--package", "pkg_name", "--bin", "pkg_name"]); + const args = Cargo.artifactSpec(["run", "--package", "pkg_name", "--bin", "pkg_name"]); assert.deepEqual(args.cargoArgs, ["build", "--package", "pkg_name", "--bin", "pkg_name", "--message-format=json"]); assert.deepEqual(args.filter, undefined); @@ -36,14 +36,14 @@ suite('Launch configuration', () => { test('One of Multiple Binaries', async () => { - const args = cargo.artifactSpec(["run", "--package", "pkg_name", "--bin", "bin2"]); + const args = Cargo.artifactSpec(["run", "--package", "pkg_name", "--bin", "bin2"]); assert.deepEqual(args.cargoArgs, ["build", "--package", "pkg_name", "--bin", "bin2", "--message-format=json"]); assert.deepEqual(args.filter, undefined); }); test('A test', async () => { - const args = cargo.artifactSpec(["test", "--package", "pkg_name", "--lib"]); + const args = Cargo.artifactSpec(["test", "--package", "pkg_name", "--lib"]); assert.deepEqual(args.cargoArgs, ["test", "--package", "pkg_name", "--lib", "--message-format=json", "--no-run"]); assert.notDeepEqual(args.filter, undefined); From 1211a46826ee8a08683e4cfe151649efd6fd90fa Mon Sep 17 00:00:00 2001 From: Aaron Loucks Date: Sun, 31 May 2020 18:58:54 -0400 Subject: [PATCH 017/172] Unsquish parameter types in tooltips for macro-generated functions --- crates/ra_ide/src/display/function_signature.rs | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/crates/ra_ide/src/display/function_signature.rs b/crates/ra_ide/src/display/function_signature.rs index 9572debd822c..b081ecaad1cd 100644 --- a/crates/ra_ide/src/display/function_signature.rs +++ b/crates/ra_ide/src/display/function_signature.rs @@ -207,7 +207,18 @@ impl From<&'_ ast::FnDef> for FunctionSignature { res.push(raw_param); } - res.extend(param_list.params().map(|param| param.syntax().text().to_string())); + // macro-generated functions are missing whitespace + fn fmt_param(param: ast::Param) -> String { + let text = param.syntax().text().to_string(); + match text.find(':') { + Some(pos) if 1 + pos < text.len() => { + format!("{} {}", &text[0..1 + pos].trim(), &text[1 + pos..].trim()) + } + _ => text, + } + } + + res.extend(param_list.params().map(fmt_param)); res_types.extend(param_list.params().map(|param| { let param_text = param.syntax().text().to_string(); match param_text.split(':').nth(1).and_then(|it| it.get(1..)) { From 8101ea0fdc9d2be2d3926b22af8167f7155ff0c7 Mon Sep 17 00:00:00 2001 From: Nelson Elhage Date: Sun, 31 May 2020 16:04:52 -0700 Subject: [PATCH 018/172] Update a comment for the new source organization --- crates/ra_parser/src/grammar.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/crates/ra_parser/src/grammar.rs b/crates/ra_parser/src/grammar.rs index be0cd5661bd6..293baecf6a2c 100644 --- a/crates/ra_parser/src/grammar.rs +++ b/crates/ra_parser/src/grammar.rs @@ -18,9 +18,10 @@ //! // fn foo() {} //! ``` //! -//! After adding a new inline-test, run `cargo collect-tests` to extract -//! it as a standalone text-fixture into `tests/data/parser/inline`, and -//! run `cargo test` once to create the "gold" value. +//! After adding a new inline-test, run `cargo xtask codegen` to +//! extract it as a standalone text-fixture into +//! `crates/ra_syntax/test_data/parser/`, and run `cargo test` once to +//! create the "gold" value. //! //! Coding convention: rules like `where_clause` always produce either a //! node or an error, rules like `opt_where_clause` may produce nothing. From fb971c3bce710df234feb313528da81ccd5e26f9 Mon Sep 17 00:00:00 2001 From: veetaha Date: Tue, 2 Jun 2020 02:50:05 +0300 Subject: [PATCH 019/172] Simplify --- crates/ra_syntax/src/ast.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/ra_syntax/src/ast.rs b/crates/ra_syntax/src/ast.rs index 1876afe958e0..eddc807d5a50 100644 --- a/crates/ra_syntax/src/ast.rs +++ b/crates/ra_syntax/src/ast.rs @@ -75,7 +75,7 @@ impl AstChildren { impl Iterator for AstChildren { type Item = N; fn next(&mut self) -> Option { - self.inner.by_ref().find_map(N::cast) + self.inner.find_map(N::cast) } } From 0ced18eee00c53e2c060f674918255844edf30a6 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Tue, 2 Jun 2020 14:33:47 +0200 Subject: [PATCH 020/172] Move run commands to commands.ts --- editors/code/src/commands.ts | 74 ++++++++++++++++---- editors/code/src/debug.ts | 127 +++++++++++++++++++++-------------- editors/code/src/run.ts | 74 +------------------- 3 files changed, 138 insertions(+), 137 deletions(-) diff --git a/editors/code/src/commands.ts b/editors/code/src/commands.ts index 86302db37c1e..534d2a9847e0 100644 --- a/editors/code/src/commands.ts +++ b/editors/code/src/commands.ts @@ -8,6 +8,7 @@ import { spawnSync } from 'child_process'; import { RunnableQuickPick, selectRunnable, createTask } from './run'; import { AstInspector } from './ast_inspector'; import { isRustDocument, sleep, isRustEditor } from './util'; +import { startDebugSession, makeDebugConfig } from './debug'; export * from './ast_inspector'; export * from './run'; @@ -197,20 +198,6 @@ export function toggleInlayHints(ctx: Ctx): Cmd { }; } -export function run(ctx: Ctx): Cmd { - let prevRunnable: RunnableQuickPick | undefined; - - return async () => { - const item = await selectRunnable(ctx, prevRunnable); - if (!item) return; - - item.detail = 'rerun'; - prevRunnable = item; - const task = createTask(item.runnable); - return await vscode.tasks.executeTask(task); - }; -} - // Opens the virtual file that will show the syntax tree // // The contents of the file come from the `TextDocumentContentProvider` @@ -368,3 +355,62 @@ export function applySnippetWorkspaceEditCommand(_ctx: Ctx): Cmd { await applySnippetWorkspaceEdit(edit); }; } + +export function run(ctx: Ctx): Cmd { + let prevRunnable: RunnableQuickPick | undefined; + + return async () => { + const item = await selectRunnable(ctx, prevRunnable); + if (!item) return; + + item.detail = 'rerun'; + prevRunnable = item; + const task = createTask(item.runnable); + return await vscode.tasks.executeTask(task); + }; +} + +export function runSingle(ctx: Ctx): Cmd { + return async (runnable: ra.Runnable) => { + const editor = ctx.activeRustEditor; + if (!editor) return; + + const task = createTask(runnable); + task.group = vscode.TaskGroup.Build; + task.presentationOptions = { + reveal: vscode.TaskRevealKind.Always, + panel: vscode.TaskPanelKind.Dedicated, + clear: true, + }; + + return vscode.tasks.executeTask(task); + }; +} + +export function debug(ctx: Ctx): Cmd { + let prevDebuggee: RunnableQuickPick | undefined; + + return async () => { + const item = await selectRunnable(ctx, prevDebuggee, true); + if (!item) return; + + item.detail = 'restart'; + prevDebuggee = item; + return await startDebugSession(ctx, item.runnable); + }; +} + +export function debugSingle(ctx: Ctx): Cmd { + return async (config: ra.Runnable) => { + await startDebugSession(ctx, config); + }; +} + +export function newDebugConfig(ctx: Ctx): Cmd { + return async () => { + const item = await selectRunnable(ctx, undefined, true, false); + if (!item) return; + + await makeDebugConfig(ctx, item.runnable); + }; +} diff --git a/editors/code/src/debug.ts b/editors/code/src/debug.ts index bdec5b7357fa..1e421d407ba6 100644 --- a/editors/code/src/debug.ts +++ b/editors/code/src/debug.ts @@ -9,40 +9,53 @@ import { Ctx } from "./ctx"; const debugOutput = vscode.window.createOutputChannel("Debug"); type DebugConfigProvider = (config: ra.Runnable, executable: string, sourceFileMap?: Record) => vscode.DebugConfiguration; -function getLldbDebugConfig(config: ra.Runnable, executable: string, sourceFileMap?: Record): vscode.DebugConfiguration { - return { - type: "lldb", - request: "launch", - name: config.label, - program: executable, - args: config.extraArgs, - cwd: config.cwd, - sourceMap: sourceFileMap, - sourceLanguages: ["rust"] - }; +export async function makeDebugConfig(ctx: Ctx, runnable: ra.Runnable): Promise { + const scope = ctx.activeRustEditor?.document.uri; + if (!scope) return; + + const debugConfig = await getDebugConfiguration(ctx, runnable); + if (!debugConfig) return; + + const wsLaunchSection = vscode.workspace.getConfiguration("launch", scope); + const configurations = wsLaunchSection.get("configurations") || []; + + const index = configurations.findIndex(c => c.name === debugConfig.name); + if (index !== -1) { + const answer = await vscode.window.showErrorMessage(`Launch configuration '${debugConfig.name}' already exists!`, 'Cancel', 'Update'); + if (answer === "Cancel") return; + + configurations[index] = debugConfig; + } else { + configurations.push(debugConfig); + } + + await wsLaunchSection.update("configurations", configurations); } -function getCppvsDebugConfig(config: ra.Runnable, executable: string, sourceFileMap?: Record): vscode.DebugConfiguration { - return { - type: (os.platform() === "win32") ? "cppvsdbg" : "cppdbg", - request: "launch", - name: config.label, - program: executable, - args: config.extraArgs, - cwd: config.cwd, - sourceFileMap: sourceFileMap, - }; +export async function startDebugSession(ctx: Ctx, runnable: ra.Runnable): Promise { + let debugConfig: vscode.DebugConfiguration | undefined = undefined; + let message = ""; + + const wsLaunchSection = vscode.workspace.getConfiguration("launch"); + const configurations = wsLaunchSection.get("configurations") || []; + + const index = configurations.findIndex(c => c.name === runnable.label); + if (-1 !== index) { + debugConfig = configurations[index]; + message = " (from launch.json)"; + debugOutput.clear(); + } else { + debugConfig = await getDebugConfiguration(ctx, runnable); + } + + if (!debugConfig) return false; + + debugOutput.appendLine(`Launching debug configuration${message}:`); + debugOutput.appendLine(JSON.stringify(debugConfig, null, 2)); + return vscode.debug.startDebugging(undefined, debugConfig); } -async function getDebugExecutable(config: ra.Runnable): Promise { - const cargo = new Cargo(config.cwd || '.', debugOutput); - const executable = await cargo.executableFromArgs(config.args); - - // if we are here, there were no compilation errors. - return executable; -} - -export async function getDebugConfiguration(ctx: Ctx, config: ra.Runnable): Promise { +async function getDebugConfiguration(ctx: Ctx, runnable: ra.Runnable): Promise { const editor = ctx.activeRustEditor; if (!editor) return; @@ -78,8 +91,8 @@ export async function getDebugConfiguration(ctx: Ctx, config: ra.Runnable): Prom return path.normalize(p).replace(wsFolder, '${workspaceRoot}'); } - const executable = await getDebugExecutable(config); - const debugConfig = knownEngines[debugEngine.id](config, simplifyPath(executable), debugOptions.sourceFileMap); + const executable = await getDebugExecutable(runnable); + const debugConfig = knownEngines[debugEngine.id](runnable, simplifyPath(executable), debugOptions.sourceFileMap); if (debugConfig.type in debugOptions.engineSettings) { const settingsMap = (debugOptions.engineSettings as any)[debugConfig.type]; for (var key in settingsMap) { @@ -100,25 +113,35 @@ export async function getDebugConfiguration(ctx: Ctx, config: ra.Runnable): Prom return debugConfig; } -export async function startDebugSession(ctx: Ctx, config: ra.Runnable): Promise { - let debugConfig: vscode.DebugConfiguration | undefined = undefined; - let message = ""; +async function getDebugExecutable(runnable: ra.Runnable): Promise { + const cargo = new Cargo(runnable.cwd || '.', debugOutput); + const executable = await cargo.executableFromArgs(runnable.args); - const wsLaunchSection = vscode.workspace.getConfiguration("launch"); - const configurations = wsLaunchSection.get("configurations") || []; - - const index = configurations.findIndex(c => c.name === config.label); - if (-1 !== index) { - debugConfig = configurations[index]; - message = " (from launch.json)"; - debugOutput.clear(); - } else { - debugConfig = await getDebugConfiguration(ctx, config); - } - - if (!debugConfig) return false; - - debugOutput.appendLine(`Launching debug configuration${message}:`); - debugOutput.appendLine(JSON.stringify(debugConfig, null, 2)); - return vscode.debug.startDebugging(undefined, debugConfig); + // if we are here, there were no compilation errors. + return executable; +} + +function getLldbDebugConfig(runnable: ra.Runnable, executable: string, sourceFileMap?: Record): vscode.DebugConfiguration { + return { + type: "lldb", + request: "launch", + name: runnable.label, + program: executable, + args: runnable.extraArgs, + cwd: runnable.cwd, + sourceMap: sourceFileMap, + sourceLanguages: ["rust"] + }; +} + +function getCppvsDebugConfig(runnable: ra.Runnable, executable: string, sourceFileMap?: Record): vscode.DebugConfiguration { + return { + type: (os.platform() === "win32") ? "cppvsdbg" : "cppdbg", + request: "launch", + name: runnable.label, + program: executable, + args: runnable.extraArgs, + cwd: runnable.cwd, + sourceFileMap: sourceFileMap, + }; } diff --git a/editors/code/src/run.ts b/editors/code/src/run.ts index 113354bab2ee..5fc4f8e41b0c 100644 --- a/editors/code/src/run.ts +++ b/editors/code/src/run.ts @@ -3,8 +3,8 @@ import * as lc from 'vscode-languageclient'; import * as ra from './lsp_ext'; import * as toolchain from "./toolchain"; -import { Ctx, Cmd } from './ctx'; -import { startDebugSession, getDebugConfiguration } from './debug'; +import { Ctx } from './ctx'; +import { makeDebugConfig } from './debug'; const quickPickButtons = [{ iconPath: new vscode.ThemeIcon("save"), tooltip: "Save as a launch.json configurtation." }]; @@ -65,7 +65,7 @@ export async function selectRunnable(ctx: Ctx, prevRunnable?: RunnableQuickPick, quickPick.onDidHide(() => close()), quickPick.onDidAccept(() => close(quickPick.selectedItems[0])), quickPick.onDidTriggerButton((_button) => { - (async () => await makeDebugConfig(ctx, quickPick.activeItems[0]))(); + (async () => await makeDebugConfig(ctx, quickPick.activeItems[0].runnable))(); close(); }), quickPick.onDidChangeActive((active) => { @@ -84,74 +84,6 @@ export async function selectRunnable(ctx: Ctx, prevRunnable?: RunnableQuickPick, }); } -export function runSingle(ctx: Ctx): Cmd { - return async (runnable: ra.Runnable) => { - const editor = ctx.activeRustEditor; - if (!editor) return; - - const task = createTask(runnable); - task.group = vscode.TaskGroup.Build; - task.presentationOptions = { - reveal: vscode.TaskRevealKind.Always, - panel: vscode.TaskPanelKind.Dedicated, - clear: true, - }; - - return vscode.tasks.executeTask(task); - }; -} - -export function debug(ctx: Ctx): Cmd { - let prevDebuggee: RunnableQuickPick | undefined; - - return async () => { - const item = await selectRunnable(ctx, prevDebuggee, true); - if (!item) return; - - item.detail = 'restart'; - prevDebuggee = item; - return await startDebugSession(ctx, item.runnable); - }; -} - -export function debugSingle(ctx: Ctx): Cmd { - return async (config: ra.Runnable) => { - await startDebugSession(ctx, config); - }; -} - -async function makeDebugConfig(ctx: Ctx, item: RunnableQuickPick): Promise { - const scope = ctx.activeRustEditor?.document.uri; - if (!scope) return; - - const debugConfig = await getDebugConfiguration(ctx, item.runnable); - if (!debugConfig) return; - - const wsLaunchSection = vscode.workspace.getConfiguration("launch", scope); - const configurations = wsLaunchSection.get("configurations") || []; - - const index = configurations.findIndex(c => c.name === debugConfig.name); - if (index !== -1) { - const answer = await vscode.window.showErrorMessage(`Launch configuration '${debugConfig.name}' already exists!`, 'Cancel', 'Update'); - if (answer === "Cancel") return; - - configurations[index] = debugConfig; - } else { - configurations.push(debugConfig); - } - - await wsLaunchSection.update("configurations", configurations); -} - -export function newDebugConfig(ctx: Ctx): Cmd { - return async () => { - const item = await selectRunnable(ctx, undefined, true, false); - if (!item) return; - - await makeDebugConfig(ctx, item); - }; -} - export class RunnableQuickPick implements vscode.QuickPickItem { public label: string; public description?: string | undefined; From 31f282636bb1b1d701d41f7c7fedb11a5511cabd Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Tue, 2 Jun 2020 16:30:26 +0200 Subject: [PATCH 021/172] Minor --- crates/rust-analyzer/src/cargo_target_spec.rs | 85 ++++++++++++- .../rust-analyzer/src/main_loop/handlers.rs | 120 +----------------- crates/rust-analyzer/src/to_proto.rs | 44 ++++++- 3 files changed, 125 insertions(+), 124 deletions(-) diff --git a/crates/rust-analyzer/src/cargo_target_spec.rs b/crates/rust-analyzer/src/cargo_target_spec.rs index 441fb61df006..008518a089ff 100644 --- a/crates/rust-analyzer/src/cargo_target_spec.rs +++ b/crates/rust-analyzer/src/cargo_target_spec.rs @@ -1,10 +1,10 @@ //! See `CargoTargetSpec` +use ra_cfg::CfgExpr; use ra_ide::{FileId, RunnableKind, TestId}; use ra_project_model::{self, ProjectWorkspace, TargetKind}; use crate::{world::WorldSnapshot, Result}; -use ra_syntax::SmolStr; /// Abstract representation of Cargo target. /// @@ -21,7 +21,7 @@ impl CargoTargetSpec { pub(crate) fn runnable_args( spec: Option, kind: &RunnableKind, - features_needed: &Vec, + cfgs: &[CfgExpr], ) -> Result<(Vec, Vec)> { let mut args = Vec::new(); let mut extra_args = Vec::new(); @@ -76,10 +76,14 @@ impl CargoTargetSpec { } } - features_needed.iter().for_each(|feature| { + let mut features = Vec::new(); + for cfg in cfgs { + required_features(cfg, &mut features); + } + for feature in features { args.push("--features".to_string()); - args.push(feature.to_string()); - }); + args.push(feature); + } Ok((args, extra_args)) } @@ -140,3 +144,74 @@ impl CargoTargetSpec { } } } + +/// Fill minimal features needed +fn required_features(cfg_expr: &CfgExpr, features: &mut Vec) { + match cfg_expr { + CfgExpr::KeyValue { key, value } if key == "feature" => features.push(value.to_string()), + CfgExpr::All(preds) => { + preds.iter().for_each(|cfg| required_features(cfg, features)); + } + CfgExpr::Any(preds) => { + for cfg in preds { + let len_features = features.len(); + required_features(cfg, features); + if len_features != features.len() { + break; + } + } + } + _ => {} + } +} + +#[cfg(test)] +mod tests { + use super::*; + + use mbe::{ast_to_token_tree, TokenMap}; + use ra_cfg::parse_cfg; + use ra_syntax::{ + ast::{self, AstNode}, + SmolStr, + }; + + fn get_token_tree_generated(input: &str) -> (tt::Subtree, TokenMap) { + let source_file = ast::SourceFile::parse(input).ok().unwrap(); + let tt = source_file.syntax().descendants().find_map(ast::TokenTree::cast).unwrap(); + ast_to_token_tree(&tt).unwrap() + } + + #[test] + fn test_cfg_expr_minimal_features_needed() { + let (subtree, _) = get_token_tree_generated(r#"#![cfg(feature = "baz")]"#); + let cfg_expr = parse_cfg(&subtree); + let mut min_features = vec![]; + required_features(&cfg_expr, &mut min_features); + + assert_eq!(min_features, vec![SmolStr::new("baz")]); + + let (subtree, _) = + get_token_tree_generated(r#"#![cfg(all(feature = "baz", feature = "foo"))]"#); + let cfg_expr = parse_cfg(&subtree); + + let mut min_features = vec![]; + required_features(&cfg_expr, &mut min_features); + assert_eq!(min_features, vec![SmolStr::new("baz"), SmolStr::new("foo")]); + + let (subtree, _) = + get_token_tree_generated(r#"#![cfg(any(feature = "baz", feature = "foo", unix))]"#); + let cfg_expr = parse_cfg(&subtree); + + let mut min_features = vec![]; + required_features(&cfg_expr, &mut min_features); + assert_eq!(min_features, vec![SmolStr::new("baz")]); + + let (subtree, _) = get_token_tree_generated(r#"#![cfg(foo)]"#); + let cfg_expr = parse_cfg(&subtree); + + let mut min_features = vec![]; + required_features(&cfg_expr, &mut min_features); + assert!(min_features.is_empty()); + } +} diff --git a/crates/rust-analyzer/src/main_loop/handlers.rs b/crates/rust-analyzer/src/main_loop/handlers.rs index bc7c7f1ef5d5..410c654abeba 100644 --- a/crates/rust-analyzer/src/main_loop/handlers.rs +++ b/crates/rust-analyzer/src/main_loop/handlers.rs @@ -17,14 +17,12 @@ use lsp_types::{ SemanticTokensParams, SemanticTokensRangeParams, SemanticTokensRangeResult, SemanticTokensResult, SymbolInformation, TextDocumentIdentifier, Url, WorkspaceEdit, }; -use ra_cfg::CfgExpr; use ra_ide::{ - FileId, FilePosition, FileRange, Query, RangeInfo, Runnable, RunnableKind, SearchScope, - TextEdit, + FileId, FilePosition, FileRange, Query, RangeInfo, RunnableKind, SearchScope, TextEdit, }; use ra_prof::profile; use ra_project_model::TargetKind; -use ra_syntax::{AstNode, SmolStr, SyntaxKind, TextRange, TextSize}; +use ra_syntax::{AstNode, SyntaxKind, TextRange, TextSize}; use rustc_hash::FxHashMap; use serde::{Deserialize, Serialize}; use serde_json::to_value; @@ -416,7 +414,7 @@ pub fn handle_runnables( } } } - res.push(to_lsp_runnable(&world, file_id, runnable)?); + res.push(to_proto::runnable(&world, file_id, runnable)?); } // Add `cargo check` and `cargo test` for the whole package @@ -784,7 +782,7 @@ pub fn handle_code_lens( } }; - let mut r = to_lsp_runnable(&world, file_id, runnable)?; + let mut r = to_proto::runnable(&world, file_id, runnable)?; if world.config.lens.run { let lens = CodeLens { range: r.range, @@ -959,65 +957,6 @@ pub fn publish_diagnostics(world: &WorldSnapshot, file_id: FileId) -> Result Result { - let spec = CargoTargetSpec::for_file(world, file_id)?; - let target = spec.as_ref().map(|s| s.target.clone()); - let mut features_needed = vec![]; - for cfg_expr in &runnable.cfg_exprs { - collect_minimal_features_needed(cfg_expr, &mut features_needed); - } - let (args, extra_args) = - CargoTargetSpec::runnable_args(spec, &runnable.kind, &features_needed)?; - let line_index = world.analysis().file_line_index(file_id)?; - let label = match &runnable.kind { - RunnableKind::Test { test_id, .. } => format!("test {}", test_id), - RunnableKind::TestMod { path } => format!("test-mod {}", path), - RunnableKind::Bench { test_id } => format!("bench {}", test_id), - RunnableKind::DocTest { test_id, .. } => format!("doctest {}", test_id), - RunnableKind::Bin => { - target.map_or_else(|| "run binary".to_string(), |t| format!("run {}", t)) - } - }; - - Ok(lsp_ext::Runnable { - range: to_proto::range(&line_index, runnable.range), - label, - kind: lsp_ext::RunnableKind::Cargo, - args, - extra_args, - env: { - let mut m = FxHashMap::default(); - m.insert("RUST_BACKTRACE".to_string(), "short".to_string()); - m - }, - cwd: world.workspace_root_for(file_id).map(|root| root.to_owned()), - }) -} - -/// Fill minimal features needed -fn collect_minimal_features_needed(cfg_expr: &CfgExpr, features: &mut Vec) { - match cfg_expr { - CfgExpr::KeyValue { key, value } if key == "feature" => features.push(value.clone()), - CfgExpr::All(preds) => { - preds.iter().for_each(|cfg| collect_minimal_features_needed(cfg, features)); - } - CfgExpr::Any(preds) => { - for cfg in preds { - let len_features = features.len(); - collect_minimal_features_needed(cfg, features); - if len_features != features.len() { - break; - } - } - } - _ => {} - } -} - pub fn handle_inlay_hints( world: WorldSnapshot, params: InlayHintsParams, @@ -1154,54 +1093,3 @@ pub fn handle_semantic_tokens_range( let semantic_tokens = to_proto::semantic_tokens(&text, &line_index, highlights); Ok(Some(semantic_tokens.into())) } - -#[cfg(test)] -mod tests { - use super::*; - - use mbe::{ast_to_token_tree, TokenMap}; - use ra_cfg::parse_cfg; - use ra_syntax::{ - ast::{self, AstNode}, - SmolStr, - }; - - fn get_token_tree_generated(input: &str) -> (tt::Subtree, TokenMap) { - let source_file = ast::SourceFile::parse(input).ok().unwrap(); - let tt = source_file.syntax().descendants().find_map(ast::TokenTree::cast).unwrap(); - ast_to_token_tree(&tt).unwrap() - } - - #[test] - fn test_cfg_expr_minimal_features_needed() { - let (subtree, _) = get_token_tree_generated(r#"#![cfg(feature = "baz")]"#); - let cfg_expr = parse_cfg(&subtree); - let mut min_features = vec![]; - collect_minimal_features_needed(&cfg_expr, &mut min_features); - - assert_eq!(min_features, vec![SmolStr::new("baz")]); - - let (subtree, _) = - get_token_tree_generated(r#"#![cfg(all(feature = "baz", feature = "foo"))]"#); - let cfg_expr = parse_cfg(&subtree); - - let mut min_features = vec![]; - collect_minimal_features_needed(&cfg_expr, &mut min_features); - assert_eq!(min_features, vec![SmolStr::new("baz"), SmolStr::new("foo")]); - - let (subtree, _) = - get_token_tree_generated(r#"#![cfg(any(feature = "baz", feature = "foo", unix))]"#); - let cfg_expr = parse_cfg(&subtree); - - let mut min_features = vec![]; - collect_minimal_features_needed(&cfg_expr, &mut min_features); - assert_eq!(min_features, vec![SmolStr::new("baz")]); - - let (subtree, _) = get_token_tree_generated(r#"#![cfg(foo)]"#); - let cfg_expr = parse_cfg(&subtree); - - let mut min_features = vec![]; - collect_minimal_features_needed(&cfg_expr, &mut min_features); - assert!(min_features.is_empty()); - } -} diff --git a/crates/rust-analyzer/src/to_proto.rs b/crates/rust-analyzer/src/to_proto.rs index 2fbbb4e632d9..66144fe24114 100644 --- a/crates/rust-analyzer/src/to_proto.rs +++ b/crates/rust-analyzer/src/to_proto.rs @@ -3,13 +3,16 @@ use ra_db::{FileId, FileRange}; use ra_ide::{ Assist, CompletionItem, CompletionItemKind, Documentation, FileSystemEdit, Fold, FoldKind, FunctionSignature, Highlight, HighlightModifier, HighlightTag, HighlightedRange, Indel, - InlayHint, InlayKind, InsertTextFormat, LineIndex, NavigationTarget, ReferenceAccess, Severity, - SourceChange, SourceFileEdit, TextEdit, + InlayHint, InlayKind, InsertTextFormat, LineIndex, NavigationTarget, ReferenceAccess, Runnable, + RunnableKind, Severity, SourceChange, SourceFileEdit, TextEdit, }; use ra_syntax::{SyntaxKind, TextRange, TextSize}; use ra_vfs::LineEndings; +use rustc_hash::FxHashMap; -use crate::{lsp_ext, semantic_tokens, world::WorldSnapshot, Result}; +use crate::{ + cargo_target_spec::CargoTargetSpec, lsp_ext, semantic_tokens, world::WorldSnapshot, Result, +}; pub(crate) fn position(line_index: &LineIndex, offset: TextSize) -> lsp_types::Position { let line_col = line_index.line_col(offset); @@ -627,3 +630,38 @@ pub(crate) fn code_action(world: &WorldSnapshot, assist: Assist) -> Result Result { + let spec = CargoTargetSpec::for_file(world, file_id)?; + let target = spec.as_ref().map(|s| s.target.clone()); + let (args, extra_args) = + CargoTargetSpec::runnable_args(spec, &runnable.kind, &runnable.cfg_exprs)?; + let line_index = world.analysis().file_line_index(file_id)?; + let label = match &runnable.kind { + RunnableKind::Test { test_id, .. } => format!("test {}", test_id), + RunnableKind::TestMod { path } => format!("test-mod {}", path), + RunnableKind::Bench { test_id } => format!("bench {}", test_id), + RunnableKind::DocTest { test_id, .. } => format!("doctest {}", test_id), + RunnableKind::Bin => { + target.map_or_else(|| "run binary".to_string(), |t| format!("run {}", t)) + } + }; + + Ok(lsp_ext::Runnable { + range: range(&line_index, runnable.range), + label, + kind: lsp_ext::RunnableKind::Cargo, + args, + extra_args, + env: { + let mut m = FxHashMap::default(); + m.insert("RUST_BACKTRACE".to_string(), "short".to_string()); + m + }, + cwd: world.workspace_root_for(file_id).map(|root| root.to_owned()), + }) +} From 03039821195c9d9c4bbc1e4cbddb6378c43a6c52 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Tue, 2 Jun 2020 17:22:23 +0200 Subject: [PATCH 022/172] New runnables API --- .../ra_ide/src/display/navigation_target.rs | 66 +++++++------------ crates/ra_ide/src/runnables.rs | 25 +++---- crates/rust-analyzer/src/lsp_ext.rs | 28 ++++---- .../rust-analyzer/src/main_loop/handlers.rs | 41 ++++++------ crates/rust-analyzer/src/to_proto.rs | 18 ++--- editors/code/src/debug.ts | 12 ++-- editors/code/src/lsp_ext.ts | 15 ++--- editors/code/src/run.ts | 21 ++++-- 8 files changed, 108 insertions(+), 118 deletions(-) diff --git a/crates/ra_ide/src/display/navigation_target.rs b/crates/ra_ide/src/display/navigation_target.rs index 5da28edd2414..c7bb1e69f8a5 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 { let name = module.name(db).map(|it| it.to_string().into()).unwrap_or_default(); if let Some(src) = module.declaration_source(db) { let frange = original_range(db, src.as_ref().map(|it| it.syntax())); - return NavigationTarget::from_syntax( + let mut res = NavigationTarget::from_syntax( frange.file_id, name, None, frange.range, src.value.syntax().kind(), - src.value.doc_comment_text(), - src.value.short_label(), ); + res.docs = src.value.doc_comment_text(); + res.description = src.value.short_label(); + return res; } module.to_nav(db) } @@ -130,11 +131,9 @@ impl NavigationTarget { } /// Allows `NavigationTarget` to be created from a `NameOwner` - fn from_named( + pub(crate) fn from_named( db: &RootDatabase, node: InFile<&dyn ast::NameOwner>, - docs: Option, - description: Option, ) -> NavigationTarget { //FIXME: use `_` instead of empty string let name = node.value.name().map(|it| it.text().clone()).unwrap_or_default(); @@ -148,8 +147,6 @@ impl NavigationTarget { focus_range, frange.range, node.value.syntax().kind(), - docs, - description, ) } @@ -159,8 +156,6 @@ impl NavigationTarget { focus_range: Option, full_range: TextRange, kind: SyntaxKind, - docs: Option, - description: Option, ) -> NavigationTarget { NavigationTarget { file_id, @@ -169,8 +164,8 @@ impl NavigationTarget { full_range, focus_range, container_name: None, - description, - docs, + description: None, + docs: None, } } } @@ -238,12 +233,11 @@ where { fn to_nav(&self, db: &RootDatabase) -> NavigationTarget { let src = self.source(db); - NavigationTarget::from_named( - db, - src.as_ref().map(|it| it as &dyn ast::NameOwner), - src.value.doc_comment_text(), - src.value.short_label(), - ) + let mut res = + NavigationTarget::from_named(db, src.as_ref().map(|it| it as &dyn ast::NameOwner)); + res.docs = src.value.doc_comment_text(); + res.description = src.value.short_label(); + res } } @@ -258,15 +252,7 @@ impl ToNav for hir::Module { } }; let frange = original_range(db, src.with_value(syntax)); - NavigationTarget::from_syntax( - frange.file_id, - name, - focus, - frange.range, - syntax.kind(), - None, - None, - ) + NavigationTarget::from_syntax(frange.file_id, name, focus, frange.range, syntax.kind()) } } @@ -285,8 +271,6 @@ impl ToNav for hir::ImplDef { None, frange.range, src.value.syntax().kind(), - None, - None, ) } } @@ -296,12 +280,12 @@ impl ToNav for hir::Field { let src = self.source(db); match &src.value { - FieldSource::Named(it) => NavigationTarget::from_named( - db, - src.with_value(it), - it.doc_comment_text(), - it.short_label(), - ), + FieldSource::Named(it) => { + let mut res = NavigationTarget::from_named(db, src.with_value(it)); + res.docs = it.doc_comment_text(); + res.description = it.short_label(); + res + } FieldSource::Pos(it) => { let frange = original_range(db, src.with_value(it.syntax())); NavigationTarget::from_syntax( @@ -310,8 +294,6 @@ impl ToNav for hir::Field { None, frange.range, it.syntax().kind(), - None, - None, ) } } @@ -322,12 +304,10 @@ impl ToNav for hir::MacroDef { fn to_nav(&self, db: &RootDatabase) -> NavigationTarget { let src = self.source(db); log::debug!("nav target {:#?}", src.value.syntax()); - NavigationTarget::from_named( - db, - src.as_ref().map(|it| it as &dyn ast::NameOwner), - src.value.doc_comment_text(), - None, - ) + let mut res = + NavigationTarget::from_named(db, src.as_ref().map(|it| it as &dyn ast::NameOwner)); + res.docs = src.value.doc_comment_text(); + res } } diff --git a/crates/ra_ide/src/runnables.rs b/crates/ra_ide/src/runnables.rs index 286d45eee952..9239ca61bd92 100644 --- a/crates/ra_ide/src/runnables.rs +++ b/crates/ra_ide/src/runnables.rs @@ -1,19 +1,19 @@ +use std::fmt; + use hir::{AsAssocItem, Attrs, HirFileId, InFile, Semantics}; use itertools::Itertools; +use ra_cfg::CfgExpr; use ra_ide_db::RootDatabase; use ra_syntax::{ - ast::{self, AstNode, AttrsOwner, ModuleItemOwner, NameOwner}, - match_ast, SyntaxNode, TextRange, + ast::{self, AstNode, AttrsOwner, DocCommentsOwner, ModuleItemOwner, NameOwner}, + match_ast, SyntaxNode, }; -use crate::FileId; -use ast::DocCommentsOwner; -use ra_cfg::CfgExpr; -use std::fmt::Display; +use crate::{display::ToNav, FileId, NavigationTarget}; #[derive(Debug)] pub struct Runnable { - pub range: TextRange, + pub nav: NavigationTarget, pub kind: RunnableKind, pub cfg_exprs: Vec, } @@ -24,8 +24,8 @@ pub enum TestId { Path(String), } -impl Display for TestId { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { +impl fmt::Display for TestId { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { TestId::Name(name) => write!(f, "{}", name), TestId::Path(path) => write!(f, "{}", path), @@ -131,7 +131,8 @@ fn runnable_fn( let cfg_exprs = attrs.by_key("cfg").tt_values().map(|subtree| ra_cfg::parse_cfg(subtree)).collect(); - Some(Runnable { range: fn_def.syntax().text_range(), kind, cfg_exprs }) + let nav = NavigationTarget::from_named(sema.db, InFile::new(file_id.into(), &fn_def)); + Some(Runnable { nav, kind, cfg_exprs }) } #[derive(Debug)] @@ -183,7 +184,6 @@ fn runnable_mod( if !has_test_function { return None; } - let range = module.syntax().text_range(); let module_def = sema.to_def(&module)?; let path = module_def @@ -197,7 +197,8 @@ fn runnable_mod( let cfg_exprs = attrs.by_key("cfg").tt_values().map(|subtree| ra_cfg::parse_cfg(subtree)).collect(); - Some(Runnable { range, kind: RunnableKind::TestMod { path }, cfg_exprs }) + let nav = module_def.to_nav(sema.db); + Some(Runnable { nav, kind: RunnableKind::TestMod { path }, cfg_exprs }) } #[cfg(test)] diff --git a/crates/rust-analyzer/src/lsp_ext.rs b/crates/rust-analyzer/src/lsp_ext.rs index 173c23b9e5d0..9381f75d390c 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}; use lsp_types::request::Request; use lsp_types::{Position, Range, TextDocumentIdentifier}; -use rustc_hash::FxHashMap; use serde::{Deserialize, Serialize}; pub enum AnalyzerStatus {} @@ -121,25 +120,30 @@ pub struct RunnablesParams { pub position: Option, } -// Must strictly correspond to the executable name +#[derive(Deserialize, Serialize, Debug)] +#[serde(rename_all = "camelCase")] +pub struct Runnable { + pub label: String, + #[serde(skip_serializing_if = "Option::is_none")] + pub location: Option, + pub kind: RunnableKind, + pub args: CargoRunnable, +} + #[derive(Serialize, Deserialize, Debug)] #[serde(rename_all = "lowercase")] pub enum RunnableKind { Cargo, - Rustc, - Rustup, } #[derive(Deserialize, Serialize, Debug)] #[serde(rename_all = "camelCase")] -pub struct Runnable { - pub range: Range, - pub label: String, - pub kind: RunnableKind, - pub args: Vec, - pub extra_args: Vec, - pub env: FxHashMap, - pub cwd: Option, +pub struct CargoRunnable { + pub workspace_root: Option, + // command, --package and --lib stuff + pub cargo_args: Vec, + // stuff after -- + pub executable_args: Vec, } pub enum InlayHints {} diff --git a/crates/rust-analyzer/src/main_loop/handlers.rs b/crates/rust-analyzer/src/main_loop/handlers.rs index 410c654abeba..7fd6917649a3 100644 --- a/crates/rust-analyzer/src/main_loop/handlers.rs +++ b/crates/rust-analyzer/src/main_loop/handlers.rs @@ -23,7 +23,6 @@ use ra_ide::{ use ra_prof::profile; use ra_project_model::TargetKind; use ra_syntax::{AstNode, SyntaxKind, TextRange, TextSize}; -use rustc_hash::FxHashMap; use serde::{Deserialize, Serialize}; use serde_json::to_value; use stdx::format_to; @@ -401,7 +400,7 @@ pub fn handle_runnables( let cargo_spec = CargoTargetSpec::for_file(&world, file_id)?; for runnable in world.analysis().runnables(file_id)? { if let Some(offset) = offset { - if !runnable.range.contains_inclusive(offset) { + if !runnable.nav.full_range().contains_inclusive(offset) { continue; } } @@ -422,25 +421,31 @@ pub fn handle_runnables( Some(spec) => { for &cmd in ["check", "test"].iter() { res.push(lsp_ext::Runnable { - range: Default::default(), label: format!("cargo {} -p {}", cmd, spec.package), + location: None, kind: lsp_ext::RunnableKind::Cargo, - args: vec![cmd.to_string(), "--package".to_string(), spec.package.clone()], - extra_args: Vec::new(), - env: FxHashMap::default(), - cwd: workspace_root.map(|root| root.to_owned()), + args: lsp_ext::CargoRunnable { + workspace_root: workspace_root.map(|root| root.to_owned()), + cargo_args: vec![ + cmd.to_string(), + "--package".to_string(), + spec.package.clone(), + ], + executable_args: Vec::new(), + }, }) } } None => { res.push(lsp_ext::Runnable { - range: Default::default(), label: "cargo check --workspace".to_string(), + location: None, kind: lsp_ext::RunnableKind::Cargo, - args: vec!["check".to_string(), "--workspace".to_string()], - extra_args: Vec::new(), - env: FxHashMap::default(), - cwd: workspace_root.map(|root| root.to_owned()), + args: lsp_ext::CargoRunnable { + workspace_root: workspace_root.map(|root| root.to_owned()), + cargo_args: vec!["check".to_string(), "--workspace".to_string()], + executable_args: Vec::new(), + }, }); } } @@ -782,10 +787,11 @@ pub fn handle_code_lens( } }; - let mut r = to_proto::runnable(&world, file_id, runnable)?; + let range = to_proto::range(&line_index, runnable.nav.range()); + let r = to_proto::runnable(&world, file_id, runnable)?; if world.config.lens.run { let lens = CodeLens { - range: r.range, + range, command: Some(Command { title: run_title.to_string(), command: "rust-analyzer.runSingle".into(), @@ -797,13 +803,8 @@ pub fn handle_code_lens( } if debugee && world.config.lens.debug { - if r.args[0] == "run" { - r.args[0] = "build".into(); - } else { - r.args.push("--no-run".into()); - } let debug_lens = CodeLens { - range: r.range, + range, command: Some(Command { title: "Debug".into(), command: "rust-analyzer.debugSingle".into(), diff --git a/crates/rust-analyzer/src/to_proto.rs b/crates/rust-analyzer/src/to_proto.rs index 66144fe24114..85304aa8778e 100644 --- a/crates/rust-analyzer/src/to_proto.rs +++ b/crates/rust-analyzer/src/to_proto.rs @@ -8,7 +8,6 @@ use ra_ide::{ }; use ra_syntax::{SyntaxKind, TextRange, TextSize}; use ra_vfs::LineEndings; -use rustc_hash::FxHashMap; use crate::{ cargo_target_spec::CargoTargetSpec, lsp_ext, semantic_tokens, world::WorldSnapshot, Result, @@ -638,9 +637,8 @@ pub(crate) fn runnable( ) -> Result { let spec = CargoTargetSpec::for_file(world, file_id)?; let target = spec.as_ref().map(|s| s.target.clone()); - let (args, extra_args) = + let (cargo_args, executable_args) = CargoTargetSpec::runnable_args(spec, &runnable.kind, &runnable.cfg_exprs)?; - let line_index = world.analysis().file_line_index(file_id)?; let label = match &runnable.kind { RunnableKind::Test { test_id, .. } => format!("test {}", test_id), RunnableKind::TestMod { path } => format!("test-mod {}", path), @@ -650,18 +648,16 @@ pub(crate) fn runnable( target.map_or_else(|| "run binary".to_string(), |t| format!("run {}", t)) } }; + let location = location_link(world, None, runnable.nav)?; Ok(lsp_ext::Runnable { - range: range(&line_index, runnable.range), label, + location: Some(location), kind: lsp_ext::RunnableKind::Cargo, - args, - extra_args, - env: { - let mut m = FxHashMap::default(); - m.insert("RUST_BACKTRACE".to_string(), "short".to_string()); - m + args: lsp_ext::CargoRunnable { + workspace_root: world.workspace_root_for(file_id).map(|root| root.to_owned()), + cargo_args, + executable_args, }, - cwd: world.workspace_root_for(file_id).map(|root| root.to_owned()), }) } diff --git a/editors/code/src/debug.ts b/editors/code/src/debug.ts index 1e421d407ba6..a0c9b3ab2e64 100644 --- a/editors/code/src/debug.ts +++ b/editors/code/src/debug.ts @@ -114,8 +114,8 @@ async function getDebugConfiguration(ctx: Ctx, runnable: ra.Runnable): Promise { - const cargo = new Cargo(runnable.cwd || '.', debugOutput); - const executable = await cargo.executableFromArgs(runnable.args); + const cargo = new Cargo(runnable.args.workspaceRoot || '.', debugOutput); + const executable = await cargo.executableFromArgs(runnable.args.cargoArgs); // if we are here, there were no compilation errors. return executable; @@ -127,8 +127,8 @@ function getLldbDebugConfig(runnable: ra.Runnable, executable: string, sourceFil request: "launch", name: runnable.label, program: executable, - args: runnable.extraArgs, - cwd: runnable.cwd, + args: runnable.args.executableArgs, + cwd: runnable.args.workspaceRoot, sourceMap: sourceFileMap, sourceLanguages: ["rust"] }; @@ -140,8 +140,8 @@ function getCppvsDebugConfig(runnable: ra.Runnable, executable: string, sourceFi request: "launch", name: runnable.label, program: executable, - args: runnable.extraArgs, - cwd: runnable.cwd, + args: runnable.args.executableArgs, + cwd: runnable.args.workspaceRoot, sourceFileMap: sourceFileMap, }; } diff --git a/editors/code/src/lsp_ext.ts b/editors/code/src/lsp_ext.ts index 3e0b606997fb..73d5736787d7 100644 --- a/editors/code/src/lsp_ext.ts +++ b/editors/code/src/lsp_ext.ts @@ -46,16 +46,15 @@ export interface RunnablesParams { position: lc.Position | null; } -export type RunnableKind = "cargo" | "rustc" | "rustup"; - export interface Runnable { - range: lc.Range; label: string; - kind: RunnableKind; - args: string[]; - extraArgs: string[]; - env: { [key: string]: string }; - cwd: string | null; + location?: lc.LocationLink; + kind: "cargo"; + args: { + workspaceRoot?: string; + cargoArgs: string[]; + executableArgs: string[]; + }; } export const runnables = new lc.RequestType("rust-analyzer/runnables"); diff --git a/editors/code/src/run.ts b/editors/code/src/run.ts index 5fc4f8e41b0c..5c790741f387 100644 --- a/editors/code/src/run.ts +++ b/editors/code/src/run.ts @@ -103,18 +103,27 @@ interface CargoTaskDefinition extends vscode.TaskDefinition { env?: { [key: string]: string }; } -export function createTask(spec: ra.Runnable): vscode.Task { +export function createTask(runnable: ra.Runnable): vscode.Task { const TASK_SOURCE = 'Rust'; + + let command; + switch (runnable.kind) { + case "cargo": command = toolchain.getPathForExecutable("cargo"); + } + const args = runnable.args.cargoArgs; + if (runnable.args.executableArgs.length > 0) { + args.push('--', ...runnable.args.executableArgs); + } const definition: CargoTaskDefinition = { type: 'cargo', - label: spec.label, - command: toolchain.getPathForExecutable(spec.kind), - args: spec.extraArgs ? [...spec.args, '--', ...spec.extraArgs] : spec.args, - env: Object.assign({}, process.env, spec.env), + label: runnable.label, + command, + args, + env: Object.assign({}, process.env as { [key: string]: string }, { "RUST_BACKTRACE": "short" }), }; const execOption: vscode.ShellExecutionOptions = { - cwd: spec.cwd || '.', + cwd: runnable.args.workspaceRoot || '.', env: definition.env, }; const exec = new vscode.ShellExecution( From a83ab820a4633bac718ee0fd11f06d1b3142be6b Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Tue, 2 Jun 2020 17:34:18 +0200 Subject: [PATCH 023/172] Spec better runnables --- crates/rust-analyzer/src/caps.rs | 3 ++ crates/rust-analyzer/src/lsp_ext.rs | 2 +- docs/dev/lsp-extensions.md | 80 ++++++++++++++++------------- editors/code/src/lsp_ext.ts | 2 +- 4 files changed, 49 insertions(+), 38 deletions(-) diff --git a/crates/rust-analyzer/src/caps.rs b/crates/rust-analyzer/src/caps.rs index 345693524ea5..673795e781e6 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 "ssr": true, "onEnter": true, "parentModule": true, + "runnables": { + "kinds": [ "cargo" ], + }, })), } } diff --git a/crates/rust-analyzer/src/lsp_ext.rs b/crates/rust-analyzer/src/lsp_ext.rs index 9381f75d390c..5fa1eba1c722 100644 --- a/crates/rust-analyzer/src/lsp_ext.rs +++ b/crates/rust-analyzer/src/lsp_ext.rs @@ -110,7 +110,7 @@ pub enum Runnables {} impl Request for Runnables { type Params = RunnablesParams; type Result = Vec; - const METHOD: &'static str = "rust-analyzer/runnables"; + const METHOD: &'static str = "experimental/runnables"; } #[derive(Serialize, Deserialize, Debug)] diff --git a/docs/dev/lsp-extensions.md b/docs/dev/lsp-extensions.md index b7237ee90076..647cf6107565 100644 --- a/docs/dev/lsp-extensions.md +++ b/docs/dev/lsp-extensions.md @@ -311,6 +311,50 @@ Moreover, it would be cool if editors didn't need to implement even basic langua This is how `SelectionRange` request works. * Alternatively, should we perhaps flag certain `SelectionRange`s as being brace pairs? +## Runnables + +**Issue:** https://github.com/microsoft/language-server-protocol/issues/944 + +**Server Capability:** `{ "runnables": { "kinds": string[] } }` + +This request is send from client to server to get the list of things that can be run (tests, binaries, `cargo check -p`). + +**Method:** `experimental/runnables` + +**Request:** + +```typescript +interface RunnablesParams { + textDocument: TextDocumentIdentifier; + /// If null, compute runnables for the whole file. + position?: Position; +} +``` + +**Response:** `Runnable[]` + +```typescript +interface Runnable { + label: string; + /// If this Runnable is associated with a specific function/module, etc, the location of this item + location?: LocationLink; + /// Running things is necessary technology specific, `kind` needs to be advertised via server capabilities, + // the type of `args` is specific to `kind`. The actual running is handled by the client. + kind: string; + args: any; +} +``` + +rust-analyzer supports only one `kind`, `"cargo"`. The `args` for `"cargo"` look like this: + +```typescript +{ + workspaceRoot?: string; + cargoArgs: string[]; + executableArgs: string[]; +} +``` + ## Analyzer Status **Method:** `rust-analyzer/analyzerStatus` @@ -399,39 +443,3 @@ interface InlayHint { label: string, } ``` - -## Runnables - -**Method:** `rust-analyzer/runnables` - -This request is send from client to server to get the list of things that can be run (tests, binaries, `cargo check -p`). -Note that we plan to move this request to `experimental/runnables`, as it is not really Rust-specific, but the current API is not necessary the right one. -Upstream issue: https://github.com/microsoft/language-server-protocol/issues/944 - -**Request:** - -```typescript -interface RunnablesParams { - textDocument: TextDocumentIdentifier; - /// If null, compute runnables for the whole file. - position?: Position; -} -``` - -**Response:** `Runnable[]` - -```typescript -interface Runnable { - /// The range this runnable is applicable for. - range: lc.Range; - /// The label to show in the UI. - label: string; - /// The following fields describe a process to spawn. - kind: "cargo" | "rustc" | "rustup"; - args: string[]; - /// Args for cargo after `--`. - extraArgs: string[]; - env: { [key: string]: string }; - cwd: string | null; -} -``` diff --git a/editors/code/src/lsp_ext.ts b/editors/code/src/lsp_ext.ts index 73d5736787d7..c51acfccb90a 100644 --- a/editors/code/src/lsp_ext.ts +++ b/editors/code/src/lsp_ext.ts @@ -56,7 +56,7 @@ export interface Runnable { executableArgs: string[]; }; } -export const runnables = new lc.RequestType("rust-analyzer/runnables"); +export const runnables = new lc.RequestType("experimental/runnables"); export type InlayHint = InlayHint.TypeHint | InlayHint.ParamHint | InlayHint.ChainingHint; From bc3db7c1ded0db2d3804b5ac3e5c35dd53350228 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Tue, 2 Jun 2020 18:02:58 +0200 Subject: [PATCH 024/172] Fix tests --- crates/ra_ide/src/runnables.rs | 225 ++++++++++++++++-- crates/rust-analyzer/src/lsp_ext.rs | 1 + .../rust-analyzer/tests/heavy_tests/main.rs | 113 ++++----- 3 files changed, 270 insertions(+), 69 deletions(-) diff --git a/crates/ra_ide/src/runnables.rs b/crates/ra_ide/src/runnables.rs index 9239ca61bd92..f32ce0d229b8 100644 --- a/crates/ra_ide/src/runnables.rs +++ b/crates/ra_ide/src/runnables.rs @@ -228,12 +228,38 @@ mod tests { @r###" [ Runnable { - range: 1..21, + nav: NavigationTarget { + file_id: FileId( + 1, + ), + full_range: 1..21, + name: "main", + kind: FN_DEF, + focus_range: Some( + 12..16, + ), + container_name: None, + description: None, + docs: None, + }, kind: Bin, cfg_exprs: [], }, Runnable { - range: 22..46, + nav: NavigationTarget { + file_id: FileId( + 1, + ), + full_range: 22..46, + name: "test_foo", + kind: FN_DEF, + focus_range: Some( + 33..41, + ), + container_name: None, + description: None, + docs: None, + }, kind: Test { test_id: Path( "test_foo", @@ -245,7 +271,20 @@ mod tests { cfg_exprs: [], }, Runnable { - range: 47..81, + nav: NavigationTarget { + file_id: FileId( + 1, + ), + full_range: 47..81, + name: "test_foo", + kind: FN_DEF, + focus_range: Some( + 68..76, + ), + container_name: None, + description: None, + docs: None, + }, kind: Test { test_id: Path( "test_foo", @@ -280,12 +319,38 @@ mod tests { @r###" [ Runnable { - range: 1..21, + nav: NavigationTarget { + file_id: FileId( + 1, + ), + full_range: 1..21, + name: "main", + kind: FN_DEF, + focus_range: Some( + 12..16, + ), + container_name: None, + description: None, + docs: None, + }, kind: Bin, cfg_exprs: [], }, Runnable { - range: 22..64, + nav: NavigationTarget { + file_id: FileId( + 1, + ), + full_range: 22..64, + name: "foo", + kind: FN_DEF, + focus_range: Some( + 56..59, + ), + container_name: None, + description: None, + docs: None, + }, kind: DocTest { test_id: Path( "foo", @@ -320,12 +385,38 @@ mod tests { @r###" [ Runnable { - range: 1..21, + nav: NavigationTarget { + file_id: FileId( + 1, + ), + full_range: 1..21, + name: "main", + kind: FN_DEF, + focus_range: Some( + 12..16, + ), + container_name: None, + description: None, + docs: None, + }, kind: Bin, cfg_exprs: [], }, Runnable { - range: 51..105, + nav: NavigationTarget { + file_id: FileId( + 1, + ), + full_range: 51..105, + name: "foo", + kind: FN_DEF, + focus_range: Some( + 97..100, + ), + container_name: None, + description: None, + docs: None, + }, kind: DocTest { test_id: Path( "Data::foo", @@ -355,14 +446,40 @@ mod tests { @r###" [ Runnable { - range: 1..59, + nav: NavigationTarget { + file_id: FileId( + 1, + ), + full_range: 1..59, + name: "test_mod", + kind: MODULE, + focus_range: Some( + 13..21, + ), + container_name: None, + description: None, + docs: None, + }, kind: TestMod { path: "test_mod", }, cfg_exprs: [], }, Runnable { - range: 28..57, + nav: NavigationTarget { + file_id: FileId( + 1, + ), + full_range: 28..57, + name: "test_foo1", + kind: FN_DEF, + focus_range: Some( + 43..52, + ), + container_name: None, + description: None, + docs: None, + }, kind: Test { test_id: Path( "test_mod::test_foo1", @@ -397,14 +514,40 @@ mod tests { @r###" [ Runnable { - range: 23..85, + nav: NavigationTarget { + file_id: FileId( + 1, + ), + full_range: 23..85, + name: "test_mod", + kind: MODULE, + focus_range: Some( + 27..35, + ), + container_name: None, + description: None, + docs: None, + }, kind: TestMod { path: "foo::test_mod", }, cfg_exprs: [], }, Runnable { - range: 46..79, + nav: NavigationTarget { + file_id: FileId( + 1, + ), + full_range: 46..79, + name: "test_foo1", + kind: FN_DEF, + focus_range: Some( + 65..74, + ), + container_name: None, + description: None, + docs: None, + }, kind: Test { test_id: Path( "foo::test_mod::test_foo1", @@ -441,14 +584,40 @@ mod tests { @r###" [ Runnable { - range: 41..115, + nav: NavigationTarget { + file_id: FileId( + 1, + ), + full_range: 41..115, + name: "test_mod", + kind: MODULE, + focus_range: Some( + 45..53, + ), + container_name: None, + description: None, + docs: None, + }, kind: TestMod { path: "foo::bar::test_mod", }, cfg_exprs: [], }, Runnable { - range: 68..105, + nav: NavigationTarget { + file_id: FileId( + 1, + ), + full_range: 68..105, + name: "test_foo1", + kind: FN_DEF, + focus_range: Some( + 91..100, + ), + container_name: None, + description: None, + docs: None, + }, kind: Test { test_id: Path( "foo::bar::test_mod::test_foo1", @@ -480,7 +649,20 @@ mod tests { @r###" [ Runnable { - range: 1..58, + nav: NavigationTarget { + file_id: FileId( + 1, + ), + full_range: 1..58, + name: "test_foo1", + kind: FN_DEF, + focus_range: Some( + 44..53, + ), + container_name: None, + description: None, + docs: None, + }, kind: Test { test_id: Path( "test_foo1", @@ -517,7 +699,20 @@ mod tests { @r###" [ Runnable { - range: 1..80, + nav: NavigationTarget { + file_id: FileId( + 1, + ), + full_range: 1..80, + name: "test_foo1", + kind: FN_DEF, + focus_range: Some( + 66..75, + ), + container_name: None, + description: None, + docs: None, + }, kind: Test { test_id: Path( "test_foo1", diff --git a/crates/rust-analyzer/src/lsp_ext.rs b/crates/rust-analyzer/src/lsp_ext.rs index 5fa1eba1c722..ec24ce5e0f5f 100644 --- a/crates/rust-analyzer/src/lsp_ext.rs +++ b/crates/rust-analyzer/src/lsp_ext.rs @@ -139,6 +139,7 @@ pub enum RunnableKind { #[derive(Deserialize, Serialize, Debug)] #[serde(rename_all = "camelCase")] pub struct CargoRunnable { + #[serde(skip_serializing_if = "Option::is_none")] pub workspace_root: Option, // command, --package and --lib stuff pub cargo_args: Vec, diff --git a/crates/rust-analyzer/tests/heavy_tests/main.rs b/crates/rust-analyzer/tests/heavy_tests/main.rs index 8b473ff74cd1..e18f973b824c 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() { server.request::( RunnablesParams { text_document: server.doc_id("lib.rs"), position: None }, json!([ - { - "args": [ "test" ], - "extraArgs": [ "foo", "--nocapture" ], - "kind": "cargo", - "env": { "RUST_BACKTRACE": "short" }, - "cwd": null, - "label": "test foo", - "range": { - "end": { "character": 1, "line": 2 }, - "start": { "character": 0, "line": 0 } + { + "args": { + "cargoArgs": ["test"], + "executableArgs": ["foo", "--nocapture"], + }, + "kind": "cargo", + "label": "test foo", + "location": { + "targetRange": { + "end": { "character": 1, "line": 2 }, + "start": { "character": 0, "line": 0 } + }, + "targetSelectionRange": { + "end": { "character": 6, "line": 1 }, + "start": { "character": 3, "line": 1 } + }, + "targetUri": "file:///[..]/lib.rs" + } + }, + { + "args": { + "cargoArgs": ["check", "--workspace"], + "executableArgs": [], + }, + "kind": "cargo", + "label": "cargo check --workspace" } - }, - { - "args": ["check", "--workspace"], - "extraArgs": [], - "kind": "cargo", - "env": {}, - "cwd": null, - "label": "cargo check --workspace", - "range": { - "end": { "character": 0, "line": 0 }, - "start": { "character": 0, "line": 0 } - } - } ]), ); } @@ -138,42 +141,44 @@ fn main() {} server.request::( RunnablesParams { text_document: server.doc_id("foo/tests/spam.rs"), position: None }, json!([ - { - "args": [ "test", "--package", "foo", "--test", "spam" ], - "extraArgs": [ "test_eggs", "--exact", "--nocapture" ], - "kind": "cargo", - "env": { "RUST_BACKTRACE": "short" }, - "label": "test test_eggs", - "range": { + { + "args": { + "cargoArgs": ["test", "--package", "foo", "--test", "spam"], + "executableArgs": ["test_eggs", "--exact", "--nocapture"], + "workspaceRoot": server.path().join("foo") + }, + "kind": "cargo", + "label": "test test_eggs", + "location": { + "targetRange": { "end": { "character": 17, "line": 1 }, "start": { "character": 0, "line": 0 } }, - "cwd": server.path().join("foo") - }, - { - "args": [ "check", "--package", "foo" ], - "extraArgs": [], - "kind": "cargo", - "env": {}, - "label": "cargo check -p foo", - "range": { - "end": { "character": 0, "line": 0 }, - "start": { "character": 0, "line": 0 } + "targetSelectionRange": { + "end": { "character": 12, "line": 1 }, + "start": { "character": 3, "line": 1 } }, - "cwd": server.path().join("foo") - }, - { - "args": [ "test", "--package", "foo" ], - "extraArgs": [], - "kind": "cargo", - "env": {}, - "label": "cargo test -p foo", - "range": { - "end": { "character": 0, "line": 0 }, - "start": { "character": 0, "line": 0 } - }, - "cwd": server.path().join("foo") + "targetUri": "file:///[..]/tests/spam.rs" } + }, + { + "args": { + "cargoArgs": ["check", "--package", "foo"], + "executableArgs": [], + "workspaceRoot": server.path().join("foo") + }, + "kind": "cargo", + "label": "cargo check -p foo" + }, + { + "args": { + "cargoArgs": ["test", "--package", "foo"], + "executableArgs": [], + "workspaceRoot": server.path().join("foo") + }, + "kind": "cargo", + "label": "cargo test -p foo" + } ]), ); } From 7a66d9989713475a10eb20b8c772287b435fecd3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lauren=C8=9Biu=20Nicola?= Date: Tue, 2 Jun 2020 12:24:33 +0300 Subject: [PATCH 025/172] Disable rust-analyzer.{cargo,checkOnSave}.allFeatures by default --- crates/ra_project_model/src/cargo_workspace.rs | 2 +- crates/rust-analyzer/src/config.rs | 2 +- editors/code/package.json | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/crates/ra_project_model/src/cargo_workspace.rs b/crates/ra_project_model/src/cargo_workspace.rs index a306ce95f36e..4b7444039e7b 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 { fn default() -> Self { CargoConfig { no_default_features: false, - all_features: true, + all_features: false, features: Vec::new(), load_out_dirs_from_check: false, target: None, diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs index c0f7c2c0c51b..9c6e369d2e1e 100644 --- a/crates/rust-analyzer/src/config.rs +++ b/crates/rust-analyzer/src/config.rs @@ -122,7 +122,7 @@ impl Default for Config { check: Some(FlycheckConfig::CargoCommand { command: "check".to_string(), all_targets: true, - all_features: true, + all_features: false, extra_args: Vec::new(), }), diff --git a/editors/code/package.json b/editors/code/package.json index 75dbafc05a3b..d8f4287fd88c 100644 --- a/editors/code/package.json +++ b/editors/code/package.json @@ -238,7 +238,7 @@ }, "rust-analyzer.cargo.allFeatures": { "type": "boolean", - "default": true, + "default": false, "description": "Activate all available features" }, "rust-analyzer.cargo.features": { @@ -319,7 +319,7 @@ }, "rust-analyzer.checkOnSave.allFeatures": { "type": "boolean", - "default": true, + "default": false, "markdownDescription": "Check with all features (will be passed as `--all-features`)" }, "rust-analyzer.inlayHints.enable": { From f2f3c554af2c4a698e0ceb087ceb4dfa6f95ee90 Mon Sep 17 00:00:00 2001 From: veetaha Date: Tue, 2 Jun 2020 23:15:23 +0300 Subject: [PATCH 026/172] Don't exclude dirs that appear in project root parent path --- xtask/tests/tidy.rs | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/xtask/tests/tidy.rs b/xtask/tests/tidy.rs index 4ac5d929fc6b..62626064ee51 100644 --- a/xtask/tests/tidy.rs +++ b/xtask/tests/tidy.rs @@ -180,13 +180,11 @@ impl TidyDocs { } fn is_exclude_dir(p: &Path, dirs_to_exclude: &[&str]) -> bool { - let mut cur_path = p; - while let Some(path) = cur_path.parent() { - if dirs_to_exclude.iter().any(|dir| path.ends_with(dir)) { - return true; - } - cur_path = path; - } - - false + p.strip_prefix(project_root()) + .unwrap() + .components() + .rev() + .skip(1) + .filter_map(|it| it.as_os_str().to_str()) + .any(|it| dirs_to_exclude.contains(&it)) } From 57cd936c5262c3b43626618be42d7a72f71c3539 Mon Sep 17 00:00:00 2001 From: Mikhail Rakhmanov Date: Tue, 2 Jun 2020 22:21:48 +0200 Subject: [PATCH 027/172] Preliminary implementation of lazy CodeAssits --- crates/ra_ide/src/lib.rs | 39 ++++---- crates/rust-analyzer/src/config.rs | 7 +- ..._to_proto__tests__snap_multi_line_fix.snap | 1 + ...to__tests__snap_rustc_unused_variable.snap | 1 + .../rust-analyzer/src/diagnostics/to_proto.rs | 1 + crates/rust-analyzer/src/lsp_ext.rs | 19 ++++ crates/rust-analyzer/src/main_loop.rs | 1 + .../rust-analyzer/src/main_loop/handlers.rs | 95 ++++++++++++++----- crates/rust-analyzer/src/to_proto.rs | 39 +++++++- editors/code/src/client.ts | 76 ++++++++------- editors/code/src/commands.ts | 19 +++- editors/code/src/lsp_ext.ts | 7 ++ editors/code/src/main.ts | 1 + 13 files changed, 218 insertions(+), 88 deletions(-) diff --git a/crates/ra_ide/src/lib.rs b/crates/ra_ide/src/lib.rs index 12d5716e8359..34c2d75fed2b 100644 --- a/crates/ra_ide/src/lib.rs +++ b/crates/ra_ide/src/lib.rs @@ -77,7 +77,7 @@ pub use crate::{ }; pub use hir::Documentation; -pub use ra_assists::{AssistConfig, AssistId}; +pub use ra_assists::{Assist, AssistConfig, AssistId, ResolvedAssist}; pub use ra_db::{ Canceled, CrateGraph, CrateId, Edition, FileId, FilePosition, FileRange, SourceRootId, }; @@ -142,14 +142,6 @@ pub struct AnalysisHost { db: RootDatabase, } -#[derive(Debug)] -pub struct Assist { - pub id: AssistId, - pub label: String, - pub group_label: Option, - pub source_change: SourceChange, -} - impl AnalysisHost { pub fn new(lru_capacity: Option) -> AnalysisHost { AnalysisHost { db: RootDatabase::new(lru_capacity) } @@ -470,20 +462,23 @@ impl Analysis { self.with_db(|db| completion::completions(db, config, position).map(Into::into)) } - /// Computes assists (aka code actions aka intentions) for the given + /// Computes resolved assists with source changes for the given position. + pub fn resolved_assists( + &self, + config: &AssistConfig, + frange: FileRange, + ) -> Cancelable> { + self.with_db(|db| ra_assists::Assist::resolved(db, config, frange)) + } + + /// Computes unresolved assists (aka code actions aka intentions) for the given /// position. - pub fn assists(&self, config: &AssistConfig, frange: FileRange) -> Cancelable> { - self.with_db(|db| { - ra_assists::Assist::resolved(db, config, frange) - .into_iter() - .map(|assist| Assist { - id: assist.assist.id, - label: assist.assist.label, - group_label: assist.assist.group.map(|it| it.0), - source_change: assist.source_change, - }) - .collect() - }) + pub fn unresolved_assists( + &self, + config: &AssistConfig, + frange: FileRange, + ) -> Cancelable> { + self.with_db(|db| Assist::unresolved(db, config, frange)) } /// Computes the set of diagnostics for the given file. diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs index c0f7c2c0c51b..9e8e7ab824e2 100644 --- a/crates/rust-analyzer/src/config.rs +++ b/crates/rust-analyzer/src/config.rs @@ -103,6 +103,7 @@ pub struct ClientCapsConfig { pub code_action_literals: bool, pub work_done_progress: bool, pub code_action_group: bool, + pub resolve_code_action: bool, } impl Default for Config { @@ -299,7 +300,11 @@ impl Config { let code_action_group = experimental.get("codeActionGroup").and_then(|it| it.as_bool()) == Some(true); - self.client_caps.code_action_group = code_action_group + self.client_caps.code_action_group = code_action_group; + + let resolve_code_action = + experimental.get("resolveCodeAction").and_then(|it| it.as_bool()) == Some(true); + self.client_caps.resolve_code_action = resolve_code_action; } } } diff --git a/crates/rust-analyzer/src/diagnostics/snapshots/rust_analyzer__diagnostics__to_proto__tests__snap_multi_line_fix.snap b/crates/rust-analyzer/src/diagnostics/snapshots/rust_analyzer__diagnostics__to_proto__tests__snap_multi_line_fix.snap index c40cfdcdcaf0..272057b47ea6 100644 --- a/crates/rust-analyzer/src/diagnostics/snapshots/rust_analyzer__diagnostics__to_proto__tests__snap_multi_line_fix.snap +++ b/crates/rust-analyzer/src/diagnostics/snapshots/rust_analyzer__diagnostics__to_proto__tests__snap_multi_line_fix.snap @@ -65,6 +65,7 @@ expression: diag fixes: [ CodeAction { title: "return the expression directly", + id: None, group: None, kind: Some( "quickfix", 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 6dd3fcb2eab8..9a7972ff54a4 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 @@ -50,6 +50,7 @@ expression: diag fixes: [ CodeAction { title: "consider prefixing with an underscore", + id: None, group: None, kind: Some( "quickfix", diff --git a/crates/rust-analyzer/src/diagnostics/to_proto.rs b/crates/rust-analyzer/src/diagnostics/to_proto.rs index a500d670a72c..257910e0948c 100644 --- a/crates/rust-analyzer/src/diagnostics/to_proto.rs +++ b/crates/rust-analyzer/src/diagnostics/to_proto.rs @@ -145,6 +145,7 @@ fn map_rust_child_diagnostic( } else { MappedRustChildDiagnostic::SuggestedFix(lsp_ext::CodeAction { title: rd.message.clone(), + id: None, group: None, kind: Some("quickfix".to_string()), edit: Some(lsp_ext::SnippetWorkspaceEdit { diff --git a/crates/rust-analyzer/src/lsp_ext.rs b/crates/rust-analyzer/src/lsp_ext.rs index 173c23b9e5d0..05b76e7c86ba 100644 --- a/crates/rust-analyzer/src/lsp_ext.rs +++ b/crates/rust-analyzer/src/lsp_ext.rs @@ -98,6 +98,23 @@ pub struct JoinLinesParams { pub ranges: Vec, } +pub enum ResolveCodeActionRequest {} + +impl Request for ResolveCodeActionRequest { + type Params = ResolveCodeActionParams; + type Result = Option; + const METHOD: &'static str = "experimental/resolveCodeAction"; +} + +/// Params for the ResolveCodeActionRequest +#[derive(Debug, Eq, PartialEq, Clone, Deserialize, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct ResolveCodeActionParams { + pub code_action_params: lsp_types::CodeActionParams, + pub id: String, + pub label: String, +} + pub enum OnEnter {} impl Request for OnEnter { @@ -197,6 +214,8 @@ impl Request for CodeActionRequest { pub struct CodeAction { pub title: String, #[serde(skip_serializing_if = "Option::is_none")] + pub id: Option, + #[serde(skip_serializing_if = "Option::is_none")] pub group: Option, #[serde(skip_serializing_if = "Option::is_none")] pub kind: Option, diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs index f1287d52cd32..ad9dd4c59617 100644 --- a/crates/rust-analyzer/src/main_loop.rs +++ b/crates/rust-analyzer/src/main_loop.rs @@ -517,6 +517,7 @@ fn on_request( .on::(handlers::handle_runnables)? .on::(handlers::handle_inlay_hints)? .on::(handlers::handle_code_action)? + .on::(handlers::handle_resolve_code_action)? .on::(handlers::handle_on_type_formatting)? .on::(handlers::handle_document_symbol)? .on::(handlers::handle_workspace_symbol)? diff --git a/crates/rust-analyzer/src/main_loop/handlers.rs b/crates/rust-analyzer/src/main_loop/handlers.rs index bc7c7f1ef5d5..b342f4bb7ea4 100644 --- a/crates/rust-analyzer/src/main_loop/handlers.rs +++ b/crates/rust-analyzer/src/main_loop/handlers.rs @@ -693,6 +693,45 @@ pub fn handle_formatting( }])) } +fn handle_fixes( + world: &WorldSnapshot, + params: &lsp_types::CodeActionParams, + res: &mut Vec, +) -> Result<()> { + let file_id = from_proto::file_id(&world, ¶ms.text_document.uri)?; + let line_index = world.analysis().file_line_index(file_id)?; + let range = from_proto::text_range(&line_index, params.range); + + let diagnostics = world.analysis().diagnostics(file_id)?; + + let fixes_from_diagnostics = diagnostics + .into_iter() + .filter_map(|d| Some((d.range, d.fix?))) + .filter(|(diag_range, _fix)| diag_range.intersect(range).is_some()) + .map(|(_range, fix)| fix); + for fix in fixes_from_diagnostics { + let title = fix.label; + let edit = to_proto::snippet_workspace_edit(&world, fix.source_change)?; + let action = lsp_ext::CodeAction { + title, + id: None, + group: None, + kind: None, + edit: Some(edit), + command: None, + }; + res.push(action); + } + for fix in world.check_fixes.get(&file_id).into_iter().flatten() { + let fix_range = from_proto::text_range(&line_index, fix.range); + if fix_range.intersect(range).is_none() { + continue; + } + res.push(fix.action.clone()); + } + Ok(()) +} + pub fn handle_code_action( world: WorldSnapshot, params: lsp_types::CodeActionParams, @@ -709,38 +748,48 @@ pub fn handle_code_action( let line_index = world.analysis().file_line_index(file_id)?; let range = from_proto::text_range(&line_index, params.range); let frange = FileRange { file_id, range }; - - let diagnostics = world.analysis().diagnostics(file_id)?; let mut res: Vec = Vec::new(); - let fixes_from_diagnostics = diagnostics - .into_iter() - .filter_map(|d| Some((d.range, d.fix?))) - .filter(|(diag_range, _fix)| diag_range.intersect(range).is_some()) - .map(|(_range, fix)| fix); + handle_fixes(&world, ¶ms, &mut res)?; - for fix in fixes_from_diagnostics { - let title = fix.label; - let edit = to_proto::snippet_workspace_edit(&world, fix.source_change)?; - let action = - lsp_ext::CodeAction { title, group: None, kind: None, edit: Some(edit), command: None }; - res.push(action); - } - - for fix in world.check_fixes.get(&file_id).into_iter().flatten() { - let fix_range = from_proto::text_range(&line_index, fix.range); - if fix_range.intersect(range).is_none() { - continue; + if world.config.client_caps.resolve_code_action { + for assist in world.analysis().unresolved_assists(&world.config.assist, frange)?.into_iter() + { + res.push(to_proto::unresolved_code_action(&world, assist)?.into()); + } + } else { + for assist in world.analysis().resolved_assists(&world.config.assist, frange)?.into_iter() { + res.push(to_proto::resolved_code_action(&world, assist)?.into()); } - res.push(fix.action.clone()); } - for assist in world.analysis().assists(&world.config.assist, frange)?.into_iter() { - res.push(to_proto::code_action(&world, assist)?.into()); - } Ok(Some(res)) } +pub fn handle_resolve_code_action( + world: WorldSnapshot, + params: lsp_ext::ResolveCodeActionParams, +) -> Result> { + if !world.config.client_caps.resolve_code_action { + return Ok(None); + } + + let _p = profile("handle_resolve_code_action"); + let file_id = from_proto::file_id(&world, ¶ms.code_action_params.text_document.uri)?; + let line_index = world.analysis().file_line_index(file_id)?; + let range = from_proto::text_range(&line_index, params.code_action_params.range); + let frange = FileRange { file_id, range }; + let mut res: Vec = Vec::new(); + + for assist in world.analysis().resolved_assists(&world.config.assist, frange)?.into_iter() { + res.push(to_proto::resolved_code_action(&world, assist)?.into()); + } + Ok(res + .into_iter() + .find(|action| action.id.clone().unwrap() == params.id && action.title == params.label) + .and_then(|action| action.edit)) +} + pub fn handle_code_lens( world: WorldSnapshot, params: lsp_types::CodeLensParams, diff --git a/crates/rust-analyzer/src/to_proto.rs b/crates/rust-analyzer/src/to_proto.rs index 2fbbb4e632d9..db78c4b5c505 100644 --- a/crates/rust-analyzer/src/to_proto.rs +++ b/crates/rust-analyzer/src/to_proto.rs @@ -3,8 +3,8 @@ use ra_db::{FileId, FileRange}; use ra_ide::{ Assist, CompletionItem, CompletionItemKind, Documentation, FileSystemEdit, Fold, FoldKind, FunctionSignature, Highlight, HighlightModifier, HighlightTag, HighlightedRange, Indel, - InlayHint, InlayKind, InsertTextFormat, LineIndex, NavigationTarget, ReferenceAccess, Severity, - SourceChange, SourceFileEdit, TextEdit, + InlayHint, InlayKind, InsertTextFormat, LineIndex, NavigationTarget, ReferenceAccess, + ResolvedAssist, Severity, SourceChange, SourceFileEdit, TextEdit, }; use ra_syntax::{SyntaxKind, TextRange, TextSize}; use ra_vfs::LineEndings; @@ -617,10 +617,41 @@ fn main() { } } -pub(crate) fn code_action(world: &WorldSnapshot, assist: Assist) -> Result { +pub(crate) fn unresolved_code_action( + world: &WorldSnapshot, + assist: Assist, +) -> Result { let res = lsp_ext::CodeAction { title: assist.label, - group: if world.config.client_caps.code_action_group { assist.group_label } else { None }, + id: Some(assist.id.0.to_owned()), + group: assist.group.and_then(|it| { + if world.config.client_caps.code_action_group { + None + } else { + Some(it.0) + } + }), + kind: Some(String::new()), + edit: None, + command: None, + }; + Ok(res) +} + +pub(crate) fn resolved_code_action( + world: &WorldSnapshot, + assist: ResolvedAssist, +) -> Result { + let res = lsp_ext::CodeAction { + title: assist.assist.label, + id: Some(assist.assist.id.0.to_owned()), + group: assist.assist.group.and_then(|it| { + if world.config.client_caps.code_action_group { + None + } else { + Some(it.0) + } + }), kind: Some(String::new()), edit: Some(snippet_workspace_edit(world, assist.source_change)?), command: None, diff --git a/editors/code/src/client.ts b/editors/code/src/client.ts index d64f9a3f9793..a25091f797c3 100644 --- a/editors/code/src/client.ts +++ b/editors/code/src/client.ts @@ -1,8 +1,11 @@ import * as lc from 'vscode-languageclient'; import * as vscode from 'vscode'; +import * as ra from '../src/lsp_ext'; +import * as Is from 'vscode-languageclient/lib/utils/is'; import { CallHierarchyFeature } from 'vscode-languageclient/lib/callHierarchy.proposed'; import { SemanticTokensFeature, DocumentSemanticsTokensSignature } from 'vscode-languageclient/lib/semanticTokens.proposed'; +import { assert } from './util'; export function createClient(serverPath: string, cwd: string): lc.LanguageClient { // '.' Is the fallback if no folder is open @@ -32,6 +35,8 @@ export function createClient(serverPath: string, cwd: string): lc.LanguageClient if (res === undefined) throw new Error('busy'); return res; }, + // Using custom handling of CodeActions where each code action is resloved lazily + // That's why we are not waiting for any command or edits async provideCodeActions(document: vscode.TextDocument, range: vscode.Range, context: vscode.CodeActionContext, token: vscode.CancellationToken, _next: lc.ProvideCodeActionsSignature) { const params: lc.CodeActionParams = { textDocument: client.code2ProtocolConverter.asTextDocumentIdentifier(document), @@ -43,32 +48,38 @@ export function createClient(serverPath: string, cwd: string): lc.LanguageClient const result: (vscode.CodeAction | vscode.Command)[] = []; const groups = new Map(); for (const item of values) { + // In our case we expect to get code edits only from diagnostics if (lc.CodeAction.is(item)) { + assert(!item.command, "We don't expect to receive commands in CodeActions"); const action = client.protocol2CodeConverter.asCodeAction(item); - const group = actionGroup(item); - if (isSnippetEdit(item) || group) { - action.command = { - command: "rust-analyzer.applySnippetWorkspaceEdit", - title: "", - arguments: [action.edit], - }; - action.edit = undefined; - } - - if (group) { - let entry = groups.get(group); - if (!entry) { - entry = { index: result.length, items: [] }; - groups.set(group, entry); - result.push(action); - } - entry.items.push(action); - } else { + result.push(action); + continue; + } + assert(isCodeActionWithoutEditsAndCommands(item), "We don't expect edits or commands here"); + const action = new vscode.CodeAction(item.title); + const group = (item as any).group; + const id = (item as any).id; + const resolveParams: ra.ResolveCodeActionParams = { + id: id, + // TODO: delete after discussions if needed + label: item.title, + codeActionParams: params + }; + action.command = { + command: "rust-analyzer.resolveCodeAction", + title: item.title, + arguments: [resolveParams], + }; + if (group) { + let entry = groups.get(group); + if (!entry) { + entry = { index: result.length, items: [] }; + groups.set(group, entry); result.push(action); } + entry.items.push(action); } else { - const command = client.protocol2CodeConverter.asCommand(item); - result.push(command); + result.push(action); } } for (const [group, { index, items }] of groups) { @@ -80,7 +91,7 @@ export function createClient(serverPath: string, cwd: string): lc.LanguageClient command: "rust-analyzer.applyActionGroup", title: "", arguments: [items.map((item) => { - return { label: item.title, edit: item.command!!.arguments!![0] }; + return { label: item.title, arguments: item.command!!.arguments!![0] }; })], }; result[index] = action; @@ -119,24 +130,17 @@ class ExperimentalFeatures implements lc.StaticFeature { const caps: any = capabilities.experimental ?? {}; caps.snippetTextEdit = true; caps.codeActionGroup = true; + caps.resolveCodeAction = true; capabilities.experimental = caps; } initialize(_capabilities: lc.ServerCapabilities, _documentSelector: lc.DocumentSelector | undefined): void { } } -function isSnippetEdit(action: lc.CodeAction): boolean { - const documentChanges = action.edit?.documentChanges ?? []; - for (const edit of documentChanges) { - if (lc.TextDocumentEdit.is(edit)) { - if (edit.edits.some((indel) => (indel as any).insertTextFormat === lc.InsertTextFormat.Snippet)) { - return true; - } - } - } - return false; -} - -function actionGroup(action: lc.CodeAction): string | undefined { - return (action as any).group; +function isCodeActionWithoutEditsAndCommands(value: any): boolean { + const candidate: lc.CodeAction = value; + return candidate && Is.string(candidate.title) && + (candidate.diagnostics === void 0 || Is.typedArray(candidate.diagnostics, lc.Diagnostic.is)) && + (candidate.kind === void 0 || Is.string(candidate.kind)) && + (candidate.edit === void 0 && candidate.command === void 0); } diff --git a/editors/code/src/commands.ts b/editors/code/src/commands.ts index 534d2a9847e0..3e9c3aa0e59b 100644 --- a/editors/code/src/commands.ts +++ b/editors/code/src/commands.ts @@ -343,10 +343,25 @@ export function showReferences(ctx: Ctx): Cmd { } export function applyActionGroup(_ctx: Ctx): Cmd { - return async (actions: { label: string; edit: vscode.WorkspaceEdit }[]) => { + return async (actions: { label: string; arguments: ra.ResolveCodeActionParams }[]) => { const selectedAction = await vscode.window.showQuickPick(actions); if (!selectedAction) return; - await applySnippetWorkspaceEdit(selectedAction.edit); + vscode.commands.executeCommand( + 'rust-analyzer.resolveCodeAction', + selectedAction.arguments, + ); + }; +} + +export function resolveCodeAction(ctx: Ctx): Cmd { + const client = ctx.client; + return async (params: ra.ResolveCodeActionParams) => { + const item: lc.WorkspaceEdit = await client.sendRequest(ra.resolveCodeAction, params); + if (!item) { + return; + } + const edit = client.protocol2CodeConverter.asWorkspaceEdit(item); + await applySnippetWorkspaceEdit(edit); }; } diff --git a/editors/code/src/lsp_ext.ts b/editors/code/src/lsp_ext.ts index 3e0b606997fb..f881bae4721a 100644 --- a/editors/code/src/lsp_ext.ts +++ b/editors/code/src/lsp_ext.ts @@ -33,6 +33,13 @@ export const matchingBrace = new lc.RequestType("experimental/parentModule"); +export interface ResolveCodeActionParams { + id: string; + label: string; + codeActionParams: lc.CodeActionParams; +} +export const resolveCodeAction = new lc.RequestType('experimental/resolveCodeAction'); + export interface JoinLinesParams { textDocument: lc.TextDocumentIdentifier; ranges: lc.Range[]; diff --git a/editors/code/src/main.ts b/editors/code/src/main.ts index b7337621cb61..a92c676fa2dd 100644 --- a/editors/code/src/main.ts +++ b/editors/code/src/main.ts @@ -98,6 +98,7 @@ export async function activate(context: vscode.ExtensionContext) { ctx.registerCommand('debugSingle', commands.debugSingle); ctx.registerCommand('showReferences', commands.showReferences); ctx.registerCommand('applySnippetWorkspaceEdit', commands.applySnippetWorkspaceEditCommand); + ctx.registerCommand('resolveCodeAction', commands.resolveCodeAction); ctx.registerCommand('applyActionGroup', commands.applyActionGroup); ctx.pushCleanup(activateTaskProvider(workspaceFolder)); From a9cb2933fbeddef4ed70bde77ded4f9bb185548e Mon Sep 17 00:00:00 2001 From: Paul Daniel Faria Date: Tue, 2 Jun 2020 18:49:09 -0400 Subject: [PATCH 028/172] Add highlight support for unsafe fn calls and raw ptr deref --- crates/ra_hir/src/code_model.rs | 8 ++++ crates/ra_hir_def/src/data.rs | 6 ++- .../src/snapshots/highlight_injection.html | 1 + .../src/snapshots/highlight_strings.html | 1 + .../src/snapshots/highlight_unsafe.html | 48 +++++++++++++++++++ crates/ra_ide/src/snapshots/highlighting.html | 1 + .../src/snapshots/rainbow_highlighting.html | 1 + crates/ra_ide/src/syntax_highlighting.rs | 25 +++++++++- crates/ra_ide/src/syntax_highlighting/html.rs | 1 + crates/ra_ide/src/syntax_highlighting/tags.rs | 8 ++-- .../ra_ide/src/syntax_highlighting/tests.rs | 31 ++++++++++++ 11 files changed, 125 insertions(+), 6 deletions(-) create mode 100644 crates/ra_ide/src/snapshots/highlight_unsafe.html diff --git a/crates/ra_hir/src/code_model.rs b/crates/ra_hir/src/code_model.rs index e40aeffbcd4f..4a06f3bcddb6 100644 --- a/crates/ra_hir/src/code_model.rs +++ b/crates/ra_hir/src/code_model.rs @@ -637,6 +637,10 @@ impl Function { db.function_data(self.id).params.clone() } + pub fn is_unsafe(self, db: &dyn HirDatabase) -> bool { + db.function_data(self.id).is_unsafe + } + pub fn diagnostics(self, db: &dyn HirDatabase, sink: &mut DiagnosticSink) { let _p = profile("Function::diagnostics"); let infer = db.infer(self.id.into()); @@ -1190,6 +1194,10 @@ impl Type { ) } + pub fn is_raw_ptr(&self) -> bool { + matches!(&self.ty.value, Ty::Apply(ApplicationTy { ctor: TypeCtor::RawPtr(..), .. })) + } + pub fn contains_unknown(&self) -> bool { return go(&self.ty.value); diff --git a/crates/ra_hir_def/src/data.rs b/crates/ra_hir_def/src/data.rs index e2130d931fdb..807195d25ad9 100644 --- a/crates/ra_hir_def/src/data.rs +++ b/crates/ra_hir_def/src/data.rs @@ -34,6 +34,7 @@ pub struct FunctionData { /// True if the first param is `self`. This is relevant to decide whether this /// can be called as a method. pub has_self_param: bool, + pub is_unsafe: bool, pub visibility: RawVisibility, } @@ -85,11 +86,14 @@ impl FunctionData { ret_type }; + let is_unsafe = src.value.unsafe_token().is_some(); + let vis_default = RawVisibility::default_for_container(loc.container); let visibility = RawVisibility::from_ast_with_default(db, vis_default, src.map(|s| s.visibility())); - let sig = FunctionData { name, params, ret_type, has_self_param, visibility, attrs }; + let sig = + FunctionData { name, params, ret_type, has_self_param, is_unsafe, visibility, attrs }; Arc::new(sig) } } diff --git a/crates/ra_ide/src/snapshots/highlight_injection.html b/crates/ra_ide/src/snapshots/highlight_injection.html index 68fc589bc785..fcdc98201f2e 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 .string_literal { color: #CC9393; } .field { color: #94BFF3; } .function { color: #93E0E3; } +.operator.unsafe { color: #E28C14; } .parameter { color: #94BFF3; } .text { color: #DCDCCC; } .type { color: #7CB8BB; } diff --git a/crates/ra_ide/src/snapshots/highlight_strings.html b/crates/ra_ide/src/snapshots/highlight_strings.html index 41cddd0ff261..e97192b614a0 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 .string_literal { color: #CC9393; } .field { color: #94BFF3; } .function { color: #93E0E3; } +.operator.unsafe { color: #E28C14; } .parameter { color: #94BFF3; } .text { color: #DCDCCC; } .type { color: #7CB8BB; } 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 000000000000..17ffc727cd06 --- /dev/null +++ b/crates/ra_ide/src/snapshots/highlight_unsafe.html @@ -0,0 +1,48 @@ + + +
unsafe fn unsafe_fn() {}
+
+struct HasUnsafeFn;
+
+impl HasUnsafeFn {
+    unsafe fn unsafe_method(&self) {}
+}
+
+fn main() {
+    let x = &5 as *const usize;
+    unsafe {
+        unsafe_fn();
+        HasUnsafeFn.unsafe_method();
+        let y = *x;
+        let z = -x;
+    }
+}
\ 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 352e350955fc..42c5f3e5515e 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 .string_literal { color: #CC9393; } .field { color: #94BFF3; } .function { color: #93E0E3; } +.operator.unsafe { color: #E28C14; } .parameter { color: #94BFF3; } .text { color: #DCDCCC; } .type { color: #7CB8BB; } diff --git a/crates/ra_ide/src/snapshots/rainbow_highlighting.html b/crates/ra_ide/src/snapshots/rainbow_highlighting.html index 2a0294f719f5..2dd61d20d691 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 .string_literal { color: #CC9393; } .field { color: #94BFF3; } .function { color: #93E0E3; } +.operator.unsafe { color: #E28C14; } .parameter { color: #94BFF3; } .text { color: #DCDCCC; } .type { color: #7CB8BB; } diff --git a/crates/ra_ide/src/syntax_highlighting.rs b/crates/ra_ide/src/syntax_highlighting.rs index 0b53ebe69563..19ecd54d6cf2 100644 --- a/crates/ra_ide/src/syntax_highlighting.rs +++ b/crates/ra_ide/src/syntax_highlighting.rs @@ -406,6 +406,23 @@ fn highlight_element( _ => h, } } + PREFIX_EXPR => { + let prefix_expr = element.into_node().and_then(ast::PrefixExpr::cast)?; + match prefix_expr.op_kind() { + Some(ast::PrefixOp::Deref) => {} + _ => return None, + } + + let expr = prefix_expr.expr()?; + let ty = sema.type_of_expr(&expr)?; + if !ty.is_raw_ptr() { + return None; + } + + let mut h = Highlight::new(HighlightTag::Operator); + h |= HighlightModifier::Unsafe; + h + } k if k.is_keyword() => { let h = Highlight::new(HighlightTag::Keyword); @@ -458,7 +475,13 @@ fn highlight_name(db: &RootDatabase, def: Definition) -> Highlight { Definition::Field(_) => HighlightTag::Field, Definition::ModuleDef(def) => match def { hir::ModuleDef::Module(_) => HighlightTag::Module, - hir::ModuleDef::Function(_) => HighlightTag::Function, + hir::ModuleDef::Function(func) => { + let mut h = HighlightTag::Function.into(); + if func.is_unsafe(db) { + h |= HighlightModifier::Unsafe; + } + return h; + } hir::ModuleDef::Adt(hir::Adt::Struct(_)) => HighlightTag::Struct, hir::ModuleDef::Adt(hir::Adt::Enum(_)) => HighlightTag::Enum, 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 edfe61f39a13..7d946c98dae7 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 .string_literal { color: #CC9393; } .field { color: #94BFF3; } .function { color: #93E0E3; } +.operator.unsafe { color: #E28C14; } .parameter { color: #94BFF3; } .text { color: #DCDCCC; } .type { color: #7CB8BB; } diff --git a/crates/ra_ide/src/syntax_highlighting/tags.rs b/crates/ra_ide/src/syntax_highlighting/tags.rs index 1514531de2e3..94f466966a3a 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 { Enum, EnumVariant, Field, + FormatSpecifier, Function, Keyword, Lifetime, Macro, Module, NumericLiteral, + Operator, SelfKeyword, SelfType, Static, @@ -41,8 +43,6 @@ pub enum HighlightTag { Union, Local, UnresolvedReference, - FormatSpecifier, - Operator, } #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] @@ -72,12 +72,14 @@ impl HighlightTag { HighlightTag::Enum => "enum", HighlightTag::EnumVariant => "enum_variant", HighlightTag::Field => "field", + HighlightTag::FormatSpecifier => "format_specifier", HighlightTag::Function => "function", HighlightTag::Keyword => "keyword", HighlightTag::Lifetime => "lifetime", HighlightTag::Macro => "macro", HighlightTag::Module => "module", HighlightTag::NumericLiteral => "numeric_literal", + HighlightTag::Operator => "operator", HighlightTag::SelfKeyword => "self_keyword", HighlightTag::SelfType => "self_type", HighlightTag::Static => "static", @@ -89,8 +91,6 @@ impl HighlightTag { HighlightTag::Union => "union", HighlightTag::Local => "variable", HighlightTag::UnresolvedReference => "unresolved_reference", - HighlightTag::FormatSpecifier => "format_specifier", - HighlightTag::Operator => "operator", } } } diff --git a/crates/ra_ide/src/syntax_highlighting/tests.rs b/crates/ra_ide/src/syntax_highlighting/tests.rs index 7dc229cab73d..36a1aa419bc3 100644 --- a/crates/ra_ide/src/syntax_highlighting/tests.rs +++ b/crates/ra_ide/src/syntax_highlighting/tests.rs @@ -258,3 +258,34 @@ fn main() { fs::write(dst_file, &actual_html).unwrap(); assert_eq_text!(expected_html, actual_html); } + +#[test] +fn test_unsafe_highlighting() { + let (analysis, file_id) = single_file( + r#" +unsafe fn unsafe_fn() {} + +struct HasUnsafeFn; + +impl HasUnsafeFn { + unsafe fn unsafe_method(&self) {} +} + +fn main() { + let x = &5 as *const usize; + unsafe { + unsafe_fn(); + HasUnsafeFn.unsafe_method(); + let y = *x; + let z = -x; + } +} +"# + .trim(), + ); + let dst_file = project_dir().join("crates/ra_ide/src/snapshots/highlight_unsafe.html"); + let actual_html = &analysis.highlight_as_html(file_id, false).unwrap(); + let expected_html = &read_text(&dst_file); + fs::write(dst_file, &actual_html).unwrap(); + assert_eq_text!(expected_html, actual_html); +} From 599c105e6fabb2b81c2d0a11b86c0c96f6ab1b88 Mon Sep 17 00:00:00 2001 From: Gabriel Valfridsson Date: Wed, 3 Jun 2020 00:51:23 +0200 Subject: [PATCH 029/172] Hide squiggly for unused and unnecessary --- ...agnostics__to_proto__tests__snap_rustc_unused_variable.snap | 2 +- crates/rust-analyzer/src/diagnostics/to_proto.rs | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) 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 6dd3fcb2eab8..33b516e269b8 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 }, }, severity: Some( - Warning, + Hint, ), code: Some( String( diff --git a/crates/rust-analyzer/src/diagnostics/to_proto.rs b/crates/rust-analyzer/src/diagnostics/to_proto.rs index a500d670a72c..6a6e7b45735a 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( return Vec::new(); } - let severity = map_level_to_severity(rd.level); + let mut severity = map_level_to_severity(rd.level); let mut source = String::from("rustc"); let mut code = rd.code.as_ref().map(|c| c.code.clone()); @@ -225,6 +225,7 @@ pub(crate) fn map_rust_diagnostic_to_lsp( } if is_unused_or_unnecessary(rd) { + severity = Some(DiagnosticSeverity::Hint); tags.push(DiagnosticTag::Unnecessary); } From 994006585be6850b250b21aac76a58f1324cad5d Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Tue, 2 Jun 2020 11:16:49 +0200 Subject: [PATCH 030/172] Start documenting review process --- docs/dev/README.md | 105 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 104 insertions(+), 1 deletion(-) diff --git a/docs/dev/README.md b/docs/dev/README.md index 65cc9fc12c0c..1de5a2aab1d7 100644 --- a/docs/dev/README.md +++ b/docs/dev/README.md @@ -30,7 +30,7 @@ https://rust-lang.zulipchat.com/#narrow/stream/185405-t-compiler.2Fwg-rls-2.2E0 * [good-first-issue](https://github.com/rust-analyzer/rust-analyzer/labels/good%20first%20issue) are good issues to get into the project. -* [E-mentor](https://github.com/rust-analyzer/rust-analyzer/issues?q=is%3Aopen+is%3Aissue+label%3AE-mentor) +* [E-has-instructions](https://github.com/rust-analyzer/rust-analyzer/issues?q=is%3Aopen+is%3Aissue+label%3AE-has-instructions) issues have links to the code in question and tests. * [E-easy](https://github.com/rust-analyzer/rust-analyzer/issues?q=is%3Aopen+is%3Aissue+label%3AE-easy), [E-medium](https://github.com/rust-analyzer/rust-analyzer/issues?q=is%3Aopen+is%3Aissue+label%3AE-medium), @@ -117,6 +117,109 @@ Additionally, I use `cargo run --release -p rust-analyzer -- analysis-stats path/to/some/rust/crate` to run a batch analysis. This is primarily useful for performance optimizations, or for bug minimization. +# Code Style & Review Process + +Our approach to "clean code" is two fold: + +* We generally don't block PRs on style changes. +* At the same time, all code in rust-analyzer is constantly refactored. + +It is explicitly OK for reviewer to flag only some nits in the PR, and than send a follow up cleanup PR for things which are easier to explain by example, cc-ing the original author. +Sending small cleanup PRs (like rename a single local variable) is encouraged. + +## Scale of Changes + +Everyone knows that it's better to send small & focused pull requests. +The problem is, sometimes you *have* to, eg, rewrite the whole compiler, and that just doesn't fit into a set of isolated PRs. + +The main thing too keep an eye on is the boundaries between various components. +There are three kinds of changes: + +1. Internals of a single component are changed. + Specifically, you don't change any `pub` items. + A good example here would be an addition of a new assist. + +2. API of a component is expanded. + Specifically, you add a new `pub` function which wasn't there before. + A good example here would be expansion of assist API, for example, to implement lazy assists or assists groups. + +3. A new dependency between components is introduced. + Specifically, you add a `pub use` reexport from another crate or you add a new line to `[dependencies]` section of `Cargo.toml`. + A good example here would be adding reference search capability to the assists crates. + +For the first group, the change is generally merged as long as: + +* it works for the happy case, +* it has tests, +* it doesn't panic for unhappy case. + +For the second group, the change would be subjected to quite a bit of scrutiny and iteration. +The new API needs to be right (or at least easy to change later). +The actual implementation doesn't matter that much. +It's very important to minimize the amount of changed lines of code for changes of the second kind. +Often, you start doing change of the first kind, only to realise that you need to elevate to a change of the second kind. +In this case, we'll probably ask you to split API changes into a separate PR. + +Changes of the third group should be pretty rare, so we don't specify any specific process for them. +That said, adding an innocent-looking `pub use` is a very simple way to break encapsulation, keep an eye on it! + +Note: if you enjoyed this abstract hand-waving about boundaries, you might appreciate +https://www.tedinski.com/2018/02/06/system-boundaries.html + +## Order of Imports + +We separate import groups with blank lines + +``` +mod x; +mod y; + +use std::{ ... } + +use crate_foo::{ ... } +use crate_bar::{ ... } + +use crate::{} + +use super::{} // but prefer `use crate::` +``` + +## Order of Items + +Optimize for the reader who sees the file for the first time, and wants to get the general idea about what's going on. +People read things from top to bottom, so place most important things first. + +Specifically, if all items except one are private, always put the non-private item on top. + +Put `struct`s and `enum`s first, functions and impls last. + +Do + +``` +// Good +struct Foo { + bars: Vec +} + +struct Bar; +``` + +rather than + +``` +// Not as good +struct Bar; + +struct Foo { + bars: Vec +} +``` + +## Documentation + +For `.md` and `.adoc` files, prefer a sentence-per-line format, don't wrap lines. +If the line is too long, you want to split the sentence in two :-) + # Logging Logging is done by both rust-analyzer and VS Code, so it might be tricky to From ca80544f4baef69f3bb80e6b069291fbc89a927c Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Wed, 3 Jun 2020 10:33:01 +0200 Subject: [PATCH 031/172] Put important things on top --- crates/ra_project_model/src/json_project.rs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/crates/ra_project_model/src/json_project.rs b/crates/ra_project_model/src/json_project.rs index bd2bae15e690..09c06fef9359 100644 --- a/crates/ra_project_model/src/json_project.rs +++ b/crates/ra_project_model/src/json_project.rs @@ -5,6 +5,13 @@ use std::path::PathBuf; use rustc_hash::{FxHashMap, FxHashSet}; use serde::Deserialize; +/// Roots and crates that compose this Rust project. +#[derive(Clone, Debug, Deserialize)] +pub struct JsonProject { + pub(crate) roots: Vec, + pub(crate) crates: Vec, +} + /// A root points to the directory which contains Rust crates. rust-analyzer watches all files in /// all roots. Roots might be nested. #[derive(Clone, Debug, Deserialize)] @@ -57,13 +64,6 @@ pub struct Dep { pub(crate) name: String, } -/// Roots and crates that compose this Rust project. -#[derive(Clone, Debug, Deserialize)] -pub struct JsonProject { - pub(crate) roots: Vec, - pub(crate) crates: Vec, -} - #[cfg(test)] mod tests { use super::*; From d4b21476a8f8dc882e2ace78e156da6fcb70a8ff Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Wed, 3 Jun 2020 10:52:35 +0200 Subject: [PATCH 032/172] Derive local roots from Workspaces --- crates/rust-analyzer/src/main_loop.rs | 1 - crates/rust-analyzer/src/world.rs | 24 ++++++++++++++---------- 2 files changed, 14 insertions(+), 11 deletions(-) diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs index f1287d52cd32..2e5499485f29 100644 --- a/crates/rust-analyzer/src/main_loop.rs +++ b/crates/rust-analyzer/src/main_loop.rs @@ -164,7 +164,6 @@ pub fn main_loop(ws_roots: Vec, config: Config, connection: Connection) } WorldState::new( - ws_roots, workspaces, config.lru_capacity, &globs, diff --git a/crates/rust-analyzer/src/world.rs b/crates/rust-analyzer/src/world.rs index 367272925be5..c1010e86aac5 100644 --- a/crates/rust-analyzer/src/world.rs +++ b/crates/rust-analyzer/src/world.rs @@ -58,7 +58,7 @@ fn create_flycheck(workspaces: &[ProjectWorkspace], config: &FlycheckConfig) -> #[derive(Debug)] pub struct WorldState { pub config: Config, - pub roots: Vec, + pub local_roots: Vec, pub workspaces: Arc>, pub analysis_host: AnalysisHost, pub vfs: Arc>, @@ -81,7 +81,6 @@ pub struct WorldSnapshot { impl WorldState { pub fn new( - folder_roots: Vec, workspaces: Vec, lru_capacity: Option, exclude_globs: &[Glob], @@ -93,6 +92,7 @@ impl WorldState { let extern_dirs: FxHashSet<_> = workspaces.iter().flat_map(ProjectWorkspace::out_dirs).collect(); + let mut local_roots = Vec::new(); let roots: Vec<_> = { let create_filter = |is_member| { RustPackageFilterBuilder::default() @@ -100,12 +100,16 @@ impl WorldState { .exclude(exclude_globs.iter().cloned()) .into_vfs_filter() }; - folder_roots + workspaces .iter() - .map(|path| RootEntry::new(path.clone(), create_filter(true))) - .chain(workspaces.iter().flat_map(ProjectWorkspace::to_roots).map(|pkg_root| { - RootEntry::new(pkg_root.path().to_owned(), create_filter(pkg_root.is_member())) - })) + .flat_map(ProjectWorkspace::to_roots) + .map(|pkg_root| { + let path = pkg_root.path().to_owned(); + if pkg_root.is_member() { + local_roots.push(path.clone()); + } + RootEntry::new(path, create_filter(pkg_root.is_member())) + }) .chain( extern_dirs .iter() @@ -121,7 +125,7 @@ impl WorldState { let mut extern_source_roots = FxHashMap::default(); for r in vfs_roots { let vfs_root_path = vfs.root2path(r); - let is_local = folder_roots.iter().any(|it| vfs_root_path.starts_with(it)); + let is_local = local_roots.iter().any(|it| vfs_root_path.starts_with(it)); change.add_root(SourceRootId(r.0), is_local); change.set_debug_root_path(SourceRootId(r.0), vfs_root_path.display().to_string()); @@ -178,7 +182,7 @@ impl WorldState { analysis_host.apply_change(change); WorldState { config, - roots: folder_roots, + local_roots, workspaces: Arc::new(workspaces), analysis_host, vfs: Arc::new(RwLock::new(vfs)), @@ -216,7 +220,7 @@ impl WorldState { match c { VfsChange::AddRoot { root, files } => { let root_path = self.vfs.read().root2path(root); - let is_local = self.roots.iter().any(|r| root_path.starts_with(r)); + let is_local = self.local_roots.iter().any(|r| root_path.starts_with(r)); if is_local { *roots_scanned += 1; for (file, path, text) in files { From ee181cf6833f338869f878eff11e026abe4e984e Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Wed, 3 Jun 2020 11:07:04 +0200 Subject: [PATCH 033/172] Drop no-project test Eventually, we should support "just open random rust file" use case, we don't really do this now, so let's avoid spending time on it until we fix it properly. --- .../rust-analyzer/tests/heavy_tests/main.rs | 49 ------------------- 1 file changed, 49 deletions(-) diff --git a/crates/rust-analyzer/tests/heavy_tests/main.rs b/crates/rust-analyzer/tests/heavy_tests/main.rs index c80745945cba..69dc719c54a5 100644 --- a/crates/rust-analyzer/tests/heavy_tests/main.rs +++ b/crates/rust-analyzer/tests/heavy_tests/main.rs @@ -58,55 +58,6 @@ use std::collections::Spam; eprintln!("completion took {:?}", completion_start.elapsed()); } -#[test] -fn test_runnables_no_project() { - if skip_slow_tests() { - return; - } - - let server = project( - r" -//- lib.rs -#[test] -fn foo() { -} -", - ); - server.wait_until_workspace_is_loaded(); - server.request::( - RunnablesParams { text_document: server.doc_id("lib.rs"), position: None }, - json!([ - { - "args": { - "cargoArgs": ["test"], - "executableArgs": ["foo", "--nocapture"], - }, - "kind": "cargo", - "label": "test foo", - "location": { - "targetRange": { - "end": { "character": 1, "line": 2 }, - "start": { "character": 0, "line": 0 } - }, - "targetSelectionRange": { - "end": { "character": 6, "line": 1 }, - "start": { "character": 3, "line": 1 } - }, - "targetUri": "file:///[..]/lib.rs" - } - }, - { - "args": { - "cargoArgs": ["check", "--workspace"], - "executableArgs": [], - }, - "kind": "cargo", - "label": "cargo check --workspace" - } - ]), - ); -} - #[test] fn test_runnables_project() { if skip_slow_tests() { From a87cd8ecc66f1b644e4b11b8438fde1d5575b73c Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Wed, 3 Jun 2020 11:16:08 +0200 Subject: [PATCH 034/172] Rename WorldState -> GlobalState --- crates/rust-analyzer/src/cargo_target_spec.rs | 4 +- crates/rust-analyzer/src/from_proto.rs | 8 +- .../src/{world.rs => global_state.rs} | 18 +- crates/rust-analyzer/src/lib.rs | 2 +- crates/rust-analyzer/src/main_loop.rs | 88 ++--- .../rust-analyzer/src/main_loop/handlers.rs | 357 +++++++++--------- crates/rust-analyzer/src/to_proto.rs | 87 +++-- 7 files changed, 291 insertions(+), 273 deletions(-) rename crates/rust-analyzer/src/{world.rs => global_state.rs} (97%) diff --git a/crates/rust-analyzer/src/cargo_target_spec.rs b/crates/rust-analyzer/src/cargo_target_spec.rs index 008518a089ff..44f856f6b457 100644 --- a/crates/rust-analyzer/src/cargo_target_spec.rs +++ b/crates/rust-analyzer/src/cargo_target_spec.rs @@ -4,7 +4,7 @@ use ra_cfg::CfgExpr; use ra_ide::{FileId, RunnableKind, TestId}; use ra_project_model::{self, ProjectWorkspace, TargetKind}; -use crate::{world::WorldSnapshot, Result}; +use crate::{global_state::GlobalStateSnapshot, Result}; /// Abstract representation of Cargo target. /// @@ -89,7 +89,7 @@ impl CargoTargetSpec { } pub(crate) fn for_file( - world: &WorldSnapshot, + world: &GlobalStateSnapshot, file_id: FileId, ) -> Result> { let &crate_id = match world.analysis().crate_for(file_id)?.first() { diff --git a/crates/rust-analyzer/src/from_proto.rs b/crates/rust-analyzer/src/from_proto.rs index 4bb16a496c09..206673829c48 100644 --- a/crates/rust-analyzer/src/from_proto.rs +++ b/crates/rust-analyzer/src/from_proto.rs @@ -3,7 +3,7 @@ use ra_db::{FileId, FilePosition, FileRange}; use ra_ide::{LineCol, LineIndex}; use ra_syntax::{TextRange, TextSize}; -use crate::{world::WorldSnapshot, Result}; +use crate::{global_state::GlobalStateSnapshot, Result}; pub(crate) fn offset(line_index: &LineIndex, position: lsp_types::Position) -> TextSize { let line_col = LineCol { line: position.line as u32, col_utf16: position.character as u32 }; @@ -16,12 +16,12 @@ pub(crate) fn text_range(line_index: &LineIndex, range: lsp_types::Range) -> Tex TextRange::new(start, end) } -pub(crate) fn file_id(world: &WorldSnapshot, url: &lsp_types::Url) -> Result { +pub(crate) fn file_id(world: &GlobalStateSnapshot, url: &lsp_types::Url) -> Result { world.uri_to_file_id(url) } pub(crate) fn file_position( - world: &WorldSnapshot, + world: &GlobalStateSnapshot, tdpp: lsp_types::TextDocumentPositionParams, ) -> Result { let file_id = file_id(world, &tdpp.text_document.uri)?; @@ -31,7 +31,7 @@ pub(crate) fn file_position( } pub(crate) fn file_range( - world: &WorldSnapshot, + world: &GlobalStateSnapshot, text_document_identifier: lsp_types::TextDocumentIdentifier, range: lsp_types::Range, ) -> Result { diff --git a/crates/rust-analyzer/src/world.rs b/crates/rust-analyzer/src/global_state.rs similarity index 97% rename from crates/rust-analyzer/src/world.rs rename to crates/rust-analyzer/src/global_state.rs index c1010e86aac5..0bebb5bf615a 100644 --- a/crates/rust-analyzer/src/world.rs +++ b/crates/rust-analyzer/src/global_state.rs @@ -50,13 +50,13 @@ fn create_flycheck(workspaces: &[ProjectWorkspace], config: &FlycheckConfig) -> }) } -/// `WorldState` is the primary mutable state of the language server +/// `GlobalState` is the primary mutable state of the language server /// /// The most interesting components are `vfs`, which stores a consistent /// snapshot of the file systems, and `analysis_host`, which stores our /// incremental salsa database. #[derive(Debug)] -pub struct WorldState { +pub struct GlobalState { pub config: Config, pub local_roots: Vec, pub workspaces: Arc>, @@ -70,7 +70,7 @@ pub struct WorldState { } /// An immutable snapshot of the world's state at a point in time. -pub struct WorldSnapshot { +pub struct GlobalStateSnapshot { pub config: Config, pub workspaces: Arc>, pub analysis: Analysis, @@ -79,14 +79,14 @@ pub struct WorldSnapshot { vfs: Arc>, } -impl WorldState { +impl GlobalState { pub fn new( workspaces: Vec, lru_capacity: Option, exclude_globs: &[Glob], watch: Watch, config: Config, - ) -> WorldState { + ) -> GlobalState { let mut change = AnalysisChange::new(); let extern_dirs: FxHashSet<_> = @@ -180,7 +180,7 @@ impl WorldState { let mut analysis_host = AnalysisHost::new(lru_capacity); analysis_host.apply_change(change); - WorldState { + GlobalState { config, local_roots, workspaces: Arc::new(workspaces), @@ -255,8 +255,8 @@ impl WorldState { self.analysis_host.apply_change(change); } - pub fn snapshot(&self) -> WorldSnapshot { - WorldSnapshot { + pub fn snapshot(&self) -> GlobalStateSnapshot { + GlobalStateSnapshot { config: self.config.clone(), workspaces: Arc::clone(&self.workspaces), analysis: self.analysis_host.analysis(), @@ -279,7 +279,7 @@ impl WorldState { } } -impl WorldSnapshot { +impl GlobalStateSnapshot { pub fn analysis(&self) -> &Analysis { &self.analysis } diff --git a/crates/rust-analyzer/src/lib.rs b/crates/rust-analyzer/src/lib.rs index 57d0e92188e3..609cb69d3bff 100644 --- a/crates/rust-analyzer/src/lib.rs +++ b/crates/rust-analyzer/src/lib.rs @@ -26,7 +26,7 @@ mod main_loop; mod markdown; pub mod lsp_ext; pub mod config; -mod world; +mod global_state; mod diagnostics; mod semantic_tokens; diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs index 2e5499485f29..35f2d7001e1c 100644 --- a/crates/rust-analyzer/src/main_loop.rs +++ b/crates/rust-analyzer/src/main_loop.rs @@ -38,12 +38,13 @@ use threadpool::ThreadPool; use crate::{ config::{Config, FilesWatcher}, diagnostics::{to_proto::url_from_path_with_drive_lowercasing, DiagnosticTask}, - from_proto, lsp_ext, + from_proto, + global_state::{GlobalState, GlobalStateSnapshot}, + lsp_ext, main_loop::{ pending_requests::{PendingRequest, PendingRequests}, subscriptions::Subscriptions, }, - world::{WorldSnapshot, WorldState}, Result, }; @@ -92,7 +93,7 @@ pub fn main_loop(ws_roots: Vec, config: Config, connection: Connection) } let mut loop_state = LoopState::default(); - let mut world_state = { + let mut global_state = { let workspaces = { // FIXME: support dynamic workspace loading. let project_roots: FxHashSet<_> = ws_roots @@ -163,7 +164,7 @@ pub fn main_loop(ws_roots: Vec, config: Config, connection: Connection) connection.sender.send(request.into()).unwrap(); } - WorldState::new( + GlobalState::new( workspaces, config.lru_capacity, &globs, @@ -172,7 +173,7 @@ pub fn main_loop(ws_roots: Vec, config: Config, connection: Connection) ) }; - loop_state.roots_total = world_state.vfs.read().n_roots(); + loop_state.roots_total = global_state.vfs.read().n_roots(); let pool = ThreadPool::default(); let (task_sender, task_receiver) = unbounded::(); @@ -190,12 +191,12 @@ pub fn main_loop(ws_roots: Vec, config: Config, connection: Connection) Err(RecvError) => return Err("client exited without shutdown".into()), }, recv(task_receiver) -> task => Event::Task(task.unwrap()), - recv(world_state.task_receiver) -> task => match task { + recv(global_state.task_receiver) -> task => match task { Ok(task) => Event::Vfs(task), Err(RecvError) => return Err("vfs died".into()), }, recv(libdata_receiver) -> data => Event::Lib(data.unwrap()), - recv(world_state.flycheck.as_ref().map_or(&never(), |it| &it.task_recv)) -> task => match task { + recv(global_state.flycheck.as_ref().map_or(&never(), |it| &it.task_recv)) -> task => match task { Ok(task) => Event::CheckWatcher(task), Err(RecvError) => return Err("check watcher died".into()), } @@ -210,16 +211,16 @@ pub fn main_loop(ws_roots: Vec, config: Config, connection: Connection) &task_sender, &libdata_sender, &connection, - &mut world_state, + &mut global_state, &mut loop_state, event, )?; } } - world_state.analysis_host.request_cancellation(); + global_state.analysis_host.request_cancellation(); log::info!("waiting for tasks to finish..."); task_receiver.into_iter().for_each(|task| { - on_task(task, &connection.sender, &mut loop_state.pending_requests, &mut world_state) + on_task(task, &connection.sender, &mut loop_state.pending_requests, &mut global_state) }); libdata_receiver.into_iter().for_each(drop); log::info!("...tasks have finished"); @@ -228,7 +229,7 @@ pub fn main_loop(ws_roots: Vec, config: Config, connection: Connection) drop(pool); log::info!("...threadpool has finished"); - let vfs = Arc::try_unwrap(world_state.vfs).expect("all snapshots should be dead"); + let vfs = Arc::try_unwrap(global_state.vfs).expect("all snapshots should be dead"); drop(vfs); Ok(()) @@ -319,7 +320,7 @@ fn loop_turn( task_sender: &Sender, libdata_sender: &Sender, connection: &Connection, - world_state: &mut WorldState, + global_state: &mut GlobalState, loop_state: &mut LoopState, event: Event, ) -> Result<()> { @@ -335,22 +336,22 @@ fn loop_turn( match event { Event::Task(task) => { - on_task(task, &connection.sender, &mut loop_state.pending_requests, world_state); - world_state.maybe_collect_garbage(); + on_task(task, &connection.sender, &mut loop_state.pending_requests, global_state); + global_state.maybe_collect_garbage(); } Event::Vfs(task) => { - world_state.vfs.write().handle_task(task); + global_state.vfs.write().handle_task(task); } Event::Lib(lib) => { - world_state.add_lib(lib); - world_state.maybe_collect_garbage(); + global_state.add_lib(lib); + global_state.maybe_collect_garbage(); loop_state.in_flight_libraries -= 1; loop_state.roots_scanned += 1; } - Event::CheckWatcher(task) => on_check_task(task, world_state, task_sender)?, + Event::CheckWatcher(task) => on_check_task(task, global_state, task_sender)?, Event::Msg(msg) => match msg { Message::Request(req) => on_request( - world_state, + global_state, &mut loop_state.pending_requests, pool, task_sender, @@ -359,7 +360,7 @@ fn loop_turn( req, )?, Message::Notification(not) => { - on_notification(&connection.sender, world_state, loop_state, not)?; + on_notification(&connection.sender, global_state, loop_state, not)?; } Message::Response(resp) => { let removed = loop_state.pending_responses.remove(&resp.id); @@ -378,9 +379,9 @@ fn loop_turn( } (None, Some(configs)) => { if let Some(new_config) = configs.get(0) { - let mut config = world_state.config.clone(); + let mut config = global_state.config.clone(); config.update(&new_config); - world_state.update_configuration(config); + global_state.update_configuration(config); } } (None, None) => { @@ -393,7 +394,7 @@ fn loop_turn( }; let mut state_changed = false; - if let Some(changes) = world_state.process_changes(&mut loop_state.roots_scanned) { + if let Some(changes) = global_state.process_changes(&mut loop_state.roots_scanned) { state_changed = true; loop_state.pending_libraries.extend(changes); } @@ -415,7 +416,7 @@ fn loop_turn( } let show_progress = - !loop_state.workspace_loaded && world_state.config.client_caps.work_done_progress; + !loop_state.workspace_loaded && global_state.config.client_caps.work_done_progress; if !loop_state.workspace_loaded && loop_state.roots_scanned == loop_state.roots_total @@ -424,7 +425,7 @@ fn loop_turn( { state_changed = true; loop_state.workspace_loaded = true; - if let Some(flycheck) = &world_state.flycheck { + if let Some(flycheck) = &global_state.flycheck { flycheck.update(); } } @@ -436,13 +437,13 @@ fn loop_turn( if state_changed && loop_state.workspace_loaded { update_file_notifications_on_threadpool( pool, - world_state.snapshot(), + global_state.snapshot(), task_sender.clone(), loop_state.subscriptions.subscriptions(), ); pool.execute({ let subs = loop_state.subscriptions.subscriptions(); - let snap = world_state.snapshot(); + let snap = global_state.snapshot(); move || snap.analysis().prime_caches(subs).unwrap_or_else(|_: Canceled| ()) }); } @@ -466,7 +467,7 @@ fn on_task( task: Task, msg_sender: &Sender, pending_requests: &mut PendingRequests, - state: &mut WorldState, + state: &mut GlobalState, ) { match task { Task::Respond(response) => { @@ -484,7 +485,7 @@ fn on_task( } fn on_request( - world: &mut WorldState, + global_state: &mut GlobalState, pending_requests: &mut PendingRequests, pool: &ThreadPool, task_sender: &Sender, @@ -495,7 +496,7 @@ fn on_request( let mut pool_dispatcher = PoolDispatcher { req: Some(req), pool, - world, + global_state, task_sender, msg_sender, pending_requests, @@ -551,7 +552,7 @@ fn on_request( fn on_notification( msg_sender: &Sender, - state: &mut WorldState, + state: &mut GlobalState, loop_state: &mut LoopState, not: Notification, ) -> Result<()> { @@ -725,7 +726,7 @@ fn apply_document_changes( fn on_check_task( task: CheckTask, - world_state: &mut WorldState, + global_state: &mut GlobalState, task_sender: &Sender, ) -> Result<()> { match task { @@ -744,7 +745,7 @@ fn on_check_task( .uri .to_file_path() .map_err(|()| format!("invalid uri: {}", diag.location.uri))?; - let file_id = match world_state.vfs.read().path2file(&path) { + let file_id = match global_state.vfs.read().path2file(&path) { Some(file) => FileId(file.0), None => { log::error!( @@ -764,7 +765,7 @@ fn on_check_task( } CheckTask::Status(status) => { - if world_state.config.client_caps.work_done_progress { + if global_state.config.client_caps.work_done_progress { let progress = match status { Status::Being => { lsp_types::WorkDoneProgress::Begin(lsp_types::WorkDoneProgressBegin { @@ -803,7 +804,7 @@ fn on_check_task( Ok(()) } -fn on_diagnostic_task(task: DiagnosticTask, msg_sender: &Sender, state: &mut WorldState) { +fn on_diagnostic_task(task: DiagnosticTask, msg_sender: &Sender, state: &mut GlobalState) { let subscriptions = state.diagnostics.handle_task(task); for file_id in subscriptions { @@ -878,7 +879,7 @@ fn send_startup_progress(sender: &Sender, loop_state: &mut LoopState) { struct PoolDispatcher<'a> { req: Option, pool: &'a ThreadPool, - world: &'a mut WorldState, + global_state: &'a mut GlobalState, pending_requests: &'a mut PendingRequests, msg_sender: &'a Sender, task_sender: &'a Sender, @@ -889,7 +890,7 @@ impl<'a> PoolDispatcher<'a> { /// Dispatches the request onto the current thread fn on_sync( &mut self, - f: fn(&mut WorldState, R::Params) -> Result, + f: fn(&mut GlobalState, R::Params) -> Result, ) -> Result<&mut Self> where R: lsp_types::request::Request + 'static, @@ -902,18 +903,21 @@ impl<'a> PoolDispatcher<'a> { return Ok(self); } }; - let world = panic::AssertUnwindSafe(&mut *self.world); + let world = panic::AssertUnwindSafe(&mut *self.global_state); let task = panic::catch_unwind(move || { let result = f(world.0, params); result_to_task::(id, result) }) .map_err(|_| format!("sync task {:?} panicked", R::METHOD))?; - on_task(task, self.msg_sender, self.pending_requests, self.world); + on_task(task, self.msg_sender, self.pending_requests, self.global_state); Ok(self) } /// Dispatches the request onto thread pool - fn on(&mut self, f: fn(WorldSnapshot, R::Params) -> Result) -> Result<&mut Self> + fn on( + &mut self, + f: fn(GlobalStateSnapshot, R::Params) -> Result, + ) -> Result<&mut Self> where R: lsp_types::request::Request + 'static, R::Params: DeserializeOwned + Send + 'static, @@ -927,7 +931,7 @@ impl<'a> PoolDispatcher<'a> { }; self.pool.execute({ - let world = self.world.snapshot(); + let world = self.global_state.snapshot(); let sender = self.task_sender.clone(); move || { let result = f(world, params); @@ -1011,7 +1015,7 @@ where fn update_file_notifications_on_threadpool( pool: &ThreadPool, - world: WorldSnapshot, + world: GlobalStateSnapshot, task_sender: Sender, subscriptions: Vec, ) { diff --git a/crates/rust-analyzer/src/main_loop/handlers.rs b/crates/rust-analyzer/src/main_loop/handlers.rs index 7fd6917649a3..1bb8e44735f5 100644 --- a/crates/rust-analyzer/src/main_loop/handlers.rs +++ b/crates/rust-analyzer/src/main_loop/handlers.rs @@ -32,17 +32,16 @@ use crate::{ config::RustfmtConfig, diagnostics::DiagnosticTask, from_json, from_proto, + global_state::GlobalStateSnapshot, lsp_ext::{self, InlayHint, InlayHintsParams}, - to_proto, - world::WorldSnapshot, - LspError, Result, + to_proto, LspError, Result, }; -pub fn handle_analyzer_status(world: WorldSnapshot, _: ()) -> Result { +pub fn handle_analyzer_status(snap: GlobalStateSnapshot, _: ()) -> Result { let _p = profile("handle_analyzer_status"); - let mut buf = world.status(); + let mut buf = snap.status(); format_to!(buf, "\n\nrequests:\n"); - let requests = world.latest_requests.read(); + let requests = snap.latest_requests.read(); for (is_last, r) in requests.iter() { let mark = if is_last { "*" } else { " " }; format_to!(buf, "{}{:4} {:<36}{}ms\n", mark, r.id, r.method, r.duration.as_millis()); @@ -51,37 +50,37 @@ pub fn handle_analyzer_status(world: WorldSnapshot, _: ()) -> Result { } pub fn handle_syntax_tree( - world: WorldSnapshot, + snap: GlobalStateSnapshot, params: lsp_ext::SyntaxTreeParams, ) -> Result { let _p = profile("handle_syntax_tree"); - let id = from_proto::file_id(&world, ¶ms.text_document.uri)?; - let line_index = world.analysis().file_line_index(id)?; + let id = from_proto::file_id(&snap, ¶ms.text_document.uri)?; + let line_index = snap.analysis().file_line_index(id)?; let text_range = params.range.map(|r| from_proto::text_range(&line_index, r)); - let res = world.analysis().syntax_tree(id, text_range)?; + let res = snap.analysis().syntax_tree(id, text_range)?; Ok(res) } pub fn handle_expand_macro( - world: WorldSnapshot, + snap: GlobalStateSnapshot, params: lsp_ext::ExpandMacroParams, ) -> Result> { let _p = profile("handle_expand_macro"); - let file_id = from_proto::file_id(&world, ¶ms.text_document.uri)?; - let line_index = world.analysis().file_line_index(file_id)?; + let file_id = from_proto::file_id(&snap, ¶ms.text_document.uri)?; + let line_index = snap.analysis().file_line_index(file_id)?; let offset = from_proto::offset(&line_index, params.position); - let res = world.analysis().expand_macro(FilePosition { file_id, offset })?; + let res = snap.analysis().expand_macro(FilePosition { file_id, offset })?; Ok(res.map(|it| lsp_ext::ExpandedMacro { name: it.name, expansion: it.expansion })) } pub fn handle_selection_range( - world: WorldSnapshot, + snap: GlobalStateSnapshot, params: lsp_types::SelectionRangeParams, ) -> Result>> { let _p = profile("handle_selection_range"); - let file_id = from_proto::file_id(&world, ¶ms.text_document.uri)?; - let line_index = world.analysis().file_line_index(file_id)?; + let file_id = from_proto::file_id(&snap, ¶ms.text_document.uri)?; + let line_index = snap.analysis().file_line_index(file_id)?; let res: Result> = params .positions .into_iter() @@ -93,7 +92,7 @@ pub fn handle_selection_range( loop { ranges.push(range); let frange = FileRange { file_id, range }; - let next = world.analysis().extend_selection(frange)?; + let next = snap.analysis().extend_selection(frange)?; if next == range { break; } else { @@ -119,18 +118,18 @@ pub fn handle_selection_range( } pub fn handle_matching_brace( - world: WorldSnapshot, + snap: GlobalStateSnapshot, params: lsp_ext::MatchingBraceParams, ) -> Result> { let _p = profile("handle_matching_brace"); - let file_id = from_proto::file_id(&world, ¶ms.text_document.uri)?; - let line_index = world.analysis().file_line_index(file_id)?; + let file_id = from_proto::file_id(&snap, ¶ms.text_document.uri)?; + let line_index = snap.analysis().file_line_index(file_id)?; let res = params .positions .into_iter() .map(|position| { let offset = from_proto::offset(&line_index, position); - let offset = match world.analysis().matching_brace(FilePosition { file_id, offset }) { + let offset = match snap.analysis().matching_brace(FilePosition { file_id, offset }) { Ok(Some(matching_brace_offset)) => matching_brace_offset, Err(_) | Ok(None) => offset, }; @@ -141,17 +140,17 @@ pub fn handle_matching_brace( } pub fn handle_join_lines( - world: WorldSnapshot, + snap: GlobalStateSnapshot, params: lsp_ext::JoinLinesParams, ) -> Result> { let _p = profile("handle_join_lines"); - let file_id = from_proto::file_id(&world, ¶ms.text_document.uri)?; - let line_index = world.analysis().file_line_index(file_id)?; - let line_endings = world.file_line_endings(file_id); + let file_id = from_proto::file_id(&snap, ¶ms.text_document.uri)?; + let line_index = snap.analysis().file_line_index(file_id)?; + let line_endings = snap.file_line_endings(file_id); let mut res = TextEdit::default(); for range in params.ranges { let range = from_proto::text_range(&line_index, range); - let edit = world.analysis().join_lines(FileRange { file_id, range })?; + let edit = snap.analysis().join_lines(FileRange { file_id, range })?; match res.union(edit) { Ok(()) => (), Err(_edit) => { @@ -164,37 +163,37 @@ pub fn handle_join_lines( } pub fn handle_on_enter( - world: WorldSnapshot, + snap: GlobalStateSnapshot, params: lsp_types::TextDocumentPositionParams, ) -> Result>> { let _p = profile("handle_on_enter"); - let position = from_proto::file_position(&world, params)?; - let edit = match world.analysis().on_enter(position)? { + let position = from_proto::file_position(&snap, params)?; + let edit = match snap.analysis().on_enter(position)? { None => return Ok(None), Some(it) => it, }; - let line_index = world.analysis().file_line_index(position.file_id)?; - let line_endings = world.file_line_endings(position.file_id); + let line_index = snap.analysis().file_line_index(position.file_id)?; + let line_endings = snap.file_line_endings(position.file_id); let edit = to_proto::snippet_text_edit_vec(&line_index, line_endings, true, edit); Ok(Some(edit)) } // Don't forget to add new trigger characters to `ServerCapabilities` in `caps.rs`. pub fn handle_on_type_formatting( - world: WorldSnapshot, + snap: GlobalStateSnapshot, params: lsp_types::DocumentOnTypeFormattingParams, ) -> Result>> { let _p = profile("handle_on_type_formatting"); - let mut position = from_proto::file_position(&world, params.text_document_position)?; - let line_index = world.analysis().file_line_index(position.file_id)?; - let line_endings = world.file_line_endings(position.file_id); + let mut position = from_proto::file_position(&snap, params.text_document_position)?; + let line_index = snap.analysis().file_line_index(position.file_id)?; + let line_endings = snap.file_line_endings(position.file_id); // in `ra_ide`, the `on_type` invariant is that // `text.char_at(position) == typed_char`. position.offset -= TextSize::of('.'); let char_typed = params.ch.chars().next().unwrap_or('\0'); assert!({ - let text = world.analysis().file_text(position.file_id)?; + let text = snap.analysis().file_text(position.file_id)?; text[usize::from(position.offset)..].starts_with(char_typed) }); @@ -206,7 +205,7 @@ pub fn handle_on_type_formatting( return Ok(None); } - let edit = world.analysis().on_char_typed(position, char_typed)?; + let edit = snap.analysis().on_char_typed(position, char_typed)?; let mut edit = match edit { Some(it) => it, None => return Ok(None), @@ -220,16 +219,16 @@ pub fn handle_on_type_formatting( } pub fn handle_document_symbol( - world: WorldSnapshot, + snap: GlobalStateSnapshot, params: lsp_types::DocumentSymbolParams, ) -> Result> { let _p = profile("handle_document_symbol"); - let file_id = from_proto::file_id(&world, ¶ms.text_document.uri)?; - let line_index = world.analysis().file_line_index(file_id)?; + let file_id = from_proto::file_id(&snap, ¶ms.text_document.uri)?; + let line_index = snap.analysis().file_line_index(file_id)?; let mut parents: Vec<(DocumentSymbol, Option)> = Vec::new(); - for symbol in world.analysis().file_structure(file_id)? { + for symbol in snap.analysis().file_structure(file_id)? { let doc_symbol = DocumentSymbol { name: symbol.label, detail: symbol.detail, @@ -255,10 +254,10 @@ pub fn handle_document_symbol( } } - let res = if world.config.client_caps.hierarchical_symbols { + let res = if snap.config.client_caps.hierarchical_symbols { document_symbols.into() } else { - let url = to_proto::url(&world, file_id)?; + let url = to_proto::url(&snap, file_id)?; let mut symbol_information = Vec::::new(); for symbol in document_symbols { flatten_document_symbol(&symbol, None, &url, &mut symbol_information); @@ -288,7 +287,7 @@ pub fn handle_document_symbol( } pub fn handle_workspace_symbol( - world: WorldSnapshot, + snap: GlobalStateSnapshot, params: lsp_types::WorkspaceSymbolParams, ) -> Result>> { let _p = profile("handle_workspace_symbol"); @@ -306,22 +305,22 @@ pub fn handle_workspace_symbol( q.limit(128); q }; - let mut res = exec_query(&world, query)?; + let mut res = exec_query(&snap, query)?; if res.is_empty() && !all_symbols { let mut query = Query::new(params.query); query.limit(128); - res = exec_query(&world, query)?; + res = exec_query(&snap, query)?; } return Ok(Some(res)); - fn exec_query(world: &WorldSnapshot, query: Query) -> Result> { + fn exec_query(snap: &GlobalStateSnapshot, query: Query) -> Result> { let mut res = Vec::new(); - for nav in world.analysis().symbol_search(query)? { + for nav in snap.analysis().symbol_search(query)? { let info = SymbolInformation { name: nav.name().to_string(), kind: to_proto::symbol_kind(nav.kind()), - location: to_proto::location(world, nav.file_range())?, + location: to_proto::location(snap, nav.file_range())?, container_name: nav.container_name().map(|v| v.to_string()), deprecated: None, }; @@ -332,73 +331,73 @@ pub fn handle_workspace_symbol( } pub fn handle_goto_definition( - world: WorldSnapshot, + snap: GlobalStateSnapshot, params: lsp_types::GotoDefinitionParams, ) -> Result> { let _p = profile("handle_goto_definition"); - let position = from_proto::file_position(&world, params.text_document_position_params)?; - let nav_info = match world.analysis().goto_definition(position)? { + let position = from_proto::file_position(&snap, params.text_document_position_params)?; + let nav_info = match snap.analysis().goto_definition(position)? { None => return Ok(None), Some(it) => it, }; let src = FileRange { file_id: position.file_id, range: nav_info.range }; - let res = to_proto::goto_definition_response(&world, Some(src), nav_info.info)?; + let res = to_proto::goto_definition_response(&snap, Some(src), nav_info.info)?; Ok(Some(res)) } pub fn handle_goto_implementation( - world: WorldSnapshot, + snap: GlobalStateSnapshot, params: lsp_types::request::GotoImplementationParams, ) -> Result> { let _p = profile("handle_goto_implementation"); - let position = from_proto::file_position(&world, params.text_document_position_params)?; - let nav_info = match world.analysis().goto_implementation(position)? { + let position = from_proto::file_position(&snap, params.text_document_position_params)?; + let nav_info = match snap.analysis().goto_implementation(position)? { None => return Ok(None), Some(it) => it, }; let src = FileRange { file_id: position.file_id, range: nav_info.range }; - let res = to_proto::goto_definition_response(&world, Some(src), nav_info.info)?; + let res = to_proto::goto_definition_response(&snap, Some(src), nav_info.info)?; Ok(Some(res)) } pub fn handle_goto_type_definition( - world: WorldSnapshot, + snap: GlobalStateSnapshot, params: lsp_types::request::GotoTypeDefinitionParams, ) -> Result> { let _p = profile("handle_goto_type_definition"); - let position = from_proto::file_position(&world, params.text_document_position_params)?; - let nav_info = match world.analysis().goto_type_definition(position)? { + let position = from_proto::file_position(&snap, params.text_document_position_params)?; + let nav_info = match snap.analysis().goto_type_definition(position)? { None => return Ok(None), Some(it) => it, }; let src = FileRange { file_id: position.file_id, range: nav_info.range }; - let res = to_proto::goto_definition_response(&world, Some(src), nav_info.info)?; + let res = to_proto::goto_definition_response(&snap, Some(src), nav_info.info)?; Ok(Some(res)) } pub fn handle_parent_module( - world: WorldSnapshot, + snap: GlobalStateSnapshot, params: lsp_types::TextDocumentPositionParams, ) -> Result> { let _p = profile("handle_parent_module"); - let position = from_proto::file_position(&world, params)?; - let navs = world.analysis().parent_module(position)?; - let res = to_proto::goto_definition_response(&world, None, navs)?; + let position = from_proto::file_position(&snap, params)?; + let navs = snap.analysis().parent_module(position)?; + let res = to_proto::goto_definition_response(&snap, None, navs)?; Ok(Some(res)) } pub fn handle_runnables( - world: WorldSnapshot, + snap: GlobalStateSnapshot, params: lsp_ext::RunnablesParams, ) -> Result> { let _p = profile("handle_runnables"); - let file_id = from_proto::file_id(&world, ¶ms.text_document.uri)?; - let line_index = world.analysis().file_line_index(file_id)?; + let file_id = from_proto::file_id(&snap, ¶ms.text_document.uri)?; + let line_index = snap.analysis().file_line_index(file_id)?; let offset = params.position.map(|it| from_proto::offset(&line_index, it)); let mut res = Vec::new(); - let workspace_root = world.workspace_root_for(file_id); - let cargo_spec = CargoTargetSpec::for_file(&world, file_id)?; - for runnable in world.analysis().runnables(file_id)? { + let workspace_root = snap.workspace_root_for(file_id); + let cargo_spec = CargoTargetSpec::for_file(&snap, file_id)?; + for runnable in snap.analysis().runnables(file_id)? { if let Some(offset) = offset { if !runnable.nav.full_range().contains_inclusive(offset) { continue; @@ -413,7 +412,7 @@ pub fn handle_runnables( } } } - res.push(to_proto::runnable(&world, file_id, runnable)?); + res.push(to_proto::runnable(&snap, file_id, runnable)?); } // Add `cargo check` and `cargo test` for the whole package @@ -453,16 +452,16 @@ pub fn handle_runnables( } pub fn handle_completion( - world: WorldSnapshot, + snap: GlobalStateSnapshot, params: lsp_types::CompletionParams, ) -> Result> { let _p = profile("handle_completion"); - let position = from_proto::file_position(&world, params.text_document_position)?; + let position = from_proto::file_position(&snap, params.text_document_position)?; let completion_triggered_after_single_colon = { let mut res = false; if let Some(ctx) = params.context { if ctx.trigger_character.unwrap_or_default() == ":" { - let source_file = world.analysis().parse(position.file_id)?; + let source_file = snap.analysis().parse(position.file_id)?; let syntax = source_file.syntax(); let text = syntax.text(); if let Some(next_char) = text.char_at(position.offset) { @@ -480,12 +479,12 @@ pub fn handle_completion( return Ok(None); } - let items = match world.analysis().completions(&world.config.completion, position)? { + let items = match snap.analysis().completions(&snap.config.completion, position)? { None => return Ok(None), Some(items) => items, }; - let line_index = world.analysis().file_line_index(position.file_id)?; - let line_endings = world.file_line_endings(position.file_id); + let line_index = snap.analysis().file_line_index(position.file_id)?; + let line_endings = snap.file_line_endings(position.file_id); let items: Vec = items .into_iter() .map(|item| to_proto::completion_item(&line_index, line_endings, item)) @@ -495,15 +494,15 @@ pub fn handle_completion( } pub fn handle_folding_range( - world: WorldSnapshot, + snap: GlobalStateSnapshot, params: FoldingRangeParams, ) -> Result>> { let _p = profile("handle_folding_range"); - let file_id = from_proto::file_id(&world, ¶ms.text_document.uri)?; - let folds = world.analysis().folding_ranges(file_id)?; - let text = world.analysis().file_text(file_id)?; - let line_index = world.analysis().file_line_index(file_id)?; - let line_folding_only = world.config.client_caps.line_folding_only; + let file_id = from_proto::file_id(&snap, ¶ms.text_document.uri)?; + let folds = snap.analysis().folding_ranges(file_id)?; + let text = snap.analysis().file_text(file_id)?; + let line_index = snap.analysis().file_line_index(file_id)?; + let line_folding_only = snap.config.client_caps.line_folding_only; let res = folds .into_iter() .map(|it| to_proto::folding_range(&*text, &line_index, line_folding_only, it)) @@ -512,16 +511,16 @@ pub fn handle_folding_range( } pub fn handle_signature_help( - world: WorldSnapshot, + snap: GlobalStateSnapshot, params: lsp_types::SignatureHelpParams, ) -> Result> { let _p = profile("handle_signature_help"); - let position = from_proto::file_position(&world, params.text_document_position_params)?; - let call_info = match world.analysis().call_info(position)? { + let position = from_proto::file_position(&snap, params.text_document_position_params)?; + let call_info = match snap.analysis().call_info(position)? { None => return Ok(None), Some(it) => it, }; - let concise = !world.config.call_info_full; + let concise = !snap.config.call_info_full; let mut active_parameter = call_info.active_parameter.map(|it| it as i64); if concise && call_info.signature.has_self_param { active_parameter = active_parameter.map(|it| it.saturating_sub(1)); @@ -535,14 +534,17 @@ pub fn handle_signature_help( })) } -pub fn handle_hover(world: WorldSnapshot, params: lsp_types::HoverParams) -> Result> { +pub fn handle_hover( + snap: GlobalStateSnapshot, + params: lsp_types::HoverParams, +) -> Result> { let _p = profile("handle_hover"); - let position = from_proto::file_position(&world, params.text_document_position_params)?; - let info = match world.analysis().hover(position)? { + let position = from_proto::file_position(&snap, params.text_document_position_params)?; + let info = match snap.analysis().hover(position)? { None => return Ok(None), Some(info) => info, }; - let line_index = world.analysis.file_line_index(position.file_id)?; + let line_index = snap.analysis.file_line_index(position.file_id)?; let range = to_proto::range(&line_index, info.range); let res = Hover { contents: HoverContents::Markup(MarkupContent { @@ -555,26 +557,29 @@ pub fn handle_hover(world: WorldSnapshot, params: lsp_types::HoverParams) -> Res } pub fn handle_prepare_rename( - world: WorldSnapshot, + snap: GlobalStateSnapshot, params: lsp_types::TextDocumentPositionParams, ) -> Result> { let _p = profile("handle_prepare_rename"); - let position = from_proto::file_position(&world, params)?; + let position = from_proto::file_position(&snap, params)?; - let optional_change = world.analysis().rename(position, "dummy")?; + let optional_change = snap.analysis().rename(position, "dummy")?; let range = match optional_change { None => return Ok(None), Some(it) => it.range, }; - let line_index = world.analysis().file_line_index(position.file_id)?; + let line_index = snap.analysis().file_line_index(position.file_id)?; let range = to_proto::range(&line_index, range); Ok(Some(PrepareRenameResponse::Range(range))) } -pub fn handle_rename(world: WorldSnapshot, params: RenameParams) -> Result> { +pub fn handle_rename( + snap: GlobalStateSnapshot, + params: RenameParams, +) -> Result> { let _p = profile("handle_rename"); - let position = from_proto::file_position(&world, params.text_document_position)?; + let position = from_proto::file_position(&snap, params.text_document_position)?; if params.new_name.is_empty() { return Err(LspError::new( @@ -584,36 +589,36 @@ pub fn handle_rename(world: WorldSnapshot, params: RenameParams) -> Result return Ok(None), Some(it) => it.info, }; - let workspace_edit = to_proto::workspace_edit(&world, source_change)?; + let workspace_edit = to_proto::workspace_edit(&snap, source_change)?; Ok(Some(workspace_edit)) } pub fn handle_references( - world: WorldSnapshot, + snap: GlobalStateSnapshot, params: lsp_types::ReferenceParams, ) -> Result>> { let _p = profile("handle_references"); - let position = from_proto::file_position(&world, params.text_document_position)?; + let position = from_proto::file_position(&snap, params.text_document_position)?; - let refs = match world.analysis().find_all_refs(position, None)? { + let refs = match snap.analysis().find_all_refs(position, None)? { None => return Ok(None), Some(refs) => refs, }; let locations = if params.context.include_declaration { refs.into_iter() - .filter_map(|reference| to_proto::location(&world, reference.file_range).ok()) + .filter_map(|reference| to_proto::location(&snap, reference.file_range).ok()) .collect() } else { // Only iterate over the references if include_declaration was false refs.references() .iter() - .filter_map(|reference| to_proto::location(&world, reference.file_range).ok()) + .filter_map(|reference| to_proto::location(&snap, reference.file_range).ok()) .collect() }; @@ -621,24 +626,24 @@ pub fn handle_references( } pub fn handle_formatting( - world: WorldSnapshot, + snap: GlobalStateSnapshot, params: DocumentFormattingParams, ) -> Result>> { let _p = profile("handle_formatting"); - let file_id = from_proto::file_id(&world, ¶ms.text_document.uri)?; - let file = world.analysis().file_text(file_id)?; - let crate_ids = world.analysis().crate_for(file_id)?; + let file_id = from_proto::file_id(&snap, ¶ms.text_document.uri)?; + let file = snap.analysis().file_text(file_id)?; + let crate_ids = snap.analysis().crate_for(file_id)?; - let file_line_index = world.analysis().file_line_index(file_id)?; + let file_line_index = snap.analysis().file_line_index(file_id)?; let end_position = to_proto::position(&file_line_index, TextSize::of(file.as_str())); - let mut rustfmt = match &world.config.rustfmt { + let mut rustfmt = match &snap.config.rustfmt { RustfmtConfig::Rustfmt { extra_args } => { let mut cmd = process::Command::new("rustfmt"); cmd.args(extra_args); if let Some(&crate_id) = crate_ids.first() { // Assume all crates are in the same edition - let edition = world.analysis().crate_edition(crate_id)?; + let edition = snap.analysis().crate_edition(crate_id)?; cmd.arg("--edition"); cmd.arg(edition.to_string()); } @@ -697,23 +702,23 @@ pub fn handle_formatting( } pub fn handle_code_action( - world: WorldSnapshot, + snap: GlobalStateSnapshot, params: lsp_types::CodeActionParams, ) -> Result>> { let _p = profile("handle_code_action"); // We intentionally don't support command-based actions, as those either // requires custom client-code anyway, or requires server-initiated edits. // Server initiated edits break causality, so we avoid those as well. - if !world.config.client_caps.code_action_literals { + if !snap.config.client_caps.code_action_literals { return Ok(None); } - let file_id = from_proto::file_id(&world, ¶ms.text_document.uri)?; - let line_index = world.analysis().file_line_index(file_id)?; + let file_id = from_proto::file_id(&snap, ¶ms.text_document.uri)?; + let line_index = snap.analysis().file_line_index(file_id)?; let range = from_proto::text_range(&line_index, params.range); let frange = FileRange { file_id, range }; - let diagnostics = world.analysis().diagnostics(file_id)?; + let diagnostics = snap.analysis().diagnostics(file_id)?; let mut res: Vec = Vec::new(); let fixes_from_diagnostics = diagnostics @@ -724,13 +729,13 @@ pub fn handle_code_action( for fix in fixes_from_diagnostics { let title = fix.label; - let edit = to_proto::snippet_workspace_edit(&world, fix.source_change)?; + let edit = to_proto::snippet_workspace_edit(&snap, fix.source_change)?; let action = lsp_ext::CodeAction { title, group: None, kind: None, edit: Some(edit), command: None }; res.push(action); } - for fix in world.check_fixes.get(&file_id).into_iter().flatten() { + for fix in snap.check_fixes.get(&file_id).into_iter().flatten() { let fix_range = from_proto::text_range(&line_index, fix.range); if fix_range.intersect(range).is_none() { continue; @@ -738,31 +743,31 @@ pub fn handle_code_action( res.push(fix.action.clone()); } - for assist in world.analysis().assists(&world.config.assist, frange)?.into_iter() { - res.push(to_proto::code_action(&world, assist)?.into()); + for assist in snap.analysis().assists(&snap.config.assist, frange)?.into_iter() { + res.push(to_proto::code_action(&snap, assist)?.into()); } Ok(Some(res)) } pub fn handle_code_lens( - world: WorldSnapshot, + snap: GlobalStateSnapshot, params: lsp_types::CodeLensParams, ) -> Result>> { let _p = profile("handle_code_lens"); let mut lenses: Vec = Default::default(); - if world.config.lens.none() { + if snap.config.lens.none() { // early return before any db query! return Ok(Some(lenses)); } - let file_id = from_proto::file_id(&world, ¶ms.text_document.uri)?; - let line_index = world.analysis().file_line_index(file_id)?; - let cargo_spec = CargoTargetSpec::for_file(&world, file_id)?; + let file_id = from_proto::file_id(&snap, ¶ms.text_document.uri)?; + let line_index = snap.analysis().file_line_index(file_id)?; + let cargo_spec = CargoTargetSpec::for_file(&snap, file_id)?; - if world.config.lens.runnable() { + if snap.config.lens.runnable() { // Gather runnables - for runnable in world.analysis().runnables(file_id)? { + for runnable in snap.analysis().runnables(file_id)? { let (run_title, debugee) = match &runnable.kind { RunnableKind::Test { .. } | RunnableKind::TestMod { .. } => { ("▶\u{fe0e} Run Test", true) @@ -788,8 +793,8 @@ pub fn handle_code_lens( }; let range = to_proto::range(&line_index, runnable.nav.range()); - let r = to_proto::runnable(&world, file_id, runnable)?; - if world.config.lens.run { + let r = to_proto::runnable(&snap, file_id, runnable)?; + if snap.config.lens.run { let lens = CodeLens { range, command: Some(Command { @@ -802,7 +807,7 @@ pub fn handle_code_lens( lenses.push(lens); } - if debugee && world.config.lens.debug { + if debugee && snap.config.lens.debug { let debug_lens = CodeLens { range, command: Some(Command { @@ -817,11 +822,10 @@ pub fn handle_code_lens( } } - if world.config.lens.impementations { + if snap.config.lens.impementations { // Handle impls lenses.extend( - world - .analysis() + snap.analysis() .file_structure(file_id)? .into_iter() .filter(|it| match it.kind { @@ -856,14 +860,17 @@ enum CodeLensResolveData { Impls(lsp_types::request::GotoImplementationParams), } -pub fn handle_code_lens_resolve(world: WorldSnapshot, code_lens: CodeLens) -> Result { +pub fn handle_code_lens_resolve( + snap: GlobalStateSnapshot, + code_lens: CodeLens, +) -> Result { let _p = profile("handle_code_lens_resolve"); let data = code_lens.data.unwrap(); let resolve = from_json::>("CodeLensResolveData", data)?; match resolve { Some(CodeLensResolveData::Impls(lens_params)) => { let locations: Vec = - match handle_goto_implementation(world, lens_params.clone())? { + match handle_goto_implementation(snap, lens_params.clone())? { Some(lsp_types::GotoDefinitionResponse::Scalar(loc)) => vec![loc], Some(lsp_types::GotoDefinitionResponse::Array(locs)) => locs, Some(lsp_types::GotoDefinitionResponse::Link(links)) => links @@ -902,14 +909,14 @@ pub fn handle_code_lens_resolve(world: WorldSnapshot, code_lens: CodeLens) -> Re } pub fn handle_document_highlight( - world: WorldSnapshot, + snap: GlobalStateSnapshot, params: lsp_types::DocumentHighlightParams, ) -> Result>> { let _p = profile("handle_document_highlight"); - let position = from_proto::file_position(&world, params.text_document_position_params)?; - let line_index = world.analysis().file_line_index(position.file_id)?; + let position = from_proto::file_position(&snap, params.text_document_position_params)?; + let line_index = snap.analysis().file_line_index(position.file_id)?; - let refs = match world + let refs = match snap .analysis() .find_all_refs(position, Some(SearchScope::single_file(position.file_id)))? { @@ -929,19 +936,19 @@ pub fn handle_document_highlight( } pub fn handle_ssr( - world: WorldSnapshot, + snap: GlobalStateSnapshot, params: lsp_ext::SsrParams, ) -> Result { let _p = profile("handle_ssr"); let source_change = - world.analysis().structural_search_replace(¶ms.query, params.parse_only)??; - to_proto::workspace_edit(&world, source_change) + snap.analysis().structural_search_replace(¶ms.query, params.parse_only)??; + to_proto::workspace_edit(&snap, source_change) } -pub fn publish_diagnostics(world: &WorldSnapshot, file_id: FileId) -> Result { +pub fn publish_diagnostics(snap: &GlobalStateSnapshot, file_id: FileId) -> Result { let _p = profile("publish_diagnostics"); - let line_index = world.analysis().file_line_index(file_id)?; - let diagnostics: Vec = world + let line_index = snap.analysis().file_line_index(file_id)?; + let diagnostics: Vec = snap .analysis() .diagnostics(file_id)? .into_iter() @@ -959,28 +966,28 @@ pub fn publish_diagnostics(world: &WorldSnapshot, file_id: FileId) -> Result Result> { let _p = profile("handle_inlay_hints"); - let file_id = from_proto::file_id(&world, ¶ms.text_document.uri)?; - let analysis = world.analysis(); + let file_id = from_proto::file_id(&snap, ¶ms.text_document.uri)?; + let analysis = snap.analysis(); let line_index = analysis.file_line_index(file_id)?; Ok(analysis - .inlay_hints(file_id, &world.config.inlay_hints)? + .inlay_hints(file_id, &snap.config.inlay_hints)? .into_iter() .map(|it| to_proto::inlay_int(&line_index, it)) .collect()) } pub fn handle_call_hierarchy_prepare( - world: WorldSnapshot, + snap: GlobalStateSnapshot, params: CallHierarchyPrepareParams, ) -> Result>> { let _p = profile("handle_call_hierarchy_prepare"); - let position = from_proto::file_position(&world, params.text_document_position_params)?; + let position = from_proto::file_position(&snap, params.text_document_position_params)?; - let nav_info = match world.analysis().call_hierarchy(position)? { + let nav_info = match snap.analysis().call_hierarchy(position)? { None => return Ok(None), Some(it) => it, }; @@ -989,24 +996,24 @@ pub fn handle_call_hierarchy_prepare( let res = navs .into_iter() .filter(|it| it.kind() == SyntaxKind::FN_DEF) - .map(|it| to_proto::call_hierarchy_item(&world, it)) + .map(|it| to_proto::call_hierarchy_item(&snap, it)) .collect::>>()?; Ok(Some(res)) } pub fn handle_call_hierarchy_incoming( - world: WorldSnapshot, + snap: GlobalStateSnapshot, params: CallHierarchyIncomingCallsParams, ) -> Result>> { let _p = profile("handle_call_hierarchy_incoming"); let item = params.item; let doc = TextDocumentIdentifier::new(item.uri); - let frange = from_proto::file_range(&world, doc, item.range)?; + let frange = from_proto::file_range(&snap, doc, item.range)?; let fpos = FilePosition { file_id: frange.file_id, offset: frange.range.start() }; - let call_items = match world.analysis().incoming_calls(fpos)? { + let call_items = match snap.analysis().incoming_calls(fpos)? { None => return Ok(None), Some(it) => it, }; @@ -1015,8 +1022,8 @@ pub fn handle_call_hierarchy_incoming( for call_item in call_items.into_iter() { let file_id = call_item.target.file_id(); - let line_index = world.analysis().file_line_index(file_id)?; - let item = to_proto::call_hierarchy_item(&world, call_item.target)?; + let line_index = snap.analysis().file_line_index(file_id)?; + let item = to_proto::call_hierarchy_item(&snap, call_item.target)?; res.push(CallHierarchyIncomingCall { from: item, from_ranges: call_item @@ -1031,17 +1038,17 @@ pub fn handle_call_hierarchy_incoming( } pub fn handle_call_hierarchy_outgoing( - world: WorldSnapshot, + snap: GlobalStateSnapshot, params: CallHierarchyOutgoingCallsParams, ) -> Result>> { let _p = profile("handle_call_hierarchy_outgoing"); let item = params.item; let doc = TextDocumentIdentifier::new(item.uri); - let frange = from_proto::file_range(&world, doc, item.range)?; + let frange = from_proto::file_range(&snap, doc, item.range)?; let fpos = FilePosition { file_id: frange.file_id, offset: frange.range.start() }; - let call_items = match world.analysis().outgoing_calls(fpos)? { + let call_items = match snap.analysis().outgoing_calls(fpos)? { None => return Ok(None), Some(it) => it, }; @@ -1050,8 +1057,8 @@ pub fn handle_call_hierarchy_outgoing( for call_item in call_items.into_iter() { let file_id = call_item.target.file_id(); - let line_index = world.analysis().file_line_index(file_id)?; - let item = to_proto::call_hierarchy_item(&world, call_item.target)?; + let line_index = snap.analysis().file_line_index(file_id)?; + let item = to_proto::call_hierarchy_item(&snap, call_item.target)?; res.push(CallHierarchyOutgoingCall { to: item, from_ranges: call_item @@ -1066,31 +1073,31 @@ pub fn handle_call_hierarchy_outgoing( } pub fn handle_semantic_tokens( - world: WorldSnapshot, + snap: GlobalStateSnapshot, params: SemanticTokensParams, ) -> Result> { let _p = profile("handle_semantic_tokens"); - let file_id = from_proto::file_id(&world, ¶ms.text_document.uri)?; - let text = world.analysis().file_text(file_id)?; - let line_index = world.analysis().file_line_index(file_id)?; + let file_id = from_proto::file_id(&snap, ¶ms.text_document.uri)?; + let text = snap.analysis().file_text(file_id)?; + let line_index = snap.analysis().file_line_index(file_id)?; - let highlights = world.analysis().highlight(file_id)?; + let highlights = snap.analysis().highlight(file_id)?; let semantic_tokens = to_proto::semantic_tokens(&text, &line_index, highlights); Ok(Some(semantic_tokens.into())) } pub fn handle_semantic_tokens_range( - world: WorldSnapshot, + snap: GlobalStateSnapshot, params: SemanticTokensRangeParams, ) -> Result> { let _p = profile("handle_semantic_tokens_range"); - let frange = from_proto::file_range(&world, params.text_document, params.range)?; - let text = world.analysis().file_text(frange.file_id)?; - let line_index = world.analysis().file_line_index(frange.file_id)?; + let frange = from_proto::file_range(&snap, params.text_document, params.range)?; + let text = snap.analysis().file_text(frange.file_id)?; + let line_index = snap.analysis().file_line_index(frange.file_id)?; - let highlights = world.analysis().highlight_range(frange)?; + let highlights = snap.analysis().highlight_range(frange)?; let semantic_tokens = to_proto::semantic_tokens(&text, &line_index, highlights); Ok(Some(semantic_tokens.into())) } diff --git a/crates/rust-analyzer/src/to_proto.rs b/crates/rust-analyzer/src/to_proto.rs index 85304aa8778e..0915a7fcb786 100644 --- a/crates/rust-analyzer/src/to_proto.rs +++ b/crates/rust-analyzer/src/to_proto.rs @@ -10,7 +10,8 @@ use ra_syntax::{SyntaxKind, TextRange, TextSize}; use ra_vfs::LineEndings; use crate::{ - cargo_target_spec::CargoTargetSpec, lsp_ext, semantic_tokens, world::WorldSnapshot, Result, + cargo_target_spec::CargoTargetSpec, global_state::GlobalStateSnapshot, lsp_ext, + semantic_tokens, Result, }; pub(crate) fn position(line_index: &LineIndex, offset: TextSize) -> lsp_types::Position { @@ -384,41 +385,44 @@ pub(crate) fn folding_range( } } -pub(crate) fn url(world: &WorldSnapshot, file_id: FileId) -> Result { - world.file_id_to_uri(file_id) +pub(crate) fn url(snap: &GlobalStateSnapshot, file_id: FileId) -> Result { + snap.file_id_to_uri(file_id) } pub(crate) fn versioned_text_document_identifier( - world: &WorldSnapshot, + snap: &GlobalStateSnapshot, file_id: FileId, version: Option, ) -> Result { - let res = lsp_types::VersionedTextDocumentIdentifier { uri: url(world, file_id)?, version }; + let res = lsp_types::VersionedTextDocumentIdentifier { uri: url(snap, file_id)?, version }; Ok(res) } -pub(crate) fn location(world: &WorldSnapshot, frange: FileRange) -> Result { - let url = url(world, frange.file_id)?; - let line_index = world.analysis().file_line_index(frange.file_id)?; +pub(crate) fn location( + snap: &GlobalStateSnapshot, + frange: FileRange, +) -> Result { + let url = url(snap, frange.file_id)?; + let line_index = snap.analysis().file_line_index(frange.file_id)?; let range = range(&line_index, frange.range); let loc = lsp_types::Location::new(url, range); Ok(loc) } pub(crate) fn location_link( - world: &WorldSnapshot, + snap: &GlobalStateSnapshot, src: Option, target: NavigationTarget, ) -> Result { let origin_selection_range = match src { Some(src) => { - let line_index = world.analysis().file_line_index(src.file_id)?; + let line_index = snap.analysis().file_line_index(src.file_id)?; let range = range(&line_index, src.range); Some(range) } None => None, }; - let (target_uri, target_range, target_selection_range) = location_info(world, target)?; + let (target_uri, target_range, target_selection_range) = location_info(snap, target)?; let res = lsp_types::LocationLink { origin_selection_range, target_uri, @@ -429,12 +433,12 @@ pub(crate) fn location_link( } fn location_info( - world: &WorldSnapshot, + snap: &GlobalStateSnapshot, target: NavigationTarget, ) -> Result<(lsp_types::Url, lsp_types::Range, lsp_types::Range)> { - let line_index = world.analysis().file_line_index(target.file_id())?; + let line_index = snap.analysis().file_line_index(target.file_id())?; - let target_uri = url(world, target.file_id())?; + let target_uri = url(snap, target.file_id())?; let target_range = range(&line_index, target.full_range()); let target_selection_range = target.focus_range().map(|it| range(&line_index, it)).unwrap_or(target_range); @@ -442,14 +446,14 @@ fn location_info( } pub(crate) fn goto_definition_response( - world: &WorldSnapshot, + snap: &GlobalStateSnapshot, src: Option, targets: Vec, ) -> Result { - if world.config.client_caps.location_link { + if snap.config.client_caps.location_link { let links = targets .into_iter() - .map(|nav| location_link(world, src, nav)) + .map(|nav| location_link(snap, src, nav)) .collect::>>()?; Ok(links.into()) } else { @@ -457,7 +461,7 @@ pub(crate) fn goto_definition_response( .into_iter() .map(|nav| { location( - world, + snap, FileRange { file_id: nav.file_id(), range: nav.focus_range().unwrap_or(nav.range()), @@ -470,13 +474,13 @@ pub(crate) fn goto_definition_response( } pub(crate) fn snippet_text_document_edit( - world: &WorldSnapshot, + snap: &GlobalStateSnapshot, is_snippet: bool, source_file_edit: SourceFileEdit, ) -> Result { - let text_document = versioned_text_document_identifier(world, source_file_edit.file_id, None)?; - let line_index = world.analysis().file_line_index(source_file_edit.file_id)?; - let line_endings = world.file_line_endings(source_file_edit.file_id); + let text_document = versioned_text_document_identifier(snap, source_file_edit.file_id, None)?; + let line_index = snap.analysis().file_line_index(source_file_edit.file_id)?; + let line_endings = snap.file_line_endings(source_file_edit.file_id); let edits = source_file_edit .edit .into_iter() @@ -486,17 +490,17 @@ pub(crate) fn snippet_text_document_edit( } pub(crate) fn resource_op( - world: &WorldSnapshot, + snap: &GlobalStateSnapshot, file_system_edit: FileSystemEdit, ) -> Result { let res = match file_system_edit { FileSystemEdit::CreateFile { source_root, path } => { - let uri = world.path_to_uri(source_root, &path)?; + let uri = snap.path_to_uri(source_root, &path)?; lsp_types::ResourceOp::Create(lsp_types::CreateFile { uri, options: None }) } FileSystemEdit::MoveFile { src, dst_source_root, dst_path } => { - let old_uri = world.file_id_to_uri(src)?; - let new_uri = world.path_to_uri(dst_source_root, &dst_path)?; + let old_uri = snap.file_id_to_uri(src)?; + let new_uri = snap.path_to_uri(dst_source_root, &dst_path)?; lsp_types::ResourceOp::Rename(lsp_types::RenameFile { old_uri, new_uri, options: None }) } }; @@ -504,16 +508,16 @@ pub(crate) fn resource_op( } pub(crate) fn snippet_workspace_edit( - world: &WorldSnapshot, + snap: &GlobalStateSnapshot, source_change: SourceChange, ) -> Result { let mut document_changes: Vec = Vec::new(); for op in source_change.file_system_edits { - let op = resource_op(&world, op)?; + let op = resource_op(&snap, op)?; document_changes.push(lsp_ext::SnippetDocumentChangeOperation::Op(op)); } for edit in source_change.source_file_edits { - let edit = snippet_text_document_edit(&world, source_change.is_snippet, edit)?; + let edit = snippet_text_document_edit(&snap, source_change.is_snippet, edit)?; document_changes.push(lsp_ext::SnippetDocumentChangeOperation::Edit(edit)); } let workspace_edit = @@ -522,11 +526,11 @@ pub(crate) fn snippet_workspace_edit( } pub(crate) fn workspace_edit( - world: &WorldSnapshot, + snap: &GlobalStateSnapshot, source_change: SourceChange, ) -> Result { assert!(!source_change.is_snippet); - snippet_workspace_edit(world, source_change).map(|it| it.into()) + snippet_workspace_edit(snap, source_change).map(|it| it.into()) } impl From for lsp_types::WorkspaceEdit { @@ -565,13 +569,13 @@ impl From for lsp_types::WorkspaceEdit { } pub fn call_hierarchy_item( - world: &WorldSnapshot, + snap: &GlobalStateSnapshot, target: NavigationTarget, ) -> Result { let name = target.name().to_string(); let detail = target.description().map(|it| it.to_string()); let kind = symbol_kind(target.kind()); - let (uri, range, selection_range) = location_info(world, target)?; + let (uri, range, selection_range) = location_info(snap, target)?; Ok(lsp_types::CallHierarchyItem { name, kind, tags: None, detail, uri, range, selection_range }) } @@ -619,23 +623,26 @@ fn main() { } } -pub(crate) fn code_action(world: &WorldSnapshot, assist: Assist) -> Result { +pub(crate) fn code_action( + snap: &GlobalStateSnapshot, + assist: Assist, +) -> Result { let res = lsp_ext::CodeAction { title: assist.label, - group: if world.config.client_caps.code_action_group { assist.group_label } else { None }, + group: if snap.config.client_caps.code_action_group { assist.group_label } else { None }, kind: Some(String::new()), - edit: Some(snippet_workspace_edit(world, assist.source_change)?), + edit: Some(snippet_workspace_edit(snap, assist.source_change)?), command: None, }; Ok(res) } pub(crate) fn runnable( - world: &WorldSnapshot, + snap: &GlobalStateSnapshot, file_id: FileId, runnable: Runnable, ) -> Result { - let spec = CargoTargetSpec::for_file(world, file_id)?; + let spec = CargoTargetSpec::for_file(snap, file_id)?; let target = spec.as_ref().map(|s| s.target.clone()); let (cargo_args, executable_args) = CargoTargetSpec::runnable_args(spec, &runnable.kind, &runnable.cfg_exprs)?; @@ -648,14 +655,14 @@ pub(crate) fn runnable( target.map_or_else(|| "run binary".to_string(), |t| format!("run {}", t)) } }; - let location = location_link(world, None, runnable.nav)?; + let location = location_link(snap, None, runnable.nav)?; Ok(lsp_ext::Runnable { label, location: Some(location), kind: lsp_ext::RunnableKind::Cargo, args: lsp_ext::CargoRunnable { - workspace_root: world.workspace_root_for(file_id).map(|root| root.to_owned()), + workspace_root: snap.workspace_root_for(file_id).map(|root| root.to_owned()), cargo_args, executable_args, }, From 0a88de809f13f3b4abe0ffa11ff87c6f845050bd Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Wed, 3 Jun 2020 11:44:51 +0200 Subject: [PATCH 035/172] Move project discovery --- crates/ra_project_model/src/lib.rs | 16 ++++++++++++++-- crates/rust-analyzer/src/main_loop.rs | 8 ++------ 2 files changed, 16 insertions(+), 8 deletions(-) diff --git a/crates/ra_project_model/src/lib.rs b/crates/ra_project_model/src/lib.rs index 9b30bef8d308..d5f82f17a9b6 100644 --- a/crates/ra_project_model/src/lib.rs +++ b/crates/ra_project_model/src/lib.rs @@ -14,7 +14,7 @@ use std::{ use anyhow::{bail, Context, Result}; use ra_cfg::CfgOptions; use ra_db::{CrateGraph, CrateName, Edition, Env, ExternSource, ExternSourceId, FileId}; -use rustc_hash::FxHashMap; +use rustc_hash::{FxHashMap, FxHashSet}; use serde_json::from_reader; pub use crate::{ @@ -57,7 +57,7 @@ impl PackageRoot { } } -#[derive(Debug, Clone, PartialEq, Eq, Hash)] +#[derive(Debug, Clone, PartialEq, Eq, Hash, Ord, PartialOrd)] pub enum ProjectRoot { ProjectJson(PathBuf), CargoToml(PathBuf), @@ -128,6 +128,18 @@ impl ProjectRoot { .collect() } } + + pub fn discover_all(paths: &[impl AsRef]) -> Vec { + let mut res = paths + .iter() + .filter_map(|it| ProjectRoot::discover(it.as_ref()).ok()) + .flatten() + .collect::>() + .into_iter() + .collect::>(); + res.sort(); + res + } } impl ProjectWorkspace { diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs index 35f2d7001e1c..82348c6fc068 100644 --- a/crates/rust-analyzer/src/main_loop.rs +++ b/crates/rust-analyzer/src/main_loop.rs @@ -28,7 +28,7 @@ use lsp_types::{ use ra_flycheck::{CheckTask, Status}; use ra_ide::{Canceled, FileId, LibraryData, LineIndex, SourceRootId}; use ra_prof::profile; -use ra_project_model::{PackageRoot, ProjectWorkspace}; +use ra_project_model::{PackageRoot, ProjectRoot, ProjectWorkspace}; use ra_vfs::{VfsFile, VfsTask, Watch}; use relative_path::RelativePathBuf; use rustc_hash::FxHashSet; @@ -96,11 +96,7 @@ pub fn main_loop(ws_roots: Vec, config: Config, connection: Connection) let mut global_state = { let workspaces = { // FIXME: support dynamic workspace loading. - let project_roots: FxHashSet<_> = ws_roots - .iter() - .filter_map(|it| ra_project_model::ProjectRoot::discover(it).ok()) - .flatten() - .collect(); + let project_roots = ProjectRoot::discover_all(&ws_roots); if project_roots.is_empty() && config.notifications.cargo_toml_not_found { show_message( From 03a76191a18b450a8e4ae309296fd7c0614d14d2 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Wed, 3 Jun 2020 12:05:50 +0200 Subject: [PATCH 036/172] Rename ProjectRoot -> ProjectManifest --- crates/ra_project_model/src/lib.rs | 30 +++++++++++----------- crates/rust-analyzer/src/cli/load_cargo.rs | 4 +-- crates/rust-analyzer/src/main_loop.rs | 4 +-- 3 files changed, 19 insertions(+), 19 deletions(-) diff --git a/crates/ra_project_model/src/lib.rs b/crates/ra_project_model/src/lib.rs index d5f82f17a9b6..a9961269026a 100644 --- a/crates/ra_project_model/src/lib.rs +++ b/crates/ra_project_model/src/lib.rs @@ -58,24 +58,24 @@ impl PackageRoot { } #[derive(Debug, Clone, PartialEq, Eq, Hash, Ord, PartialOrd)] -pub enum ProjectRoot { +pub enum ProjectManifest { ProjectJson(PathBuf), CargoToml(PathBuf), } -impl ProjectRoot { - pub fn from_manifest_file(path: PathBuf) -> Result { +impl ProjectManifest { + pub fn from_manifest_file(path: PathBuf) -> Result { if path.ends_with("rust-project.json") { - return Ok(ProjectRoot::ProjectJson(path)); + return Ok(ProjectManifest::ProjectJson(path)); } if path.ends_with("Cargo.toml") { - return Ok(ProjectRoot::CargoToml(path)); + return Ok(ProjectManifest::CargoToml(path)); } bail!("project root must point to Cargo.toml or rust-project.json: {}", path.display()) } - pub fn discover_single(path: &Path) -> Result { - let mut candidates = ProjectRoot::discover(path)?; + pub fn discover_single(path: &Path) -> Result { + let mut candidates = ProjectManifest::discover(path)?; let res = match candidates.pop() { None => bail!("no projects"), Some(it) => it, @@ -87,12 +87,12 @@ impl ProjectRoot { Ok(res) } - pub fn discover(path: &Path) -> io::Result> { + pub fn discover(path: &Path) -> io::Result> { if let Some(project_json) = find_in_parent_dirs(path, "rust-project.json") { - return Ok(vec![ProjectRoot::ProjectJson(project_json)]); + return Ok(vec![ProjectManifest::ProjectJson(project_json)]); } return find_cargo_toml(path) - .map(|paths| paths.into_iter().map(ProjectRoot::CargoToml).collect()); + .map(|paths| paths.into_iter().map(ProjectManifest::CargoToml).collect()); fn find_cargo_toml(path: &Path) -> io::Result> { match find_in_parent_dirs(path, "Cargo.toml") { @@ -129,10 +129,10 @@ impl ProjectRoot { } } - pub fn discover_all(paths: &[impl AsRef]) -> Vec { + pub fn discover_all(paths: &[impl AsRef]) -> Vec { let mut res = paths .iter() - .filter_map(|it| ProjectRoot::discover(it.as_ref()).ok()) + .filter_map(|it| ProjectManifest::discover(it.as_ref()).ok()) .flatten() .collect::>() .into_iter() @@ -144,12 +144,12 @@ impl ProjectRoot { impl ProjectWorkspace { pub fn load( - root: ProjectRoot, + root: ProjectManifest, cargo_features: &CargoConfig, with_sysroot: bool, ) -> Result { let res = match root { - ProjectRoot::ProjectJson(project_json) => { + ProjectManifest::ProjectJson(project_json) => { let file = File::open(&project_json).with_context(|| { format!("Failed to open json file {}", project_json.display()) })?; @@ -160,7 +160,7 @@ impl ProjectWorkspace { })?, } } - ProjectRoot::CargoToml(cargo_toml) => { + ProjectManifest::CargoToml(cargo_toml) => { let cargo = CargoWorkspace::from_cargo_metadata(&cargo_toml, cargo_features) .with_context(|| { format!( diff --git a/crates/rust-analyzer/src/cli/load_cargo.rs b/crates/rust-analyzer/src/cli/load_cargo.rs index 8eaf75ff6133..67491b42a7a9 100644 --- a/crates/rust-analyzer/src/cli/load_cargo.rs +++ b/crates/rust-analyzer/src/cli/load_cargo.rs @@ -8,7 +8,7 @@ use crossbeam_channel::{unbounded, Receiver}; use ra_db::{ExternSourceId, FileId, SourceRootId}; use ra_ide::{AnalysisChange, AnalysisHost}; use ra_project_model::{ - get_rustc_cfg_options, CargoConfig, PackageRoot, ProcMacroClient, ProjectRoot, ProjectWorkspace, + get_rustc_cfg_options, CargoConfig, PackageRoot, ProcMacroClient, ProjectManifest, ProjectWorkspace, }; use ra_vfs::{RootEntry, Vfs, VfsChange, VfsTask, Watch}; use rustc_hash::{FxHashMap, FxHashSet}; @@ -28,7 +28,7 @@ pub fn load_cargo( with_proc_macro: bool, ) -> Result<(AnalysisHost, FxHashMap)> { let root = std::env::current_dir()?.join(root); - let root = ProjectRoot::discover_single(&root)?; + let root = ProjectManifest::discover_single(&root)?; let ws = ProjectWorkspace::load( root, &CargoConfig { load_out_dirs_from_check, ..Default::default() }, diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs index 82348c6fc068..ea5b4c91c80f 100644 --- a/crates/rust-analyzer/src/main_loop.rs +++ b/crates/rust-analyzer/src/main_loop.rs @@ -28,7 +28,7 @@ use lsp_types::{ use ra_flycheck::{CheckTask, Status}; use ra_ide::{Canceled, FileId, LibraryData, LineIndex, SourceRootId}; use ra_prof::profile; -use ra_project_model::{PackageRoot, ProjectRoot, ProjectWorkspace}; +use ra_project_model::{PackageRoot, ProjectManifest, ProjectWorkspace}; use ra_vfs::{VfsFile, VfsTask, Watch}; use relative_path::RelativePathBuf; use rustc_hash::FxHashSet; @@ -96,7 +96,7 @@ pub fn main_loop(ws_roots: Vec, config: Config, connection: Connection) let mut global_state = { let workspaces = { // FIXME: support dynamic workspace loading. - let project_roots = ProjectRoot::discover_all(&ws_roots); + let project_roots = ProjectManifest::discover_all(&ws_roots); if project_roots.is_empty() && config.notifications.cargo_toml_not_found { show_message( From 8baa4c5d07690dc908b6299471c936c0d87ad871 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Wed, 3 Jun 2020 12:22:01 +0200 Subject: [PATCH 037/172] Groundwork for specifying the set of projects via config --- crates/rust-analyzer/src/bin/main.rs | 41 +++++++++++++------ crates/rust-analyzer/src/cli/load_cargo.rs | 3 +- crates/rust-analyzer/src/config.rs | 25 ++++++++++- crates/rust-analyzer/src/main_loop.rs | 29 ++++++------- .../tests/heavy_tests/support.rs | 31 +++++++------- 5 files changed, 83 insertions(+), 46 deletions(-) diff --git a/crates/rust-analyzer/src/bin/main.rs b/crates/rust-analyzer/src/bin/main.rs index e82fd57de84d..8d071ab1ca2b 100644 --- a/crates/rust-analyzer/src/bin/main.rs +++ b/crates/rust-analyzer/src/bin/main.rs @@ -4,9 +4,14 @@ mod args; use lsp_server::Connection; -use rust_analyzer::{cli, config::Config, from_json, Result}; +use rust_analyzer::{ + cli, + config::{Config, LinkedProject}, + from_json, Result, +}; use crate::args::HelpPrinted; +use ra_project_model::ProjectManifest; fn main() -> Result<()> { setup_logging()?; @@ -97,17 +102,6 @@ fn run_server() -> Result<()> { log::info!("Client '{}' {}", client_info.name, client_info.version.unwrap_or_default()); } - let cwd = std::env::current_dir()?; - let root = initialize_params.root_uri.and_then(|it| it.to_file_path().ok()).unwrap_or(cwd); - - let workspace_roots = initialize_params - .workspace_folders - .map(|workspaces| { - workspaces.into_iter().filter_map(|it| it.uri.to_file_path().ok()).collect::>() - }) - .filter(|workspaces| !workspaces.is_empty()) - .unwrap_or_else(|| vec![root]); - let config = { let mut config = Config::default(); if let Some(value) = &initialize_params.initialization_options { @@ -115,10 +109,31 @@ fn run_server() -> Result<()> { } config.update_caps(&initialize_params.capabilities); + if config.linked_projects.is_empty() { + let cwd = std::env::current_dir()?; + let root = + initialize_params.root_uri.and_then(|it| it.to_file_path().ok()).unwrap_or(cwd); + let workspace_roots = initialize_params + .workspace_folders + .map(|workspaces| { + workspaces + .into_iter() + .filter_map(|it| it.uri.to_file_path().ok()) + .collect::>() + }) + .filter(|workspaces| !workspaces.is_empty()) + .unwrap_or_else(|| vec![root]); + + config.linked_projects = ProjectManifest::discover_all(&workspace_roots) + .into_iter() + .map(LinkedProject::from) + .collect(); + } + config }; - rust_analyzer::main_loop(workspace_roots, config, connection)?; + rust_analyzer::main_loop(config, connection)?; log::info!("shutting down IO..."); io_threads.join()?; diff --git a/crates/rust-analyzer/src/cli/load_cargo.rs b/crates/rust-analyzer/src/cli/load_cargo.rs index 67491b42a7a9..c7e86fe0c48b 100644 --- a/crates/rust-analyzer/src/cli/load_cargo.rs +++ b/crates/rust-analyzer/src/cli/load_cargo.rs @@ -8,7 +8,8 @@ use crossbeam_channel::{unbounded, Receiver}; use ra_db::{ExternSourceId, FileId, SourceRootId}; use ra_ide::{AnalysisChange, AnalysisHost}; use ra_project_model::{ - get_rustc_cfg_options, CargoConfig, PackageRoot, ProcMacroClient, ProjectManifest, ProjectWorkspace, + get_rustc_cfg_options, CargoConfig, PackageRoot, ProcMacroClient, ProjectManifest, + ProjectWorkspace, }; use ra_vfs::{RootEntry, Vfs, VfsChange, VfsTask, Watch}; use rustc_hash::{FxHashMap, FxHashSet}; diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs index 9c6e369d2e1e..761bc9c2d92a 100644 --- a/crates/rust-analyzer/src/config.rs +++ b/crates/rust-analyzer/src/config.rs @@ -12,14 +12,13 @@ use std::{ffi::OsString, path::PathBuf}; use lsp_types::ClientCapabilities; use ra_flycheck::FlycheckConfig; use ra_ide::{AssistConfig, CompletionConfig, InlayHintsConfig}; -use ra_project_model::CargoConfig; +use ra_project_model::{CargoConfig, JsonProject, ProjectManifest}; use serde::Deserialize; #[derive(Debug, Clone)] pub struct Config { pub client_caps: ClientCapsConfig, - pub with_sysroot: bool, pub publish_diagnostics: bool, pub lru_capacity: Option, pub proc_macro_srv: Option<(PathBuf, Vec)>, @@ -35,6 +34,27 @@ pub struct Config { pub assist: AssistConfig, pub call_info_full: bool, pub lens: LensConfig, + + pub with_sysroot: bool, + pub linked_projects: Vec, +} + +#[derive(Debug, Clone)] +pub enum LinkedProject { + ProjectManifest(ProjectManifest), + JsonProject(JsonProject), +} + +impl From for LinkedProject { + fn from(v: ProjectManifest) -> Self { + LinkedProject::ProjectManifest(v) + } +} + +impl From for LinkedProject { + fn from(v: JsonProject) -> Self { + LinkedProject::JsonProject(v) + } } #[derive(Clone, Debug, PartialEq, Eq)] @@ -141,6 +161,7 @@ impl Default for Config { assist: AssistConfig::default(), call_info_full: true, lens: LensConfig::default(), + linked_projects: Vec::new(), } } } diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs index ea5b4c91c80f..d901f21d7e7e 100644 --- a/crates/rust-analyzer/src/main_loop.rs +++ b/crates/rust-analyzer/src/main_loop.rs @@ -12,13 +12,11 @@ use std::{ fmt, ops::Range, panic, - path::PathBuf, sync::Arc, time::{Duration, Instant}, }; use crossbeam_channel::{never, select, unbounded, RecvError, Sender}; -use itertools::Itertools; use lsp_server::{Connection, ErrorCode, Message, Notification, Request, RequestId, Response}; use lsp_types::{ DidChangeTextDocumentParams, NumberOrString, TextDocumentContentChangeEvent, WorkDoneProgress, @@ -28,7 +26,7 @@ use lsp_types::{ use ra_flycheck::{CheckTask, Status}; use ra_ide::{Canceled, FileId, LibraryData, LineIndex, SourceRootId}; use ra_prof::profile; -use ra_project_model::{PackageRoot, ProjectManifest, ProjectWorkspace}; +use ra_project_model::{PackageRoot, ProjectWorkspace}; use ra_vfs::{VfsFile, VfsTask, Watch}; use relative_path::RelativePathBuf; use rustc_hash::FxHashSet; @@ -36,7 +34,7 @@ use serde::{de::DeserializeOwned, Serialize}; use threadpool::ThreadPool; use crate::{ - config::{Config, FilesWatcher}, + config::{Config, FilesWatcher, LinkedProject}, diagnostics::{to_proto::url_from_path_with_drive_lowercasing, DiagnosticTask}, from_proto, global_state::{GlobalState, GlobalStateSnapshot}, @@ -70,7 +68,7 @@ impl fmt::Display for LspError { impl Error for LspError {} -pub fn main_loop(ws_roots: Vec, config: Config, connection: Connection) -> Result<()> { +pub fn main_loop(config: Config, connection: Connection) -> Result<()> { log::info!("initial config: {:#?}", config); // Windows scheduler implements priority boosts: if thread waits for an @@ -95,25 +93,24 @@ pub fn main_loop(ws_roots: Vec, config: Config, connection: Connection) let mut loop_state = LoopState::default(); let mut global_state = { let workspaces = { - // FIXME: support dynamic workspace loading. - let project_roots = ProjectManifest::discover_all(&ws_roots); - - if project_roots.is_empty() && config.notifications.cargo_toml_not_found { + if config.linked_projects.is_empty() && config.notifications.cargo_toml_not_found { show_message( lsp_types::MessageType::Error, - format!( - "rust-analyzer failed to discover workspace, no Cargo.toml found, dirs searched: {}", - ws_roots.iter().format_with(", ", |it, f| f(&it.display())) - ), + "rust-analyzer failed to discover workspace".to_string(), &connection.sender, ); }; - project_roots - .into_iter() + config + .linked_projects + .iter() + .filter_map(|project| match project { + LinkedProject::ProjectManifest(it) => Some(it), + LinkedProject::JsonProject(_) => None, + }) .filter_map(|root| { ra_project_model::ProjectWorkspace::load( - root, + root.clone(), &config.cargo, config.with_sysroot, ) diff --git a/crates/rust-analyzer/tests/heavy_tests/support.rs b/crates/rust-analyzer/tests/heavy_tests/support.rs index 66a6f4d541a2..30d03b622b91 100644 --- a/crates/rust-analyzer/tests/heavy_tests/support.rs +++ b/crates/rust-analyzer/tests/heavy_tests/support.rs @@ -19,8 +19,9 @@ use serde_json::{to_string_pretty, Value}; use tempfile::TempDir; use test_utils::{find_mismatch, parse_fixture}; +use ra_project_model::ProjectManifest; use rust_analyzer::{ - config::{ClientCapsConfig, Config}, + config::{ClientCapsConfig, Config, LinkedProject}, main_loop, }; @@ -42,7 +43,7 @@ impl<'a> Project<'a> { self } - pub fn root(mut self, path: &str) -> Project<'a> { + pub(crate) fn root(mut self, path: &str) -> Project<'a> { self.roots.push(path.into()); self } @@ -74,7 +75,16 @@ impl<'a> Project<'a> { paths.push((path, entry.text)); } - let roots = self.roots.into_iter().map(|root| tmp_dir.path().join(root)).collect(); + let mut roots = + self.roots.into_iter().map(|root| tmp_dir.path().join(root)).collect::>(); + if roots.is_empty() { + roots.push(tmp_dir.path().to_path_buf()); + } + let linked_projects = roots + .into_iter() + .map(|it| ProjectManifest::discover_single(&it).unwrap()) + .map(LinkedProject::from) + .collect::>(); let mut config = Config { client_caps: ClientCapsConfig { @@ -84,6 +94,7 @@ impl<'a> Project<'a> { ..Default::default() }, with_sysroot: self.with_sysroot, + linked_projects, ..Config::default() }; @@ -91,7 +102,7 @@ impl<'a> Project<'a> { f(&mut config) } - Server::new(tmp_dir, config, roots, paths) + Server::new(tmp_dir, config, paths) } } @@ -109,20 +120,12 @@ pub struct Server { } impl Server { - fn new( - dir: TempDir, - config: Config, - roots: Vec, - files: Vec<(PathBuf, String)>, - ) -> Server { - let path = dir.path().to_path_buf(); - - let roots = if roots.is_empty() { vec![path] } else { roots }; + fn new(dir: TempDir, config: Config, files: Vec<(PathBuf, String)>) -> Server { let (connection, client) = Connection::memory(); let _thread = jod_thread::Builder::new() .name("test server".to_string()) - .spawn(move || main_loop(roots, config, connection).unwrap()) + .spawn(move || main_loop(config, connection).unwrap()) .expect("failed to spawn a thread"); let res = From 2e7d12d2f3ccfd1ef9946d1212e2455e5937c521 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Wed, 3 Jun 2020 12:39:11 +0200 Subject: [PATCH 038/172] Drop test for old format --- crates/rust-analyzer/tests/heavy_tests/main.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/crates/rust-analyzer/tests/heavy_tests/main.rs b/crates/rust-analyzer/tests/heavy_tests/main.rs index 69dc719c54a5..c1805787a398 100644 --- a/crates/rust-analyzer/tests/heavy_tests/main.rs +++ b/crates/rust-analyzer/tests/heavy_tests/main.rs @@ -331,8 +331,6 @@ fn test_missing_module_code_action_in_json_project() { "deps": [], "edition": "2015", "cfg": [ "cfg_atom_1", "feature=cfg_1"], - "atom_cfgs": ["atom_2"], - "key_value_cfgs": { "feature": "key_value_feature", "other": "value"} } ] }); From 4c655c01f31ceffae4f8219f9706992e0e7f188a Mon Sep 17 00:00:00 2001 From: Aaron Loucks Date: Sat, 30 May 2020 14:21:06 -0400 Subject: [PATCH 039/172] Enable hover and autocomplete docs on macro generated items --- crates/ra_hir_def/src/attr.rs | 8 ++++- crates/ra_hir_def/src/docs.rs | 43 +++++++++++++++++++++-- crates/ra_hir_expand/src/name.rs | 1 + crates/ra_ide/src/hover.rs | 55 +++++++++++++++++++++++++++--- crates/ra_syntax/src/ast/traits.rs | 13 +++++-- 5 files changed, 110 insertions(+), 10 deletions(-) diff --git a/crates/ra_hir_def/src/attr.rs b/crates/ra_hir_def/src/attr.rs index 8b6c0bedee78..2eeba0572991 100644 --- a/crates/ra_hir_def/src/attr.rs +++ b/crates/ra_hir_def/src/attr.rs @@ -87,12 +87,18 @@ impl Attrs { } pub(crate) fn new(owner: &dyn AttrsOwner, hygiene: &Hygiene) -> Attrs { + let docs = ast::CommentIter::from_syntax_node(owner.syntax()).doc_comment_text().map( + |docs_text| Attr { + input: Some(AttrInput::Literal(SmolStr::new(docs_text))), + path: ModPath::from(hir_expand::name!(doc)), + }, + ); let mut attrs = owner.attrs().peekable(); let entries = if attrs.peek().is_none() { // Avoid heap allocation None } else { - Some(attrs.flat_map(|ast| Attr::from_src(ast, hygiene)).collect()) + Some(attrs.flat_map(|ast| Attr::from_src(ast, hygiene)).chain(docs).collect()) }; Attrs { entries } } diff --git a/crates/ra_hir_def/src/docs.rs b/crates/ra_hir_def/src/docs.rs index b221ae1cece3..74b9f8199c10 100644 --- a/crates/ra_hir_def/src/docs.rs +++ b/crates/ra_hir_def/src/docs.rs @@ -70,6 +70,45 @@ impl Documentation { } } -pub(crate) fn docs_from_ast(node: &impl ast::DocCommentsOwner) -> Option { - node.doc_comment_text().map(|it| Documentation::new(&it)) +pub(crate) fn docs_from_ast(node: &N) -> Option +where + N: ast::DocCommentsOwner + ast::AttrsOwner, +{ + let doc_comment_text = node.doc_comment_text(); + let doc_attr_text = expand_doc_attrs(node); + let docs = merge_doc_comments_and_attrs(doc_comment_text, doc_attr_text); + docs.map(|it| Documentation::new(&it)) +} + +fn merge_doc_comments_and_attrs( + doc_comment_text: Option, + doc_attr_text: Option, +) -> Option { + match (doc_comment_text, doc_attr_text) { + (Some(mut comment_text), Some(attr_text)) => { + comment_text.push_str("\n\n"); + comment_text.push_str(&attr_text); + Some(comment_text) + } + (Some(comment_text), None) => Some(comment_text), + (None, Some(attr_text)) => Some(attr_text), + (None, None) => None, + } +} + +fn expand_doc_attrs(owner: &dyn ast::AttrsOwner) -> Option { + let mut docs = String::new(); + for attr in owner.attrs() { + if let Some(("doc", value)) = + attr.as_simple_key_value().as_ref().map(|(k, v)| (k.as_str(), v.as_str())) + { + docs.push_str(value); + docs.push_str("\n\n"); + } + } + if docs.is_empty() { + None + } else { + Some(docs) + } } diff --git a/crates/ra_hir_expand/src/name.rs b/crates/ra_hir_expand/src/name.rs index ea495cb11a2b..660bdfe3365b 100644 --- a/crates/ra_hir_expand/src/name.rs +++ b/crates/ra_hir_expand/src/name.rs @@ -153,6 +153,7 @@ pub mod known { str, // Special names macro_rules, + doc, // Components of known path (value or mod name) std, core, diff --git a/crates/ra_ide/src/hover.rs b/crates/ra_ide/src/hover.rs index d96cb5596918..e25a7dacf01b 100644 --- a/crates/ra_ide/src/hover.rs +++ b/crates/ra_ide/src/hover.rs @@ -169,13 +169,19 @@ fn hover_text_from_name_kind(db: &RootDatabase, def: Definition) -> Option { let src = it.source(db); - hover_text(src.value.doc_comment_text(), Some(macro_label(&src.value)), mod_path) + let doc_comment_text = src.value.doc_comment_text(); + let doc_attr_text = expand_doc_attrs(&src.value); + let docs = merge_doc_comments_and_attrs(doc_comment_text, doc_attr_text); + hover_text(docs, Some(macro_label(&src.value)), mod_path) } Definition::Field(it) => { let src = it.source(db); match src.value { FieldSource::Named(it) => { - hover_text(it.doc_comment_text(), it.short_label(), mod_path) + let doc_comment_text = it.doc_comment_text(); + let doc_attr_text = expand_doc_attrs(&it); + let docs = merge_doc_comments_and_attrs(doc_comment_text, doc_attr_text); + hover_text(docs, it.short_label(), mod_path) } _ => None, } @@ -183,7 +189,10 @@ fn hover_text_from_name_kind(db: &RootDatabase, def: Definition) -> Option match it { ModuleDef::Module(it) => match it.definition_source(db).value { ModuleSource::Module(it) => { - hover_text(it.doc_comment_text(), it.short_label(), mod_path) + let doc_comment_text = it.doc_comment_text(); + let doc_attr_text = expand_doc_attrs(&it); + let docs = merge_doc_comments_and_attrs(doc_comment_text, doc_attr_text); + hover_text(docs, it.short_label(), mod_path) } _ => None, }, @@ -208,10 +217,46 @@ fn hover_text_from_name_kind(db: &RootDatabase, def: Definition) -> Option(db: &RootDatabase, def: D, mod_path: Option) -> Option where D: HasSource, - A: ast::DocCommentsOwner + ast::NameOwner + ShortLabel, + A: ast::DocCommentsOwner + ast::NameOwner + ShortLabel + ast::AttrsOwner, { let src = def.source(db); - hover_text(src.value.doc_comment_text(), src.value.short_label(), mod_path) + let doc_comment_text = src.value.doc_comment_text(); + let doc_attr_text = expand_doc_attrs(&src.value); + let docs = merge_doc_comments_and_attrs(doc_comment_text, doc_attr_text); + hover_text(docs, src.value.short_label(), mod_path) + } +} + +fn merge_doc_comments_and_attrs( + doc_comment_text: Option, + doc_attr_text: Option, +) -> Option { + match (doc_comment_text, doc_attr_text) { + (Some(mut comment_text), Some(attr_text)) => { + comment_text.push_str("\n\n"); + comment_text.push_str(&attr_text); + Some(comment_text) + } + (Some(comment_text), None) => Some(comment_text), + (None, Some(attr_text)) => Some(attr_text), + (None, None) => None, + } +} + +fn expand_doc_attrs(owner: &dyn ast::AttrsOwner) -> Option { + let mut docs = String::new(); + for attr in owner.attrs() { + if let Some(("doc", value)) = + attr.as_simple_key_value().as_ref().map(|(k, v)| (k.as_str(), v.as_str())) + { + docs.push_str(value); + docs.push_str("\n\n"); + } + } + if docs.is_empty() { + None + } else { + Some(docs) } } diff --git a/crates/ra_syntax/src/ast/traits.rs b/crates/ra_syntax/src/ast/traits.rs index bfc05e08bf24..a8f2454fd96e 100644 --- a/crates/ra_syntax/src/ast/traits.rs +++ b/crates/ra_syntax/src/ast/traits.rs @@ -83,13 +83,22 @@ pub trait DocCommentsOwner: AstNode { CommentIter { iter: self.syntax().children_with_tokens() } } + fn doc_comment_text(&self) -> Option { + self.doc_comments().doc_comment_text() + } +} + +impl CommentIter { + pub fn from_syntax_node(syntax_node: &ast::SyntaxNode) -> CommentIter { + CommentIter { iter: syntax_node.children_with_tokens() } + } + /// Returns the textual content of a doc comment block as a single string. /// That is, strips leading `///` (+ optional 1 character of whitespace), /// trailing `*/`, trailing whitespace and then joins the lines. - fn doc_comment_text(&self) -> Option { + pub fn doc_comment_text(self) -> Option { let mut has_comments = false; let docs = self - .doc_comments() .filter(|comment| comment.kind().doc.is_some()) .map(|comment| { has_comments = true; From 5837acce532e0cd65a1c0cb8c03cc18a4c22f327 Mon Sep 17 00:00:00 2001 From: Aaron Loucks Date: Sun, 31 May 2020 11:33:48 -0400 Subject: [PATCH 040/172] Add basic hover and completion doc tests for macro generated items --- crates/ra_hir_def/src/docs.rs | 2 +- crates/ra_ide/src/completion.rs | 78 ++++++++++++++++++++++++ crates/ra_ide/src/hover.rs | 104 +++++++++++++++++++++++++++++++- 3 files changed, 182 insertions(+), 2 deletions(-) diff --git a/crates/ra_hir_def/src/docs.rs b/crates/ra_hir_def/src/docs.rs index 74b9f8199c10..ab43cd3d3395 100644 --- a/crates/ra_hir_def/src/docs.rs +++ b/crates/ra_hir_def/src/docs.rs @@ -109,6 +109,6 @@ fn expand_doc_attrs(owner: &dyn ast::AttrsOwner) -> Option { if docs.is_empty() { None } else { - Some(docs) + Some(docs.trim_end_matches("\n\n").to_owned()) } } diff --git a/crates/ra_ide/src/completion.rs b/crates/ra_ide/src/completion.rs index d890b69d26fa..a721e23c6934 100644 --- a/crates/ra_ide/src/completion.rs +++ b/crates/ra_ide/src/completion.rs @@ -125,3 +125,81 @@ pub(crate) fn completions( Some(acc) } + +#[cfg(test)] +mod tests { + use crate::completion::completion_config::CompletionConfig; + use crate::mock_analysis::analysis_and_position; + + struct DetailAndDocumentation<'a> { + detail: &'a str, + documentation: &'a str, + } + + fn check_detail_and_documentation(fixture: &str, expected: DetailAndDocumentation) { + let (analysis, position) = analysis_and_position(fixture); + let config = CompletionConfig::default(); + let completions = analysis.completions(&config, position).unwrap().unwrap(); + for item in completions { + if item.detail() == Some(expected.detail) { + let opt = item.documentation(); + let doc = opt.as_ref().map(|it| it.as_str()); + assert_eq!(doc, Some(expected.documentation)); + return; + } + } + panic!("completion detail not found: {}", expected.detail) + } + + #[test] + fn test_completion_detail_from_macro_generated_struct_fn_doc_attr() { + check_detail_and_documentation( + r#" + //- /lib.rs + macro_rules! bar { + () => { + struct Bar; + impl Bar { + #[doc = "Do the foo"] + fn foo(&self) {} + } + } + } + + bar!(); + + fn foo() { + let bar = Bar; + bar.fo<|>; + } + "#, + DetailAndDocumentation { detail: "fn foo(&self)", documentation: "Do the foo" }, + ); + } + + #[test] + fn test_completion_detail_from_macro_generated_struct_fn_doc_comment() { + check_detail_and_documentation( + r#" + //- /lib.rs + macro_rules! bar { + () => { + struct Bar; + impl Bar { + /// Do the foo + fn foo(&self) {} + } + } + } + + bar!(); + + fn foo() { + let bar = Bar; + bar.fo<|>; + } + "#, + DetailAndDocumentation { detail: "fn foo(&self)", documentation: " Do the foo" }, + ); + } +} diff --git a/crates/ra_ide/src/hover.rs b/crates/ra_ide/src/hover.rs index e25a7dacf01b..731fc3673535 100644 --- a/crates/ra_ide/src/hover.rs +++ b/crates/ra_ide/src/hover.rs @@ -256,7 +256,7 @@ fn expand_doc_attrs(owner: &dyn ast::AttrsOwner) -> Option { if docs.is_empty() { None } else { - Some(docs) + Some(docs.trim_end_matches("\n\n").to_owned()) } } @@ -996,4 +996,106 @@ fn func(foo: i32) { if true { <|>foo; }; } &["mod my"], ); } + + #[test] + fn test_hover_struct_doc_comment() { + check_hover_result( + r#" + //- /lib.rs + /// bar docs + struct Bar; + + fn foo() { + let bar = Ba<|>r; + } + "#, + &["struct Bar\n```\n___\n\nbar docs"], + ); + } + + #[test] + fn test_hover_struct_doc_attr() { + check_hover_result( + r#" + //- /lib.rs + #[doc = "bar docs"] + struct Bar; + + fn foo() { + let bar = Ba<|>r; + } + "#, + &["struct Bar\n```\n___\n\nbar docs"], + ); + } + + #[test] + fn test_hover_struct_doc_attr_multiple_and_mixed() { + check_hover_result( + r#" + //- /lib.rs + /// bar docs 0 + #[doc = "bar docs 1"] + #[doc = "bar docs 2"] + struct Bar; + + fn foo() { + let bar = Ba<|>r; + } + "#, + &["struct Bar\n```\n___\n\nbar docs 0\n\nbar docs 1\n\nbar docs 2"], + ); + } + + #[test] + fn test_hover_macro_generated_struct_fn_doc_comment() { + check_hover_result( + r#" + //- /lib.rs + macro_rules! bar { + () => { + struct Bar; + impl Bar { + /// Do the foo + fn foo(&self) {} + } + } + } + + bar!(); + + fn foo() { + let bar = Bar; + bar.fo<|>o(); + } + "#, + &["Bar\n```\n\n```rust\nfn foo(&self)\n```\n___\n\n Do the foo"], + ); + } + + #[test] + fn test_hover_macro_generated_struct_fn_doc_attr() { + check_hover_result( + r#" + //- /lib.rs + macro_rules! bar { + () => { + struct Bar; + impl Bar { + #[doc = "Do the foo"] + fn foo(&self) {} + } + } + } + + bar!(); + + fn foo() { + let bar = Bar; + bar.fo<|>o(); + } + "#, + &["Bar\n```\n\n```rust\nfn foo(&self)\n```\n___\n\nDo the foo"], + ); + } } From 85c4edb0afbc7cc855c434e5e7ec69aa469e0c4b Mon Sep 17 00:00:00 2001 From: Aaron Loucks Date: Wed, 3 Jun 2020 06:14:56 -0400 Subject: [PATCH 041/172] Consolidate documentation expansion and merging Removes the duplicated `expand_doc_attrs` and `merge_doc_comments_and_attrs` functions from `ra_ide` and exposes the same functionality via `ra_hir::Documentation::from_ast`. --- crates/ra_hir_def/src/docs.rs | 7 ++++ crates/ra_ide/src/hover.rs | 60 ++++------------------------------- 2 files changed, 14 insertions(+), 53 deletions(-) diff --git a/crates/ra_hir_def/src/docs.rs b/crates/ra_hir_def/src/docs.rs index ab43cd3d3395..2630b3d895e6 100644 --- a/crates/ra_hir_def/src/docs.rs +++ b/crates/ra_hir_def/src/docs.rs @@ -29,6 +29,13 @@ impl Documentation { Documentation(s.into()) } + pub fn from_ast(node: &N) -> Option + where + N: ast::DocCommentsOwner + ast::AttrsOwner, + { + docs_from_ast(node) + } + pub fn as_str(&self) -> &str { &*self.0 } diff --git a/crates/ra_ide/src/hover.rs b/crates/ra_ide/src/hover.rs index 731fc3673535..9636cd0d6af7 100644 --- a/crates/ra_ide/src/hover.rs +++ b/crates/ra_ide/src/hover.rs @@ -1,8 +1,8 @@ use std::iter::once; use hir::{ - Adt, AsAssocItem, AssocItemContainer, FieldSource, HasSource, HirDisplay, ModuleDef, - ModuleSource, Semantics, + Adt, AsAssocItem, AssocItemContainer, Documentation, FieldSource, HasSource, HirDisplay, + ModuleDef, ModuleSource, Semantics, }; use itertools::Itertools; use ra_db::SourceDatabase; @@ -10,12 +10,7 @@ use ra_ide_db::{ defs::{classify_name, classify_name_ref, Definition}, RootDatabase, }; -use ra_syntax::{ - ast::{self, DocCommentsOwner}, - match_ast, AstNode, - SyntaxKind::*, - SyntaxToken, TokenAtOffset, -}; +use ra_syntax::{ast, match_ast, AstNode, SyntaxKind::*, SyntaxToken, TokenAtOffset}; use crate::{ display::{macro_label, rust_code_markup, rust_code_markup_with_doc, ShortLabel}, @@ -169,18 +164,14 @@ fn hover_text_from_name_kind(db: &RootDatabase, def: Definition) -> Option { let src = it.source(db); - let doc_comment_text = src.value.doc_comment_text(); - let doc_attr_text = expand_doc_attrs(&src.value); - let docs = merge_doc_comments_and_attrs(doc_comment_text, doc_attr_text); + let docs = Documentation::from_ast(&src.value).map(Into::into); hover_text(docs, Some(macro_label(&src.value)), mod_path) } Definition::Field(it) => { let src = it.source(db); match src.value { FieldSource::Named(it) => { - let doc_comment_text = it.doc_comment_text(); - let doc_attr_text = expand_doc_attrs(&it); - let docs = merge_doc_comments_and_attrs(doc_comment_text, doc_attr_text); + let docs = Documentation::from_ast(&it).map(Into::into); hover_text(docs, it.short_label(), mod_path) } _ => None, @@ -189,9 +180,7 @@ fn hover_text_from_name_kind(db: &RootDatabase, def: Definition) -> Option match it { ModuleDef::Module(it) => match it.definition_source(db).value { ModuleSource::Module(it) => { - let doc_comment_text = it.doc_comment_text(); - let doc_attr_text = expand_doc_attrs(&it); - let docs = merge_doc_comments_and_attrs(doc_comment_text, doc_attr_text); + let docs = Documentation::from_ast(&it).map(Into::into); hover_text(docs, it.short_label(), mod_path) } _ => None, @@ -220,46 +209,11 @@ fn hover_text_from_name_kind(db: &RootDatabase, def: Definition) -> Option, - doc_attr_text: Option, -) -> Option { - match (doc_comment_text, doc_attr_text) { - (Some(mut comment_text), Some(attr_text)) => { - comment_text.push_str("\n\n"); - comment_text.push_str(&attr_text); - Some(comment_text) - } - (Some(comment_text), None) => Some(comment_text), - (None, Some(attr_text)) => Some(attr_text), - (None, None) => None, - } -} - -fn expand_doc_attrs(owner: &dyn ast::AttrsOwner) -> Option { - let mut docs = String::new(); - for attr in owner.attrs() { - if let Some(("doc", value)) = - attr.as_simple_key_value().as_ref().map(|(k, v)| (k.as_str(), v.as_str())) - { - docs.push_str(value); - docs.push_str("\n\n"); - } - } - if docs.is_empty() { - None - } else { - Some(docs.trim_end_matches("\n\n").to_owned()) - } -} - fn pick_best(tokens: TokenAtOffset) -> Option { return tokens.max_by_key(priority); fn priority(n: &SyntaxToken) -> usize { From ed866892640214d315d3e9503ccaed96ca87ccc0 Mon Sep 17 00:00:00 2001 From: Aaron Loucks Date: Wed, 3 Jun 2020 06:55:27 -0400 Subject: [PATCH 042/172] Update generated feature docs --- docs/user/generated_features.adoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/user/generated_features.adoc b/docs/user/generated_features.adoc index 12812fa0be7e..4b93b759fa42 100644 --- a/docs/user/generated_features.adoc +++ b/docs/user/generated_features.adoc @@ -76,7 +76,7 @@ Navigates to the type of an identifier. === Hover -**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide/src/hover.rs#L63[hover.rs] +**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide/src/hover.rs#L58[hover.rs] Shows additional information, like type of an expression or documentation for definition when "focusing" code. Focusing is usually hovering with a mouse, but can also be triggered with a shortcut. From f06b2bcd91329fb795839a4eabd8f43aa472aeb2 Mon Sep 17 00:00:00 2001 From: Aaron Loucks Date: Wed, 3 Jun 2020 07:26:15 -0400 Subject: [PATCH 043/172] Use split1 when formatting function signature params --- Cargo.lock | 1 + crates/ra_ide/src/display/function_signature.rs | 8 +++----- crates/stdx/src/lib.rs | 5 +++++ crates/test_utils/Cargo.toml | 3 ++- crates/test_utils/src/lib.rs | 6 +----- 5 files changed, 12 insertions(+), 11 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index def4ed45e04e..aca283cda34f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1639,6 +1639,7 @@ dependencies = [ "relative-path", "rustc-hash", "serde_json", + "stdx", "text-size", ] diff --git a/crates/ra_ide/src/display/function_signature.rs b/crates/ra_ide/src/display/function_signature.rs index b081ecaad1cd..ca8a6a650994 100644 --- a/crates/ra_ide/src/display/function_signature.rs +++ b/crates/ra_ide/src/display/function_signature.rs @@ -10,7 +10,7 @@ use std::{ use hir::{Docs, Documentation, HasSource, HirDisplay}; use ra_ide_db::RootDatabase; use ra_syntax::ast::{self, AstNode, NameOwner, VisibilityOwner}; -use stdx::SepBy; +use stdx::{split1, SepBy}; use crate::display::{generic_parameters, where_predicates}; @@ -210,10 +210,8 @@ impl From<&'_ ast::FnDef> for FunctionSignature { // macro-generated functions are missing whitespace fn fmt_param(param: ast::Param) -> String { let text = param.syntax().text().to_string(); - match text.find(':') { - Some(pos) if 1 + pos < text.len() => { - format!("{} {}", &text[0..1 + pos].trim(), &text[1 + pos..].trim()) - } + match split1(&text, ':') { + Some((left, right)) => format!("{}: {}", left.trim(), right.trim()), _ => text, } } diff --git a/crates/stdx/src/lib.rs b/crates/stdx/src/lib.rs index 71a57fba230f..c0356344ca24 100644 --- a/crates/stdx/src/lib.rs +++ b/crates/stdx/src/lib.rs @@ -124,3 +124,8 @@ pub fn replace(buf: &mut String, from: char, to: &str) { // FIXME: do this in place. *buf = buf.replace(from, to) } + +pub fn split1(haystack: &str, delim: char) -> Option<(&str, &str)> { + let idx = haystack.find(delim)?; + Some((&haystack[..idx], &haystack[idx + delim.len_utf8()..])) +} diff --git a/crates/test_utils/Cargo.toml b/crates/test_utils/Cargo.toml index 4d185b01c752..8840bf36ae32 100644 --- a/crates/test_utils/Cargo.toml +++ b/crates/test_utils/Cargo.toml @@ -14,4 +14,5 @@ serde_json = "1.0.48" relative-path = "1.0.0" rustc-hash = "1.1.0" -ra_cfg = { path = "../ra_cfg" } \ No newline at end of file +ra_cfg = { path = "../ra_cfg" } +stdx = { path = "../stdx" } \ No newline at end of file diff --git a/crates/test_utils/src/lib.rs b/crates/test_utils/src/lib.rs index 1bd97215cb83..2141bfc20277 100644 --- a/crates/test_utils/src/lib.rs +++ b/crates/test_utils/src/lib.rs @@ -15,6 +15,7 @@ use std::{ }; pub use ra_cfg::CfgOptions; +use stdx::split1; pub use relative_path::{RelativePath, RelativePathBuf}; pub use rustc_hash::FxHashMap; @@ -332,11 +333,6 @@ fn parse_meta(meta: &str) -> FixtureMeta { FixtureMeta::File(FileMeta { path, crate_name: krate, deps, edition, cfg, env }) } -fn split1(haystack: &str, delim: char) -> Option<(&str, &str)> { - let idx = haystack.find(delim)?; - Some((&haystack[..idx], &haystack[idx + delim.len_utf8()..])) -} - /// Adjusts the indentation of the first line to the minimum indentation of the rest of the lines. /// This allows fixtures to start off in a different indentation, e.g. to align the first line with /// the other lines visually: From fa019c8f562326a720d2ef9165626c4c5703f67b Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Wed, 3 Jun 2020 14:48:38 +0200 Subject: [PATCH 044/172] Document rust-project.json --- crates/ra_project_model/src/lib.rs | 10 ++++-- crates/rust-analyzer/src/config.rs | 23 ++++++++++++ crates/rust-analyzer/src/main_loop.rs | 35 +++++++++--------- docs/user/manual.adoc | 51 +++++++++++++++++++++++++++ editors/code/package.json | 19 ++++++++++ 5 files changed, 118 insertions(+), 20 deletions(-) diff --git a/crates/ra_project_model/src/lib.rs b/crates/ra_project_model/src/lib.rs index a9961269026a..7ad941279506 100644 --- a/crates/ra_project_model/src/lib.rs +++ b/crates/ra_project_model/src/lib.rs @@ -32,6 +32,12 @@ pub enum ProjectWorkspace { Json { project: JsonProject }, } +impl From for ProjectWorkspace { + fn from(project: JsonProject) -> ProjectWorkspace { + ProjectWorkspace::Json { project } + } +} + /// `PackageRoot` describes a package root folder. /// Which may be an external dependency, or a member of /// the current workspace. @@ -144,11 +150,11 @@ impl ProjectManifest { impl ProjectWorkspace { pub fn load( - root: ProjectManifest, + manifest: ProjectManifest, cargo_features: &CargoConfig, with_sysroot: bool, ) -> Result { - let res = match root { + let res = match manifest { ProjectManifest::ProjectJson(project_json) => { let file = File::open(&project_json).with_context(|| { format!("Failed to open json file {}", project_json.display()) diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs index 761bc9c2d92a..0e5dc56fd744 100644 --- a/crates/rust-analyzer/src/config.rs +++ b/crates/rust-analyzer/src/config.rs @@ -261,6 +261,22 @@ impl Config { self.lens = LensConfig::NO_LENS; } + if let Some(linked_projects) = get::>(value, "/linkedProjects") { + if !linked_projects.is_empty() { + self.linked_projects.clear(); + for linked_project in linked_projects { + let linked_project = match linked_project { + ManifestOrJsonProject::Manifest(it) => match ProjectManifest::from_manifest_file(it) { + Ok(it) => it.into(), + Err(_) => continue, + } + ManifestOrJsonProject::JsonProject(it) => it.into(), + }; + self.linked_projects.push(linked_project); + } + } + } + log::info!("Config::update() = {:#?}", self); fn get<'a, T: Deserialize<'a>>(value: &'a serde_json::Value, pointer: &str) -> Option { @@ -324,3 +340,10 @@ impl Config { } } } + +#[derive(Deserialize)] +#[serde(untagged)] +enum ManifestOrJsonProject { + Manifest(PathBuf), + JsonProject(JsonProject), +} diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs index d901f21d7e7e..1f8f6b978616 100644 --- a/crates/rust-analyzer/src/main_loop.rs +++ b/crates/rust-analyzer/src/main_loop.rs @@ -105,24 +105,23 @@ pub fn main_loop(config: Config, connection: Connection) -> Result<()> { .linked_projects .iter() .filter_map(|project| match project { - LinkedProject::ProjectManifest(it) => Some(it), - LinkedProject::JsonProject(_) => None, - }) - .filter_map(|root| { - ra_project_model::ProjectWorkspace::load( - root.clone(), - &config.cargo, - config.with_sysroot, - ) - .map_err(|err| { - log::error!("failed to load workspace: {:#}", err); - show_message( - lsp_types::MessageType::Error, - format!("rust-analyzer failed to load workspace: {:#}", err), - &connection.sender, - ); - }) - .ok() + LinkedProject::ProjectManifest(manifest) => { + ra_project_model::ProjectWorkspace::load( + manifest.clone(), + &config.cargo, + config.with_sysroot, + ) + .map_err(|err| { + log::error!("failed to load workspace: {:#}", err); + show_message( + lsp_types::MessageType::Error, + format!("rust-analyzer failed to load workspace: {:#}", err), + &connection.sender, + ); + }) + .ok() + } + LinkedProject::JsonProject(it) => Some(it.clone().into()), }) .collect::>() }; diff --git a/docs/user/manual.adoc b/docs/user/manual.adoc index 202783fd9538..ea714f49addf 100644 --- a/docs/user/manual.adoc +++ b/docs/user/manual.adoc @@ -269,6 +269,57 @@ Gnome Builder currently has support for RLS, and there's no way to configure the 1. Rename, symlink or copy the `rust-analyzer` binary to `rls` and place it somewhere Builder can find (in `PATH`, or under `~/.cargo/bin`). 2. Enable the Rust Builder plugin. +== Non-Cargo Based Projects + +rust-analyzer does not require Cargo. +However, if you use some other build system, you'll have to describe the structure of your project for rust-analyzer in the `rust-project.json` format: + +[source,TypeScript] +---- +interface JsonProject { + /// The set of paths containing the crates for this project. + /// Any `Crate` must be nested inside some `root`. + roots: string[]; + /// The set of crates comprising the current project. + /// Must include all transitive dependencies as well as sysroot crate (libstd, libcore and such). + crates: Crate[]; +} + +interface Crate { + /// Path to the root module of the crate. + root_module: string; + /// Edition of the crate. + edition: "2015" | "2018"; + /// Dependencies + deps: Dep[]; + /// The set of cfgs activated for a given crate, like `["unix", "feature=foo", "feature=bar"]`. + cfg: string[]; + + /// value of the OUT_DIR env variable. + out_dir?: string; + /// For proc-macro crates, path to compiles proc-macro (.so file). + proc_macro_dylib_path?: string; +} + +interface Dep { + /// Index of a crate in the `crates` array. + crate: number, + /// Name as should appear in the (implicit) `extern crate name` declaration. + name: string, +} +---- + +This format is provisional and subject to change. +Specifically, the `roots` setup will be different eventually. + +There are tree ways to feed `rust-project.json` to rust-analyzer: + +* Place `rust-project.json` file at the root of the project, and rust-anlayzer will discover it. +* Specify `"rust-analyzer.linkedProjects": [ "path/to/rust-project.json" ]` in the settings (and make sure that your LSP client sends settings as a part of initialize request). +* Specify `"rust-analyzer.linkedProjects": [ { "roots": [...], "crates": [...] }]` inline. + +See https://github.com/rust-analyzer/rust-project.json-example for a small example. + == Features include::./generated_features.adoc[] diff --git a/editors/code/package.json b/editors/code/package.json index d8f4287fd88c..30ab7ba4a9f8 100644 --- a/editors/code/package.json +++ b/editors/code/package.json @@ -475,6 +475,25 @@ "markdownDescription": "Whether to show Implementations lens. Only applies when `#rust-analyzer.lens.enable#` is set.", "type": "boolean", "default": true + }, + "rust-analyzer.linkedProjects": { + "markdownDescription": [ + "Disable project auto-discovery in favor of explicitly specified set of projects.", + "Elements must be paths pointing to Cargo.toml, rust-project.json, or JSON objects in rust-project.json format" + ], + "type": "array", + "items": { + "type": [ + "string", + "object" + ] + }, + "default": null + }, + "rust-analyzer.withSysroot": { + "markdownDescription": "Internal config for debugging, disables loading of sysroot crates", + "type": "boolean", + "default": true } } }, From 9e71fc0314a2555eda446a64057df7e8f78fb7c9 Mon Sep 17 00:00:00 2001 From: Jeremy Kolb Date: Sun, 17 May 2020 21:03:40 -0400 Subject: [PATCH 045/172] Mark fixes from diagnostics as quick fixes --- crates/rust-analyzer/src/main_loop/handlers.rs | 9 +++++++-- crates/rust-analyzer/tests/heavy_tests/main.rs | 2 ++ 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/crates/rust-analyzer/src/main_loop/handlers.rs b/crates/rust-analyzer/src/main_loop/handlers.rs index 1bb8e44735f5..c2a5bf4d6b8f 100644 --- a/crates/rust-analyzer/src/main_loop/handlers.rs +++ b/crates/rust-analyzer/src/main_loop/handlers.rs @@ -730,8 +730,13 @@ pub fn handle_code_action( for fix in fixes_from_diagnostics { let title = fix.label; let edit = to_proto::snippet_workspace_edit(&snap, fix.source_change)?; - let action = - lsp_ext::CodeAction { title, group: None, kind: None, edit: Some(edit), command: None }; + let action = lsp_ext::CodeAction { + title, + group: None, + kind: Some(lsp_types::code_action_kind::QUICKFIX.into()), + edit: Some(edit), + command: None, + }; res.push(action); } diff --git a/crates/rust-analyzer/tests/heavy_tests/main.rs b/crates/rust-analyzer/tests/heavy_tests/main.rs index c1805787a398..ad347631054b 100644 --- a/crates/rust-analyzer/tests/heavy_tests/main.rs +++ b/crates/rust-analyzer/tests/heavy_tests/main.rs @@ -298,6 +298,7 @@ fn main() {} } ] }, + "kind": "quickfix", "title": "Create module" }]), ); @@ -368,6 +369,7 @@ fn main() {{}} } ] }, + "kind": "quickfix", "title": "Create module" }]), ); From bc2edc1d1a0c1fc5404ab64275a44284f1932980 Mon Sep 17 00:00:00 2001 From: kjeremy Date: Wed, 3 Jun 2020 09:43:27 -0400 Subject: [PATCH 046/172] Cargo update --- Cargo.lock | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index cc63ec36ceda..5f88ad0c4db5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -462,9 +462,9 @@ dependencies = [ [[package]] name = "indexmap" -version = "1.3.2" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "076f042c5b7b98f31d205f1249267e12a6518c1481e9dae9764af19b707d2292" +checksum = "c398b2b113b55809ceb9ee3e753fcbac793f1956663f3c36549c1346015c2afe" dependencies = [ "autocfg", ] @@ -809,9 +809,9 @@ dependencies = [ [[package]] name = "paste" -version = "0.1.15" +version = "0.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d53181dcd37421c08d3b69f887784956674d09c3f9a47a04fece2b130a5b346b" +checksum = "d508492eeb1e5c38ee696371bf7b9fc33c83d46a7d451606b96458fbbbdc2dec" dependencies = [ "paste-impl", "proc-macro-hack", @@ -819,9 +819,9 @@ dependencies = [ [[package]] name = "paste-impl" -version = "0.1.15" +version = "0.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05ca490fa1c034a71412b4d1edcb904ec5a0981a4426c9eb2128c0fda7a68d17" +checksum = "84f328a6a63192b333fce5fbb4be79db6758a4d518dfac6d54412f1492f72d32" dependencies = [ "proc-macro-hack", "proc-macro2", @@ -871,9 +871,9 @@ checksum = "7e0456befd48169b9f13ef0f0ad46d492cf9d2dbb918bcf38e01eed4ce3ec5e4" [[package]] name = "proc-macro2" -version = "1.0.17" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1502d12e458c49a4c9cbff560d0fe0060c252bc29799ed94ca2ed4bb665a0101" +checksum = "beae6331a816b1f65d04c45b078fd8e6c93e8071771f41b8163255bbd8d7c8fa" dependencies = [ "unicode-xid", ] @@ -1401,9 +1401,9 @@ checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" [[package]] name = "ryu" -version = "1.0.4" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed3d612bc64430efeb3f7ee6ef26d590dce0c43249217bddc62112540c7941e1" +checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e" [[package]] name = "salsa" @@ -1577,9 +1577,9 @@ checksum = "ab16ced94dbd8a46c82fd81e3ed9a8727dac2977ea869d217bcc4ea1f122e81f" [[package]] name = "syn" -version = "1.0.29" +version = "1.0.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb37da98a55b1d08529362d9cbb863be17556873df2585904ab9d2bc951291d0" +checksum = "93a56fabc59dce20fe48b6c832cc249c713e7ed88fa28b0ee0a3bfcaae5fe4e2" dependencies = [ "proc-macro2", "quote", @@ -1799,9 +1799,9 @@ dependencies = [ [[package]] name = "yaml-rust" -version = "0.4.3" +version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65923dd1784f44da1d2c3dbbc5e822045628c590ba72123e1c73d3c230c4434d" +checksum = "39f0c922f1a334134dc2f7a8b67dc5d25f0735263feec974345ff706bcf20b0d" dependencies = [ "linked-hash-map", ] From 5315934d888797432d62ec4a55303aeacb8cd286 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Wed, 3 Jun 2020 18:22:05 +0200 Subject: [PATCH 047/172] Don't store generated docs in the repo --- .gitignore | 2 + docs/user/generated_assists.adoc | 1015 ------------------------- docs/user/generated_features.adoc | 298 -------- xtask/src/codegen.rs | 6 +- xtask/src/codegen/gen_assists_docs.rs | 11 +- xtask/src/lib.rs | 2 + xtask/src/main.rs | 1 + xtask/tests/tidy.rs | 9 +- 8 files changed, 16 insertions(+), 1328 deletions(-) delete mode 100644 docs/user/generated_assists.adoc delete mode 100644 docs/user/generated_features.adoc diff --git a/.gitignore b/.gitignore index dab51647db70..aef0fac3397f 100644 --- a/.gitignore +++ b/.gitignore @@ -8,3 +8,5 @@ crates/*/target *.iml .vscode/settings.json *.html +generated_assists.adoc +generated_features.adoc diff --git a/docs/user/generated_assists.adoc b/docs/user/generated_assists.adoc deleted file mode 100644 index 4d2fb31d4840..000000000000 --- a/docs/user/generated_assists.adoc +++ /dev/null @@ -1,1015 +0,0 @@ -[discrete] -=== `add_custom_impl` -**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_assists/src/handlers/add_custom_impl.rs#L14[add_custom_impl.rs] - -Adds impl block for derived trait. - -.Before -```rust -#[derive(Deb┃ug, Display)] -struct S; -``` - -.After -```rust -#[derive(Display)] -struct S; - -impl Debug for S { - $0 -} -``` - - -[discrete] -=== `add_derive` -**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_assists/src/handlers/add_derive.rs#L9[add_derive.rs] - -Adds a new `#[derive()]` clause to a struct or enum. - -.Before -```rust -struct Point { - x: u32, - y: u32,┃ -} -``` - -.After -```rust -#[derive($0)] -struct Point { - x: u32, - y: u32, -} -``` - - -[discrete] -=== `add_explicit_type` -**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_assists/src/handlers/add_explicit_type.rs#L9[add_explicit_type.rs] - -Specify type for a let binding. - -.Before -```rust -fn main() { - let x┃ = 92; -} -``` - -.After -```rust -fn main() { - let x: i32 = 92; -} -``` - - -[discrete] -=== `add_from_impl_for_enum` -**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_assists/src/handlers/add_from_impl_for_enum.rs#L7[add_from_impl_for_enum.rs] - -Adds a From impl for an enum variant with one tuple field. - -.Before -```rust -enum A { ┃One(u32) } -``` - -.After -```rust -enum A { One(u32) } - -impl From for A { - fn from(v: u32) -> Self { - A::One(v) - } -} -``` - - -[discrete] -=== `add_function` -**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_assists/src/handlers/add_function.rs#L19[add_function.rs] - -Adds a stub function with a signature matching the function under the cursor. - -.Before -```rust -struct Baz; -fn baz() -> Baz { Baz } -fn foo() { - bar┃("", baz()); -} - -``` - -.After -```rust -struct Baz; -fn baz() -> Baz { Baz } -fn foo() { - bar("", baz()); -} - -fn bar(arg: &str, baz: Baz) { - ${0:todo!()} -} - -``` - - -[discrete] -=== `add_hash` -**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_assists/src/handlers/raw_string.rs#L65[raw_string.rs] - -Adds a hash to a raw string literal. - -.Before -```rust -fn main() { - r#"Hello,┃ World!"#; -} -``` - -.After -```rust -fn main() { - r##"Hello, World!"##; -} -``` - - -[discrete] -=== `add_impl` -**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_assists/src/handlers/add_impl.rs#L6[add_impl.rs] - -Adds a new inherent impl for a type. - -.Before -```rust -struct Ctx { - data: T,┃ -} -``` - -.After -```rust -struct Ctx { - data: T, -} - -impl Ctx { - $0 -} -``` - - -[discrete] -=== `add_impl_default_members` -**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_assists/src/handlers/add_missing_impl_members.rs#L64[add_missing_impl_members.rs] - -Adds scaffold for overriding default impl members. - -.Before -```rust -trait Trait { - Type X; - fn foo(&self); - fn bar(&self) {} -} - -impl Trait for () { - Type X = (); - fn foo(&self) {}┃ - -} -``` - -.After -```rust -trait Trait { - Type X; - fn foo(&self); - fn bar(&self) {} -} - -impl Trait for () { - Type X = (); - fn foo(&self) {} - $0fn bar(&self) {} - -} -``` - - -[discrete] -=== `add_impl_missing_members` -**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_assists/src/handlers/add_missing_impl_members.rs#L24[add_missing_impl_members.rs] - -Adds scaffold for required impl members. - -.Before -```rust -trait Trait { - Type X; - fn foo(&self) -> T; - fn bar(&self) {} -} - -impl Trait for () {┃ - -} -``` - -.After -```rust -trait Trait { - Type X; - fn foo(&self) -> T; - fn bar(&self) {} -} - -impl Trait for () { - fn foo(&self) -> u32 { - ${0:todo!()} - } - -} -``` - - -[discrete] -=== `add_new` -**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_assists/src/handlers/add_new.rs#L12[add_new.rs] - -Adds a new inherent impl for a type. - -.Before -```rust -struct Ctx { - data: T,┃ -} -``` - -.After -```rust -struct Ctx { - data: T, -} - -impl Ctx { - fn $0new(data: T) -> Self { Self { data } } -} - -``` - - -[discrete] -=== `add_turbo_fish` -**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_assists/src/handlers/add_turbo_fish.rs#L10[add_turbo_fish.rs] - -Adds `::<_>` to a call of a generic method or function. - -.Before -```rust -fn make() -> T { todo!() } -fn main() { - let x = make┃(); -} -``` - -.After -```rust -fn make() -> T { todo!() } -fn main() { - let x = make::<${0:_}>(); -} -``` - - -[discrete] -=== `apply_demorgan` -**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_assists/src/handlers/apply_demorgan.rs#L5[apply_demorgan.rs] - -Apply [De Morgan's law](https://en.wikipedia.org/wiki/De_Morgan%27s_laws). -This transforms expressions of the form `!l || !r` into `!(l && r)`. -This also works with `&&`. This assist can only be applied with the cursor -on either `||` or `&&`, with both operands being a negation of some kind. -This means something of the form `!x` or `x != y`. - -.Before -```rust -fn main() { - if x != 4 ||┃ !y {} -} -``` - -.After -```rust -fn main() { - if !(x == 4 && y) {} -} -``` - - -[discrete] -=== `auto_import` -**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_assists/src/handlers/auto_import.rs#L18[auto_import.rs] - -If the name is unresolved, provides all possible imports for it. - -.Before -```rust -fn main() { - let map = HashMap┃::new(); -} -``` - -.After -```rust -use std::collections::HashMap; - -fn main() { - let map = HashMap::new(); -} -``` - - -[discrete] -=== `change_return_type_to_result` -**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_assists/src/handlers/change_return_type_to_result.rs#L8[change_return_type_to_result.rs] - -Change the function's return type to Result. - -.Before -```rust -fn foo() -> i32┃ { 42i32 } -``` - -.After -```rust -fn foo() -> Result { Ok(42i32) } -``` - - -[discrete] -=== `change_visibility` -**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_assists/src/handlers/change_visibility.rs#L14[change_visibility.rs] - -Adds or changes existing visibility specifier. - -.Before -```rust -┃fn frobnicate() {} -``` - -.After -```rust -pub(crate) fn frobnicate() {} -``` - - -[discrete] -=== `convert_to_guarded_return` -**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_assists/src/handlers/early_return.rs#L21[early_return.rs] - -Replace a large conditional with a guarded return. - -.Before -```rust -fn main() { - ┃if cond { - foo(); - bar(); - } -} -``` - -.After -```rust -fn main() { - if !cond { - return; - } - foo(); - bar(); -} -``` - - -[discrete] -=== `fill_match_arms` -**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_assists/src/handlers/fill_match_arms.rs#L14[fill_match_arms.rs] - -Adds missing clauses to a `match` expression. - -.Before -```rust -enum Action { Move { distance: u32 }, Stop } - -fn handle(action: Action) { - match action { - ┃ - } -} -``` - -.After -```rust -enum Action { Move { distance: u32 }, Stop } - -fn handle(action: Action) { - match action { - $0Action::Move { distance } => {} - Action::Stop => {} - } -} -``` - - -[discrete] -=== `fix_visibility` -**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_assists/src/handlers/fix_visibility.rs#L13[fix_visibility.rs] - -Makes inaccessible item public. - -.Before -```rust -mod m { - fn frobnicate() {} -} -fn main() { - m::frobnicate┃() {} -} -``` - -.After -```rust -mod m { - $0pub(crate) fn frobnicate() {} -} -fn main() { - m::frobnicate() {} -} -``` - - -[discrete] -=== `flip_binexpr` -**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_assists/src/handlers/flip_binexpr.rs#L5[flip_binexpr.rs] - -Flips operands of a binary expression. - -.Before -```rust -fn main() { - let _ = 90 +┃ 2; -} -``` - -.After -```rust -fn main() { - let _ = 2 + 90; -} -``` - - -[discrete] -=== `flip_comma` -**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_assists/src/handlers/flip_comma.rs#L5[flip_comma.rs] - -Flips two comma-separated items. - -.Before -```rust -fn main() { - ((1, 2),┃ (3, 4)); -} -``` - -.After -```rust -fn main() { - ((3, 4), (1, 2)); -} -``` - - -[discrete] -=== `flip_trait_bound` -**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_assists/src/handlers/flip_trait_bound.rs#L9[flip_trait_bound.rs] - -Flips two trait bounds. - -.Before -```rust -fn foo() { } -``` - -.After -```rust -fn foo() { } -``` - - -[discrete] -=== `inline_local_variable` -**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_assists/src/handlers/inline_local_variable.rs#L13[inline_local_variable.rs] - -Inlines local variable. - -.Before -```rust -fn main() { - let x┃ = 1 + 2; - x * 4; -} -``` - -.After -```rust -fn main() { - (1 + 2) * 4; -} -``` - - -[discrete] -=== `introduce_named_lifetime` -**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_assists/src/handlers/introduce_named_lifetime.rs#L12[introduce_named_lifetime.rs] - -Change an anonymous lifetime to a named lifetime. - -.Before -```rust -impl Cursor<'_┃> { - fn node(self) -> &SyntaxNode { - match self { - Cursor::Replace(node) | Cursor::Before(node) => node, - } - } -} -``` - -.After -```rust -impl<'a> Cursor<'a> { - fn node(self) -> &SyntaxNode { - match self { - Cursor::Replace(node) | Cursor::Before(node) => node, - } - } -} -``` - - -[discrete] -=== `introduce_variable` -**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_assists/src/handlers/introduce_variable.rs#L14[introduce_variable.rs] - -Extracts subexpression into a variable. - -.Before -```rust -fn main() { - ┃(1 + 2)┃ * 4; -} -``` - -.After -```rust -fn main() { - let $0var_name = (1 + 2); - var_name * 4; -} -``` - - -[discrete] -=== `invert_if` -**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_assists/src/handlers/invert_if.rs#L12[invert_if.rs] - -Apply invert_if -This transforms if expressions of the form `if !x {A} else {B}` into `if x {B} else {A}` -This also works with `!=`. This assist can only be applied with the cursor -on `if`. - -.Before -```rust -fn main() { - if┃ !y { A } else { B } -} -``` - -.After -```rust -fn main() { - if y { B } else { A } -} -``` - - -[discrete] -=== `make_raw_string` -**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_assists/src/handlers/raw_string.rs#L10[raw_string.rs] - -Adds `r#` to a plain string literal. - -.Before -```rust -fn main() { - "Hello,┃ World!"; -} -``` - -.After -```rust -fn main() { - r#"Hello, World!"#; -} -``` - - -[discrete] -=== `make_usual_string` -**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_assists/src/handlers/raw_string.rs#L39[raw_string.rs] - -Turns a raw string into a plain string. - -.Before -```rust -fn main() { - r#"Hello,┃ "World!""#; -} -``` - -.After -```rust -fn main() { - "Hello, \"World!\""; -} -``` - - -[discrete] -=== `merge_imports` -**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_assists/src/handlers/merge_imports.rs#L14[merge_imports.rs] - -Merges two imports with a common prefix. - -.Before -```rust -use std::┃fmt::Formatter; -use std::io; -``` - -.After -```rust -use std::{fmt::Formatter, io}; -``` - - -[discrete] -=== `merge_match_arms` -**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_assists/src/handlers/merge_match_arms.rs#L11[merge_match_arms.rs] - -Merges identical match arms. - -.Before -```rust -enum Action { Move { distance: u32 }, Stop } - -fn handle(action: Action) { - match action { - ┃Action::Move(..) => foo(), - Action::Stop => foo(), - } -} -``` - -.After -```rust -enum Action { Move { distance: u32 }, Stop } - -fn handle(action: Action) { - match action { - Action::Move(..) | Action::Stop => foo(), - } -} -``` - - -[discrete] -=== `move_arm_cond_to_match_guard` -**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_assists/src/handlers/move_guard.rs#L56[move_guard.rs] - -Moves if expression from match arm body into a guard. - -.Before -```rust -enum Action { Move { distance: u32 }, Stop } - -fn handle(action: Action) { - match action { - Action::Move { distance } => ┃if distance > 10 { foo() }, - _ => (), - } -} -``` - -.After -```rust -enum Action { Move { distance: u32 }, Stop } - -fn handle(action: Action) { - match action { - Action::Move { distance } if distance > 10 => foo(), - _ => (), - } -} -``` - - -[discrete] -=== `move_bounds_to_where_clause` -**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_assists/src/handlers/move_bounds.rs#L10[move_bounds.rs] - -Moves inline type bounds to a where clause. - -.Before -```rust -fn apply U>(f: F, x: T) -> U { - f(x) -} -``` - -.After -```rust -fn apply(f: F, x: T) -> U where F: FnOnce(T) -> U { - f(x) -} -``` - - -[discrete] -=== `move_guard_to_arm_body` -**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_assists/src/handlers/move_guard.rs#L8[move_guard.rs] - -Moves match guard into match arm body. - -.Before -```rust -enum Action { Move { distance: u32 }, Stop } - -fn handle(action: Action) { - match action { - Action::Move { distance } ┃if distance > 10 => foo(), - _ => (), - } -} -``` - -.After -```rust -enum Action { Move { distance: u32 }, Stop } - -fn handle(action: Action) { - match action { - Action::Move { distance } => if distance > 10 { foo() }, - _ => (), - } -} -``` - - -[discrete] -=== `remove_dbg` -**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_assists/src/handlers/remove_dbg.rs#L8[remove_dbg.rs] - -Removes `dbg!()` macro call. - -.Before -```rust -fn main() { - ┃dbg!(92); -} -``` - -.After -```rust -fn main() { - 92; -} -``` - - -[discrete] -=== `remove_hash` -**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_assists/src/handlers/raw_string.rs#L89[raw_string.rs] - -Removes a hash from a raw string literal. - -.Before -```rust -fn main() { - r#"Hello,┃ World!"#; -} -``` - -.After -```rust -fn main() { - r"Hello, World!"; -} -``` - - -[discrete] -=== `remove_mut` -**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_assists/src/handlers/remove_mut.rs#L5[remove_mut.rs] - -Removes the `mut` keyword. - -.Before -```rust -impl Walrus { - fn feed(&mut┃ self, amount: u32) {} -} -``` - -.After -```rust -impl Walrus { - fn feed(&self, amount: u32) {} -} -``` - - -[discrete] -=== `reorder_fields` -**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_assists/src/handlers/reorder_fields.rs#L10[reorder_fields.rs] - -Reorder the fields of record literals and record patterns in the same order as in -the definition. - -.Before -```rust -struct Foo {foo: i32, bar: i32}; -const test: Foo = ┃Foo {bar: 0, foo: 1} -``` - -.After -```rust -struct Foo {foo: i32, bar: i32}; -const test: Foo = Foo {foo: 1, bar: 0} -``` - - -[discrete] -=== `replace_if_let_with_match` -**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_assists/src/handlers/replace_if_let_with_match.rs#L13[replace_if_let_with_match.rs] - -Replaces `if let` with an else branch with a `match` expression. - -.Before -```rust -enum Action { Move { distance: u32 }, Stop } - -fn handle(action: Action) { - ┃if let Action::Move { distance } = action { - foo(distance) - } else { - bar() - } -} -``` - -.After -```rust -enum Action { Move { distance: u32 }, Stop } - -fn handle(action: Action) { - match action { - Action::Move { distance } => foo(distance), - _ => bar(), - } -} -``` - - -[discrete] -=== `replace_let_with_if_let` -**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_assists/src/handlers/replace_let_with_if_let.rs#L14[replace_let_with_if_let.rs] - -Replaces `let` with an `if-let`. - -.Before -```rust - -fn main(action: Action) { - ┃let x = compute(); -} - -fn compute() -> Option { None } -``` - -.After -```rust - -fn main(action: Action) { - if let Some(x) = compute() { - } -} - -fn compute() -> Option { None } -``` - - -[discrete] -=== `replace_qualified_name_with_use` -**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_assists/src/handlers/replace_qualified_name_with_use.rs#L6[replace_qualified_name_with_use.rs] - -Adds a use statement for a given fully-qualified name. - -.Before -```rust -fn process(map: std::collections::┃HashMap) {} -``` - -.After -```rust -use std::collections::HashMap; - -fn process(map: HashMap) {} -``` - - -[discrete] -=== `replace_unwrap_with_match` -**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_assists/src/handlers/replace_unwrap_with_match.rs#L17[replace_unwrap_with_match.rs] - -Replaces `unwrap` a `match` expression. Works for Result and Option. - -.Before -```rust -enum Result { Ok(T), Err(E) } -fn main() { - let x: Result = Result::Ok(92); - let y = x.┃unwrap(); -} -``` - -.After -```rust -enum Result { Ok(T), Err(E) } -fn main() { - let x: Result = Result::Ok(92); - let y = match x { - Ok(a) => a, - $0_ => unreachable!(), - }; -} -``` - - -[discrete] -=== `split_import` -**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_assists/src/handlers/split_import.rs#L7[split_import.rs] - -Wraps the tail of import into braces. - -.Before -```rust -use std::┃collections::HashMap; -``` - -.After -```rust -use std::{collections::HashMap}; -``` - - -[discrete] -=== `unwrap_block` -**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_assists/src/handlers/unwrap_block.rs#L9[unwrap_block.rs] - -This assist removes if...else, for, while and loop control statements to just keep the body. - -.Before -```rust -fn foo() { - if true {┃ - println!("foo"); - } -} -``` - -.After -```rust -fn foo() { - println!("foo"); -} -``` diff --git a/docs/user/generated_features.adoc b/docs/user/generated_features.adoc deleted file mode 100644 index 4b93b759fa42..000000000000 --- a/docs/user/generated_features.adoc +++ /dev/null @@ -1,298 +0,0 @@ -=== Expand Macro Recursively -**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide/src/expand_macro.rs#L15[expand_macro.rs] - -Shows the full macro expansion of the macro at current cursor. - -|=== -| Editor | Action Name - -| VS Code | **Rust Analyzer: Expand macro recursively** -|=== - - -=== Extend Selection -**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide/src/extend_selection.rs#L15[extend_selection.rs] - -Extends the current selection to the encompassing syntactic construct -(expression, statement, item, module, etc). It works with multiple cursors. - -|=== -| Editor | Shortcut - -| VS Code | kbd:[Ctrl+Shift+→] -|=== - - -=== File Structure -**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide/src/display/structure.rs#L17[structure.rs] - -Provides a tree of the symbols defined in the file. Can be used to - -* fuzzy search symbol in a file (super useful) -* draw breadcrumbs to describe the context around the cursor -* draw outline of the file - -|=== -| Editor | Shortcut - -| VS Code | kbd:[Ctrl+Shift+O] -|=== - - -=== Go to Definition -**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide/src/goto_definition.rs#L18[goto_definition.rs] - -Navigates to the definition of an identifier. - -|=== -| Editor | Shortcut - -| VS Code | kbd:[F12] -|=== - - -=== Go to Implementation -**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide/src/goto_implementation.rs#L7[goto_implementation.rs] - -Navigates to the impl block of structs, enums or traits. Also implemented as a code lens. - -|=== -| Editor | Shortcut - -| VS Code | kbd:[Ctrl+F12] -|=== - - -=== Go to Type Definition -**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide/src/goto_type_definition.rs#L6[goto_type_definition.rs] - -Navigates to the type of an identifier. - -|=== -| Editor | Action Name - -| VS Code | **Go to Type Definition* -|=== - - -=== Hover -**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide/src/hover.rs#L58[hover.rs] - -Shows additional information, like type of an expression or documentation for definition when "focusing" code. -Focusing is usually hovering with a mouse, but can also be triggered with a shortcut. - - -=== Inlay Hints -**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide/src/inlay_hints.rs#L40[inlay_hints.rs] - -rust-analyzer shows additional information inline with the source code. -Editors usually render this using read-only virtual text snippets interspersed with code. - -rust-analyzer shows hits for - -* types of local variables -* names of function arguments -* types of chained expressions - -**Note:** VS Code does not have native support for inlay hints https://github.com/microsoft/vscode/issues/16221[yet] and the hints are implemented using decorations. -This approach has limitations, the caret movement and bracket highlighting near the edges of the hint may be weird: -https://github.com/rust-analyzer/rust-analyzer/issues/1623[1], https://github.com/rust-analyzer/rust-analyzer/issues/3453[2]. - -|=== -| Editor | Action Name - -| VS Code | **Rust Analyzer: Toggle inlay hints* -|=== - - -=== Join Lines -**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide/src/join_lines.rs#L12[join_lines.rs] - -Join selected lines into one, smartly fixing up whitespace, trailing commas, and braces. - -|=== -| Editor | Action Name - -| VS Code | **Rust Analyzer: Join lines** -|=== - - -=== Magic Completions -**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide/src/completion.rs#L38[completion.rs] - -In addition to usual reference completion, rust-analyzer provides some ✨magic✨ -completions as well: - -Keywords like `if`, `else` `while`, `loop` are completed with braces, and cursor -is placed at the appropriate position. Even though `if` is easy to type, you -still want to complete it, to get ` { }` for free! `return` is inserted with a -space or `;` depending on the return type of the function. - -When completing a function call, `()` are automatically inserted. If a function -takes arguments, the cursor is positioned inside the parenthesis. - -There are postfix completions, which can be triggered by typing something like -`foo().if`. The word after `.` determines postfix completion. Possible variants are: - -- `expr.if` -> `if expr {}` or `if let ... {}` for `Option` or `Result` -- `expr.match` -> `match expr {}` -- `expr.while` -> `while expr {}` or `while let ... {}` for `Option` or `Result` -- `expr.ref` -> `&expr` -- `expr.refm` -> `&mut expr` -- `expr.not` -> `!expr` -- `expr.dbg` -> `dbg!(expr)` - -There also snippet completions: - -.Expressions -- `pd` -> `println!("{:?}")` -- `ppd` -> `println!("{:#?}")` - -.Items -- `tfn` -> `#[test] fn f(){}` -- `tmod` -> -```rust -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_fn() {} -} -``` - - -=== Matching Brace -**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide/src/matching_brace.rs#L3[matching_brace.rs] - -If the cursor is on any brace (`<>(){}[]`) which is a part of a brace-pair, -moves cursor to the matching brace. It uses the actual parser to determine -braces, so it won't confuse generics with comparisons. - -|=== -| Editor | Action Name - -| VS Code | **Rust Analyzer: Find matching brace** -|=== - - -=== On Typing Assists -**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide/src/typing.rs#L35[typing.rs] - -Some features trigger on typing certain characters: - -- typing `let =` tries to smartly add `;` if `=` is followed by an existing expression -- Enter inside comments automatically inserts `///` -- typing `.` in a chain method call auto-indents - - -=== Parent Module -**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide/src/parent_module.rs#L12[parent_module.rs] - -Navigates to the parent module of the current module. - -|=== -| Editor | Action Name - -| VS Code | **Rust Analyzer: Locate parent module** -|=== - - -=== Run -**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide/src/runnables.rs#L45[runnables.rs] - -Shows a popup suggesting to run a test/benchmark/binary **at the current cursor -location**. Super useful for repeatedly running just a single test. Do bind this -to a shortcut! - -|=== -| Editor | Action Name - -| VS Code | **Rust Analyzer: Run** -|=== - - -=== Semantic Syntax Highlighting -**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide/src/syntax_highlighting.rs#L33[syntax_highlighting.rs] - -rust-analyzer highlights the code semantically. -For example, `bar` in `foo::Bar` might be colored differently depending on whether `Bar` is an enum or a trait. -rust-analyzer does not specify colors directly, instead it assigns tag (like `struct`) and a set of modifiers (like `declaration`) to each token. -It's up to the client to map those to specific colors. - -The general rule is that a reference to an entity gets colored the same way as the entity itself. -We also give special modifier for `mut` and `&mut` local variables. - - -=== Show Syntax Tree -**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide/src/syntax_tree.rs#L9[syntax_tree.rs] - -Shows the parse tree of the current file. It exists mostly for debugging -rust-analyzer itself. - -|=== -| Editor | Action Name - -| VS Code | **Rust Analyzer: Show Syntax Tree** -|=== - - -=== Status -**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide/src/status.rs#L27[status.rs] - -Shows internal statistic about memory usage of rust-analyzer. - -|=== -| Editor | Action Name - -| VS Code | **Rust Analyzer: Status** -|=== - - -=== Structural Seach and Replace -**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide/src/ssr.rs#L26[ssr.rs] - -Search and replace with named wildcards that will match any expression. -The syntax for a structural search replace command is ` ==>> `. -A `$:expr` placeholder in the search pattern will match any expression and `$` will reference it in the replacement. -Available via the command `rust-analyzer.ssr`. - -```rust -// Using structural search replace command [foo($a:expr, $b:expr) ==>> ($a).foo($b)] - -// BEFORE -String::from(foo(y + 5, z)) - -// AFTER -String::from((y + 5).foo(z)) -``` - -|=== -| Editor | Action Name - -| VS Code | **Rust Analyzer: Structural Search Replace** -|=== - - -=== Workspace Symbol -**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide_db/src/symbol_index.rs#L113[symbol_index.rs] - -Uses fuzzy-search to find types, modules and functions by name across your -project and dependencies. This is **the** most useful feature, which improves code -navigation tremendously. It mostly works on top of the built-in LSP -functionality, however `#` and `*` symbols can be used to narrow down the -search. Specifically, - -- `Foo` searches for `Foo` type in the current workspace -- `foo#` searches for `foo` function in the current workspace -- `Foo*` searches for `Foo` type among dependencies, including `stdlib` -- `foo#*` searches for `foo` function among dependencies - -That is, `#` switches from "types" to all symbols, `*` switches from the current -workspace to dependencies. - -|=== -| Editor | Shortcut - -| VS Code | kbd:[Ctrl+T] -|=== diff --git a/xtask/src/codegen.rs b/xtask/src/codegen.rs index 5511c01d5487..f5f4b964a4c1 100644 --- a/xtask/src/codegen.rs +++ b/xtask/src/codegen.rs @@ -18,8 +18,10 @@ use std::{ use crate::{not_bash::fs2, project_root, Result}; pub use self::{ - gen_assists_docs::generate_assists_docs, gen_feature_docs::generate_feature_docs, - gen_parser_tests::generate_parser_tests, gen_syntax::generate_syntax, + gen_assists_docs::{generate_assists_docs, generate_assists_tests}, + gen_feature_docs::generate_feature_docs, + gen_parser_tests::generate_parser_tests, + gen_syntax::generate_syntax, }; const GRAMMAR_DIR: &str = "crates/ra_parser/src/grammar"; diff --git a/xtask/src/codegen/gen_assists_docs.rs b/xtask/src/codegen/gen_assists_docs.rs index 6c1be53503dc..526941f73ace 100644 --- a/xtask/src/codegen/gen_assists_docs.rs +++ b/xtask/src/codegen/gen_assists_docs.rs @@ -7,16 +7,17 @@ use crate::{ project_root, rust_files, Result, }; +pub fn generate_assists_tests(mode: Mode) -> Result<()> { + let assists = Assist::collect()?; + generate_tests(&assists, mode) +} + pub fn generate_assists_docs(mode: Mode) -> Result<()> { let assists = Assist::collect()?; - generate_tests(&assists, mode)?; - let contents = assists.into_iter().map(|it| it.to_string()).collect::>().join("\n\n"); let contents = contents.trim().to_string() + "\n"; let dst = project_root().join("docs/user/generated_assists.adoc"); - codegen::update(&dst, &contents, mode)?; - - Ok(()) + codegen::update(&dst, &contents, mode) } #[derive(Debug)] diff --git a/xtask/src/lib.rs b/xtask/src/lib.rs index 874957885e84..739f49f7be81 100644 --- a/xtask/src/lib.rs +++ b/xtask/src/lib.rs @@ -160,6 +160,8 @@ pub fn run_release(dry_run: bool) -> Result<()> { run!("git reset --hard tags/nightly")?; run!("git push")?; } + codegen::generate_assists_docs(Mode::Overwrite)?; + codegen::generate_feature_docs(Mode::Overwrite)?; let website_root = project_root().join("../rust-analyzer.github.io"); let changelog_dir = website_root.join("./thisweek/_posts"); diff --git a/xtask/src/main.rs b/xtask/src/main.rs index 9d7cdd1145ae..81bb3a33f29e 100644 --- a/xtask/src/main.rs +++ b/xtask/src/main.rs @@ -74,6 +74,7 @@ FLAGS: args.finish()?; codegen::generate_syntax(Mode::Overwrite)?; codegen::generate_parser_tests(Mode::Overwrite)?; + codegen::generate_assists_tests(Mode::Overwrite)?; codegen::generate_assists_docs(Mode::Overwrite)?; codegen::generate_feature_docs(Mode::Overwrite)?; Ok(()) diff --git a/xtask/tests/tidy.rs b/xtask/tests/tidy.rs index 62626064ee51..d38ac7f17e78 100644 --- a/xtask/tests/tidy.rs +++ b/xtask/tests/tidy.rs @@ -25,18 +25,11 @@ fn generated_tests_are_fresh() { #[test] fn generated_assists_are_fresh() { - if let Err(error) = codegen::generate_assists_docs(Mode::Verify) { + if let Err(error) = codegen::generate_assists_tests(Mode::Verify) { panic!("{}. Please update assists by running `cargo xtask codegen`", error); } } -#[test] -fn generated_features_are_fresh() { - if let Err(error) = codegen::generate_feature_docs(Mode::Verify) { - panic!("{}. Please update features by running `cargo xtask codegen`", error); - } -} - #[test] fn check_code_formatting() { if let Err(error) = run_rustfmt(Mode::Verify) { From bacd0428fa0fd744eb0aac6d5d7abd18c6c707b7 Mon Sep 17 00:00:00 2001 From: Mikhail Rakhmanov Date: Wed, 3 Jun 2020 18:39:01 +0200 Subject: [PATCH 048/172] Fix review comments --- crates/rust-analyzer/src/lsp_ext.rs | 1 - .../rust-analyzer/src/main_loop/handlers.rs | 27 +++++++-------- crates/rust-analyzer/src/to_proto.rs | 34 ++++++------------- editors/code/src/client.ts | 2 -- editors/code/src/lsp_ext.ts | 1 - 5 files changed, 24 insertions(+), 41 deletions(-) diff --git a/crates/rust-analyzer/src/lsp_ext.rs b/crates/rust-analyzer/src/lsp_ext.rs index 4b436c3013f2..3b957534ddd0 100644 --- a/crates/rust-analyzer/src/lsp_ext.rs +++ b/crates/rust-analyzer/src/lsp_ext.rs @@ -111,7 +111,6 @@ impl Request for ResolveCodeActionRequest { pub struct ResolveCodeActionParams { pub code_action_params: lsp_types::CodeActionParams, pub id: String, - pub label: String, } pub enum OnEnter {} diff --git a/crates/rust-analyzer/src/main_loop/handlers.rs b/crates/rust-analyzer/src/main_loop/handlers.rs index 3c40644414bb..fab82ff7ead0 100644 --- a/crates/rust-analyzer/src/main_loop/handlers.rs +++ b/crates/rust-analyzer/src/main_loop/handlers.rs @@ -756,9 +756,13 @@ pub fn handle_code_action( handle_fixes(&world, ¶ms, &mut res)?; if world.config.client_caps.resolve_code_action { - for assist in world.analysis().unresolved_assists(&world.config.assist, frange)?.into_iter() + for (index, assist) in world + .analysis() + .unresolved_assists(&world.config.assist, frange)? + .into_iter() + .enumerate() { - res.push(to_proto::unresolved_code_action(&world, assist)?); + res.push(to_proto::unresolved_code_action(&world, assist, index)?); } } else { for assist in world.analysis().resolved_assists(&world.config.assist, frange)?.into_iter() { @@ -773,24 +777,19 @@ pub fn handle_resolve_code_action( world: WorldSnapshot, params: lsp_ext::ResolveCodeActionParams, ) -> Result> { - if !world.config.client_caps.resolve_code_action { - return Ok(None); - } - let _p = profile("handle_resolve_code_action"); let file_id = from_proto::file_id(&world, ¶ms.code_action_params.text_document.uri)?; let line_index = world.analysis().file_line_index(file_id)?; let range = from_proto::text_range(&line_index, params.code_action_params.range); let frange = FileRange { file_id, range }; - let mut res: Vec = Vec::new(); - for assist in world.analysis().resolved_assists(&world.config.assist, frange)?.into_iter() { - res.push(to_proto::resolved_code_action(&world, assist)?); - } - Ok(res - .into_iter() - .find(|action| action.id.clone().unwrap() == params.id && action.title == params.label) - .and_then(|action| action.edit)) + let assists = world.analysis().resolved_assists(&world.config.assist, frange)?; + let id_components = params.id.split(":").collect::>(); + let index = id_components.last().unwrap().parse::().unwrap(); + let id_string = id_components.first().unwrap(); + let assist = &assists[index]; + assert!(assist.assist.id.0 == *id_string); + Ok(to_proto::resolved_code_action(&world, assist.clone())?.edit) } pub fn handle_code_lens( diff --git a/crates/rust-analyzer/src/to_proto.rs b/crates/rust-analyzer/src/to_proto.rs index 3672b1a26026..fb33bdd5f69d 100644 --- a/crates/rust-analyzer/src/to_proto.rs +++ b/crates/rust-analyzer/src/to_proto.rs @@ -622,17 +622,12 @@ fn main() { pub(crate) fn unresolved_code_action( world: &WorldSnapshot, assist: Assist, + index: usize, ) -> Result { let res = lsp_ext::CodeAction { title: assist.label, - id: Some(assist.id.0.to_owned()), - group: assist.group.and_then(|it| { - if world.config.client_caps.code_action_group { - None - } else { - Some(it.0) - } - }), + id: Some(format!("{}:{}", assist.id.0.to_owned(), index.to_string())), + group: assist.group.filter(|_| world.config.client_caps.code_action_group).map(|gr| gr.0), kind: Some(String::new()), edit: None, command: None, @@ -644,21 +639,14 @@ pub(crate) fn resolved_code_action( world: &WorldSnapshot, assist: ResolvedAssist, ) -> Result { - let res = lsp_ext::CodeAction { - title: assist.assist.label, - id: Some(assist.assist.id.0.to_owned()), - group: assist.assist.group.and_then(|it| { - if world.config.client_caps.code_action_group { - None - } else { - Some(it.0) - } - }), - kind: Some(String::new()), - edit: Some(snippet_workspace_edit(world, assist.source_change)?), - command: None, - }; - Ok(res) + let change = assist.source_change; + unresolved_code_action(world, assist.assist, 0).and_then(|it| { + Ok(lsp_ext::CodeAction { + id: None, + edit: Some(snippet_workspace_edit(world, change)?), + ..it + }) + }) } pub(crate) fn runnable( diff --git a/editors/code/src/client.ts b/editors/code/src/client.ts index a25091f797c3..40ad1e3cd847 100644 --- a/editors/code/src/client.ts +++ b/editors/code/src/client.ts @@ -61,8 +61,6 @@ export function createClient(serverPath: string, cwd: string): lc.LanguageClient const id = (item as any).id; const resolveParams: ra.ResolveCodeActionParams = { id: id, - // TODO: delete after discussions if needed - label: item.title, codeActionParams: params }; action.command = { diff --git a/editors/code/src/lsp_ext.ts b/editors/code/src/lsp_ext.ts index 35d73ce31da3..9793b926c26e 100644 --- a/editors/code/src/lsp_ext.ts +++ b/editors/code/src/lsp_ext.ts @@ -35,7 +35,6 @@ export const parentModule = new lc.RequestType('experimental/resolveCodeAction'); From 1f7de306f547ecb394a34445fd6ac1d6bc8ab439 Mon Sep 17 00:00:00 2001 From: Mikhail Rakhmanov Date: Wed, 3 Jun 2020 18:57:50 +0200 Subject: [PATCH 049/172] Add documentation --- docs/dev/lsp-extensions.md | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/docs/dev/lsp-extensions.md b/docs/dev/lsp-extensions.md index 647cf6107565..7f7940d0b6af 100644 --- a/docs/dev/lsp-extensions.md +++ b/docs/dev/lsp-extensions.md @@ -97,6 +97,30 @@ Invoking code action at this position will yield two code actions for importing * Is a fixed two-level structure enough? * Should we devise a general way to encode custom interaction protocols for GUI refactorings? +## Lazy assists with `ResolveCodeAction` + +**Issue:** https://github.com/microsoft/language-server-protocol/issues/787 + +**Client Capability** `{ "resolveCodeAction": boolean }` + +If this capability is set, the assists will be computed lazily. Thus `CodeAction` returned from the server will only contain `id` but not `edit` or `command` fields. The only exclusion from the rule is the diagnostic edits. + +After the client got the id, it should then call `experimental/resolveCodeAction` command on the server and provide the following payload: + +```typescript +interface ResolveCodeActionParams { + id: string; + codeActionParams: lc.CodeActionParams; +} +``` + +As a result of the command call the client will get the respective workspace edit (`lc.WorkspaceEdit`). + +### Unresolved Questions + +* Apply smarter filtering for ids? +* Upon `resolveCodeAction` command only call the assits which should be resolved and not all of them? + ## Parent Module **Issue:** https://github.com/microsoft/language-server-protocol/issues/1002 From 6cd2e04bd2a703c335566224e8b6bf773b83c0c6 Mon Sep 17 00:00:00 2001 From: Mikhail Rakhmanov Date: Wed, 3 Jun 2020 19:33:57 +0200 Subject: [PATCH 050/172] Fix more comments --- crates/rust-analyzer/src/main_loop/handlers.rs | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/crates/rust-analyzer/src/main_loop/handlers.rs b/crates/rust-analyzer/src/main_loop/handlers.rs index a3361d6dc5e9..6acf80c5827f 100644 --- a/crates/rust-analyzer/src/main_loop/handlers.rs +++ b/crates/rust-analyzer/src/main_loop/handlers.rs @@ -25,7 +25,7 @@ use ra_project_model::TargetKind; use ra_syntax::{AstNode, SyntaxKind, TextRange, TextSize}; use serde::{Deserialize, Serialize}; use serde_json::to_value; -use stdx::format_to; +use stdx::{format_to, split1}; use crate::{ cargo_target_spec::CargoTargetSpec, @@ -786,11 +786,10 @@ pub fn handle_resolve_code_action( let frange = FileRange { file_id, range }; let assists = snap.analysis().resolved_assists(&snap.config.assist, frange)?; - let id_components = params.id.split(":").collect::>(); - let index = id_components.last().unwrap().parse::().unwrap(); - let id_string = id_components.first().unwrap(); + let (id_string, index) = split1(¶ms.id, ':').unwrap(); + let index = index.parse::().unwrap(); let assist = &assists[index]; - assert!(assist.assist.id.0 == *id_string); + assert!(assist.assist.id.0 == id_string); Ok(to_proto::resolved_code_action(&snap, assist.clone())?.edit) } From a9d567584857b1be4ca8eaa5ef2c7d85f7b2845e Mon Sep 17 00:00:00 2001 From: Mikhail Rakhmanov Date: Wed, 3 Jun 2020 20:10:33 +0200 Subject: [PATCH 051/172] Fix incorrect behaviour if not resolved --- crates/ra_assists/src/assist_context.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/crates/ra_assists/src/assist_context.rs b/crates/ra_assists/src/assist_context.rs index bc54814948de..1925db8b2754 100644 --- a/crates/ra_assists/src/assist_context.rs +++ b/crates/ra_assists/src/assist_context.rs @@ -179,6 +179,7 @@ impl Assists { f: impl FnOnce(&mut AssistDirector), ) -> Option<()> { if !self.resolve { + self.buf.push((label, None)); return None; } let mut director = AssistDirector::default(); From a6d3c77bdd998499941a6aceccde85f7a94b804d Mon Sep 17 00:00:00 2001 From: Mikhail Rakhmanov Date: Wed, 3 Jun 2020 20:43:57 +0200 Subject: [PATCH 052/172] Fixed tests --- .../extract_struct_from_enum_variant.rs | 21 +++++++++++-------- crates/ra_assists/src/lib.rs | 2 +- crates/ra_assists/src/tests/generated.rs | 15 +++++++++++++ 3 files changed, 28 insertions(+), 10 deletions(-) diff --git a/crates/ra_assists/src/handlers/extract_struct_from_enum_variant.rs b/crates/ra_assists/src/handlers/extract_struct_from_enum_variant.rs index a36587633b16..2b1951aff555 100644 --- a/crates/ra_assists/src/handlers/extract_struct_from_enum_variant.rs +++ b/crates/ra_assists/src/handlers/extract_struct_from_enum_variant.rs @@ -18,9 +18,9 @@ use ra_db::FileId; use ra_fmt::leading_indent; use rustc_hash::FxHashSet; -// Assist extract_struct_from_enum +// Assist: extract_struct_from_enum_variant // -// Extracts a struct from enum variant +// Extracts a struct from enum variant. // // ``` // enum A { <|>One(u32, u32) } @@ -29,9 +29,12 @@ use rustc_hash::FxHashSet; // ``` // struct One(pub u32, pub u32); // -// enum A { One(One) }" +// enum A { One(One) } // ``` -pub(crate) fn extract_struct_from_enum(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { +pub(crate) fn extract_struct_from_enum_variant( + acc: &mut Assists, + ctx: &AssistContext, +) -> Option<()> { let variant = ctx.find_node_at_offset::()?; let field_list = match variant.kind() { ast::StructKind::Tuple(field_list) => field_list, @@ -221,7 +224,7 @@ mod tests { #[test] fn test_extract_struct_several_fields() { check_assist( - extract_struct_from_enum, + extract_struct_from_enum_variant, "enum A { <|>One(u32, u32) }", r#"struct One(pub u32, pub u32); @@ -232,7 +235,7 @@ enum A { One(One) }"#, #[test] fn test_extract_struct_one_field() { check_assist( - extract_struct_from_enum, + extract_struct_from_enum_variant, "enum A { <|>One(u32) }", r#"struct One(pub u32); @@ -243,7 +246,7 @@ enum A { One(One) }"#, #[test] fn test_extract_struct_pub_visibility() { check_assist( - extract_struct_from_enum, + extract_struct_from_enum_variant, "pub enum A { <|>One(u32, u32) }", r#"pub struct One(pub u32, pub u32); @@ -254,7 +257,7 @@ pub enum A { One(One) }"#, #[test] fn test_extract_struct_with_complex_imports() { check_assist( - extract_struct_from_enum, + extract_struct_from_enum_variant, r#"mod my_mod { fn another_fn() { let m = my_other_mod::MyEnum::MyField(1, 1); @@ -305,7 +308,7 @@ fn another_fn() { fn check_not_applicable(ra_fixture: &str) { let fixture = format!("//- main.rs crate:main deps:core\n{}\n{}", ra_fixture, FamousDefs::FIXTURE); - check_assist_not_applicable(extract_struct_from_enum, &fixture) + check_assist_not_applicable(extract_struct_from_enum_variant, &fixture) } #[test] diff --git a/crates/ra_assists/src/lib.rs b/crates/ra_assists/src/lib.rs index 88ce9b62ebf0..185428bd5593 100644 --- a/crates/ra_assists/src/lib.rs +++ b/crates/ra_assists/src/lib.rs @@ -156,7 +156,7 @@ mod handlers { change_return_type_to_result::change_return_type_to_result, change_visibility::change_visibility, early_return::convert_to_guarded_return, - extract_struct_from_enum_variant::extract_struct_from_enum, + extract_struct_from_enum_variant::extract_struct_from_enum_variant, fill_match_arms::fill_match_arms, fix_visibility::fix_visibility, flip_binexpr::flip_binexpr, diff --git a/crates/ra_assists/src/tests/generated.rs b/crates/ra_assists/src/tests/generated.rs index d17504529fd4..40a223727c1f 100644 --- a/crates/ra_assists/src/tests/generated.rs +++ b/crates/ra_assists/src/tests/generated.rs @@ -337,6 +337,21 @@ fn main() { ) } +#[test] +fn doctest_extract_struct_from_enum_variant() { + check_doc_test( + "extract_struct_from_enum_variant", + r#####" +enum A { <|>One(u32, u32) } +"#####, + r#####" +struct One(pub u32, pub u32); + +enum A { One(One) } +"#####, + ) +} + #[test] fn doctest_fill_match_arms() { check_doc_test( From 41ae7ed79f75d52179e3553e30e47709a82e693b Mon Sep 17 00:00:00 2001 From: Veetaha Date: Thu, 4 Jun 2020 01:48:47 +0300 Subject: [PATCH 053/172] Bufgix --- docs/dev/README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/dev/README.md b/docs/dev/README.md index 1de5a2aab1d7..194a40e15c42 100644 --- a/docs/dev/README.md +++ b/docs/dev/README.md @@ -170,7 +170,7 @@ https://www.tedinski.com/2018/02/06/system-boundaries.html We separate import groups with blank lines -``` +```rust mod x; mod y; @@ -195,7 +195,7 @@ Put `struct`s and `enum`s first, functions and impls last. Do -``` +```rust // Good struct Foo { bars: Vec @@ -206,7 +206,7 @@ struct Bar; rather than -``` +```rust // Not as good struct Bar; From fb632c747d953d615575850477d4662802be320f Mon Sep 17 00:00:00 2001 From: Avi Dessauer Date: Wed, 3 Jun 2020 15:21:47 -0400 Subject: [PATCH 054/172] Parse default unsafe & default const --- crates/ra_parser/src/grammar/items.rs | 26 +++++++++++++- .../inline/err/0010_wrong_order_fns.rast | 34 +++++++++++-------- 2 files changed, 45 insertions(+), 15 deletions(-) diff --git a/crates/ra_parser/src/grammar/items.rs b/crates/ra_parser/src/grammar/items.rs index 67a924de5397..41f8bb0b6e01 100644 --- a/crates/ra_parser/src/grammar/items.rs +++ b/crates/ra_parser/src/grammar/items.rs @@ -118,7 +118,15 @@ pub(super) fn maybe_item(p: &mut Parser, m: Marker, flavor: ItemFlavor) -> Resul && p.at_contextual_kw("default") && (match p.nth(1) { T![impl] => true, - T![fn] | T![type] => { + T![unsafe] => { + if T![impl] == p.nth(2) { + p.bump_remap(T![default]); + p.bump_remap(T![unsafe]); + has_mods = true; + } + false + } + T![fn] | T![type] | T![const] => { if let ItemFlavor::Mod = flavor { true } else { @@ -187,6 +195,9 @@ pub(super) fn maybe_item(p: &mut Parser, m: Marker, flavor: ItemFlavor) -> Resul // test default_impl // default impl Foo {} + // test default_unsafe_impl + // default unsafe impl Foo {} + // test_err default_fn_type // trait T { // default type T = Bar; @@ -199,6 +210,19 @@ pub(super) fn maybe_item(p: &mut Parser, m: Marker, flavor: ItemFlavor) -> Resul // default fn foo() {} // } + // test_err default_const + // trait T { + // default const f: u8 = 0; + // } + + // test default_const + // impl T for Foo { + // default const f: u8 = 0; + // } + T![const] => { + consts::const_def(p, m); + } + // test unsafe_default_impl // unsafe default impl Foo {} T![impl] => { diff --git a/crates/ra_syntax/test_data/parser/inline/err/0010_wrong_order_fns.rast b/crates/ra_syntax/test_data/parser/inline/err/0010_wrong_order_fns.rast index 9be441110ff4..53f7ebaf9a72 100644 --- a/crates/ra_syntax/test_data/parser/inline/err/0010_wrong_order_fns.rast +++ b/crates/ra_syntax/test_data/parser/inline/err/0010_wrong_order_fns.rast @@ -17,23 +17,29 @@ SOURCE_FILE@0..50 L_CURLY@22..23 "{" R_CURLY@23..24 "}" WHITESPACE@24..25 "\n" - ERROR@25..31 + CONST_DEF@25..46 UNSAFE_KW@25..31 "unsafe" - WHITESPACE@31..32 " " - FN_DEF@32..49 + WHITESPACE@31..32 " " CONST_KW@32..37 "const" WHITESPACE@37..38 " " - FN_KW@38..40 "fn" + ERROR@38..40 + FN_KW@38..40 "fn" WHITESPACE@40..41 " " - NAME@41..44 - IDENT@41..44 "bar" - PARAM_LIST@44..46 - L_PAREN@44..45 "(" - R_PAREN@45..46 ")" - WHITESPACE@46..47 " " - BLOCK_EXPR@47..49 - L_CURLY@47..48 "{" - R_CURLY@48..49 "}" + PATH_TYPE@41..46 + PATH@41..46 + PATH_SEGMENT@41..46 + NAME_REF@41..44 + IDENT@41..44 "bar" + PARAM_LIST@44..46 + L_PAREN@44..45 "(" + R_PAREN@45..46 ")" + WHITESPACE@46..47 " " + ERROR@47..49 + L_CURLY@47..48 "{" + R_CURLY@48..49 "}" WHITESPACE@49..50 "\n" error 6..6: expected existential, fn, trait or impl -error 31..31: expected existential, fn, trait or impl +error 38..38: expected a name +error 40..40: expected COLON +error 46..46: expected SEMICOLON +error 47..47: expected an item From a5588b9e19cbbc18b1afd9afcc9bab2bce2b711c Mon Sep 17 00:00:00 2001 From: Avi Dessauer Date: Wed, 3 Jun 2020 20:39:57 -0400 Subject: [PATCH 055/172] Update tests --- .../parser/inline/err/0015_default_const.rast | 40 +++++++++++++++++ .../parser/inline/err/0015_default_const.rs | 3 ++ .../parser/inline/ok/0162_default_const.rast | 44 +++++++++++++++++++ .../parser/inline/ok/0162_default_const.rs | 3 ++ .../inline/ok/0163_default_unsafe_impl.rast | 18 ++++++++ .../inline/ok/0163_default_unsafe_impl.rs | 1 + 6 files changed, 109 insertions(+) create mode 100644 crates/ra_syntax/test_data/parser/inline/err/0015_default_const.rast create mode 100644 crates/ra_syntax/test_data/parser/inline/err/0015_default_const.rs create mode 100644 crates/ra_syntax/test_data/parser/inline/ok/0162_default_const.rast create mode 100644 crates/ra_syntax/test_data/parser/inline/ok/0162_default_const.rs create mode 100644 crates/ra_syntax/test_data/parser/inline/ok/0163_default_unsafe_impl.rast create mode 100644 crates/ra_syntax/test_data/parser/inline/ok/0163_default_unsafe_impl.rs diff --git a/crates/ra_syntax/test_data/parser/inline/err/0015_default_const.rast b/crates/ra_syntax/test_data/parser/inline/err/0015_default_const.rast new file mode 100644 index 000000000000..8eb583ef85cc --- /dev/null +++ b/crates/ra_syntax/test_data/parser/inline/err/0015_default_const.rast @@ -0,0 +1,40 @@ +SOURCE_FILE@0..39 + TRAIT_DEF@0..38 + TRAIT_KW@0..5 "trait" + WHITESPACE@5..6 " " + NAME@6..7 + IDENT@6..7 "T" + WHITESPACE@7..8 " " + ITEM_LIST@8..38 + L_CURLY@8..9 "{" + WHITESPACE@9..12 "\n " + MACRO_CALL@12..19 + PATH@12..19 + PATH_SEGMENT@12..19 + NAME_REF@12..19 + IDENT@12..19 "default" + WHITESPACE@19..20 " " + CONST_DEF@20..36 + CONST_KW@20..25 "const" + WHITESPACE@25..26 " " + NAME@26..27 + IDENT@26..27 "f" + COLON@27..28 ":" + WHITESPACE@28..29 " " + PATH_TYPE@29..31 + PATH@29..31 + PATH_SEGMENT@29..31 + NAME_REF@29..31 + IDENT@29..31 "u8" + WHITESPACE@31..32 " " + EQ@32..33 "=" + WHITESPACE@33..34 " " + LITERAL@34..35 + INT_NUMBER@34..35 "0" + SEMICOLON@35..36 ";" + WHITESPACE@36..37 "\n" + R_CURLY@37..38 "}" + WHITESPACE@38..39 "\n" +error 19..19: expected BANG +error 19..19: expected `{`, `[`, `(` +error 19..19: expected SEMICOLON diff --git a/crates/ra_syntax/test_data/parser/inline/err/0015_default_const.rs b/crates/ra_syntax/test_data/parser/inline/err/0015_default_const.rs new file mode 100644 index 000000000000..80f15474a5d0 --- /dev/null +++ b/crates/ra_syntax/test_data/parser/inline/err/0015_default_const.rs @@ -0,0 +1,3 @@ +trait T { + default const f: u8 = 0; +} diff --git a/crates/ra_syntax/test_data/parser/inline/ok/0162_default_const.rast b/crates/ra_syntax/test_data/parser/inline/ok/0162_default_const.rast new file mode 100644 index 000000000000..dab0247ee6fa --- /dev/null +++ b/crates/ra_syntax/test_data/parser/inline/ok/0162_default_const.rast @@ -0,0 +1,44 @@ +SOURCE_FILE@0..46 + IMPL_DEF@0..45 + IMPL_KW@0..4 "impl" + WHITESPACE@4..5 " " + PATH_TYPE@5..6 + PATH@5..6 + PATH_SEGMENT@5..6 + NAME_REF@5..6 + IDENT@5..6 "T" + WHITESPACE@6..7 " " + FOR_KW@7..10 "for" + WHITESPACE@10..11 " " + PATH_TYPE@11..14 + PATH@11..14 + PATH_SEGMENT@11..14 + NAME_REF@11..14 + IDENT@11..14 "Foo" + WHITESPACE@14..15 " " + ITEM_LIST@15..45 + L_CURLY@15..16 "{" + WHITESPACE@16..19 "\n " + CONST_DEF@19..43 + DEFAULT_KW@19..26 "default" + WHITESPACE@26..27 " " + CONST_KW@27..32 "const" + WHITESPACE@32..33 " " + NAME@33..34 + IDENT@33..34 "f" + COLON@34..35 ":" + WHITESPACE@35..36 " " + PATH_TYPE@36..38 + PATH@36..38 + PATH_SEGMENT@36..38 + NAME_REF@36..38 + IDENT@36..38 "u8" + WHITESPACE@38..39 " " + EQ@39..40 "=" + WHITESPACE@40..41 " " + LITERAL@41..42 + INT_NUMBER@41..42 "0" + SEMICOLON@42..43 ";" + WHITESPACE@43..44 "\n" + R_CURLY@44..45 "}" + WHITESPACE@45..46 "\n" diff --git a/crates/ra_syntax/test_data/parser/inline/ok/0162_default_const.rs b/crates/ra_syntax/test_data/parser/inline/ok/0162_default_const.rs new file mode 100644 index 000000000000..dfb3b92dca79 --- /dev/null +++ b/crates/ra_syntax/test_data/parser/inline/ok/0162_default_const.rs @@ -0,0 +1,3 @@ +impl T for Foo { + default const f: u8 = 0; +} diff --git a/crates/ra_syntax/test_data/parser/inline/ok/0163_default_unsafe_impl.rast b/crates/ra_syntax/test_data/parser/inline/ok/0163_default_unsafe_impl.rast new file mode 100644 index 000000000000..a9eda5668112 --- /dev/null +++ b/crates/ra_syntax/test_data/parser/inline/ok/0163_default_unsafe_impl.rast @@ -0,0 +1,18 @@ +SOURCE_FILE@0..27 + IMPL_DEF@0..26 + DEFAULT_KW@0..7 "default" + WHITESPACE@7..8 " " + UNSAFE_KW@8..14 "unsafe" + WHITESPACE@14..15 " " + IMPL_KW@15..19 "impl" + WHITESPACE@19..20 " " + PATH_TYPE@20..23 + PATH@20..23 + PATH_SEGMENT@20..23 + NAME_REF@20..23 + IDENT@20..23 "Foo" + WHITESPACE@23..24 " " + ITEM_LIST@24..26 + L_CURLY@24..25 "{" + R_CURLY@25..26 "}" + WHITESPACE@26..27 "\n" diff --git a/crates/ra_syntax/test_data/parser/inline/ok/0163_default_unsafe_impl.rs b/crates/ra_syntax/test_data/parser/inline/ok/0163_default_unsafe_impl.rs new file mode 100644 index 000000000000..ba0998ff4d99 --- /dev/null +++ b/crates/ra_syntax/test_data/parser/inline/ok/0163_default_unsafe_impl.rs @@ -0,0 +1 @@ +default unsafe impl Foo {} From 19b27f2ec93e30a32643caf173accdc238d86683 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Wed, 3 Jun 2020 17:55:06 -0700 Subject: [PATCH 056/172] Declare required lsp-server dependency of rust-analyzer crate My codebase already depended on lsp-server and introducing a dependency on rust-analyzer failed at first because it assumes some functions that were first present in lsp-server 0.3.2. Without this change: error[E0599]: no method named `initialize_start` found for struct `lsp_server::Connection` in the current scope --> crates/rust-analyzer/./src/bin/main.rs:83:57 | 83 | let (initialize_id, initialize_params) = connection.initialize_start()?; | ^^^^^^^^^^^^^^^^ method not found in `lsp_server::Connection` error[E0599]: no method named `initialize_finish` found for struct `lsp_server::Connection` in the current scope --> crates/rust-analyzer/./src/bin/main.rs:99:16 | 99 | connection.initialize_finish(initialize_id, initialize_result)?; | ^^^^^^^^^^^^^^^^^ method not found in `lsp_server::Connection` --- crates/rust-analyzer/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/rust-analyzer/Cargo.toml b/crates/rust-analyzer/Cargo.toml index 65b487db3b98..a010125b7601 100644 --- a/crates/rust-analyzer/Cargo.toml +++ b/crates/rust-analyzer/Cargo.toml @@ -32,7 +32,7 @@ threadpool = "1.7.1" stdx = { path = "../stdx" } -lsp-server = "0.3.1" +lsp-server = "0.3.2" ra_flycheck = { path = "../ra_flycheck" } ra_ide = { path = "../ra_ide" } ra_prof = { path = "../ra_prof" } From 73480409440a04183cd7b74a166d0bf7f2c30d03 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Wed, 3 Jun 2020 18:01:24 -0700 Subject: [PATCH 057/172] Remove unneeded "./" prefix affecting error messages Before: error[E0599]: no method named `initialize_finish` found for struct `lsp_server::Connection` in the current scope --> crates/rust-analyzer/./src/bin/main.rs:99:16 | 99 | connection.initialize_finish(initialize_id, initialize_result)?; | ^^^^^^^^^^^^^^^^^ method not found in `lsp_server::Connection` After: error[E0599]: no method named `initialize_finish` found for struct `lsp_server::Connection` in the current scope --> crates/rust-analyzer/src/bin/main.rs:99:16 | 99 | connection.initialize_finish(initialize_id, initialize_result)?; | ^^^^^^^^^^^^^^^^^ method not found in `lsp_server::Connection` --- crates/rust-analyzer/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/rust-analyzer/Cargo.toml b/crates/rust-analyzer/Cargo.toml index 65b487db3b98..bf8ca9e7d5c5 100644 --- a/crates/rust-analyzer/Cargo.toml +++ b/crates/rust-analyzer/Cargo.toml @@ -10,7 +10,7 @@ doctest = false [[bin]] name = "rust-analyzer" -path = "./src/bin/main.rs" +path = "src/bin/main.rs" [dependencies] anyhow = "1.0.26" From 4461796f33d3d50de4d704a76fae6fa3cb2b73aa Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Wed, 3 Jun 2020 19:05:55 -0700 Subject: [PATCH 058/172] Fix type inference failure when built with log/kv_unstable This code is broken by an `impl From for fmt::Error` in the log crate when building in a codebase that has the log/kv_unstable feature enabled. $ cargo check --manifest-path crates/ra_hir_def/Cargo.toml Checking ra_hir_def v0.1.0 Finished dev [unoptimized] target(s) in 0.75s $ cargo check --manifest-path crates/ra_hir_def/Cargo.toml --features log/kv_unstable Checking ra_hir_def v0.1.0 error[E0282]: type annotations needed for the closure `fn(&str) -> std::result::Result<(), _>` --> crates/ra_hir_def/src/path.rs:278:17 | 278 | f.write_str("::")?; | ^^^^^^^^^^^^^^^^^^ cannot infer type | help: give this closure an explicit return type without `_` placeholders | 276 | let mut add_segment = |s| -> std::result::Result<(), _> { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ --- crates/ra_hir_def/src/path.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/ra_hir_def/src/path.rs b/crates/ra_hir_def/src/path.rs index e84efe2abd04..4512448e0284 100644 --- a/crates/ra_hir_def/src/path.rs +++ b/crates/ra_hir_def/src/path.rs @@ -273,7 +273,7 @@ impl From for ModPath { impl Display for ModPath { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let mut first_segment = true; - let mut add_segment = |s| { + let mut add_segment = |s| -> fmt::Result { if !first_segment { f.write_str("::")?; } From b0c8a2be7bd0d053eb9dd0e02fe2cf08b093a19a Mon Sep 17 00:00:00 2001 From: Mikhail Rakhmanov Date: Thu, 4 Jun 2020 10:03:44 +0200 Subject: [PATCH 059/172] Remove AsName import --- .../handlers/extract_struct_from_enum_variant.rs | 13 +++++++++---- crates/ra_assists/src/utils/insert_use.rs | 11 ++++++++++- crates/ra_hir/src/lib.rs | 5 ++--- 3 files changed, 21 insertions(+), 8 deletions(-) diff --git a/crates/ra_assists/src/handlers/extract_struct_from_enum_variant.rs b/crates/ra_assists/src/handlers/extract_struct_from_enum_variant.rs index 2b1951aff555..72e5dd7356ca 100644 --- a/crates/ra_assists/src/handlers/extract_struct_from_enum_variant.rs +++ b/crates/ra_assists/src/handlers/extract_struct_from_enum_variant.rs @@ -9,11 +9,11 @@ use ra_syntax::{ use crate::{ assist_context::{AssistBuilder, AssistDirector}, - utils::insert_use_statement, + utils::insert_use::insert_use_statement_with_string_path, AssistContext, AssistId, Assists, }; use ast::{ArgListOwner, VisibilityOwner}; -use hir::{AsName, EnumVariant, Module, ModuleDef}; +use hir::{EnumVariant, Module, ModuleDef}; use ra_db::FileId; use ra_fmt::leading_indent; use rustc_hash::FxHashSet; @@ -109,8 +109,13 @@ fn insert_import( let mod_path = module.find_use_path(db, module_def.clone()); if let Some(mut mod_path) = mod_path { mod_path.segments.pop(); - mod_path.segments.push(path_segment.as_name()); - insert_use_statement(path.syntax(), &mod_path, ctx, builder.text_edit_builder()); + let use_path = format!("{}::{}", mod_path.to_string(), path_segment.to_string()); + insert_use_statement_with_string_path( + path.syntax(), + &use_path, + ctx, + builder.text_edit_builder(), + ); } Some(()) } diff --git a/crates/ra_assists/src/utils/insert_use.rs b/crates/ra_assists/src/utils/insert_use.rs index 0ee43482f798..114f5949a6aa 100644 --- a/crates/ra_assists/src/utils/insert_use.rs +++ b/crates/ra_assists/src/utils/insert_use.rs @@ -23,7 +23,16 @@ pub(crate) fn insert_use_statement( ctx: &AssistContext, builder: &mut TextEditBuilder, ) { - let target = path_to_import.to_string().split("::").map(SmolStr::new).collect::>(); + insert_use_statement_with_string_path(position, &path_to_import.to_string(), ctx, builder); +} + +pub(crate) fn insert_use_statement_with_string_path( + position: &SyntaxNode, + path_to_import: &str, + ctx: &AssistContext, + builder: &mut TextEditBuilder, +) { + let target = path_to_import.split("::").map(SmolStr::new).collect::>(); let container = ctx.sema.ancestors_with_macros(position.clone()).find_map(|n| { if let Some(module) = ast::Module::cast(n.clone()) { return module.item_list().map(|it| it.syntax().clone()); diff --git a/crates/ra_hir/src/lib.rs b/crates/ra_hir/src/lib.rs index f4a6b0503875..3364a822f431 100644 --- a/crates/ra_hir/src/lib.rs +++ b/crates/ra_hir/src/lib.rs @@ -71,8 +71,7 @@ pub use hir_def::{ type_ref::Mutability, }; pub use hir_expand::{ - hygiene::Hygiene, - name::{AsName, Name}, - HirFileId, InFile, MacroCallId, MacroCallLoc, MacroDefId, MacroFile, Origin, + hygiene::Hygiene, name::Name, HirFileId, InFile, MacroCallId, MacroCallLoc, MacroDefId, + MacroFile, Origin, }; pub use hir_ty::{display::HirDisplay, CallableDef}; From 3ec2dcfc0d7ea7d9fb9c499616e9eca06b5c865b Mon Sep 17 00:00:00 2001 From: Avi Dessauer Date: Thu, 4 Jun 2020 13:00:21 -0400 Subject: [PATCH 060/172] Address review --- crates/ra_parser/src/grammar/items.rs | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/crates/ra_parser/src/grammar/items.rs b/crates/ra_parser/src/grammar/items.rs index 41f8bb0b6e01..0d3568e5fc0b 100644 --- a/crates/ra_parser/src/grammar/items.rs +++ b/crates/ra_parser/src/grammar/items.rs @@ -119,9 +119,11 @@ pub(super) fn maybe_item(p: &mut Parser, m: Marker, flavor: ItemFlavor) -> Resul && (match p.nth(1) { T![impl] => true, T![unsafe] => { - if T![impl] == p.nth(2) { + // test default_unsafe_impl + // default unsafe impl Foo {} + if p.nth(2) == T![impl] { p.bump_remap(T![default]); - p.bump_remap(T![unsafe]); + p.bump(T![unsafe]); has_mods = true; } false @@ -195,9 +197,6 @@ pub(super) fn maybe_item(p: &mut Parser, m: Marker, flavor: ItemFlavor) -> Resul // test default_impl // default impl Foo {} - // test default_unsafe_impl - // default unsafe impl Foo {} - // test_err default_fn_type // trait T { // default type T = Bar; From c4fd46398132a409d7947141094d7301c0f0af73 Mon Sep 17 00:00:00 2001 From: Avi Dessauer Date: Thu, 4 Jun 2020 13:06:26 -0400 Subject: [PATCH 061/172] Move default const test out of line --- crates/ra_parser/src/grammar/items.rs | 10 ---------- .../0043_default_const.rast} | 0 .../0043_default_const.rs} | 0 .../0066_default_const.rast} | 0 .../0162_default_const.rs => ok/0066_default_const.rs} | 0 5 files changed, 10 deletions(-) rename crates/ra_syntax/test_data/parser/{inline/err/0015_default_const.rast => err/0043_default_const.rast} (100%) rename crates/ra_syntax/test_data/parser/{inline/err/0015_default_const.rs => err/0043_default_const.rs} (100%) rename crates/ra_syntax/test_data/parser/{inline/ok/0162_default_const.rast => ok/0066_default_const.rast} (100%) rename crates/ra_syntax/test_data/parser/{inline/ok/0162_default_const.rs => ok/0066_default_const.rs} (100%) diff --git a/crates/ra_parser/src/grammar/items.rs b/crates/ra_parser/src/grammar/items.rs index 0d3568e5fc0b..9c14b954af4b 100644 --- a/crates/ra_parser/src/grammar/items.rs +++ b/crates/ra_parser/src/grammar/items.rs @@ -208,16 +208,6 @@ pub(super) fn maybe_item(p: &mut Parser, m: Marker, flavor: ItemFlavor) -> Resul // default type T = Bar; // default fn foo() {} // } - - // test_err default_const - // trait T { - // default const f: u8 = 0; - // } - - // test default_const - // impl T for Foo { - // default const f: u8 = 0; - // } T![const] => { consts::const_def(p, m); } diff --git a/crates/ra_syntax/test_data/parser/inline/err/0015_default_const.rast b/crates/ra_syntax/test_data/parser/err/0043_default_const.rast similarity index 100% rename from crates/ra_syntax/test_data/parser/inline/err/0015_default_const.rast rename to crates/ra_syntax/test_data/parser/err/0043_default_const.rast diff --git a/crates/ra_syntax/test_data/parser/inline/err/0015_default_const.rs b/crates/ra_syntax/test_data/parser/err/0043_default_const.rs similarity index 100% rename from crates/ra_syntax/test_data/parser/inline/err/0015_default_const.rs rename to crates/ra_syntax/test_data/parser/err/0043_default_const.rs diff --git a/crates/ra_syntax/test_data/parser/inline/ok/0162_default_const.rast b/crates/ra_syntax/test_data/parser/ok/0066_default_const.rast similarity index 100% rename from crates/ra_syntax/test_data/parser/inline/ok/0162_default_const.rast rename to crates/ra_syntax/test_data/parser/ok/0066_default_const.rast diff --git a/crates/ra_syntax/test_data/parser/inline/ok/0162_default_const.rs b/crates/ra_syntax/test_data/parser/ok/0066_default_const.rs similarity index 100% rename from crates/ra_syntax/test_data/parser/inline/ok/0162_default_const.rs rename to crates/ra_syntax/test_data/parser/ok/0066_default_const.rs From d08c63cb9e3574fa97374a8529136814530bf416 Mon Sep 17 00:00:00 2001 From: Jonas Schievink Date: Wed, 20 May 2020 23:51:20 +0200 Subject: [PATCH 062/172] Add an ImportMap --- crates/ra_db/src/lib.rs | 4 +- crates/ra_hir_def/src/db.rs | 4 + crates/ra_hir_def/src/find_path.rs | 19 +- crates/ra_hir_def/src/import_map.rs | 323 ++++++++++++++++++++++++++++ crates/ra_hir_def/src/lib.rs | 1 + crates/ra_hir_def/src/path.rs | 13 ++ crates/ra_hir_def/src/per_ns.rs | 10 +- 7 files changed, 360 insertions(+), 14 deletions(-) create mode 100644 crates/ra_hir_def/src/import_map.rs diff --git a/crates/ra_db/src/lib.rs b/crates/ra_db/src/lib.rs index fd4280de2df4..e7868268b1e5 100644 --- a/crates/ra_db/src/lib.rs +++ b/crates/ra_db/src/lib.rs @@ -11,8 +11,8 @@ use ra_syntax::{ast, Parse, SourceFile, TextRange, TextSize}; pub use crate::{ cancellation::Canceled, input::{ - CrateGraph, CrateId, CrateName, Dependency, Edition, Env, ExternSource, ExternSourceId, - FileId, ProcMacroId, SourceRoot, SourceRootId, + CrateData, CrateGraph, CrateId, CrateName, Dependency, Edition, Env, ExternSource, + ExternSourceId, FileId, ProcMacroId, SourceRoot, SourceRootId, }, }; pub use relative_path::{RelativePath, RelativePathBuf}; diff --git a/crates/ra_hir_def/src/db.rs b/crates/ra_hir_def/src/db.rs index 945a0025e504..a23d65371fb6 100644 --- a/crates/ra_hir_def/src/db.rs +++ b/crates/ra_hir_def/src/db.rs @@ -14,6 +14,7 @@ use crate::{ docs::Documentation, find_path, generics::GenericParams, + import_map::ImportMap, item_scope::ItemInNs, lang_item::{LangItemTarget, LangItems}, nameres::{raw::RawItems, CrateDefMap}, @@ -122,6 +123,9 @@ pub trait DefDatabase: InternDatabase + AstDatabase + Upcast { #[salsa::invoke(find_path::find_path_inner_query)] fn find_path_inner(&self, item: ItemInNs, from: ModuleId, max_len: usize) -> Option; + + #[salsa::invoke(ImportMap::import_map_query)] + fn import_map(&self, krate: CrateId) -> Arc; } fn crate_def_map_wait(db: &impl DefDatabase, krate: CrateId) -> Arc { diff --git a/crates/ra_hir_def/src/find_path.rs b/crates/ra_hir_def/src/find_path.rs index 4db7984730aa..088e8dd32ff1 100644 --- a/crates/ra_hir_def/src/find_path.rs +++ b/crates/ra_hir_def/src/find_path.rs @@ -36,17 +36,6 @@ impl ModPath { let first_segment = self.segments.first(); first_segment == Some(&known::alloc) || first_segment == Some(&known::core) } - - fn len(&self) -> usize { - self.segments.len() - + match self.kind { - PathKind::Plain => 0, - PathKind::Super(i) => i as usize, - PathKind::Crate => 1, - PathKind::Abs => 0, - PathKind::DollarCrate(_) => 1, - } - } } pub(crate) fn find_path_inner_query( @@ -192,9 +181,17 @@ fn find_importable_locations( ) -> Vec<(ModuleId, Name)> { let crate_graph = db.crate_graph(); let mut result = Vec::new(); + // We only look in the crate from which we are importing, and the direct // dependencies. We cannot refer to names from transitive dependencies // directly (only through reexports in direct dependencies). + + // For the crate from which we're importing, we have to check whether any + // module visible to `from` exports the item we're looking for. + // For dependencies of the crate only `pub` items reachable through `pub` + // modules from the crate root are relevant. For that we precompute an + // import map that tells us the shortest path to any importable item with a + // single lookup. for krate in Some(from.krate) .into_iter() .chain(crate_graph[from.krate].dependencies.iter().map(|dep| dep.crate_id)) diff --git a/crates/ra_hir_def/src/import_map.rs b/crates/ra_hir_def/src/import_map.rs new file mode 100644 index 000000000000..7dae64efa741 --- /dev/null +++ b/crates/ra_hir_def/src/import_map.rs @@ -0,0 +1,323 @@ +//! A map of all publicly exported items in a crate. + +use crate::{ + db::DefDatabase, + item_scope::ItemInNs, + path::{ModPath, PathKind}, + visibility::Visibility, + ModuleDefId, ModuleId, +}; +use ra_db::CrateId; +use rustc_hash::FxHashMap; +use std::{collections::hash_map::Entry, sync::Arc}; + +/// A map from publicly exported items to the path needed to import/name them from a downstream +/// crate. +/// +/// Reexports of items are taken into account, ie. if something is exported under multiple +/// names, the one with the shortest import path will be used. +/// +/// Note that all paths are relative to the containing crate's root, so the crate name still needs +/// to be prepended to the `ModPath` before the path is valid. +#[derive(Debug, Eq, PartialEq)] +pub struct ImportMap { + map: FxHashMap, +} + +impl ImportMap { + pub fn import_map_query(db: &dyn DefDatabase, krate: CrateId) -> Arc { + let _p = ra_prof::profile("import_map_query"); + let def_map = db.crate_def_map(krate); + let mut import_map = FxHashMap::with_capacity_and_hasher(64, Default::default()); + + // We look only into modules that are public(ly reexported), starting with the crate root. + let empty = ModPath { kind: PathKind::Plain, segments: vec![] }; + let root = ModuleId { krate, local_id: def_map.root }; + let mut worklist = vec![(root, empty)]; + while let Some((module, mod_path)) = worklist.pop() { + let ext_def_map; + let mod_data = if module.krate == krate { + &def_map[module.local_id] + } else { + // The crate might reexport a module defined in another crate. + ext_def_map = db.crate_def_map(module.krate); + &ext_def_map[module.local_id] + }; + + let visible_items = mod_data.scope.entries().filter_map(|(name, per_ns)| { + let per_ns = per_ns.filter_visibility(|vis| vis == Visibility::Public); + if per_ns.is_none() { + None + } else { + Some((name, per_ns)) + } + }); + + for (name, per_ns) in visible_items { + let mk_path = || { + let mut path = mod_path.clone(); + path.segments.push(name.clone()); + path + }; + + for item in per_ns.iter_items() { + let path = mk_path(); + match import_map.entry(item) { + Entry::Vacant(entry) => { + entry.insert(path); + } + Entry::Occupied(mut entry) => { + // If the new path is shorter, prefer that one. + if path.len() < entry.get().len() { + *entry.get_mut() = path; + } else { + continue; + } + } + } + + // If we've just added a path to a module, descend into it. + if let Some(ModuleDefId::ModuleId(mod_id)) = item.as_module_def_id() { + worklist.push((mod_id, mk_path())); + } + } + } + } + + Arc::new(Self { map: import_map }) + } + + /// Returns the `ModPath` needed to import/mention `item`, relative to this crate's root. + pub fn path_of(&self, item: ItemInNs) -> Option<&ModPath> { + self.map.get(&item) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::test_db::TestDB; + use insta::assert_snapshot; + use ra_db::fixture::WithFixture; + use ra_db::SourceDatabase; + + fn import_map(ra_fixture: &str) -> String { + let db = TestDB::with_files(ra_fixture); + let crate_graph = db.crate_graph(); + + let import_maps: Vec<_> = crate_graph + .iter() + .filter_map(|krate| { + let cdata = &crate_graph[krate]; + let name = cdata.display_name.as_ref()?; + + let map = db.import_map(krate); + + let mut importable_paths: Vec<_> = map + .map + .iter() + .map(|(item, modpath)| { + let ns = match item { + ItemInNs::Types(_) => "t", + ItemInNs::Values(_) => "v", + ItemInNs::Macros(_) => "m", + }; + format!("- {} ({})", modpath, ns) + }) + .collect(); + + importable_paths.sort(); + let importable_paths = importable_paths.join("\n"); + + Some(format!("{}:\n{}", name, importable_paths)) + }) + .collect(); + + import_maps.join("\n") + } + + #[test] + fn smoke() { + let map = import_map( + r" + //- /main.rs crate:main deps:lib + + mod private { + pub use lib::Pub; + pub struct InPrivateModule; + } + + pub mod publ1 { + use lib::Pub; + } + + pub mod real_pub { + pub use lib::Pub; + } + pub mod real_pu2 { // same path length as above + pub use lib::Pub; + } + + //- /lib.rs crate:lib + pub struct Pub {} + pub struct Pub2; // t + v + struct Priv; + ", + ); + + assert_snapshot!(map, @r###" + main: + - publ1 (t) + - real_pu2 (t) + - real_pub (t) + - real_pub::Pub (t) + lib: + - Pub (t) + - Pub2 (t) + - Pub2 (v) + "###); + } + + #[test] + fn prefers_shortest_path() { + let map = import_map( + r" + //- /main.rs crate:main + + pub mod sub { + pub mod subsub { + pub struct Def {} + } + + pub use super::sub::subsub::Def; + } + ", + ); + + assert_snapshot!(map, @r###" + main: + - sub (t) + - sub::Def (t) + - sub::subsub (t) + "###); + } + + #[test] + fn type_reexport_cross_crate() { + // Reexports need to be visible from a crate, even if the original crate exports the item + // at a shorter path. + let map = import_map( + r" + //- /main.rs crate:main deps:lib + pub mod m { + pub use lib::S; + } + //- /lib.rs crate:lib + pub struct S; + ", + ); + + assert_snapshot!(map, @r###" + main: + - m (t) + - m::S (t) + - m::S (v) + lib: + - S (t) + - S (v) + "###); + } + + #[test] + fn macro_reexport() { + let map = import_map( + r" + //- /main.rs crate:main deps:lib + pub mod m { + pub use lib::pub_macro; + } + //- /lib.rs crate:lib + #[macro_export] + macro_rules! pub_macro { + () => {}; + } + ", + ); + + assert_snapshot!(map, @r###" + main: + - m (t) + - m::pub_macro (m) + lib: + - pub_macro (m) + "###); + } + + #[test] + fn module_reexport() { + // Reexporting modules from a dependency adds all contents to the import map. + let map = import_map( + r" + //- /main.rs crate:main deps:lib + pub use lib::module as reexported_module; + //- /lib.rs crate:lib + pub mod module { + pub struct S; + } + ", + ); + + assert_snapshot!(map, @r###" + main: + - reexported_module (t) + - reexported_module::S (t) + - reexported_module::S (v) + lib: + - module (t) + - module::S (t) + - module::S (v) + "###); + } + + #[test] + fn cyclic_module_reexport() { + // Reexporting modules from a dependency adds all contents to the import map. + let map = import_map( + r" + //- /lib.rs crate:lib + pub mod module { + pub struct S; + pub use super::sub::*; + } + + pub mod sub { + pub use super::module; + } + ", + ); + + assert_snapshot!(map, @r###" + lib: + - module (t) + - module::S (t) + - module::S (v) + - sub (t) + "###); + } + + #[test] + fn private_macro() { + let map = import_map( + r" + //- /lib.rs crate:lib + macro_rules! private_macro { + () => {}; + } + ", + ); + + assert_snapshot!(map, @r###" + lib: + "###); + } +} diff --git a/crates/ra_hir_def/src/lib.rs b/crates/ra_hir_def/src/lib.rs index 5325a27608ea..de490fcc58af 100644 --- a/crates/ra_hir_def/src/lib.rs +++ b/crates/ra_hir_def/src/lib.rs @@ -43,6 +43,7 @@ pub mod child_by_source; pub mod visibility; pub mod find_path; +pub mod import_map; #[cfg(test)] mod test_db; diff --git a/crates/ra_hir_def/src/path.rs b/crates/ra_hir_def/src/path.rs index 4512448e0284..bfa921de2220 100644 --- a/crates/ra_hir_def/src/path.rs +++ b/crates/ra_hir_def/src/path.rs @@ -76,6 +76,19 @@ impl ModPath { } } + /// Returns the number of segments in the path (counting special segments like `$crate` and + /// `super`). + pub fn len(&self) -> usize { + self.segments.len() + + match self.kind { + PathKind::Plain => 0, + PathKind::Super(i) => i as usize, + PathKind::Crate => 1, + PathKind::Abs => 0, + PathKind::DollarCrate(_) => 1, + } + } + pub fn is_ident(&self) -> bool { self.kind == PathKind::Plain && self.segments.len() == 1 } diff --git a/crates/ra_hir_def/src/per_ns.rs b/crates/ra_hir_def/src/per_ns.rs index 6e435c8c12a7..74665c58851c 100644 --- a/crates/ra_hir_def/src/per_ns.rs +++ b/crates/ra_hir_def/src/per_ns.rs @@ -5,7 +5,7 @@ use hir_expand::MacroDefId; -use crate::{visibility::Visibility, ModuleDefId}; +use crate::{item_scope::ItemInNs, visibility::Visibility, ModuleDefId}; #[derive(Debug, Copy, Clone, PartialEq, Eq)] pub struct PerNs { @@ -84,4 +84,12 @@ impl PerNs { macros: self.macros.or(other.macros), } } + + pub fn iter_items(self) -> impl Iterator { + self.types + .map(|it| ItemInNs::Types(it.0)) + .into_iter() + .chain(self.values.map(|it| ItemInNs::Values(it.0)).into_iter()) + .chain(self.macros.map(|it| ItemInNs::Macros(it.0)).into_iter()) + } } From 3c496f7fa7afe78102ea2c7ee5f7e006a66629d4 Mon Sep 17 00:00:00 2001 From: Jonas Schievink Date: Thu, 4 Jun 2020 19:30:29 +0200 Subject: [PATCH 063/172] Use `ImportMap` in `find_path`, remove old queries --- crates/ra_hir_def/src/db.rs | 16 +-- crates/ra_hir_def/src/find_path.rs | 193 +++++++++++++++++----------- crates/ra_hir_def/src/item_scope.rs | 22 +++- 3 files changed, 137 insertions(+), 94 deletions(-) diff --git a/crates/ra_hir_def/src/db.rs b/crates/ra_hir_def/src/db.rs index a23d65371fb6..10cc26480f36 100644 --- a/crates/ra_hir_def/src/db.rs +++ b/crates/ra_hir_def/src/db.rs @@ -1,7 +1,7 @@ //! Defines database & queries for name resolution. use std::sync::Arc; -use hir_expand::{db::AstDatabase, name::Name, HirFileId}; +use hir_expand::{db::AstDatabase, HirFileId}; use ra_db::{salsa, CrateId, SourceDatabase, Upcast}; use ra_prof::profile; use ra_syntax::SmolStr; @@ -12,14 +12,10 @@ use crate::{ body::{scope::ExprScopes, Body, BodySourceMap}, data::{ConstData, FunctionData, ImplData, StaticData, TraitData, TypeAliasData}, docs::Documentation, - find_path, generics::GenericParams, import_map::ImportMap, - item_scope::ItemInNs, lang_item::{LangItemTarget, LangItems}, nameres::{raw::RawItems, CrateDefMap}, - path::ModPath, - visibility::Visibility, AttrDefId, ConstId, ConstLoc, DefWithBodyId, EnumId, EnumLoc, FunctionId, FunctionLoc, GenericDefId, ImplId, ImplLoc, ModuleId, StaticId, StaticLoc, StructId, StructLoc, TraitId, TraitLoc, TypeAliasId, TypeAliasLoc, UnionId, UnionLoc, @@ -114,16 +110,6 @@ pub trait DefDatabase: InternDatabase + AstDatabase + Upcast { #[salsa::invoke(Documentation::documentation_query)] fn documentation(&self, def: AttrDefId) -> Option; - #[salsa::invoke(find_path::importable_locations_of_query)] - fn importable_locations_of( - &self, - item: ItemInNs, - krate: CrateId, - ) -> Arc<[(ModuleId, Name, Visibility)]>; - - #[salsa::invoke(find_path::find_path_inner_query)] - fn find_path_inner(&self, item: ItemInNs, from: ModuleId, max_len: usize) -> Option; - #[salsa::invoke(ImportMap::import_map_query)] fn import_map(&self, krate: CrateId) -> Arc; } diff --git a/crates/ra_hir_def/src/find_path.rs b/crates/ra_hir_def/src/find_path.rs index 088e8dd32ff1..79f4afab623f 100644 --- a/crates/ra_hir_def/src/find_path.rs +++ b/crates/ra_hir_def/src/find_path.rs @@ -1,7 +1,5 @@ //! An algorithm to find a path to refer to a certain item. -use std::sync::Arc; - use hir_expand::name::{known, AsName, Name}; use ra_prof::profile; use test_utils::mark; @@ -11,8 +9,9 @@ use crate::{ item_scope::ItemInNs, path::{ModPath, PathKind}, visibility::Visibility, - CrateId, ModuleDefId, ModuleId, + ModuleDefId, ModuleId, }; +use rustc_hash::FxHashSet; // FIXME: handle local items @@ -20,7 +19,7 @@ use crate::{ /// *from where* you're referring to the item, hence the `from` parameter. pub fn find_path(db: &dyn DefDatabase, item: ItemInNs, from: ModuleId) -> Option { let _p = profile("find_path"); - db.find_path_inner(item, from, MAX_PATH_LEN) + find_path_inner(db, item, from, MAX_PATH_LEN) } const MAX_PATH_LEN: usize = 15; @@ -38,7 +37,7 @@ impl ModPath { } } -pub(crate) fn find_path_inner_query( +pub(crate) fn find_path_inner( db: &dyn DefDatabase, item: ItemInNs, from: ModuleId, @@ -122,31 +121,61 @@ pub(crate) fn find_path_inner_query( } // - otherwise, look for modules containing (reexporting) it and import it from one of those + let crate_root = ModuleId { local_id: def_map.root, krate: from.krate }; let crate_attrs = db.attrs(crate_root.into()); let prefer_no_std = crate_attrs.by_key("no_std").exists(); - let importable_locations = find_importable_locations(db, item, from); let mut best_path = None; let mut best_path_len = max_len; - for (module_id, name) in importable_locations { - let mut path = match db.find_path_inner( - ItemInNs::Types(ModuleDefId::ModuleId(module_id)), - from, - best_path_len - 1, - ) { - None => continue, - Some(path) => path, - }; - path.segments.push(name); - let new_path = if let Some(best_path) = best_path { - select_best_path(best_path, path, prefer_no_std) - } else { - path - }; - best_path_len = new_path.len(); - best_path = Some(new_path); + if item.defining_crate(db) == Some(from.krate) { + // Item was defined in the same crate that wants to import it. It cannot be found in any + // dependency in this case. + + let local_imports = find_local_import_locations(db, item, from); + for (module_id, name) in local_imports { + if let Some(mut path) = find_path_inner( + db, + ItemInNs::Types(ModuleDefId::ModuleId(module_id)), + from, + best_path_len - 1, + ) { + path.segments.push(name); + + let new_path = if let Some(best_path) = best_path { + select_best_path(best_path, path, prefer_no_std) + } else { + path + }; + best_path_len = new_path.len(); + best_path = Some(new_path); + } + } + } else { + // Item was defined in some upstream crate. This means that it must be exported from one, + // too (unless we can't name it at all). It could *also* be (re)exported by the same crate + // that wants to import it here, but we always prefer to use the external path here. + + let crate_graph = db.crate_graph(); + let extern_paths = crate_graph[from.krate].dependencies.iter().filter_map(|dep| { + let import_map = db.import_map(dep.crate_id); + import_map.path_of(item).map(|modpath| { + let mut modpath = modpath.clone(); + modpath.segments.insert(0, dep.as_name()); + modpath + }) + }); + + for path in extern_paths { + let new_path = if let Some(best_path) = best_path { + select_best_path(best_path, path, prefer_no_std) + } else { + path + }; + best_path = Some(new_path); + } } + best_path } @@ -174,77 +203,86 @@ fn select_best_path(old_path: ModPath, new_path: ModPath, prefer_no_std: bool) - } } -fn find_importable_locations( +/// Finds locations in `from.krate` from which `item` can be imported by `from`. +fn find_local_import_locations( db: &dyn DefDatabase, item: ItemInNs, from: ModuleId, ) -> Vec<(ModuleId, Name)> { - let crate_graph = db.crate_graph(); - let mut result = Vec::new(); + let _p = profile("find_local_import_locations"); - // We only look in the crate from which we are importing, and the direct - // dependencies. We cannot refer to names from transitive dependencies - // directly (only through reexports in direct dependencies). + // `from` can import anything below `from` with visibility of at least `from`, and anything + // above `from` with any visibility. That means we do not need to descend into private siblings + // of `from` (and similar). - // For the crate from which we're importing, we have to check whether any - // module visible to `from` exports the item we're looking for. - // For dependencies of the crate only `pub` items reachable through `pub` - // modules from the crate root are relevant. For that we precompute an - // import map that tells us the shortest path to any importable item with a - // single lookup. - for krate in Some(from.krate) - .into_iter() - .chain(crate_graph[from.krate].dependencies.iter().map(|dep| dep.crate_id)) - { - result.extend( - db.importable_locations_of(item, krate) - .iter() - .filter(|(_, _, vis)| vis.is_visible_from(db, from)) - .map(|(m, n, _)| (*m, n.clone())), - ); + let def_map = db.crate_def_map(from.krate); + + // Compute the initial worklist. We start with all direct child modules of `from` as well as all + // of its (recursive) parent modules. + let data = &def_map.modules[from.local_id]; + let mut worklist = data + .children + .values() + .map(|child| ModuleId { krate: from.krate, local_id: *child }) + .collect::>(); + let mut parent = data.parent; + while let Some(p) = parent { + worklist.push(ModuleId { krate: from.krate, local_id: p }); + parent = def_map.modules[p].parent; } - result -} -/// Collects all locations from which we might import the item in a particular -/// crate. These include the original definition of the item, and any -/// non-private `use`s. -/// -/// Note that the crate doesn't need to be the one in which the item is defined; -/// it might be re-exported in other crates. -pub(crate) fn importable_locations_of_query( - db: &dyn DefDatabase, - item: ItemInNs, - krate: CrateId, -) -> Arc<[(ModuleId, Name, Visibility)]> { - let _p = profile("importable_locations_of_query"); - let def_map = db.crate_def_map(krate); - let mut result = Vec::new(); - for (local_id, data) in def_map.modules.iter() { + let mut seen: FxHashSet<_> = FxHashSet::default(); + + let mut locations = Vec::new(); + while let Some(module) = worklist.pop() { + if !seen.insert(module) { + continue; // already processed this module + } + + let ext_def_map; + let data = if module.krate == from.krate { + &def_map[module.local_id] + } else { + // The crate might reexport a module defined in another crate. + ext_def_map = db.crate_def_map(module.krate); + &ext_def_map[module.local_id] + }; + if let Some((name, vis)) = data.scope.name_of(item) { - let is_private = if let Visibility::Module(private_to) = vis { - private_to.local_id == local_id - } else { - false - }; - let is_original_def = if let Some(module_def_id) = item.as_module_def_id() { - data.scope.declarations().any(|it| it == module_def_id) - } else { - false - }; - if is_private && !is_original_def { + if vis.is_visible_from(db, from) { + let is_private = if let Visibility::Module(private_to) = vis { + private_to.local_id == module.local_id + } else { + false + }; + let is_original_def = if let Some(module_def_id) = item.as_module_def_id() { + data.scope.declarations().any(|it| it == module_def_id) + } else { + false + }; + // Ignore private imports. these could be used if we are // in a submodule of this module, but that's usually not // what the user wants; and if this module can import // the item and we're a submodule of it, so can we. // Also this keeps the cached data smaller. - continue; + if !is_private || is_original_def { + locations.push((module, name.clone())); + } + } + } + + // Descend into all modules visible from `from`. + for (_, per_ns) in data.scope.entries() { + if let Some((ModuleDefId::ModuleId(module), vis)) = per_ns.take_types_vis() { + if vis.is_visible_from(db, from) { + worklist.push(module); + } } - result.push((ModuleId { krate, local_id }, name.clone(), vis)); } } - Arc::from(result) + locations } #[cfg(test)] @@ -382,6 +420,7 @@ mod tests { #[test] fn different_crate_renamed() { + // Even if a local path exists, if the item is defined externally, prefer an external path. let code = r#" //- /main.rs crate:main deps:std extern crate std as std_renamed; @@ -389,7 +428,7 @@ mod tests { //- /std.rs crate:std pub struct S; "#; - check_found_path(code, "std_renamed::S"); + check_found_path(code, "std::S"); } #[test] diff --git a/crates/ra_hir_def/src/item_scope.rs b/crates/ra_hir_def/src/item_scope.rs index fc15948adf4d..ede1aa045357 100644 --- a/crates/ra_hir_def/src/item_scope.rs +++ b/crates/ra_hir_def/src/item_scope.rs @@ -6,9 +6,10 @@ use once_cell::sync::Lazy; use rustc_hash::FxHashMap; use crate::{ - per_ns::PerNs, visibility::Visibility, AdtId, BuiltinType, ImplId, MacroDefId, ModuleDefId, - TraitId, + db::DefDatabase, per_ns::PerNs, visibility::Visibility, AdtId, BuiltinType, HasModule, ImplId, + Lookup, MacroDefId, ModuleDefId, TraitId, }; +use ra_db::CrateId; #[derive(Debug, Default, PartialEq, Eq)] pub struct ItemScope { @@ -203,4 +204,21 @@ impl ItemInNs { ItemInNs::Macros(_) => None, } } + + pub fn defining_crate(&self, db: &dyn DefDatabase) -> Option { + Some(match self { + ItemInNs::Types(did) | ItemInNs::Values(did) => match did { + ModuleDefId::ModuleId(id) => id.krate, + ModuleDefId::FunctionId(id) => id.lookup(db).module(db).krate, + ModuleDefId::AdtId(id) => id.module(db).krate, + ModuleDefId::EnumVariantId(id) => id.parent.lookup(db).container.module(db).krate, + ModuleDefId::ConstId(id) => id.lookup(db).container.module(db).krate, + ModuleDefId::StaticId(id) => id.lookup(db).container.module(db).krate, + ModuleDefId::TraitId(id) => id.lookup(db).container.module(db).krate, + ModuleDefId::TypeAliasId(id) => id.lookup(db).module(db).krate, + ModuleDefId::BuiltinType(_) => return None, + }, + ItemInNs::Macros(id) => return id.krate, + }) + } } From 921306757baa636af7872b003d33dc1a8bd2b725 Mon Sep 17 00:00:00 2001 From: Jess Balint Date: Wed, 3 Jun 2020 17:54:23 -0500 Subject: [PATCH 064/172] introduce_named_lifetime assist wasn't applicable when type parameter followed anonymous lifetime token (fixes #4684) --- .../src/handlers/introduce_named_lifetime.rs | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/crates/ra_assists/src/handlers/introduce_named_lifetime.rs b/crates/ra_assists/src/handlers/introduce_named_lifetime.rs index beb5b7366d90..28fcbc9ba62e 100644 --- a/crates/ra_assists/src/handlers/introduce_named_lifetime.rs +++ b/crates/ra_assists/src/handlers/introduce_named_lifetime.rs @@ -41,8 +41,6 @@ pub(crate) fn introduce_named_lifetime(acc: &mut Assists, ctx: &AssistContext) - if let Some(fn_def) = lifetime_token.ancestors().find_map(ast::FnDef::cast) { generate_fn_def_assist(acc, &fn_def, lifetime_token.text_range()) } else if let Some(impl_def) = lifetime_token.ancestors().find_map(ast::ImplDef::cast) { - // only allow naming the last anonymous lifetime - lifetime_token.next_token().filter(|tok| tok.kind() == SyntaxKind::R_ANGLE)?; generate_impl_def_assist(acc, &impl_def, lifetime_token.text_range()) } else { None @@ -190,6 +188,23 @@ mod tests { ); } + #[test] + fn test_impl_with_other_type_param() { + check_assist( + introduce_named_lifetime, + "impl fmt::Display for SepByBuilder<'_<|>, I> + where + I: Iterator, + I::Item: fmt::Display, + {", + "impl fmt::Display for SepByBuilder<'a, I> + where + I: Iterator, + I::Item: fmt::Display, + {", + ) + } + #[test] fn test_example_case_cursor_before_tick() { check_assist( From 74c3e7a1adf9d14bbac5cbbe9cd893d758f82561 Mon Sep 17 00:00:00 2001 From: Mikhail Rakhmanov Date: Fri, 5 Jun 2020 11:45:41 +0200 Subject: [PATCH 065/172] Remove unnecessary return --- .../src/handlers/extract_struct_from_enum_variant.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/ra_assists/src/handlers/extract_struct_from_enum_variant.rs b/crates/ra_assists/src/handlers/extract_struct_from_enum_variant.rs index 72e5dd7356ca..ef963ddba316 100644 --- a/crates/ra_assists/src/handlers/extract_struct_from_enum_variant.rs +++ b/crates/ra_assists/src/handlers/extract_struct_from_enum_variant.rs @@ -52,7 +52,7 @@ pub(crate) fn extract_struct_from_enum_variant( ImportsLocator::new(ctx.db).find_imports(&enum_name).first()?.left()?; let current_module = current_module_def.module(ctx.db)?; let target = variant.syntax().text_range(); - return acc.add_in_multiple_files( + acc.add_in_multiple_files( AssistId("extract_struct_from_enum_variant"), "Extract struct from enum variant", target, @@ -85,7 +85,7 @@ pub(crate) fn extract_struct_from_enum_variant( let list_range = field_list.syntax().text_range(); update_variant(edit, &variant_name, ctx.frange.file_id, list_range); }, - ); + ) } fn existing_struct_def(db: &RootDatabase, variant_name: &str, variant: &EnumVariant) -> bool { From e0e9c6d1a4cfdd4410b802ad8db1e29c9f1b4291 Mon Sep 17 00:00:00 2001 From: Jonas Schievink Date: Fri, 5 Jun 2020 13:04:35 +0200 Subject: [PATCH 066/172] Fix wrong comment --- crates/ra_hir_def/src/import_map.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/ra_hir_def/src/import_map.rs b/crates/ra_hir_def/src/import_map.rs index 7dae64efa741..7c8c4b6cb554 100644 --- a/crates/ra_hir_def/src/import_map.rs +++ b/crates/ra_hir_def/src/import_map.rs @@ -281,7 +281,7 @@ mod tests { #[test] fn cyclic_module_reexport() { - // Reexporting modules from a dependency adds all contents to the import map. + // A cyclic reexport does not hang. let map = import_map( r" //- /lib.rs crate:lib From 86fbd8cc2b71f473b9e6d41f4fe3e1a114f1f992 Mon Sep 17 00:00:00 2001 From: Jonas Schievink Date: Fri, 5 Jun 2020 13:05:19 +0200 Subject: [PATCH 067/172] defining_crate -> krate --- crates/ra_hir_def/src/find_path.rs | 2 +- crates/ra_hir_def/src/item_scope.rs | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/crates/ra_hir_def/src/find_path.rs b/crates/ra_hir_def/src/find_path.rs index 79f4afab623f..e6cd89478964 100644 --- a/crates/ra_hir_def/src/find_path.rs +++ b/crates/ra_hir_def/src/find_path.rs @@ -128,7 +128,7 @@ pub(crate) fn find_path_inner( let mut best_path = None; let mut best_path_len = max_len; - if item.defining_crate(db) == Some(from.krate) { + if item.krate(db) == Some(from.krate) { // Item was defined in the same crate that wants to import it. It cannot be found in any // dependency in this case. diff --git a/crates/ra_hir_def/src/item_scope.rs b/crates/ra_hir_def/src/item_scope.rs index ede1aa045357..d340e1f137c6 100644 --- a/crates/ra_hir_def/src/item_scope.rs +++ b/crates/ra_hir_def/src/item_scope.rs @@ -205,7 +205,8 @@ impl ItemInNs { } } - pub fn defining_crate(&self, db: &dyn DefDatabase) -> Option { + /// Returns the crate defining this item (or `None` if `self` is built-in). + pub fn krate(&self, db: &dyn DefDatabase) -> Option { Some(match self { ItemInNs::Types(did) | ItemInNs::Values(did) => match did { ModuleDefId::ModuleId(id) => id.krate, From f085e592fe4ddd47c20e96a944c5902ff8a2f439 Mon Sep 17 00:00:00 2001 From: Jonas Schievink Date: Fri, 5 Jun 2020 13:10:43 +0200 Subject: [PATCH 068/172] Measure memory usage of ImportMap --- crates/ra_hir/src/db.rs | 10 +++++----- crates/ra_ide_db/src/change.rs | 1 + 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/crates/ra_hir/src/db.rs b/crates/ra_hir/src/db.rs index ec931b34fe87..098b665292c6 100644 --- a/crates/ra_hir/src/db.rs +++ b/crates/ra_hir/src/db.rs @@ -3,11 +3,11 @@ pub use hir_def::db::{ AttrsQuery, BodyQuery, BodyWithSourceMapQuery, ConstDataQuery, CrateDefMapQueryQuery, CrateLangItemsQuery, DefDatabase, DefDatabaseStorage, DocumentationQuery, EnumDataQuery, - ExprScopesQuery, FunctionDataQuery, GenericParamsQuery, ImplDataQuery, InternConstQuery, - InternDatabase, InternDatabaseStorage, InternEnumQuery, InternFunctionQuery, InternImplQuery, - InternStaticQuery, InternStructQuery, InternTraitQuery, InternTypeAliasQuery, InternUnionQuery, - LangItemQuery, ModuleLangItemsQuery, RawItemsQuery, StaticDataQuery, StructDataQuery, - TraitDataQuery, TypeAliasDataQuery, UnionDataQuery, + ExprScopesQuery, FunctionDataQuery, GenericParamsQuery, ImplDataQuery, ImportMapQuery, + InternConstQuery, InternDatabase, InternDatabaseStorage, InternEnumQuery, InternFunctionQuery, + InternImplQuery, InternStaticQuery, InternStructQuery, InternTraitQuery, InternTypeAliasQuery, + InternUnionQuery, LangItemQuery, ModuleLangItemsQuery, RawItemsQuery, StaticDataQuery, + StructDataQuery, TraitDataQuery, TypeAliasDataQuery, UnionDataQuery, }; pub use hir_expand::db::{ AstDatabase, AstDatabaseStorage, AstIdMapQuery, InternEagerExpansionQuery, InternMacroQuery, diff --git a/crates/ra_ide_db/src/change.rs b/crates/ra_ide_db/src/change.rs index 8446ef88e8c5..ea78e49e050e 100644 --- a/crates/ra_ide_db/src/change.rs +++ b/crates/ra_ide_db/src/change.rs @@ -334,6 +334,7 @@ impl RootDatabase { hir::db::CrateLangItemsQuery hir::db::LangItemQuery hir::db::DocumentationQuery + hir::db::ImportMapQuery // InternDatabase hir::db::InternFunctionQuery From 5f23f8ca449e456087d0657f925837bfb1a3f5ec Mon Sep 17 00:00:00 2001 From: Jonas Schievink Date: Fri, 5 Jun 2020 13:11:53 +0200 Subject: [PATCH 069/172] Make `find_path_inner` private again --- crates/ra_hir_def/src/find_path.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/ra_hir_def/src/find_path.rs b/crates/ra_hir_def/src/find_path.rs index e6cd89478964..d6ae970dc4f5 100644 --- a/crates/ra_hir_def/src/find_path.rs +++ b/crates/ra_hir_def/src/find_path.rs @@ -37,7 +37,7 @@ impl ModPath { } } -pub(crate) fn find_path_inner( +fn find_path_inner( db: &dyn DefDatabase, item: ItemInNs, from: ModuleId, From 8395396782e343c6fe6bd318c74e8c9884b22323 Mon Sep 17 00:00:00 2001 From: Jonas Schievink Date: Fri, 5 Jun 2020 13:15:16 +0200 Subject: [PATCH 070/172] Reorder imports --- crates/ra_hir_def/src/find_path.rs | 2 +- crates/ra_hir_def/src/import_map.rs | 8 +++++--- crates/ra_hir_def/src/item_scope.rs | 2 +- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/crates/ra_hir_def/src/find_path.rs b/crates/ra_hir_def/src/find_path.rs index d6ae970dc4f5..a7f59e0287ff 100644 --- a/crates/ra_hir_def/src/find_path.rs +++ b/crates/ra_hir_def/src/find_path.rs @@ -2,6 +2,7 @@ use hir_expand::name::{known, AsName, Name}; use ra_prof::profile; +use rustc_hash::FxHashSet; use test_utils::mark; use crate::{ @@ -11,7 +12,6 @@ use crate::{ visibility::Visibility, ModuleDefId, ModuleId, }; -use rustc_hash::FxHashSet; // FIXME: handle local items diff --git a/crates/ra_hir_def/src/import_map.rs b/crates/ra_hir_def/src/import_map.rs index 7c8c4b6cb554..1c812a19a63b 100644 --- a/crates/ra_hir_def/src/import_map.rs +++ b/crates/ra_hir_def/src/import_map.rs @@ -1,5 +1,10 @@ //! A map of all publicly exported items in a crate. +use std::{collections::hash_map::Entry, sync::Arc}; + +use ra_db::CrateId; +use rustc_hash::FxHashMap; + use crate::{ db::DefDatabase, item_scope::ItemInNs, @@ -7,9 +12,6 @@ use crate::{ visibility::Visibility, ModuleDefId, ModuleId, }; -use ra_db::CrateId; -use rustc_hash::FxHashMap; -use std::{collections::hash_map::Entry, sync::Arc}; /// A map from publicly exported items to the path needed to import/name them from a downstream /// crate. diff --git a/crates/ra_hir_def/src/item_scope.rs b/crates/ra_hir_def/src/item_scope.rs index d340e1f137c6..b03ba939a5a5 100644 --- a/crates/ra_hir_def/src/item_scope.rs +++ b/crates/ra_hir_def/src/item_scope.rs @@ -3,13 +3,13 @@ use hir_expand::name::Name; use once_cell::sync::Lazy; +use ra_db::CrateId; use rustc_hash::FxHashMap; use crate::{ db::DefDatabase, per_ns::PerNs, visibility::Visibility, AdtId, BuiltinType, HasModule, ImplId, Lookup, MacroDefId, ModuleDefId, TraitId, }; -use ra_db::CrateId; #[derive(Debug, Default, PartialEq, Eq)] pub struct ItemScope { From 5dda9955380c6214aa5720ad640b76b870aaa556 Mon Sep 17 00:00:00 2001 From: Mikhail Rakhmanov Date: Fri, 5 Jun 2020 13:17:17 +0200 Subject: [PATCH 071/172] Fix review comments --- .../extract_struct_from_enum_variant.rs | 41 ++++++++----------- crates/ra_assists/src/utils/insert_use.rs | 11 +---- 2 files changed, 19 insertions(+), 33 deletions(-) diff --git a/crates/ra_assists/src/handlers/extract_struct_from_enum_variant.rs b/crates/ra_assists/src/handlers/extract_struct_from_enum_variant.rs index ef963ddba316..2c455a1fd367 100644 --- a/crates/ra_assists/src/handlers/extract_struct_from_enum_variant.rs +++ b/crates/ra_assists/src/handlers/extract_struct_from_enum_variant.rs @@ -1,6 +1,4 @@ -use ra_ide_db::{ - defs::Definition, imports_locator::ImportsLocator, search::Reference, RootDatabase, -}; +use ra_ide_db::{defs::Definition, search::Reference, RootDatabase}; use ra_syntax::{ algo::find_node_at_offset, ast::{self, AstNode, NameOwner}, @@ -9,11 +7,11 @@ use ra_syntax::{ use crate::{ assist_context::{AssistBuilder, AssistDirector}, - utils::insert_use::insert_use_statement_with_string_path, + utils::insert_use_statement, AssistContext, AssistId, Assists, }; use ast::{ArgListOwner, VisibilityOwner}; -use hir::{EnumVariant, Module, ModuleDef}; +use hir::{EnumVariant, Module, ModuleDef, Name}; use ra_db::FileId; use ra_fmt::leading_indent; use rustc_hash::FxHashSet; @@ -46,11 +44,11 @@ pub(crate) fn extract_struct_from_enum_variant( return None; } let enum_ast = variant.parent_enum(); - let enum_name = enum_ast.name()?.to_string(); let visibility = enum_ast.visibility(); - let current_module_def = - ImportsLocator::new(ctx.db).find_imports(&enum_name).first()?.left()?; - let current_module = current_module_def.module(ctx.db)?; + let enum_hir = ctx.sema.to_def(&enum_ast)?; + let variant_hir_name = variant_hir.name(ctx.db); + let enum_module_def = ModuleDef::from(enum_hir); + let current_module = enum_hir.module(ctx.db); let target = variant.syntax().text_range(); acc.add_in_multiple_files( AssistId("extract_struct_from_enum_variant"), @@ -69,7 +67,8 @@ pub(crate) fn extract_struct_from_enum_variant( edit, reference, &source_file, - ¤t_module_def, + &enum_module_def, + &variant_hir_name, &mut visited_modules_set, ); } @@ -102,20 +101,15 @@ fn insert_import( builder: &mut AssistBuilder, path: &ast::PathExpr, module: &Module, - module_def: &ModuleDef, - path_segment: ast::NameRef, + enum_module_def: &ModuleDef, + variant_hir_name: &Name, ) -> Option<()> { let db = ctx.db; - let mod_path = module.find_use_path(db, module_def.clone()); + let mod_path = module.find_use_path(db, enum_module_def.clone()); if let Some(mut mod_path) = mod_path { mod_path.segments.pop(); - let use_path = format!("{}::{}", mod_path.to_string(), path_segment.to_string()); - insert_use_statement_with_string_path( - path.syntax(), - &use_path, - ctx, - builder.text_edit_builder(), - ); + mod_path.segments.push(variant_hir_name.clone()); + insert_use_statement(path.syntax(), &mod_path, ctx, builder.text_edit_builder()); } Some(()) } @@ -175,7 +169,8 @@ fn update_reference( edit: &mut AssistDirector, reference: Reference, source_file: &SourceFile, - module_def: &ModuleDef, + enum_module_def: &ModuleDef, + variant_hir_name: &Name, visited_modules_set: &mut FxHashSet, ) -> Option<()> { let path_expr: ast::PathExpr = find_node_at_offset::( @@ -185,7 +180,6 @@ fn update_reference( let call = path_expr.syntax().parent().and_then(ast::CallExpr::cast)?; let list = call.arg_list()?; let segment = path_expr.path()?.segment()?; - let segment_name = segment.name_ref()?; let module = ctx.sema.scope(&path_expr.syntax()).module()?; let list_range = list.syntax().text_range(); let inside_list_range = TextRange::new( @@ -194,7 +188,8 @@ fn update_reference( ); edit.perform(reference.file_range.file_id, |builder| { if !visited_modules_set.contains(&module) { - if insert_import(ctx, builder, &path_expr, &module, module_def, segment_name).is_some() + if insert_import(ctx, builder, &path_expr, &module, enum_module_def, variant_hir_name) + .is_some() { visited_modules_set.insert(module); } diff --git a/crates/ra_assists/src/utils/insert_use.rs b/crates/ra_assists/src/utils/insert_use.rs index 114f5949a6aa..0ee43482f798 100644 --- a/crates/ra_assists/src/utils/insert_use.rs +++ b/crates/ra_assists/src/utils/insert_use.rs @@ -23,16 +23,7 @@ pub(crate) fn insert_use_statement( ctx: &AssistContext, builder: &mut TextEditBuilder, ) { - insert_use_statement_with_string_path(position, &path_to_import.to_string(), ctx, builder); -} - -pub(crate) fn insert_use_statement_with_string_path( - position: &SyntaxNode, - path_to_import: &str, - ctx: &AssistContext, - builder: &mut TextEditBuilder, -) { - let target = path_to_import.split("::").map(SmolStr::new).collect::>(); + let target = path_to_import.to_string().split("::").map(SmolStr::new).collect::>(); let container = ctx.sema.ancestors_with_macros(position.clone()).find_map(|n| { if let Some(module) = ast::Module::cast(n.clone()) { return module.item_list().map(|it| it.syntax().clone()); From 2fb3d87bf77826f213d2876c921308e9f168ca63 Mon Sep 17 00:00:00 2001 From: Jonas Schievink Date: Fri, 5 Jun 2020 13:36:19 +0200 Subject: [PATCH 072/172] impl Debug for ImportMap --- crates/ra_hir_def/src/import_map.rs | 42 ++++++++++++++++------------- 1 file changed, 23 insertions(+), 19 deletions(-) diff --git a/crates/ra_hir_def/src/import_map.rs b/crates/ra_hir_def/src/import_map.rs index 1c812a19a63b..70749f3800ca 100644 --- a/crates/ra_hir_def/src/import_map.rs +++ b/crates/ra_hir_def/src/import_map.rs @@ -1,6 +1,6 @@ //! A map of all publicly exported items in a crate. -use std::{collections::hash_map::Entry, sync::Arc}; +use std::{collections::hash_map::Entry, fmt, sync::Arc}; use ra_db::CrateId; use rustc_hash::FxHashMap; @@ -21,7 +21,7 @@ use crate::{ /// /// Note that all paths are relative to the containing crate's root, so the crate name still needs /// to be prepended to the `ModPath` before the path is valid. -#[derive(Debug, Eq, PartialEq)] +#[derive(Eq, PartialEq)] pub struct ImportMap { map: FxHashMap, } @@ -95,6 +95,26 @@ impl ImportMap { } } +impl fmt::Debug for ImportMap { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let mut importable_paths: Vec<_> = self + .map + .iter() + .map(|(item, modpath)| { + let ns = match item { + ItemInNs::Types(_) => "t", + ItemInNs::Values(_) => "v", + ItemInNs::Macros(_) => "m", + }; + format!("- {} ({})", modpath, ns) + }) + .collect(); + + importable_paths.sort(); + f.write_str(&importable_paths.join("\n")) + } +} + #[cfg(test)] mod tests { use super::*; @@ -115,23 +135,7 @@ mod tests { let map = db.import_map(krate); - let mut importable_paths: Vec<_> = map - .map - .iter() - .map(|(item, modpath)| { - let ns = match item { - ItemInNs::Types(_) => "t", - ItemInNs::Values(_) => "v", - ItemInNs::Macros(_) => "m", - }; - format!("- {} ({})", modpath, ns) - }) - .collect(); - - importable_paths.sort(); - let importable_paths = importable_paths.join("\n"); - - Some(format!("{}:\n{}", name, importable_paths)) + Some(format!("{}:\n{:?}", name, map)) }) .collect(); From 522d24a607c100fdc12fa6650807a1dfcb0f4b12 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Fri, 5 Jun 2020 13:58:52 +0200 Subject: [PATCH 073/172] Inlay Hints: more directly account for self param --- crates/ra_ide/src/inlay_hints.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/crates/ra_ide/src/inlay_hints.rs b/crates/ra_ide/src/inlay_hints.rs index 75bd3c96bb1f..49366de983be 100644 --- a/crates/ra_ide/src/inlay_hints.rs +++ b/crates/ra_ide/src/inlay_hints.rs @@ -149,11 +149,10 @@ fn get_param_name_hints( ast::Expr::MethodCallExpr(expr) => expr.arg_list()?.args(), _ => return None, }; - let args_count = args.clone().count(); let fn_signature = get_fn_signature(sema, &expr)?; let n_params_to_skip = - if fn_signature.has_self_param && fn_signature.parameter_names.len() > args_count { + if fn_signature.has_self_param && matches!(&expr, ast::Expr::MethodCallExpr(_)) { 1 } else { 0 From 7d0dd17b09240385333805637ea17992a8089cf2 Mon Sep 17 00:00:00 2001 From: vsrs Date: Wed, 3 Jun 2020 14:15:54 +0300 Subject: [PATCH 074/172] Add hover actions as LSP extension --- crates/ra_ide/src/hover.rs | 134 ++++++++++++++++-- crates/ra_ide/src/lib.rs | 2 +- crates/ra_ide_db/src/defs.rs | 2 +- crates/rust-analyzer/src/config.rs | 28 ++-- crates/rust-analyzer/src/lsp_ext.rs | 34 +++++ crates/rust-analyzer/src/main_loop.rs | 2 +- .../rust-analyzer/src/main_loop/handlers.rs | 119 ++++++++++++---- editors/code/package.json | 16 ++- editors/code/src/client.ts | 45 ++++++ editors/code/src/config.ts | 13 +- editors/code/src/lsp_ext.ts | 12 ++ 11 files changed, 351 insertions(+), 56 deletions(-) diff --git a/crates/ra_ide/src/hover.rs b/crates/ra_ide/src/hover.rs index 9636cd0d6af7..baa9fc8a8ff9 100644 --- a/crates/ra_ide/src/hover.rs +++ b/crates/ra_ide/src/hover.rs @@ -13,14 +13,43 @@ use ra_ide_db::{ use ra_syntax::{ast, match_ast, AstNode, SyntaxKind::*, SyntaxToken, TokenAtOffset}; use crate::{ - display::{macro_label, rust_code_markup, rust_code_markup_with_doc, ShortLabel}, - FilePosition, RangeInfo, + display::{macro_label, rust_code_markup, rust_code_markup_with_doc, ShortLabel, ToNav}, + FilePosition, RangeInfo, NavigationTarget, }; +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct HoverConfig { + pub implementations: bool, +} + +impl Default for HoverConfig { + fn default() -> Self { + Self { implementations: true } + } +} + +impl HoverConfig { + pub const NO_ACTIONS: Self = Self { implementations: false }; + + pub fn any(&self) -> bool { + self.implementations + } + + pub fn none(&self) -> bool { + !self.any() + } +} + +#[derive(Debug, Clone)] +pub enum HoverAction { + Implementaion(FilePosition), +} + /// Contains the results when hovering over an item #[derive(Debug, Default)] pub struct HoverResult { results: Vec, + actions: Vec, } impl HoverResult { @@ -48,10 +77,20 @@ impl HoverResult { &self.results } + pub fn actions(&self) -> &[HoverAction] { + &self.actions + } + + pub fn push_action(&mut self, action: HoverAction) { + self.actions.push(action); + } + /// Returns the results converted into markup /// for displaying in a UI + /// + /// Does not process actions! pub fn to_markup(&self) -> String { - self.results.join("\n\n---\n") + self.results.join("\n\n___\n") } } @@ -82,6 +121,10 @@ pub(crate) fn hover(db: &RootDatabase, position: FilePosition) -> Option Option Option { + fn to_action(nav_target: NavigationTarget) -> HoverAction { + HoverAction::Implementaion(FilePosition { + file_id: nav_target.file_id(), + offset: nav_target.range().start(), + }) + } + + match def { + Definition::ModuleDef(it) => match it { + ModuleDef::Adt(Adt::Struct(it)) => Some(to_action(it.to_nav(db))), + ModuleDef::Adt(Adt::Union(it)) => Some(to_action(it.to_nav(db))), + ModuleDef::Adt(Adt::Enum(it)) => Some(to_action(it.to_nav(db))), + ModuleDef::Trait(it) => Some(to_action(it.to_nav(db))), + _ => None, + }, + _ => None, + } +} + fn hover_text( docs: Option, desc: Option, @@ -228,6 +291,8 @@ fn pick_best(tokens: TokenAtOffset) -> Option { #[cfg(test)] mod tests { + use super::*; + use ra_db::FileLoader; use ra_syntax::TextRange; @@ -241,7 +306,14 @@ mod tests { s.map(trim_markup) } - fn check_hover_result(fixture: &str, expected: &[&str]) -> String { + fn assert_impl_action(action: &HoverAction, position: u32) { + let offset = match action { + HoverAction::Implementaion(pos) => pos.offset + }; + assert_eq!(offset, position.into()); + } + + fn check_hover_result(fixture: &str, expected: &[&str]) -> (String, Vec) { let (analysis, position) = analysis_and_position(fixture); let hover = analysis.hover(position).unwrap().unwrap(); let mut results = Vec::from(hover.info.results()); @@ -256,7 +328,7 @@ mod tests { assert_eq!(hover.info.len(), expected.len()); let content = analysis.db.file_text(position.file_id); - content[hover.range].to_string() + (content[hover.range].to_string(), hover.info.actions().to_vec()) } fn check_hover_no_result(fixture: &str) { @@ -746,7 +818,7 @@ fn func(foo: i32) { if true { <|>foo; }; } #[test] fn test_hover_through_macro() { - let hover_on = check_hover_result( + let (hover_on, _) = check_hover_result( " //- /lib.rs macro_rules! id { @@ -767,7 +839,7 @@ fn func(foo: i32) { if true { <|>foo; }; } #[test] fn test_hover_through_expr_in_macro() { - let hover_on = check_hover_result( + let (hover_on, _) = check_hover_result( " //- /lib.rs macro_rules! id { @@ -785,7 +857,7 @@ fn func(foo: i32) { if true { <|>foo; }; } #[test] fn test_hover_through_expr_in_macro_recursive() { - let hover_on = check_hover_result( + let (hover_on, _) = check_hover_result( " //- /lib.rs macro_rules! id_deep { @@ -806,7 +878,7 @@ fn func(foo: i32) { if true { <|>foo; }; } #[test] fn test_hover_through_func_in_macro_recursive() { - let hover_on = check_hover_result( + let (hover_on, _) = check_hover_result( " //- /lib.rs macro_rules! id_deep { @@ -830,7 +902,7 @@ fn func(foo: i32) { if true { <|>foo; }; } #[test] fn test_hover_through_literal_string_in_macro() { - let hover_on = check_hover_result( + let (hover_on, _) = check_hover_result( r#" //- /lib.rs macro_rules! arr { @@ -849,7 +921,7 @@ fn func(foo: i32) { if true { <|>foo; }; } #[test] fn test_hover_through_assert_macro() { - let hover_on = check_hover_result( + let (hover_on, _) = check_hover_result( r#" //- /lib.rs #[rustc_builtin_macro] @@ -925,13 +997,14 @@ fn func(foo: i32) { if true { <|>foo; }; } #[test] fn test_hover_trait_show_qualifiers() { - check_hover_result( + let (_, actions) = check_hover_result( " //- /lib.rs unsafe trait foo<|>() {} ", &["unsafe trait foo"], ); + assert_impl_action(&actions[0], 13); } #[test] @@ -1052,4 +1125,41 @@ fn func(foo: i32) { if true { <|>foo; }; } &["Bar\n```\n\n```rust\nfn foo(&self)\n```\n___\n\nDo the foo"], ); } + + #[test] + fn test_hover_trait_hash_impl_action() { + let (_, actions) = check_hover_result( + " + //- /lib.rs + trait foo<|>() {} + ", + &["trait foo"], + ); + assert_impl_action(&actions[0], 6); + } + + #[test] + fn test_hover_struct_hash_impl_action() { + let (_, actions) = check_hover_result( + " + //- /lib.rs + struct foo<|>() {} + ", + &["struct foo"], + ); + assert_impl_action(&actions[0], 7); + } + + #[test] + fn test_hover_union_hash_impl_action() { + let (_, actions) = check_hover_result( + " + //- /lib.rs + union foo<|>() {} + ", + &["union foo"], + ); + assert_impl_action(&actions[0], 6); + } + } diff --git a/crates/ra_ide/src/lib.rs b/crates/ra_ide/src/lib.rs index 34c2d75fed2b..a9601400f188 100644 --- a/crates/ra_ide/src/lib.rs +++ b/crates/ra_ide/src/lib.rs @@ -66,7 +66,7 @@ pub use crate::{ display::{file_structure, FunctionSignature, NavigationTarget, StructureNode}, expand_macro::ExpandedMacro, folding_ranges::{Fold, FoldKind}, - hover::HoverResult, + hover::{HoverResult, HoverAction, HoverConfig}, inlay_hints::{InlayHint, InlayHintsConfig, InlayKind}, references::{Declaration, Reference, ReferenceAccess, ReferenceKind, ReferenceSearchResult}, runnables::{Runnable, RunnableKind, TestId}, diff --git a/crates/ra_ide_db/src/defs.rs b/crates/ra_ide_db/src/defs.rs index 8b06cbfc54b7..1db60b87fb82 100644 --- a/crates/ra_ide_db/src/defs.rs +++ b/crates/ra_ide_db/src/defs.rs @@ -18,7 +18,7 @@ use ra_syntax::{ use crate::RootDatabase; // FIXME: a more precise name would probably be `Symbol`? -#[derive(Debug, PartialEq, Eq)] +#[derive(Debug, PartialEq, Eq, Copy, Clone)] pub enum Definition { Macro(MacroDef), Field(Field), diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs index 23168c3ae9a2..e7c8595772ce 100644 --- a/crates/rust-analyzer/src/config.rs +++ b/crates/rust-analyzer/src/config.rs @@ -11,7 +11,7 @@ use std::{ffi::OsString, path::PathBuf}; use lsp_types::ClientCapabilities; use ra_flycheck::FlycheckConfig; -use ra_ide::{AssistConfig, CompletionConfig, InlayHintsConfig}; +use ra_ide::{AssistConfig, CompletionConfig, InlayHintsConfig, HoverConfig}; use ra_project_model::{CargoConfig, JsonProject, ProjectManifest}; use serde::Deserialize; @@ -34,6 +34,7 @@ pub struct Config { pub assist: AssistConfig, pub call_info_full: bool, pub lens: LensConfig, + pub hover: HoverConfig, pub with_sysroot: bool, pub linked_projects: Vec, @@ -124,6 +125,7 @@ pub struct ClientCapsConfig { pub work_done_progress: bool, pub code_action_group: bool, pub resolve_code_action: bool, + pub hover_actions: bool, } impl Default for Config { @@ -162,6 +164,7 @@ impl Default for Config { assist: AssistConfig::default(), call_info_full: true, lens: LensConfig::default(), + hover: HoverConfig::default(), linked_projects: Vec::new(), } } @@ -278,6 +281,14 @@ impl Config { } } + let mut use_hover_actions = false; + set(value, "/hoverActions/enable", &mut use_hover_actions); + if use_hover_actions { + set(value, "/hoverActions/implementations", &mut self.hover.implementations); + } else { + self.hover = HoverConfig::NO_ACTIONS; + } + log::info!("Config::update() = {:#?}", self); fn get<'a, T: Deserialize<'a>>(value: &'a serde_json::Value, pointer: &str) -> Option { @@ -331,17 +342,14 @@ impl Config { self.assist.allow_snippets(false); if let Some(experimental) = &caps.experimental { - let snippet_text_edit = - experimental.get("snippetTextEdit").and_then(|it| it.as_bool()) == Some(true); + let get_bool = |index: &str| experimental.get(index).and_then(|it| it.as_bool()) == Some(true); + + let snippet_text_edit = get_bool("snippetTextEdit"); self.assist.allow_snippets(snippet_text_edit); - let code_action_group = - experimental.get("codeActionGroup").and_then(|it| it.as_bool()) == Some(true); - self.client_caps.code_action_group = code_action_group; - - let resolve_code_action = - experimental.get("resolveCodeAction").and_then(|it| it.as_bool()) == Some(true); - self.client_caps.resolve_code_action = resolve_code_action; + self.client_caps.code_action_group = get_bool("codeActionGroup"); + self.client_caps.resolve_code_action = get_bool("resolveCodeAction"); + self.client_caps.hover_actions = get_bool("hoverActions"); } } } diff --git a/crates/rust-analyzer/src/lsp_ext.rs b/crates/rust-analyzer/src/lsp_ext.rs index 3b957534ddd0..145a389ce176 100644 --- a/crates/rust-analyzer/src/lsp_ext.rs +++ b/crates/rust-analyzer/src/lsp_ext.rs @@ -260,3 +260,37 @@ pub struct SnippetTextEdit { #[serde(skip_serializing_if = "Option::is_none")] pub insert_text_format: Option, } + +pub enum HoverRequest {} + +impl Request for HoverRequest { + type Params = lsp_types::HoverParams; + type Result = Option; + const METHOD: &'static str = "textDocument/hover"; +} + +#[derive(Debug, PartialEq, Clone, Deserialize, Serialize)] +pub struct Hover { + pub contents: lsp_types::HoverContents, + #[serde(skip_serializing_if = "Option::is_none")] + pub range: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub actions: Option>, +} + +#[derive(Debug, PartialEq, Eq, Clone, Default, Deserialize, Serialize)] +pub struct CommandLinkGroup { + pub title: Option, + pub commands: Vec, +} + +// LSP v3.15 Command does not have a `tooltip` field, vscode supports one. +#[derive(Debug, PartialEq, Eq, Clone, Default, Deserialize, Serialize)] +pub struct CommandLink { + pub title: String, + pub command: String, + #[serde(skip_serializing_if = "Option::is_none")] + pub tooltip: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub arguments: Option>, +} diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs index e60337b8e8b6..752dbf145298 100644 --- a/crates/rust-analyzer/src/main_loop.rs +++ b/crates/rust-analyzer/src/main_loop.rs @@ -510,6 +510,7 @@ fn on_request( .on::(handlers::handle_inlay_hints)? .on::(handlers::handle_code_action)? .on::(handlers::handle_resolve_code_action)? + .on::(handlers::handle_hover)? .on::(handlers::handle_on_type_formatting)? .on::(handlers::handle_document_symbol)? .on::(handlers::handle_workspace_symbol)? @@ -521,7 +522,6 @@ fn on_request( .on::(handlers::handle_code_lens_resolve)? .on::(handlers::handle_folding_range)? .on::(handlers::handle_signature_help)? - .on::(handlers::handle_hover)? .on::(handlers::handle_prepare_rename)? .on::(handlers::handle_rename)? .on::(handlers::handle_references)? diff --git a/crates/rust-analyzer/src/main_loop/handlers.rs b/crates/rust-analyzer/src/main_loop/handlers.rs index 6acf80c5827f..0958a231fd6a 100644 --- a/crates/rust-analyzer/src/main_loop/handlers.rs +++ b/crates/rust-analyzer/src/main_loop/handlers.rs @@ -12,13 +12,14 @@ use lsp_types::{ CallHierarchyIncomingCall, CallHierarchyIncomingCallsParams, CallHierarchyItem, CallHierarchyOutgoingCall, CallHierarchyOutgoingCallsParams, CallHierarchyPrepareParams, CodeLens, Command, CompletionItem, Diagnostic, DocumentFormattingParams, DocumentHighlight, - DocumentSymbol, FoldingRange, FoldingRangeParams, Hover, HoverContents, Location, - MarkupContent, MarkupKind, Position, PrepareRenameResponse, Range, RenameParams, - SemanticTokensParams, SemanticTokensRangeParams, SemanticTokensRangeResult, - SemanticTokensResult, SymbolInformation, TextDocumentIdentifier, Url, WorkspaceEdit, + DocumentSymbol, FoldingRange, FoldingRangeParams, HoverContents, Location, MarkupContent, + MarkupKind, Position, PrepareRenameResponse, Range, RenameParams, SemanticTokensParams, + SemanticTokensRangeParams, SemanticTokensRangeResult, SemanticTokensResult, SymbolInformation, + TextDocumentIdentifier, Url, WorkspaceEdit, }; use ra_ide::{ - FileId, FilePosition, FileRange, Query, RangeInfo, RunnableKind, SearchScope, TextEdit, + FileId, FilePosition, FileRange, HoverAction, Query, RangeInfo, RunnableKind, SearchScope, + TextEdit, }; use ra_prof::profile; use ra_project_model::TargetKind; @@ -537,7 +538,7 @@ pub fn handle_signature_help( pub fn handle_hover( snap: GlobalStateSnapshot, params: lsp_types::HoverParams, -) -> Result> { +) -> Result> { let _p = profile("handle_hover"); let position = from_proto::file_position(&snap, params.text_document_position_params)?; let info = match snap.analysis().hover(position)? { @@ -546,12 +547,13 @@ pub fn handle_hover( }; let line_index = snap.analysis.file_line_index(position.file_id)?; let range = to_proto::range(&line_index, info.range); - let res = Hover { + let res = lsp_ext::Hover { contents: HoverContents::Markup(MarkupContent { kind: MarkupKind::Markdown, value: crate::markdown::format_docs(&info.info.to_markup()), }), range: Some(range), + actions: Some(prepare_hover_actions(&world, info.info.actions())), }; Ok(Some(res)) } @@ -924,24 +926,13 @@ pub fn handle_code_lens_resolve( _ => vec![], }; - let title = if locations.len() == 1 { - "1 implementation".into() - } else { - format!("{} implementations", locations.len()) - }; - - // We cannot use the 'editor.action.showReferences' command directly - // because that command requires vscode types which we convert in the handler - // on the client side. - let cmd = Command { + let title = implementation_title(locations.len()); + let cmd = show_references_command( title, - command: "rust-analyzer.showReferences".into(), - arguments: Some(vec![ - to_value(&lens_params.text_document_position_params.text_document.uri).unwrap(), - to_value(code_lens.range.start).unwrap(), - to_value(locations).unwrap(), - ]), - }; + &lens_params.text_document_position_params.text_document.uri, + code_lens.range.start, + locations, + ); Ok(CodeLens { range: code_lens.range, command: Some(cmd), data: None }) } None => Ok(CodeLens { @@ -1145,3 +1136,83 @@ pub fn handle_semantic_tokens_range( let semantic_tokens = to_proto::semantic_tokens(&text, &line_index, highlights); Ok(Some(semantic_tokens.into())) } + +fn implementation_title(count: usize) -> String { + if count == 1 { + "1 implementation".into() + } else { + format!("{} implementations", count) + } +} + +fn show_references_command( + title: String, + uri: &lsp_types::Url, + position: lsp_types::Position, + locations: Vec, +) -> Command { + // We cannot use the 'editor.action.showReferences' command directly + // because that command requires vscode types which we convert in the handler + // on the client side. + + Command { + title, + command: "rust-analyzer.showReferences".into(), + arguments: Some(vec![ + to_value(uri).unwrap(), + to_value(position).unwrap(), + to_value(locations).unwrap(), + ]), + } +} + +fn to_command_link(command: Command, tooltip: String) -> lsp_ext::CommandLink { + lsp_ext::CommandLink { + tooltip: Some(tooltip), + title: command.title, + command: command.command, + arguments: command.arguments, + } +} + +fn show_impl_command_link( + world: &WorldSnapshot, + position: &FilePosition, +) -> Option { + if world.config.hover.implementations { + if let Some(nav_data) = world.analysis().goto_implementation(*position).unwrap_or(None) { + let uri = to_proto::url(world, position.file_id).ok()?; + let line_index = world.analysis().file_line_index(position.file_id).ok()?; + let position = to_proto::position(&line_index, position.offset); + let locations: Vec<_> = nav_data + .info + .iter() + .filter_map(|it| to_proto::location(world, it.file_range()).ok()) + .collect(); + let title = implementation_title(locations.len()); + let command = show_references_command(title, &uri, position, locations); + + return Some(lsp_ext::CommandLinkGroup { + commands: vec![to_command_link(command, "Go to implementations".into())], + ..Default::default() + }); + } + } + None +} + +fn prepare_hover_actions( + world: &WorldSnapshot, + actions: &[HoverAction], +) -> Vec { + if world.config.hover.none() || !world.config.client_caps.hover_actions { + return Vec::new(); + } + + actions + .iter() + .filter_map(|it| match it { + HoverAction::Implementaion(position) => show_impl_command_link(world, position), + }) + .collect() +} diff --git a/editors/code/package.json b/editors/code/package.json index 30ab7ba4a9f8..b9c57db3bcf7 100644 --- a/editors/code/package.json +++ b/editors/code/package.json @@ -462,17 +462,27 @@ "default": true }, "rust-analyzer.lens.run": { - "markdownDescription": "Whether to show Run lens. Only applies when `#rust-analyzer.lens.enable#` is set.", + "markdownDescription": "Whether to show `Run` lens. Only applies when `#rust-analyzer.lens.enable#` is set.", "type": "boolean", "default": true }, "rust-analyzer.lens.debug": { - "markdownDescription": "Whether to show Debug lens. Only applies when `#rust-analyzer.lens.enable#` is set.", + "markdownDescription": "Whether to show `Debug` lens. Only applies when `#rust-analyzer.lens.enable#` is set.", "type": "boolean", "default": true }, "rust-analyzer.lens.implementations": { - "markdownDescription": "Whether to show Implementations lens. Only applies when `#rust-analyzer.lens.enable#` is set.", + "markdownDescription": "Whether to show `Implementations` lens. Only applies when `#rust-analyzer.lens.enable#` is set.", + "type": "boolean", + "default": true + }, + "rust-analyzer.hoverActions.enable": { + "description": "Whether to show HoverActions in Rust files.", + "type": "boolean", + "default": true + }, + "rust-analyzer.hoverActions.implementations": { + "markdownDescription": "Whether to show `Implementations` action. Only applies when `#rust-analyzer.hoverActions.enable#` is set.", "type": "boolean", "default": true }, diff --git a/editors/code/src/client.ts b/editors/code/src/client.ts index 40ad1e3cd847..9df6702839b4 100644 --- a/editors/code/src/client.ts +++ b/editors/code/src/client.ts @@ -7,6 +7,29 @@ import { CallHierarchyFeature } from 'vscode-languageclient/lib/callHierarchy.pr import { SemanticTokensFeature, DocumentSemanticsTokensSignature } from 'vscode-languageclient/lib/semanticTokens.proposed'; import { assert } from './util'; +function toTrusted(obj: vscode.MarkedString): vscode.MarkedString { + const md = obj; + if (md && md.value.includes("```rust")) { + md.isTrusted = true; + return md; + } + return obj; +} + +function renderCommand(cmd: CommandLink) { + return `[${cmd.title}](command:${cmd.command}?${encodeURIComponent(JSON.stringify(cmd.arguments))} '${cmd.tooltip!}')`; +} + +function renderHoverActions(actions: CommandLinkGroup[]): vscode.MarkdownString { + const text = actions.map(group => + (group.title ? (group.title + " ") : "") + group.commands.map(renderCommand).join(' | ') + ).join('___'); + + const result = new vscode.MarkdownString(text); + result.isTrusted = true; + return result; +} + export function createClient(serverPath: string, cwd: string): lc.LanguageClient { // '.' Is the fallback if no folder is open // TODO?: Workspace folders support Uri's (eg: file://test.txt). @@ -35,6 +58,27 @@ export function createClient(serverPath: string, cwd: string): lc.LanguageClient if (res === undefined) throw new Error('busy'); return res; }, + async provideHover(document: vscode.TextDocument, position: vscode.Position, token: vscode.CancellationToken, _next: lc.ProvideHoverSignature) { + return client.sendRequest(lc.HoverRequest.type, client.code2ProtocolConverter.asTextDocumentPositionParams(document, position), token).then( + (result) => { + const hover = client.protocol2CodeConverter.asHover(result); + if (hover) { + // Workaround to support command links (trusted vscode.MarkdownString) in hovers + // https://github.com/microsoft/vscode/issues/33577 + hover.contents = hover.contents.map(toTrusted); + + const actions = (result).actions; + if (actions) { + hover.contents.push(renderHoverActions(actions)); + } + } + return hover; + }, + (error) => { + client.logFailedRequest(lc.HoverRequest.type, error); + return Promise.resolve(null); + }); + }, // Using custom handling of CodeActions where each code action is resloved lazily // That's why we are not waiting for any command or edits async provideCodeActions(document: vscode.TextDocument, range: vscode.Range, context: vscode.CodeActionContext, token: vscode.CancellationToken, _next: lc.ProvideCodeActionsSignature) { @@ -129,6 +173,7 @@ class ExperimentalFeatures implements lc.StaticFeature { caps.snippetTextEdit = true; caps.codeActionGroup = true; caps.resolveCodeAction = true; + caps.hoverActions = true; capabilities.experimental = caps; } initialize(_capabilities: lc.ServerCapabilities, _documentSelector: lc.DocumentSelector | undefined): void { diff --git a/editors/code/src/config.ts b/editors/code/src/config.ts index e8abf8284eb7..d8f0037d4c95 100644 --- a/editors/code/src/config.ts +++ b/editors/code/src/config.ts @@ -16,10 +16,8 @@ export class Config { "files", "highlighting", "updates.channel", - "lens.enable", - "lens.run", - "lens.debug", - "lens.implementations", + "lens", // works as lens.* + "hoverActions", // works as hoverActions.* ] .map(opt => `${this.rootSection}.${opt}`); @@ -132,4 +130,11 @@ export class Config { implementations: this.get("lens.implementations"), }; } + + get hoverActions() { + return { + enable: this.get("hoverActions.enable"), + implementations: this.get("hoverActions.implementations"), + }; + } } diff --git a/editors/code/src/lsp_ext.ts b/editors/code/src/lsp_ext.ts index 9793b926c26e..e16ea799ce01 100644 --- a/editors/code/src/lsp_ext.ts +++ b/editors/code/src/lsp_ext.ts @@ -90,3 +90,15 @@ export interface SsrParams { parseOnly: boolean; } export const ssr = new lc.RequestType('experimental/ssr'); + +export interface CommandLink extends lc.Command { + /** + * A tooltip for the command, when represented in the UI. + */ + tooltip?: string; +} + +export interface CommandLinkGroup { + title?: string; + commands: CommandLink[]; +} From da7ec4b3398ffaf672a755bf57066e17ac42303a Mon Sep 17 00:00:00 2001 From: vsrs Date: Wed, 3 Jun 2020 14:34:11 +0300 Subject: [PATCH 075/172] Add hover actions LSP extension documentation. --- docs/dev/lsp-extensions.md | 38 ++++++++++++++++++++++++++++++++++++++ editors/code/src/client.ts | 2 +- 2 files changed, 39 insertions(+), 1 deletion(-) diff --git a/docs/dev/lsp-extensions.md b/docs/dev/lsp-extensions.md index 7f7940d0b6af..a0847dad3a8f 100644 --- a/docs/dev/lsp-extensions.md +++ b/docs/dev/lsp-extensions.md @@ -467,3 +467,41 @@ interface InlayHint { label: string, } ``` + +## Hover Actions + +**Client Capability:** `{ "hoverActions": boolean }` + +If this capability is set, `Hover` request returned from the server might contain an additional field, `actions`: + +```typescript +interface Hover { + ... + actions?: CommandLinkGroup[]; +} + +interface CommandLink extends Command { + /** + * A tooltip for the command, when represented in the UI. + */ + tooltip?: string; +} + +interface CommandLinkGroup { + title?: string; + commands: CommandLink[]; +} +``` + +Such actions on the client side are appended to a hover bottom as command links: +``` + +-----------------------------+ + | Hover content | + | | + +-----------------------------+ + | _Action1_ | _Action2_ | <- first group, no TITLE + +-----------------------------+ + | TITLE _Action1_ | _Action2_ | <- second group + +-----------------------------+ + ... +``` \ No newline at end of file diff --git a/editors/code/src/client.ts b/editors/code/src/client.ts index 9df6702839b4..f2094b5cefe4 100644 --- a/editors/code/src/client.ts +++ b/editors/code/src/client.ts @@ -66,7 +66,7 @@ export function createClient(serverPath: string, cwd: string): lc.LanguageClient // Workaround to support command links (trusted vscode.MarkdownString) in hovers // https://github.com/microsoft/vscode/issues/33577 hover.contents = hover.contents.map(toTrusted); - + const actions = (result).actions; if (actions) { hover.contents.push(renderHoverActions(actions)); From b147e6eb95b8bbc8cd9a56f9a9a629b8671bdc0e Mon Sep 17 00:00:00 2001 From: vsrs Date: Wed, 3 Jun 2020 14:44:40 +0300 Subject: [PATCH 076/172] Code formatting --- crates/ra_ide/src/hover.rs | 5 ++--- crates/ra_ide/src/lib.rs | 2 +- crates/rust-analyzer/src/config.rs | 5 +++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/crates/ra_ide/src/hover.rs b/crates/ra_ide/src/hover.rs index baa9fc8a8ff9..2b9095a82a74 100644 --- a/crates/ra_ide/src/hover.rs +++ b/crates/ra_ide/src/hover.rs @@ -14,7 +14,7 @@ use ra_syntax::{ast, match_ast, AstNode, SyntaxKind::*, SyntaxToken, TokenAtOffs use crate::{ display::{macro_label, rust_code_markup, rust_code_markup_with_doc, ShortLabel, ToNav}, - FilePosition, RangeInfo, NavigationTarget, + FilePosition, NavigationTarget, RangeInfo, }; #[derive(Clone, Debug, PartialEq, Eq)] @@ -308,7 +308,7 @@ mod tests { fn assert_impl_action(action: &HoverAction, position: u32) { let offset = match action { - HoverAction::Implementaion(pos) => pos.offset + HoverAction::Implementaion(pos) => pos.offset, }; assert_eq!(offset, position.into()); } @@ -1161,5 +1161,4 @@ fn func(foo: i32) { if true { <|>foo; }; } ); assert_impl_action(&actions[0], 6); } - } diff --git a/crates/ra_ide/src/lib.rs b/crates/ra_ide/src/lib.rs index a9601400f188..a56718d3f595 100644 --- a/crates/ra_ide/src/lib.rs +++ b/crates/ra_ide/src/lib.rs @@ -66,7 +66,7 @@ pub use crate::{ display::{file_structure, FunctionSignature, NavigationTarget, StructureNode}, expand_macro::ExpandedMacro, folding_ranges::{Fold, FoldKind}, - hover::{HoverResult, HoverAction, HoverConfig}, + hover::{HoverAction, HoverConfig, HoverResult}, inlay_hints::{InlayHint, InlayHintsConfig, InlayKind}, references::{Declaration, Reference, ReferenceAccess, ReferenceKind, ReferenceSearchResult}, runnables::{Runnable, RunnableKind, TestId}, diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs index e7c8595772ce..14c4fe9adfa5 100644 --- a/crates/rust-analyzer/src/config.rs +++ b/crates/rust-analyzer/src/config.rs @@ -11,7 +11,7 @@ use std::{ffi::OsString, path::PathBuf}; use lsp_types::ClientCapabilities; use ra_flycheck::FlycheckConfig; -use ra_ide::{AssistConfig, CompletionConfig, InlayHintsConfig, HoverConfig}; +use ra_ide::{AssistConfig, CompletionConfig, HoverConfig, InlayHintsConfig}; use ra_project_model::{CargoConfig, JsonProject, ProjectManifest}; use serde::Deserialize; @@ -342,7 +342,8 @@ impl Config { self.assist.allow_snippets(false); if let Some(experimental) = &caps.experimental { - let get_bool = |index: &str| experimental.get(index).and_then(|it| it.as_bool()) == Some(true); + let get_bool = + |index: &str| experimental.get(index).and_then(|it| it.as_bool()) == Some(true); let snippet_text_edit = get_bool("snippetTextEdit"); self.assist.allow_snippets(snippet_text_edit); From 5d0c1aa1625a17723209e537590dc7fc7f181df1 Mon Sep 17 00:00:00 2001 From: vsrs Date: Wed, 3 Jun 2020 15:13:26 +0300 Subject: [PATCH 077/172] Rebase on the latest master. --- crates/ra_ide/src/hover.rs | 6 +++--- .../rust-analyzer/src/main_loop/handlers.rs | 20 +++++++++---------- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/crates/ra_ide/src/hover.rs b/crates/ra_ide/src/hover.rs index 2b9095a82a74..2fbe0ba1f127 100644 --- a/crates/ra_ide/src/hover.rs +++ b/crates/ra_ide/src/hover.rs @@ -1127,7 +1127,7 @@ fn func(foo: i32) { if true { <|>foo; }; } } #[test] - fn test_hover_trait_hash_impl_action() { + fn test_hover_trait_has_impl_action() { let (_, actions) = check_hover_result( " //- /lib.rs @@ -1139,7 +1139,7 @@ fn func(foo: i32) { if true { <|>foo; }; } } #[test] - fn test_hover_struct_hash_impl_action() { + fn test_hover_struct_has_impl_action() { let (_, actions) = check_hover_result( " //- /lib.rs @@ -1151,7 +1151,7 @@ fn func(foo: i32) { if true { <|>foo; }; } } #[test] - fn test_hover_union_hash_impl_action() { + fn test_hover_union_has_impl_action() { let (_, actions) = check_hover_result( " //- /lib.rs diff --git a/crates/rust-analyzer/src/main_loop/handlers.rs b/crates/rust-analyzer/src/main_loop/handlers.rs index 0958a231fd6a..d998d9dddff7 100644 --- a/crates/rust-analyzer/src/main_loop/handlers.rs +++ b/crates/rust-analyzer/src/main_loop/handlers.rs @@ -553,7 +553,7 @@ pub fn handle_hover( value: crate::markdown::format_docs(&info.info.to_markup()), }), range: Some(range), - actions: Some(prepare_hover_actions(&world, info.info.actions())), + actions: Some(prepare_hover_actions(&snap, info.info.actions())), }; Ok(Some(res)) } @@ -1176,18 +1176,18 @@ fn to_command_link(command: Command, tooltip: String) -> lsp_ext::CommandLink { } fn show_impl_command_link( - world: &WorldSnapshot, + snap: &GlobalStateSnapshot, position: &FilePosition, ) -> Option { - if world.config.hover.implementations { - if let Some(nav_data) = world.analysis().goto_implementation(*position).unwrap_or(None) { - let uri = to_proto::url(world, position.file_id).ok()?; - let line_index = world.analysis().file_line_index(position.file_id).ok()?; + if snap.config.hover.implementations { + if let Some(nav_data) = snap.analysis().goto_implementation(*position).unwrap_or(None) { + let uri = to_proto::url(snap, position.file_id).ok()?; + let line_index = snap.analysis().file_line_index(position.file_id).ok()?; let position = to_proto::position(&line_index, position.offset); let locations: Vec<_> = nav_data .info .iter() - .filter_map(|it| to_proto::location(world, it.file_range()).ok()) + .filter_map(|it| to_proto::location(snap, it.file_range()).ok()) .collect(); let title = implementation_title(locations.len()); let command = show_references_command(title, &uri, position, locations); @@ -1202,17 +1202,17 @@ fn show_impl_command_link( } fn prepare_hover_actions( - world: &WorldSnapshot, + snap: &GlobalStateSnapshot, actions: &[HoverAction], ) -> Vec { - if world.config.hover.none() || !world.config.client_caps.hover_actions { + if snap.config.hover.none() || !snap.config.client_caps.hover_actions { return Vec::new(); } actions .iter() .filter_map(|it| match it { - HoverAction::Implementaion(position) => show_impl_command_link(world, position), + HoverAction::Implementaion(position) => show_impl_command_link(snap, position), }) .collect() } From 92cfc0f2a1edd4e825d4dea96d4f96dcea513629 Mon Sep 17 00:00:00 2001 From: vsrs Date: Wed, 3 Jun 2020 15:29:03 +0300 Subject: [PATCH 078/172] Add enum hover action test. --- crates/ra_ide/src/hover.rs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/crates/ra_ide/src/hover.rs b/crates/ra_ide/src/hover.rs index 2fbe0ba1f127..62df074594b5 100644 --- a/crates/ra_ide/src/hover.rs +++ b/crates/ra_ide/src/hover.rs @@ -1161,4 +1161,19 @@ fn func(foo: i32) { if true { <|>foo; }; } ); assert_impl_action(&actions[0], 6); } + + #[test] + fn test_hover_enum_has_impl_action() { + let (_, actions) = check_hover_result( + " + //- /lib.rs + enum foo<|>() { + A, + B + } + ", + &["enum foo"], + ); + assert_impl_action(&actions[0], 5); + } } From e35418ceb9b07bd596cc09144f4f4df13432a712 Mon Sep 17 00:00:00 2001 From: vsrs Date: Wed, 3 Jun 2020 16:39:32 +0300 Subject: [PATCH 079/172] Apply suggestions from @kjeremy review --- crates/rust-analyzer/src/lsp_ext.rs | 16 +++++++------- .../rust-analyzer/src/main_loop/handlers.rs | 21 ++++++++++--------- 2 files changed, 18 insertions(+), 19 deletions(-) diff --git a/crates/rust-analyzer/src/lsp_ext.rs b/crates/rust-analyzer/src/lsp_ext.rs index 145a389ce176..75ea48892c8b 100644 --- a/crates/rust-analyzer/src/lsp_ext.rs +++ b/crates/rust-analyzer/src/lsp_ext.rs @@ -271,26 +271,24 @@ impl Request for HoverRequest { #[derive(Debug, PartialEq, Clone, Deserialize, Serialize)] pub struct Hover { - pub contents: lsp_types::HoverContents, - #[serde(skip_serializing_if = "Option::is_none")] - pub range: Option, + #[serde(flatten)] + pub hover: lsp_types::Hover, #[serde(skip_serializing_if = "Option::is_none")] pub actions: Option>, } -#[derive(Debug, PartialEq, Eq, Clone, Default, Deserialize, Serialize)] +#[derive(Debug, PartialEq, Clone, Default, Deserialize, Serialize)] pub struct CommandLinkGroup { + #[serde(skip_serializing_if = "Option::is_none")] pub title: Option, pub commands: Vec, } // LSP v3.15 Command does not have a `tooltip` field, vscode supports one. -#[derive(Debug, PartialEq, Eq, Clone, Default, Deserialize, Serialize)] +#[derive(Debug, PartialEq, Clone, Default, Deserialize, Serialize)] pub struct CommandLink { - pub title: String, - pub command: String, + #[serde(flatten)] + pub command: lsp_types::Command, #[serde(skip_serializing_if = "Option::is_none")] pub tooltip: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub arguments: Option>, } diff --git a/crates/rust-analyzer/src/main_loop/handlers.rs b/crates/rust-analyzer/src/main_loop/handlers.rs index d998d9dddff7..894df5837c3d 100644 --- a/crates/rust-analyzer/src/main_loop/handlers.rs +++ b/crates/rust-analyzer/src/main_loop/handlers.rs @@ -547,15 +547,18 @@ pub fn handle_hover( }; let line_index = snap.analysis.file_line_index(position.file_id)?; let range = to_proto::range(&line_index, info.range); - let res = lsp_ext::Hover { - contents: HoverContents::Markup(MarkupContent { - kind: MarkupKind::Markdown, - value: crate::markdown::format_docs(&info.info.to_markup()), - }), - range: Some(range), + let hover = lsp_ext::Hover { + hover: lsp_types::Hover { + contents: HoverContents::Markup(MarkupContent { + kind: MarkupKind::Markdown, + value: crate::markdown::format_docs(&info.info.to_markup()), + }), + range: Some(range), + }, actions: Some(prepare_hover_actions(&snap, info.info.actions())), }; - Ok(Some(res)) + + Ok(Some(hover)) } pub fn handle_prepare_rename( @@ -1169,9 +1172,7 @@ fn show_references_command( fn to_command_link(command: Command, tooltip: String) -> lsp_ext::CommandLink { lsp_ext::CommandLink { tooltip: Some(tooltip), - title: command.title, - command: command.command, - arguments: command.arguments, + command, } } From 0fe43a124bb2b135cfd1268fda2941c3ac170c96 Mon Sep 17 00:00:00 2001 From: vsrs Date: Wed, 3 Jun 2020 17:35:26 +0300 Subject: [PATCH 080/172] Add capabilities tests. --- crates/rust-analyzer/src/lsp_ext.rs | 4 +- .../rust-analyzer/src/main_loop/handlers.rs | 7 +- .../rust-analyzer/tests/heavy_tests/main.rs | 180 ++++++++++++++++++ 3 files changed, 184 insertions(+), 7 deletions(-) diff --git a/crates/rust-analyzer/src/lsp_ext.rs b/crates/rust-analyzer/src/lsp_ext.rs index 75ea48892c8b..1371f6cb4a41 100644 --- a/crates/rust-analyzer/src/lsp_ext.rs +++ b/crates/rust-analyzer/src/lsp_ext.rs @@ -273,8 +273,8 @@ impl Request for HoverRequest { pub struct Hover { #[serde(flatten)] pub hover: lsp_types::Hover, - #[serde(skip_serializing_if = "Option::is_none")] - pub actions: Option>, + #[serde(skip_serializing_if = "Vec::is_empty")] + pub actions: Vec, } #[derive(Debug, PartialEq, Clone, Default, Deserialize, Serialize)] diff --git a/crates/rust-analyzer/src/main_loop/handlers.rs b/crates/rust-analyzer/src/main_loop/handlers.rs index 894df5837c3d..3ff7797024cf 100644 --- a/crates/rust-analyzer/src/main_loop/handlers.rs +++ b/crates/rust-analyzer/src/main_loop/handlers.rs @@ -555,7 +555,7 @@ pub fn handle_hover( }), range: Some(range), }, - actions: Some(prepare_hover_actions(&snap, info.info.actions())), + actions: prepare_hover_actions(&snap, info.info.actions()), }; Ok(Some(hover)) @@ -1170,10 +1170,7 @@ fn show_references_command( } fn to_command_link(command: Command, tooltip: String) -> lsp_ext::CommandLink { - lsp_ext::CommandLink { - tooltip: Some(tooltip), - command, - } + lsp_ext::CommandLink { tooltip: Some(tooltip), command } } fn show_impl_command_link( diff --git a/crates/rust-analyzer/tests/heavy_tests/main.rs b/crates/rust-analyzer/tests/heavy_tests/main.rs index ad347631054b..78c6195d7710 100644 --- a/crates/rust-analyzer/tests/heavy_tests/main.rs +++ b/crates/rust-analyzer/tests/heavy_tests/main.rs @@ -715,3 +715,183 @@ pub fn foo(_input: TokenStream) -> TokenStream { let value = res.get("contents").unwrap().get("value").unwrap().to_string(); assert_eq!(value, r#""```rust\nfoo::Bar\n```\n\n```rust\nfn bar()\n```""#) } + +#[test] +fn test_client_support_hover_actions() { + if skip_slow_tests() { + return; + } + + let server = Project::with_fixture( + r#" +//- Cargo.toml +[package] +name = "foo" +version = "0.0.0" + +//- src/lib.rs +struct Foo(u32); + +struct NoImpl(u32); + +impl Foo { + fn new() -> Self { + Self(1) + } +} +"#, + ) + .with_config(|config| { + config.client_caps.hover_actions = true; + }) + .server(); + + server.wait_until_workspace_is_loaded(); + + // has 1 implementation + server.request::( + HoverParams { + text_document_position_params: TextDocumentPositionParams::new( + server.doc_id("src/lib.rs"), + Position::new(0, 9), + ), + work_done_progress_params: Default::default(), + }, + json!({ + "actions": [{ + "commands": [{ + "arguments": [ + "file:///[..]src/lib.rs", + { + "character": 7, + "line": 0 + }, + [{ + "range": { "end": { "character": 1, "line": 8 }, "start": { "character": 0, "line": 4 } }, + "uri": "file:///[..]src/lib.rs" + }] + ], + "command": "rust-analyzer.showReferences", + "title": "1 implementation", + "tooltip": "Go to implementations" + }] + }], + "contents": { "kind": "markdown", "value": "```rust\nfoo\n```\n\n```rust\nstruct Foo\n```" }, + "range": { "end": { "character": 10, "line": 0 }, "start": { "character": 7, "line": 0 } } + }) + ); + + // no hover + server.request::( + HoverParams { + text_document_position_params: TextDocumentPositionParams::new( + server.doc_id("src/lib.rs"), + Position::new(1, 0), + ), + work_done_progress_params: Default::default(), + }, + json!(null), + ); + + // no implementations + server.request::( + HoverParams { + text_document_position_params: TextDocumentPositionParams::new( + server.doc_id("src/lib.rs"), + Position::new(2, 12), + ), + work_done_progress_params: Default::default(), + }, + json!({ + "actions": [{ + "commands": [{ + "arguments": [ + "file:///[..]src/lib.rs", + { "character": 7, "line": 2 }, + [] + ], + "command": "rust-analyzer.showReferences", + "title": "0 implementations", + "tooltip": "Go to implementations" + }] + }], + "contents": { "kind": "markdown", "value": "```rust\nfoo\n```\n\n```rust\nstruct NoImpl\n```" }, + "range": { "end": { "character": 13, "line": 2 }, "start": { "character": 7, "line": 2 } } + }) + ); +} + +#[test] +fn test_client_does_not_support_hover_actions() { + if skip_slow_tests() { + return; + } + + let server = Project::with_fixture( + r#" +//- Cargo.toml +[package] +name = "foo" +version = "0.0.0" + +//- src/lib.rs +struct Foo(u32); + +struct NoImpl(u32); + +impl Foo { + fn new() -> Self { + Self(1) + } +} +"#, + ) + .with_config(|config| { + config.client_caps.hover_actions = false; + }) + .server(); + + server.wait_until_workspace_is_loaded(); + + // has 1 implementation + server.request::( + HoverParams { + text_document_position_params: TextDocumentPositionParams::new( + server.doc_id("src/lib.rs"), + Position::new(0, 9), + ), + work_done_progress_params: Default::default(), + }, + json!({ + "contents": { "kind": "markdown", "value": "```rust\nfoo\n```\n\n```rust\nstruct Foo\n```" }, + "range": { "end": { "character": 10, "line": 0 }, "start": { "character": 7, "line": 0 } } + }) + ); + + // no hover + server.request::( + HoverParams { + text_document_position_params: TextDocumentPositionParams::new( + server.doc_id("src/lib.rs"), + Position::new(1, 0), + ), + work_done_progress_params: Default::default(), + }, + json!(null), + ); + + // no implementations + server.request::( + HoverParams { + text_document_position_params: TextDocumentPositionParams::new( + server.doc_id("src/lib.rs"), + Position::new(2, 12), + ), + work_done_progress_params: Default::default(), + }, + json!({ + "contents": { "kind": "markdown", "value": "```rust\nfoo\n```\n\n```rust\nstruct NoImpl\n```" }, + "range": { "end": { "character": 13, "line": 2 }, "start": { "character": 7, "line": 2 } } + }) + ); +} From bc2d1729957a25bf5ee8e2213d07460e22c76def Mon Sep 17 00:00:00 2001 From: Jonas Schievink Date: Fri, 5 Jun 2020 14:24:51 +0200 Subject: [PATCH 081/172] Clarify when we visit modules multiple times --- crates/ra_hir_def/src/import_map.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/crates/ra_hir_def/src/import_map.rs b/crates/ra_hir_def/src/import_map.rs index 70749f3800ca..4284a0a9120a 100644 --- a/crates/ra_hir_def/src/import_map.rs +++ b/crates/ra_hir_def/src/import_map.rs @@ -78,7 +78,9 @@ impl ImportMap { } } - // If we've just added a path to a module, descend into it. + // If we've just added a path to a module, descend into it. We might traverse + // modules multiple times, but only if the new path to it is shorter than the + // first (else we `continue` above). if let Some(ModuleDefId::ModuleId(mod_id)) = item.as_module_def_id() { worklist.push((mod_id, mk_path())); } From 78c9223b7bd4bffe64b7ec69e5fee08604dc0057 Mon Sep 17 00:00:00 2001 From: vsrs Date: Fri, 5 Jun 2020 15:25:01 +0300 Subject: [PATCH 082/172] Remove hover contents marking as trusted. Hover contents might be extracted from raw doc comments and need some validation. --- crates/rust-analyzer/src/config.rs | 2 +- editors/code/src/client.ts | 17 ++--------------- 2 files changed, 3 insertions(+), 16 deletions(-) diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs index 14c4fe9adfa5..8d6efdbe8cf9 100644 --- a/crates/rust-analyzer/src/config.rs +++ b/crates/rust-analyzer/src/config.rs @@ -349,7 +349,7 @@ impl Config { self.assist.allow_snippets(snippet_text_edit); self.client_caps.code_action_group = get_bool("codeActionGroup"); - self.client_caps.resolve_code_action = get_bool("resolveCodeAction"); + self.client_caps.resolve_code_action = get_bool("resolveCodeAction"); self.client_caps.hover_actions = get_bool("hoverActions"); } } diff --git a/editors/code/src/client.ts b/editors/code/src/client.ts index f2094b5cefe4..65ad573d8c91 100644 --- a/editors/code/src/client.ts +++ b/editors/code/src/client.ts @@ -7,20 +7,11 @@ import { CallHierarchyFeature } from 'vscode-languageclient/lib/callHierarchy.pr import { SemanticTokensFeature, DocumentSemanticsTokensSignature } from 'vscode-languageclient/lib/semanticTokens.proposed'; import { assert } from './util'; -function toTrusted(obj: vscode.MarkedString): vscode.MarkedString { - const md = obj; - if (md && md.value.includes("```rust")) { - md.isTrusted = true; - return md; - } - return obj; -} - -function renderCommand(cmd: CommandLink) { +function renderCommand(cmd: ra.CommandLink) { return `[${cmd.title}](command:${cmd.command}?${encodeURIComponent(JSON.stringify(cmd.arguments))} '${cmd.tooltip!}')`; } -function renderHoverActions(actions: CommandLinkGroup[]): vscode.MarkdownString { +function renderHoverActions(actions: ra.CommandLinkGroup[]): vscode.MarkdownString { const text = actions.map(group => (group.title ? (group.title + " ") : "") + group.commands.map(renderCommand).join(' | ') ).join('___'); @@ -63,10 +54,6 @@ export function createClient(serverPath: string, cwd: string): lc.LanguageClient (result) => { const hover = client.protocol2CodeConverter.asHover(result); if (hover) { - // Workaround to support command links (trusted vscode.MarkdownString) in hovers - // https://github.com/microsoft/vscode/issues/33577 - hover.contents = hover.contents.map(toTrusted); - const actions = (result).actions; if (actions) { hover.contents.push(renderHoverActions(actions)); From bd9d7b6ad885f775df91ff3dfebd8927c8e272b2 Mon Sep 17 00:00:00 2001 From: vsrs Date: Fri, 5 Jun 2020 15:26:46 +0300 Subject: [PATCH 083/172] Remove hover actions heavy tests. --- .../rust-analyzer/tests/heavy_tests/main.rs | 180 ------------------ 1 file changed, 180 deletions(-) diff --git a/crates/rust-analyzer/tests/heavy_tests/main.rs b/crates/rust-analyzer/tests/heavy_tests/main.rs index 78c6195d7710..ad347631054b 100644 --- a/crates/rust-analyzer/tests/heavy_tests/main.rs +++ b/crates/rust-analyzer/tests/heavy_tests/main.rs @@ -715,183 +715,3 @@ pub fn foo(_input: TokenStream) -> TokenStream { let value = res.get("contents").unwrap().get("value").unwrap().to_string(); assert_eq!(value, r#""```rust\nfoo::Bar\n```\n\n```rust\nfn bar()\n```""#) } - -#[test] -fn test_client_support_hover_actions() { - if skip_slow_tests() { - return; - } - - let server = Project::with_fixture( - r#" -//- Cargo.toml -[package] -name = "foo" -version = "0.0.0" - -//- src/lib.rs -struct Foo(u32); - -struct NoImpl(u32); - -impl Foo { - fn new() -> Self { - Self(1) - } -} -"#, - ) - .with_config(|config| { - config.client_caps.hover_actions = true; - }) - .server(); - - server.wait_until_workspace_is_loaded(); - - // has 1 implementation - server.request::( - HoverParams { - text_document_position_params: TextDocumentPositionParams::new( - server.doc_id("src/lib.rs"), - Position::new(0, 9), - ), - work_done_progress_params: Default::default(), - }, - json!({ - "actions": [{ - "commands": [{ - "arguments": [ - "file:///[..]src/lib.rs", - { - "character": 7, - "line": 0 - }, - [{ - "range": { "end": { "character": 1, "line": 8 }, "start": { "character": 0, "line": 4 } }, - "uri": "file:///[..]src/lib.rs" - }] - ], - "command": "rust-analyzer.showReferences", - "title": "1 implementation", - "tooltip": "Go to implementations" - }] - }], - "contents": { "kind": "markdown", "value": "```rust\nfoo\n```\n\n```rust\nstruct Foo\n```" }, - "range": { "end": { "character": 10, "line": 0 }, "start": { "character": 7, "line": 0 } } - }) - ); - - // no hover - server.request::( - HoverParams { - text_document_position_params: TextDocumentPositionParams::new( - server.doc_id("src/lib.rs"), - Position::new(1, 0), - ), - work_done_progress_params: Default::default(), - }, - json!(null), - ); - - // no implementations - server.request::( - HoverParams { - text_document_position_params: TextDocumentPositionParams::new( - server.doc_id("src/lib.rs"), - Position::new(2, 12), - ), - work_done_progress_params: Default::default(), - }, - json!({ - "actions": [{ - "commands": [{ - "arguments": [ - "file:///[..]src/lib.rs", - { "character": 7, "line": 2 }, - [] - ], - "command": "rust-analyzer.showReferences", - "title": "0 implementations", - "tooltip": "Go to implementations" - }] - }], - "contents": { "kind": "markdown", "value": "```rust\nfoo\n```\n\n```rust\nstruct NoImpl\n```" }, - "range": { "end": { "character": 13, "line": 2 }, "start": { "character": 7, "line": 2 } } - }) - ); -} - -#[test] -fn test_client_does_not_support_hover_actions() { - if skip_slow_tests() { - return; - } - - let server = Project::with_fixture( - r#" -//- Cargo.toml -[package] -name = "foo" -version = "0.0.0" - -//- src/lib.rs -struct Foo(u32); - -struct NoImpl(u32); - -impl Foo { - fn new() -> Self { - Self(1) - } -} -"#, - ) - .with_config(|config| { - config.client_caps.hover_actions = false; - }) - .server(); - - server.wait_until_workspace_is_loaded(); - - // has 1 implementation - server.request::( - HoverParams { - text_document_position_params: TextDocumentPositionParams::new( - server.doc_id("src/lib.rs"), - Position::new(0, 9), - ), - work_done_progress_params: Default::default(), - }, - json!({ - "contents": { "kind": "markdown", "value": "```rust\nfoo\n```\n\n```rust\nstruct Foo\n```" }, - "range": { "end": { "character": 10, "line": 0 }, "start": { "character": 7, "line": 0 } } - }) - ); - - // no hover - server.request::( - HoverParams { - text_document_position_params: TextDocumentPositionParams::new( - server.doc_id("src/lib.rs"), - Position::new(1, 0), - ), - work_done_progress_params: Default::default(), - }, - json!(null), - ); - - // no implementations - server.request::( - HoverParams { - text_document_position_params: TextDocumentPositionParams::new( - server.doc_id("src/lib.rs"), - Position::new(2, 12), - ), - work_done_progress_params: Default::default(), - }, - json!({ - "contents": { "kind": "markdown", "value": "```rust\nfoo\n```\n\n```rust\nstruct NoImpl\n```" }, - "range": { "end": { "character": 13, "line": 2 }, "start": { "character": 7, "line": 2 } } - }) - ); -} From e63c00f100e960f7b72997026b4b2cd3cd29774b Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Fri, 5 Jun 2020 14:55:23 +0200 Subject: [PATCH 084/172] Rename resolve_relative_path -> resolve_path For things like `concant!(env!("OUT_DIR"))`, we need to support abs paths --- crates/ra_db/src/lib.rs | 9 ++------- crates/ra_hir_def/src/nameres/mod_resolution.rs | 2 +- crates/ra_hir_def/src/test_db.rs | 8 ++------ crates/ra_hir_expand/src/builtin_macro.rs | 2 +- crates/ra_hir_expand/src/test_db.rs | 8 ++------ crates/ra_hir_ty/src/test_db.rs | 8 ++------ crates/ra_ide_db/src/lib.rs | 8 ++------ 7 files changed, 12 insertions(+), 33 deletions(-) diff --git a/crates/ra_db/src/lib.rs b/crates/ra_db/src/lib.rs index fd4280de2df4..07e1b8aba33e 100644 --- a/crates/ra_db/src/lib.rs +++ b/crates/ra_db/src/lib.rs @@ -89,8 +89,7 @@ pub const DEFAULT_LRU_CAP: usize = 128; pub trait FileLoader { /// Text of the file. fn file_text(&self, file_id: FileId) -> Arc; - fn resolve_relative_path(&self, anchor: FileId, relative_path: &RelativePath) - -> Option; + fn resolve_path(&self, anchor: FileId, relative_path: &RelativePath) -> Option; fn relevant_crates(&self, file_id: FileId) -> Arc>; fn resolve_extern_path( @@ -155,11 +154,7 @@ impl FileLoader for FileLoaderDelegate<&'_ T> { fn file_text(&self, file_id: FileId) -> Arc { SourceDatabaseExt::file_text(self.0, file_id) } - fn resolve_relative_path( - &self, - anchor: FileId, - relative_path: &RelativePath, - ) -> Option { + fn resolve_path(&self, anchor: FileId, relative_path: &RelativePath) -> Option { let path = { let mut path = self.0.file_relative_path(anchor); assert!(path.pop()); diff --git a/crates/ra_hir_def/src/nameres/mod_resolution.rs b/crates/ra_hir_def/src/nameres/mod_resolution.rs index 386c5cade62d..7af922c2157e 100644 --- a/crates/ra_hir_def/src/nameres/mod_resolution.rs +++ b/crates/ra_hir_def/src/nameres/mod_resolution.rs @@ -61,7 +61,7 @@ impl ModDir { }; for candidate in candidate_files.iter() { - if let Some(file_id) = db.resolve_relative_path(file_id, candidate) { + if let Some(file_id) = db.resolve_path(file_id, candidate) { let mut root_non_dir_owner = false; let mut mod_path = RelativePathBuf::new(); if !(candidate.ends_with("mod.rs") || attr_path.is_some()) { diff --git a/crates/ra_hir_def/src/test_db.rs b/crates/ra_hir_def/src/test_db.rs index eb83dee799eb..d33b57c93ac1 100644 --- a/crates/ra_hir_def/src/test_db.rs +++ b/crates/ra_hir_def/src/test_db.rs @@ -58,12 +58,8 @@ impl FileLoader for TestDB { fn file_text(&self, file_id: FileId) -> Arc { FileLoaderDelegate(self).file_text(file_id) } - fn resolve_relative_path( - &self, - anchor: FileId, - relative_path: &RelativePath, - ) -> Option { - FileLoaderDelegate(self).resolve_relative_path(anchor, relative_path) + fn resolve_path(&self, anchor: FileId, relative_path: &RelativePath) -> Option { + FileLoaderDelegate(self).resolve_path(anchor, relative_path) } fn relevant_crates(&self, file_id: FileId) -> Arc> { FileLoaderDelegate(self).relevant_crates(file_id) diff --git a/crates/ra_hir_expand/src/builtin_macro.rs b/crates/ra_hir_expand/src/builtin_macro.rs index 3bce8f673fd1..4374d9eda331 100644 --- a/crates/ra_hir_expand/src/builtin_macro.rs +++ b/crates/ra_hir_expand/src/builtin_macro.rs @@ -297,7 +297,7 @@ fn relative_file(db: &dyn AstDatabase, call_id: MacroCallId, path: &str) -> Opti let call_site = call_id.as_file().original_file(db); // Handle trivial case - if let Some(res) = db.resolve_relative_path(call_site, &RelativePath::new(&path)) { + if let Some(res) = db.resolve_path(call_site, &RelativePath::new(&path)) { // Prevent include itself return if res == call_site { None } else { Some(res) }; } diff --git a/crates/ra_hir_expand/src/test_db.rs b/crates/ra_hir_expand/src/test_db.rs index c1fb762debf6..60321fa0a3be 100644 --- a/crates/ra_hir_expand/src/test_db.rs +++ b/crates/ra_hir_expand/src/test_db.rs @@ -41,12 +41,8 @@ impl FileLoader for TestDB { fn file_text(&self, file_id: FileId) -> Arc { FileLoaderDelegate(self).file_text(file_id) } - fn resolve_relative_path( - &self, - anchor: FileId, - relative_path: &RelativePath, - ) -> Option { - FileLoaderDelegate(self).resolve_relative_path(anchor, relative_path) + fn resolve_path(&self, anchor: FileId, relative_path: &RelativePath) -> Option { + FileLoaderDelegate(self).resolve_path(anchor, relative_path) } fn relevant_crates(&self, file_id: FileId) -> Arc> { FileLoaderDelegate(self).relevant_crates(file_id) diff --git a/crates/ra_hir_ty/src/test_db.rs b/crates/ra_hir_ty/src/test_db.rs index 8498d3d966d0..43927c99155d 100644 --- a/crates/ra_hir_ty/src/test_db.rs +++ b/crates/ra_hir_ty/src/test_db.rs @@ -72,12 +72,8 @@ impl FileLoader for TestDB { fn file_text(&self, file_id: FileId) -> Arc { FileLoaderDelegate(self).file_text(file_id) } - fn resolve_relative_path( - &self, - anchor: FileId, - relative_path: &RelativePath, - ) -> Option { - FileLoaderDelegate(self).resolve_relative_path(anchor, relative_path) + fn resolve_path(&self, anchor: FileId, relative_path: &RelativePath) -> Option { + FileLoaderDelegate(self).resolve_path(anchor, relative_path) } fn relevant_crates(&self, file_id: FileId) -> Arc> { FileLoaderDelegate(self).relevant_crates(file_id) diff --git a/crates/ra_ide_db/src/lib.rs b/crates/ra_ide_db/src/lib.rs index 1b74e6558613..72793d63d3f9 100644 --- a/crates/ra_ide_db/src/lib.rs +++ b/crates/ra_ide_db/src/lib.rs @@ -57,12 +57,8 @@ impl FileLoader for RootDatabase { fn file_text(&self, file_id: FileId) -> Arc { FileLoaderDelegate(self).file_text(file_id) } - fn resolve_relative_path( - &self, - anchor: FileId, - relative_path: &RelativePath, - ) -> Option { - FileLoaderDelegate(self).resolve_relative_path(anchor, relative_path) + fn resolve_path(&self, anchor: FileId, relative_path: &RelativePath) -> Option { + FileLoaderDelegate(self).resolve_path(anchor, relative_path) } fn relevant_crates(&self, file_id: FileId) -> Arc> { FileLoaderDelegate(self).relevant_crates(file_id) From bba374bab2ee53643a899e7ea459b4ab27663bd0 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Fri, 5 Jun 2020 15:07:30 +0200 Subject: [PATCH 085/172] More direct signature for resolve_path --- crates/ra_db/src/lib.rs | 21 ++++++++++++------- .../ra_hir_def/src/nameres/mod_resolution.rs | 2 +- crates/ra_hir_def/src/test_db.rs | 4 ++-- crates/ra_hir_expand/src/builtin_macro.rs | 4 ++-- crates/ra_hir_expand/src/test_db.rs | 4 ++-- crates/ra_hir_ty/src/test_db.rs | 4 ++-- crates/ra_ide_db/src/lib.rs | 4 ++-- 7 files changed, 24 insertions(+), 19 deletions(-) diff --git a/crates/ra_db/src/lib.rs b/crates/ra_db/src/lib.rs index 07e1b8aba33e..2e63cb46e96f 100644 --- a/crates/ra_db/src/lib.rs +++ b/crates/ra_db/src/lib.rs @@ -89,7 +89,7 @@ pub const DEFAULT_LRU_CAP: usize = 128; pub trait FileLoader { /// Text of the file. fn file_text(&self, file_id: FileId) -> Arc; - fn resolve_path(&self, anchor: FileId, relative_path: &RelativePath) -> Option; + fn resolve_path(&self, anchor: FileId, path: &str) -> Option; fn relevant_crates(&self, file_id: FileId) -> Arc>; fn resolve_extern_path( @@ -154,16 +154,21 @@ impl FileLoader for FileLoaderDelegate<&'_ T> { fn file_text(&self, file_id: FileId) -> Arc { SourceDatabaseExt::file_text(self.0, file_id) } - fn resolve_path(&self, anchor: FileId, relative_path: &RelativePath) -> Option { - let path = { - let mut path = self.0.file_relative_path(anchor); - assert!(path.pop()); - path.push(relative_path); - path.normalize() + /// Note that we intentionally accept a `&str` and not a `&Path` here. This + /// method exists to handle `#[path = "/some/path.rs"] mod foo;` and such, + /// so the input is guaranteed to be utf-8 string. We might introduce + /// `struct StrPath(str)` for clarity some day, but it's a bit messy, so we + /// get by with a `&str` for the time being. + fn resolve_path(&self, anchor: FileId, path: &str) -> Option { + let rel_path = { + let mut rel_path = self.0.file_relative_path(anchor); + assert!(rel_path.pop()); + rel_path.push(path); + rel_path.normalize() }; let source_root = self.0.file_source_root(anchor); let source_root = self.0.source_root(source_root); - source_root.file_by_relative_path(&path) + source_root.file_by_relative_path(&rel_path) } fn relevant_crates(&self, file_id: FileId) -> Arc> { diff --git a/crates/ra_hir_def/src/nameres/mod_resolution.rs b/crates/ra_hir_def/src/nameres/mod_resolution.rs index 7af922c2157e..cede4a6fc9e3 100644 --- a/crates/ra_hir_def/src/nameres/mod_resolution.rs +++ b/crates/ra_hir_def/src/nameres/mod_resolution.rs @@ -61,7 +61,7 @@ impl ModDir { }; for candidate in candidate_files.iter() { - if let Some(file_id) = db.resolve_path(file_id, candidate) { + if let Some(file_id) = db.resolve_path(file_id, candidate.as_str()) { let mut root_non_dir_owner = false; let mut mod_path = RelativePathBuf::new(); if !(candidate.ends_with("mod.rs") || attr_path.is_some()) { diff --git a/crates/ra_hir_def/src/test_db.rs b/crates/ra_hir_def/src/test_db.rs index d33b57c93ac1..e7a5182f03c0 100644 --- a/crates/ra_hir_def/src/test_db.rs +++ b/crates/ra_hir_def/src/test_db.rs @@ -58,8 +58,8 @@ impl FileLoader for TestDB { fn file_text(&self, file_id: FileId) -> Arc { FileLoaderDelegate(self).file_text(file_id) } - fn resolve_path(&self, anchor: FileId, relative_path: &RelativePath) -> Option { - FileLoaderDelegate(self).resolve_path(anchor, relative_path) + fn resolve_path(&self, anchor: FileId, path: &str) -> Option { + FileLoaderDelegate(self).resolve_path(anchor, path) } fn relevant_crates(&self, file_id: FileId) -> Arc> { FileLoaderDelegate(self).relevant_crates(file_id) diff --git a/crates/ra_hir_expand/src/builtin_macro.rs b/crates/ra_hir_expand/src/builtin_macro.rs index 4374d9eda331..eec5fb8ebf85 100644 --- a/crates/ra_hir_expand/src/builtin_macro.rs +++ b/crates/ra_hir_expand/src/builtin_macro.rs @@ -8,7 +8,7 @@ use crate::{ use crate::{quote, EagerMacroId, LazyMacroId, MacroCallId}; use either::Either; use mbe::parse_to_token_tree; -use ra_db::{FileId, RelativePath}; +use ra_db::FileId; use ra_parser::FragmentKind; macro_rules! register_builtin { @@ -297,7 +297,7 @@ fn relative_file(db: &dyn AstDatabase, call_id: MacroCallId, path: &str) -> Opti let call_site = call_id.as_file().original_file(db); // Handle trivial case - if let Some(res) = db.resolve_path(call_site, &RelativePath::new(&path)) { + if let Some(res) = db.resolve_path(call_site, path) { // Prevent include itself return if res == call_site { None } else { Some(res) }; } diff --git a/crates/ra_hir_expand/src/test_db.rs b/crates/ra_hir_expand/src/test_db.rs index 60321fa0a3be..765a2f6d18e9 100644 --- a/crates/ra_hir_expand/src/test_db.rs +++ b/crates/ra_hir_expand/src/test_db.rs @@ -41,8 +41,8 @@ impl FileLoader for TestDB { fn file_text(&self, file_id: FileId) -> Arc { FileLoaderDelegate(self).file_text(file_id) } - fn resolve_path(&self, anchor: FileId, relative_path: &RelativePath) -> Option { - FileLoaderDelegate(self).resolve_path(anchor, relative_path) + fn resolve_path(&self, anchor: FileId, path: &str) -> Option { + FileLoaderDelegate(self).resolve_path(anchor, path) } fn relevant_crates(&self, file_id: FileId) -> Arc> { FileLoaderDelegate(self).relevant_crates(file_id) diff --git a/crates/ra_hir_ty/src/test_db.rs b/crates/ra_hir_ty/src/test_db.rs index 43927c99155d..21a3bdfd154f 100644 --- a/crates/ra_hir_ty/src/test_db.rs +++ b/crates/ra_hir_ty/src/test_db.rs @@ -72,8 +72,8 @@ impl FileLoader for TestDB { fn file_text(&self, file_id: FileId) -> Arc { FileLoaderDelegate(self).file_text(file_id) } - fn resolve_path(&self, anchor: FileId, relative_path: &RelativePath) -> Option { - FileLoaderDelegate(self).resolve_path(anchor, relative_path) + fn resolve_path(&self, anchor: FileId, path: &str) -> Option { + FileLoaderDelegate(self).resolve_path(anchor, path) } fn relevant_crates(&self, file_id: FileId) -> Arc> { FileLoaderDelegate(self).relevant_crates(file_id) diff --git a/crates/ra_ide_db/src/lib.rs b/crates/ra_ide_db/src/lib.rs index 72793d63d3f9..93d5891a04b0 100644 --- a/crates/ra_ide_db/src/lib.rs +++ b/crates/ra_ide_db/src/lib.rs @@ -57,8 +57,8 @@ impl FileLoader for RootDatabase { fn file_text(&self, file_id: FileId) -> Arc { FileLoaderDelegate(self).file_text(file_id) } - fn resolve_path(&self, anchor: FileId, relative_path: &RelativePath) -> Option { - FileLoaderDelegate(self).resolve_path(anchor, relative_path) + fn resolve_path(&self, anchor: FileId, path: &str) -> Option { + FileLoaderDelegate(self).resolve_path(anchor, path) } fn relevant_crates(&self, file_id: FileId) -> Arc> { FileLoaderDelegate(self).relevant_crates(file_id) From cb9d9040f7e5cb5971deabe3b66045010576a689 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Fri, 5 Jun 2020 15:14:47 +0200 Subject: [PATCH 086/172] Cleanup test --- .../rust-analyzer/tests/heavy_tests/main.rs | 62 ++++--------------- 1 file changed, 12 insertions(+), 50 deletions(-) diff --git a/crates/rust-analyzer/tests/heavy_tests/main.rs b/crates/rust-analyzer/tests/heavy_tests/main.rs index ad347631054b..237aaa2a1fb4 100644 --- a/crates/rust-analyzer/tests/heavy_tests/main.rs +++ b/crates/rust-analyzer/tests/heavy_tests/main.rs @@ -523,8 +523,6 @@ fn main() { let vb = B; message(); } - -fn main() { message(); } "###, ) .with_config(|config| { @@ -552,34 +550,16 @@ fn main() { message(); } }, json!([{ "originSelectionRange": { - "end": { - "character": 10, - "line": 12 - }, - "start": { - "character": 8, - "line": 12 - } + "end": { "character": 10, "line": 12 }, + "start": { "character": 8, "line": 12 } }, "targetRange": { - "end": { - "character": 9, - "line": 3 - }, - "start": { - "character": 0, - "line": 2 - } + "end": { "character": 9, "line": 3 }, + "start": { "character": 0, "line": 2 } }, "targetSelectionRange": { - "end": { - "character": 8, - "line": 3 - }, - "start": { - "character": 7, - "line": 3 - } + "end": { "character": 8, "line": 3 }, + "start": { "character": 7, "line": 3 } }, "targetUri": "file:///[..]src/main.rs" }]), @@ -595,34 +575,16 @@ fn main() { message(); } }, json!([{ "originSelectionRange": { - "end": { - "character": 10, - "line": 13 - }, - "start": { - "character": 8, - "line":13 - } + "end": { "character": 10, "line": 13 }, + "start": { "character": 8, "line":13 } }, "targetRange": { - "end": { - "character": 9, - "line": 7 - }, - "start": { - "character": 0, - "line":6 - } + "end": { "character": 9, "line": 7 }, + "start": { "character": 0, "line":6 } }, "targetSelectionRange": { - "end": { - "character": 8, - "line": 7 - }, - "start": { - "character": 7, - "line": 7 - } + "end": { "character": 8, "line": 7 }, + "start": { "character": 7, "line": 7 } }, "targetUri": "file:///[..]src/main.rs" }]), From d103d4b071833adbcf691f58125aa8aa104fba1f Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Fri, 5 Jun 2020 15:33:55 +0200 Subject: [PATCH 087/172] Update inotify https://github.com/rust-analyzer/rust-analyzer/issues/4224#issuecomment-639328191 --- Cargo.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 5f88ad0c4db5..15c6a4c11b35 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -471,9 +471,9 @@ dependencies = [ [[package]] name = "inotify" -version = "0.7.0" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24e40d6fd5d64e2082e0c796495c8ef5ad667a96d03e5aaa0becfd9d47bcbfb8" +checksum = "4816c66d2c8ae673df83366c18341538f234a26d65a9ecea5c348b453ac1d02f" dependencies = [ "bitflags", "inotify-sys", From c9a41bd92d384ae7bd1e956d215b7e6c17c1b099 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Fri, 5 Jun 2020 15:49:12 +0200 Subject: [PATCH 088/172] Actually test include!(concant!(env!())); It triggered index-based goto definition before :-( --- .../rust-analyzer/tests/heavy_tests/main.rs | 45 ++++++++++--------- 1 file changed, 23 insertions(+), 22 deletions(-) diff --git a/crates/rust-analyzer/tests/heavy_tests/main.rs b/crates/rust-analyzer/tests/heavy_tests/main.rs index 237aaa2a1fb4..0e2a83c6a0e7 100644 --- a/crates/rust-analyzer/tests/heavy_tests/main.rs +++ b/crates/rust-analyzer/tests/heavy_tests/main.rs @@ -4,9 +4,7 @@ use std::{collections::HashMap, path::PathBuf, time::Instant}; use lsp_types::{ notification::DidOpenTextDocument, - request::{ - CodeActionRequest, Completion, Formatting, GotoDefinition, GotoTypeDefinition, HoverRequest, - }, + request::{CodeActionRequest, Completion, Formatting, GotoTypeDefinition, HoverRequest}, CodeActionContext, CodeActionParams, CompletionParams, DidOpenTextDocumentParams, DocumentFormattingParams, FormattingOptions, GotoDefinitionParams, HoverParams, PartialResultParams, Position, Range, TextDocumentItem, TextDocumentPositionParams, @@ -507,6 +505,10 @@ fn main() { println!("cargo:rerun-if-changed=build.rs"); } //- src/main.rs +#[rustc_builtin_macro] macro_rules! include {} +#[rustc_builtin_macro] macro_rules! concat {} +#[rustc_builtin_macro] macro_rules! env {} + include!(concat!(env!("OUT_DIR"), "/hello.rs")); #[cfg(atom_cfg)] @@ -521,7 +523,7 @@ struct B; fn main() { let va = A; let vb = B; - message(); + let should_be_str = message(); } "###, ) @@ -530,36 +532,35 @@ fn main() { }) .server(); server.wait_until_workspace_is_loaded(); - let res = server.send_request::(GotoDefinitionParams { + let res = server.send_request::(HoverParams { text_document_position_params: TextDocumentPositionParams::new( server.doc_id("src/main.rs"), - Position::new(14, 8), + Position::new(18, 10), ), work_done_progress_params: Default::default(), - partial_result_params: Default::default(), }); - assert!(format!("{}", res).contains("hello.rs")); + assert!(res.to_string().contains("&str")); server.request::( GotoDefinitionParams { text_document_position_params: TextDocumentPositionParams::new( server.doc_id("src/main.rs"), - Position::new(12, 9), + Position::new(16, 9), ), work_done_progress_params: Default::default(), partial_result_params: Default::default(), }, json!([{ "originSelectionRange": { - "end": { "character": 10, "line": 12 }, - "start": { "character": 8, "line": 12 } + "end": { "character": 10, "line": 16 }, + "start": { "character": 8, "line": 16 } }, "targetRange": { - "end": { "character": 9, "line": 3 }, - "start": { "character": 0, "line": 2 } + "end": { "character": 9, "line": 7 }, + "start": { "character": 0, "line": 6 } }, "targetSelectionRange": { - "end": { "character": 8, "line": 3 }, - "start": { "character": 7, "line": 3 } + "end": { "character": 8, "line": 7 }, + "start": { "character": 7, "line": 7 } }, "targetUri": "file:///[..]src/main.rs" }]), @@ -568,23 +569,23 @@ fn main() { GotoDefinitionParams { text_document_position_params: TextDocumentPositionParams::new( server.doc_id("src/main.rs"), - Position::new(13, 9), + Position::new(17, 9), ), work_done_progress_params: Default::default(), partial_result_params: Default::default(), }, json!([{ "originSelectionRange": { - "end": { "character": 10, "line": 13 }, - "start": { "character": 8, "line":13 } + "end": { "character": 10, "line": 17 }, + "start": { "character": 8, "line": 17 } }, "targetRange": { - "end": { "character": 9, "line": 7 }, - "start": { "character": 0, "line":6 } + "end": { "character": 9, "line": 11 }, + "start": { "character": 0, "line":10 } }, "targetSelectionRange": { - "end": { "character": 8, "line": 7 }, - "start": { "character": 7, "line": 7 } + "end": { "character": 8, "line": 11 }, + "start": { "character": 7, "line": 11 } }, "targetUri": "file:///[..]src/main.rs" }]), From 69854f7795e2a5f961f4e35c13a655f0d41cc306 Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Fri, 5 Jun 2020 17:06:07 +0200 Subject: [PATCH 089/172] Upgrade Chalk to published version --- Cargo.lock | 20 ++++++++++++-------- crates/ra_hir_ty/Cargo.toml | 4 ++-- crates/ra_hir_ty/src/traits/chalk/mapping.rs | 5 +++++ 3 files changed, 19 insertions(+), 10 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 15c6a4c11b35..2e86b3fee195 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -113,8 +113,9 @@ checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" [[package]] name = "chalk-derive" -version = "0.10.1-dev" -source = "git+https://github.com/rust-lang/chalk.git?rev=329b7f3fdd2431ed6f6778cde53f22374c7d094c#329b7f3fdd2431ed6f6778cde53f22374c7d094c" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b9bd01eab87277d973183a1d2e56bace1c11f8242c52c20636fb7dddf343ac9" dependencies = [ "proc-macro2", "quote", @@ -124,8 +125,9 @@ dependencies = [ [[package]] name = "chalk-engine" -version = "0.10.1-dev" -source = "git+https://github.com/rust-lang/chalk.git?rev=329b7f3fdd2431ed6f6778cde53f22374c7d094c#329b7f3fdd2431ed6f6778cde53f22374c7d094c" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c7a637c3d17ed555aef16e16952a5d1e127bd55178cc30be22afeb92da90c7d" dependencies = [ "chalk-derive", "chalk-ir", @@ -134,8 +136,9 @@ dependencies = [ [[package]] name = "chalk-ir" -version = "0.10.1-dev" -source = "git+https://github.com/rust-lang/chalk.git?rev=329b7f3fdd2431ed6f6778cde53f22374c7d094c#329b7f3fdd2431ed6f6778cde53f22374c7d094c" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "595e5735ded16c3f3dc348f7b15bbb2521a0080b1863cac38ad5271589944670" dependencies = [ "chalk-derive", "lazy_static", @@ -143,8 +146,9 @@ dependencies = [ [[package]] name = "chalk-solve" -version = "0.10.1-dev" -source = "git+https://github.com/rust-lang/chalk.git?rev=329b7f3fdd2431ed6f6778cde53f22374c7d094c#329b7f3fdd2431ed6f6778cde53f22374c7d094c" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d9d938139db425867a30cc0cfec0269406d8238d0571d829041eaa7a8455d11" dependencies = [ "chalk-derive", "chalk-engine", diff --git a/crates/ra_hir_ty/Cargo.toml b/crates/ra_hir_ty/Cargo.toml index 4b8dcdc07ff5..112fcd07e7c6 100644 --- a/crates/ra_hir_ty/Cargo.toml +++ b/crates/ra_hir_ty/Cargo.toml @@ -27,8 +27,8 @@ test_utils = { path = "../test_utils" } scoped-tls = "1" -chalk-solve = { git = "https://github.com/rust-lang/chalk.git", rev = "329b7f3fdd2431ed6f6778cde53f22374c7d094c" } -chalk-ir = { git = "https://github.com/rust-lang/chalk.git", rev = "329b7f3fdd2431ed6f6778cde53f22374c7d094c" } +chalk-solve = "0.11" +chalk-ir = "0.11" [dev-dependencies] insta = "0.16.0" diff --git a/crates/ra_hir_ty/src/traits/chalk/mapping.rs b/crates/ra_hir_ty/src/traits/chalk/mapping.rs index 5f6daf842be6..9150f65d4e6e 100644 --- a/crates/ra_hir_ty/src/traits/chalk/mapping.rs +++ b/crates/ra_hir_ty/src/traits/chalk/mapping.rs @@ -447,6 +447,11 @@ impl ToChalk for GenericPredicate { let ty = from_chalk(db, projection_eq.ty); GenericPredicate::Projection(ProjectionPredicate { projection_ty, ty }) } + + chalk_ir::WhereClause::LifetimeOutlives(_) => { + // we shouldn't get these from Chalk + panic!("encountered LifetimeOutlives from Chalk") + } } } } From 02962b374ecefd6f2a75956f4fb18806531d1d51 Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Wed, 4 Mar 2020 23:00:44 +0100 Subject: [PATCH 090/172] Implement return position impl trait / opaque type support This is working, but I'm not that happy with how the lowering works. We might need an additional representation between `TypeRef` and `Ty` where names are resolved and `impl Trait` bounds are separated out, but things like inference variables don't exist and `impl Trait` is always represented the same way. Also note that this doesn't implement correct handling of RPIT *inside* the function (which involves turning the `impl Trait`s into variables and creating obligations for them). That intermediate representation might help there as well. --- crates/ra_hir_ty/src/db.rs | 20 ++- crates/ra_hir_ty/src/display.rs | 37 +++- crates/ra_hir_ty/src/infer.rs | 23 +-- crates/ra_hir_ty/src/lib.rs | 98 ++++++++++- crates/ra_hir_ty/src/lower.rs | 159 ++++++++++++++---- crates/ra_hir_ty/src/tests/traits.rs | 47 +++++- crates/ra_hir_ty/src/traits/chalk.rs | 52 ++++-- crates/ra_hir_ty/src/traits/chalk/interner.rs | 2 + crates/ra_hir_ty/src/traits/chalk/mapping.rs | 45 ++++- crates/ra_hir_ty/src/traits/chalk/tls.rs | 5 + 10 files changed, 403 insertions(+), 85 deletions(-) diff --git a/crates/ra_hir_ty/src/db.rs b/crates/ra_hir_ty/src/db.rs index 0a8bb24ac239..bf71d38d6b23 100644 --- a/crates/ra_hir_ty/src/db.rs +++ b/crates/ra_hir_ty/src/db.rs @@ -3,8 +3,8 @@ use std::sync::Arc; use hir_def::{ - db::DefDatabase, DefWithBodyId, GenericDefId, ImplId, LocalFieldId, TraitId, TypeParamId, - VariantId, + db::DefDatabase, DefWithBodyId, FunctionId, GenericDefId, ImplId, LocalFieldId, TraitId, + TypeParamId, VariantId, }; use ra_arena::map::ArenaMap; use ra_db::{impl_intern_key, salsa, CrateId, Upcast}; @@ -13,8 +13,8 @@ use ra_prof::profile; use crate::{ method_resolution::{CrateImplDefs, TyFingerprint}, traits::{chalk, AssocTyValue, Impl}, - Binders, CallableDef, GenericPredicate, InferenceResult, PolyFnSig, Substs, TraitRef, Ty, - TyDefId, TypeCtor, ValueTyDefId, + Binders, CallableDef, GenericPredicate, InferenceResult, OpaqueTyId, PolyFnSig, + ReturnTypeImplTraits, Substs, TraitRef, Ty, TyDefId, TypeCtor, ValueTyDefId, }; use hir_expand::name::Name; @@ -48,6 +48,12 @@ pub trait HirDatabase: DefDatabase + Upcast { #[salsa::invoke(crate::callable_item_sig)] fn callable_item_signature(&self, def: CallableDef) -> PolyFnSig; + #[salsa::invoke(crate::lower::return_type_impl_traits)] + fn return_type_impl_traits( + &self, + def: FunctionId, + ) -> Option>>; + #[salsa::invoke(crate::lower::generic_predicates_for_param_query)] #[salsa::cycle(crate::lower::generic_predicates_for_param_recover)] fn generic_predicates_for_param( @@ -80,6 +86,8 @@ pub trait HirDatabase: DefDatabase + Upcast { #[salsa::interned] fn intern_type_param_id(&self, param_id: TypeParamId) -> GlobalTypeParamId; #[salsa::interned] + fn intern_impl_trait_id(&self, id: OpaqueTyId) -> InternedOpaqueTyId; + #[salsa::interned] fn intern_chalk_impl(&self, impl_: Impl) -> crate::traits::GlobalImplId; #[salsa::interned] fn intern_assoc_ty_value(&self, assoc_ty_value: AssocTyValue) -> crate::traits::AssocTyValueId; @@ -142,3 +150,7 @@ fn hir_database_is_object_safe() { #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub struct GlobalTypeParamId(salsa::InternId); impl_intern_key!(GlobalTypeParamId); + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub struct InternedOpaqueTyId(salsa::InternId); +impl_intern_key!(InternedOpaqueTyId); diff --git a/crates/ra_hir_ty/src/display.rs b/crates/ra_hir_ty/src/display.rs index b9c4d2e89783..3e63a2415d8c 100644 --- a/crates/ra_hir_ty/src/display.rs +++ b/crates/ra_hir_ty/src/display.rs @@ -359,6 +359,21 @@ impl HirDisplay for ApplicationTy { write!(f, ">")?; } } + TypeCtor::OpaqueType(opaque_ty_id) => { + let bounds = match opaque_ty_id { + crate::OpaqueTyId::ReturnTypeImplTrait(func, idx) => { + let datas = + f.db.return_type_impl_traits(func).expect("impl trait id without data"); + let data = (*datas) + .as_ref() + .map(|rpit| rpit.impl_traits[idx as usize].bounds.clone()); + data.clone().subst(&self.parameters) + } + }; + write!(f, "impl ")?; + write_bounds_like_dyn_trait(&bounds.value, f)?; + // FIXME: it would maybe be good to distinguish this from the alias type (when debug printing), and to show the substitution + } TypeCtor::Closure { .. } => { let sig = self.parameters[0].callable_sig(f.db); if let Some(sig) = sig { @@ -427,14 +442,24 @@ impl HirDisplay for Ty { } } Ty::Bound(idx) => write!(f, "?{}.{}", idx.debruijn.depth(), idx.index)?, - Ty::Dyn(predicates) | Ty::Opaque(predicates) => { - match self { - Ty::Dyn(_) => write!(f, "dyn ")?, - Ty::Opaque(_) => write!(f, "impl ")?, - _ => unreachable!(), - }; + Ty::Dyn(predicates) => { + write!(f, "dyn ")?; write_bounds_like_dyn_trait(predicates, f)?; } + Ty::Opaque(opaque_ty) => { + let bounds = match opaque_ty.opaque_ty_id { + crate::OpaqueTyId::ReturnTypeImplTrait(func, idx) => { + let datas = + f.db.return_type_impl_traits(func).expect("impl trait id without data"); + let data = (*datas) + .as_ref() + .map(|rpit| rpit.impl_traits[idx as usize].bounds.clone()); + data.clone().subst(&opaque_ty.parameters) + } + }; + write!(f, "impl ")?; + write_bounds_like_dyn_trait(&bounds.value, f)?; + } Ty::Unknown => write!(f, "{{unknown}}")?, Ty::Infer(..) => write!(f, "_")?, } diff --git a/crates/ra_hir_ty/src/infer.rs b/crates/ra_hir_ty/src/infer.rs index dc77e88e50b2..d9bf3c2f0f22 100644 --- a/crates/ra_hir_ty/src/infer.rs +++ b/crates/ra_hir_ty/src/infer.rs @@ -39,8 +39,8 @@ use ra_syntax::SmolStr; use super::{ primitive::{FloatTy, IntTy}, traits::{Guidance, Obligation, ProjectionPredicate, Solution}, - ApplicationTy, GenericPredicate, InEnvironment, ProjectionTy, Substs, TraitEnvironment, - TraitRef, Ty, TypeCtor, TypeWalk, Uncertain, + ApplicationTy, InEnvironment, ProjectionTy, Substs, TraitEnvironment, TraitRef, Ty, TypeCtor, + TypeWalk, Uncertain, }; use crate::{ db::HirDatabase, infer::diagnostics::InferenceDiagnostic, lower::ImplTraitLoweringMode, @@ -383,25 +383,6 @@ impl<'a> InferenceContext<'a> { ) -> Ty { match assoc_ty { Some(res_assoc_ty) => { - // FIXME: - // Check if inner_ty is is `impl Trait` and contained input TypeAlias id - // this is a workaround while Chalk assoc type projection doesn't always work yet, - // but once that is fixed I don't think we should keep this - // (we'll probably change how associated types are resolved anyway) - if let Ty::Opaque(ref predicates) = inner_ty { - for p in predicates.iter() { - if let GenericPredicate::Projection(projection) = p { - if projection.projection_ty.associated_ty == res_assoc_ty { - if let ty_app!(_, params) = &projection.ty { - if params.len() == 0 { - return projection.ty.clone(); - } - } - } - } - } - } - let ty = self.table.new_type_var(); let builder = Substs::build_for_def(self.db, res_assoc_ty) .push(inner_ty) diff --git a/crates/ra_hir_ty/src/lib.rs b/crates/ra_hir_ty/src/lib.rs index 9fa8d3bdc3f4..135976fcda9e 100644 --- a/crates/ra_hir_ty/src/lib.rs +++ b/crates/ra_hir_ty/src/lib.rs @@ -147,6 +147,12 @@ pub enum TypeCtor { /// an **application type** like `(Iterator::Item)`. AssociatedType(TypeAliasId), + /// This represents a placeholder for an opaque type in situations where we + /// don't know the hidden type (i.e. currently almost always). This is + /// analogous to the `AssociatedType` type constructor. As with that one, + /// these are only produced by Chalk. + OpaqueType(OpaqueTyId), + /// The type of a specific closure. /// /// The closure signature is stored in a `FnPtr` type in the first type @@ -194,6 +200,14 @@ impl TypeCtor { let generic_params = generics(db.upcast(), type_alias.into()); generic_params.len() } + TypeCtor::OpaqueType(opaque_ty_id) => { + match opaque_ty_id { + OpaqueTyId::ReturnTypeImplTrait(func, _) => { + let generic_params = generics(db.upcast(), func.into()); + generic_params.len() + } + } + } TypeCtor::FnPtr { num_args } => num_args as usize + 1, TypeCtor::Tuple { cardinality } => cardinality as usize, } @@ -220,6 +234,11 @@ impl TypeCtor { TypeCtor::AssociatedType(type_alias) => { Some(type_alias.lookup(db.upcast()).module(db.upcast()).krate) } + TypeCtor::OpaqueType(opaque_ty_id) => match opaque_ty_id { + OpaqueTyId::ReturnTypeImplTrait(func, _) => { + Some(func.lookup(db.upcast()).module(db.upcast()).krate) + } + }, } } @@ -241,6 +260,7 @@ impl TypeCtor { TypeCtor::Adt(adt) => Some(adt.into()), TypeCtor::FnDef(callable) => Some(callable.into()), TypeCtor::AssociatedType(type_alias) => Some(type_alias.into()), + TypeCtor::OpaqueType(_impl_trait_id) => None, } } } @@ -254,6 +274,12 @@ pub struct ApplicationTy { pub parameters: Substs, } +#[derive(Clone, PartialEq, Eq, Debug, Hash)] +pub struct OpaqueTy { + pub opaque_ty_id: OpaqueTyId, + pub parameters: Substs, +} + /// A "projection" type corresponds to an (unnormalized) /// projection like `>::Foo`. Note that the /// trait and all its parameters are fully known. @@ -308,6 +334,12 @@ pub enum Ty { /// trait and all its parameters are fully known. Projection(ProjectionTy), + /// An opaque type (`impl Trait`). + /// + /// This is currently only used for return type impl trait; each instance of + /// `impl Trait` in a return type gets its own ID. + Opaque(OpaqueTy), + /// A placeholder for a type parameter; for example, `T` in `fn f(x: T) /// {}` when we're type-checking the body of that function. In this /// situation, we know this stands for *some* type, but don't know the exact @@ -332,12 +364,6 @@ pub enum Ty { /// didn't seem worth the overhead yet. Dyn(Arc<[GenericPredicate]>), - /// An opaque type (`impl Trait`). - /// - /// The predicates are quantified over the `Self` type; see `Ty::Dyn` for - /// more. - Opaque(Arc<[GenericPredicate]>), - /// A placeholder for a type which could not be computed; this is propagated /// to avoid useless error messages. Doubles as a placeholder where type /// variables are inserted before type checking, since we want to try to @@ -490,7 +516,7 @@ impl Deref for Substs { } } -#[derive(Copy, Clone, PartialEq, Eq, Debug)] +#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)] pub struct Binders { pub num_binders: usize, pub value: T, @@ -534,6 +560,20 @@ impl Binders { } } +impl TypeWalk for Binders { + fn walk(&self, f: &mut impl FnMut(&Ty)) { + self.value.walk(f); + } + + fn walk_mut_binders( + &mut self, + f: &mut impl FnMut(&mut Ty, DebruijnIndex), + binders: DebruijnIndex, + ) { + self.value.walk_mut_binders(f, binders.shifted_in()) + } +} + /// A trait with type parameters. This includes the `Self`, so this represents a concrete type implementing the trait. /// Name to be bikeshedded: TraitBound? TraitImplements? #[derive(Clone, PartialEq, Eq, Debug, Hash)] @@ -947,11 +987,16 @@ impl TypeWalk for Ty { t.walk(f); } } - Ty::Dyn(predicates) | Ty::Opaque(predicates) => { + Ty::Dyn(predicates) => { for p in predicates.iter() { p.walk(f); } } + Ty::Opaque(o_ty) => { + for t in o_ty.parameters.iter() { + t.walk(f); + } + } Ty::Placeholder { .. } | Ty::Bound(_) | Ty::Infer(_) | Ty::Unknown => {} } f(self); @@ -969,13 +1014,48 @@ impl TypeWalk for Ty { Ty::Projection(p_ty) => { p_ty.parameters.walk_mut_binders(f, binders); } - Ty::Dyn(predicates) | Ty::Opaque(predicates) => { + Ty::Dyn(predicates) => { for p in make_mut_slice(predicates) { p.walk_mut_binders(f, binders.shifted_in()); } } + Ty::Opaque(o_ty) => { + o_ty.parameters.walk_mut_binders(f, binders); + } Ty::Placeholder { .. } | Ty::Bound(_) | Ty::Infer(_) | Ty::Unknown => {} } f(self, binders); } } + +impl TypeWalk for Vec { + fn walk(&self, f: &mut impl FnMut(&Ty)) { + for t in self { + t.walk(f); + } + } + fn walk_mut_binders( + &mut self, + f: &mut impl FnMut(&mut Ty, DebruijnIndex), + binders: DebruijnIndex, + ) { + for t in self { + t.walk_mut_binders(f, binders); + } + } +} + +#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)] +pub enum OpaqueTyId { + ReturnTypeImplTrait(hir_def::FunctionId, u16), +} + +#[derive(Clone, PartialEq, Eq, Debug, Hash)] +pub struct ReturnTypeImplTraits { + pub(crate) impl_traits: Vec, +} + +#[derive(Clone, PartialEq, Eq, Debug, Hash)] +pub(crate) struct ReturnTypeImplTrait { + pub(crate) bounds: Binders>, +} diff --git a/crates/ra_hir_ty/src/lower.rs b/crates/ra_hir_ty/src/lower.rs index 35ac86a461eb..dfc018b0bc5e 100644 --- a/crates/ra_hir_ty/src/lower.rs +++ b/crates/ra_hir_ty/src/lower.rs @@ -31,8 +31,9 @@ use crate::{ all_super_trait_refs, associated_type_by_name_including_super_traits, generics, make_mut_slice, variant_data, }, - Binders, BoundVar, DebruijnIndex, FnSig, GenericPredicate, PolyFnSig, ProjectionPredicate, - ProjectionTy, Substs, TraitEnvironment, TraitRef, Ty, TypeCtor, TypeWalk, + Binders, BoundVar, DebruijnIndex, FnSig, GenericPredicate, OpaqueTy, OpaqueTyId, PolyFnSig, + ProjectionPredicate, ProjectionTy, ReturnTypeImplTrait, ReturnTypeImplTraits, Substs, + TraitEnvironment, TraitRef, Ty, TypeCtor, TypeWalk, }; use hir_expand::name::Name; @@ -47,7 +48,16 @@ pub struct TyLoweringContext<'a> { /// possible currently, so this should be fine for now. pub type_param_mode: TypeParamLoweringMode, pub impl_trait_mode: ImplTraitLoweringMode, - pub impl_trait_counter: std::cell::Cell, + impl_trait_counter: std::cell::Cell, + /// When turning `impl Trait` into opaque types, we have to collect the + /// bounds at the same time to get the IDs correct (without becoming too + /// complicated). I don't like using interior mutability (as for the + /// counter), but I've tried and failed to make the lifetimes work for + /// passing around a `&mut TyLoweringContext`. The core problem is that + /// we're grouping the mutable data (the counter and this field) together + /// with the immutable context (the references to the DB and resolver). + /// Splitting this up would be a possible fix. + opaque_type_data: std::cell::RefCell>, } impl<'a> TyLoweringContext<'a> { @@ -56,7 +66,34 @@ impl<'a> TyLoweringContext<'a> { let impl_trait_mode = ImplTraitLoweringMode::Disallowed; let type_param_mode = TypeParamLoweringMode::Placeholder; let in_binders = DebruijnIndex::INNERMOST; - Self { db, resolver, in_binders, impl_trait_mode, impl_trait_counter, type_param_mode } + let opaque_type_data = std::cell::RefCell::new(Vec::new()); + Self { + db, + resolver, + in_binders, + impl_trait_mode, + impl_trait_counter, + type_param_mode, + opaque_type_data, + } + } + + pub fn with_debruijn( + &self, + debruijn: DebruijnIndex, + f: impl FnOnce(&TyLoweringContext) -> T, + ) -> T { + let opaque_ty_data_vec = self.opaque_type_data.replace(Vec::new()); + let new_ctx = Self { + in_binders: debruijn, + impl_trait_counter: std::cell::Cell::new(self.impl_trait_counter.get()), + opaque_type_data: std::cell::RefCell::new(opaque_ty_data_vec), + ..*self + }; + let result = f(&new_ctx); + self.impl_trait_counter.set(new_ctx.impl_trait_counter.get()); + self.opaque_type_data.replace(new_ctx.opaque_type_data.into_inner()); + result } pub fn with_shifted_in( @@ -64,18 +101,7 @@ impl<'a> TyLoweringContext<'a> { debruijn: DebruijnIndex, f: impl FnOnce(&TyLoweringContext) -> T, ) -> T { - let new_ctx = Self { - in_binders: self.in_binders.shifted_in_from(debruijn), - impl_trait_counter: std::cell::Cell::new(self.impl_trait_counter.get()), - ..*self - }; - let result = f(&new_ctx); - self.impl_trait_counter.set(new_ctx.impl_trait_counter.get()); - result - } - - pub fn shifted_in(self, debruijn: DebruijnIndex) -> Self { - Self { in_binders: self.in_binders.shifted_in_from(debruijn), ..self } + self.with_debruijn(self.in_binders.shifted_in_from(debruijn), f) } pub fn with_impl_trait_mode(self, impl_trait_mode: ImplTraitLoweringMode) -> Self { @@ -167,20 +193,47 @@ impl Ty { TypeRef::ImplTrait(bounds) => { match ctx.impl_trait_mode { ImplTraitLoweringMode::Opaque => { - let self_ty = Ty::Bound(BoundVar::new(DebruijnIndex::INNERMOST, 0)); - let predicates = ctx.with_shifted_in(DebruijnIndex::ONE, |ctx| { - bounds - .iter() - .flat_map(|b| { - GenericPredicate::from_type_bound(ctx, b, self_ty.clone()) - }) - .collect() - }); - Ty::Opaque(predicates) + let idx = ctx.impl_trait_counter.get(); + ctx.impl_trait_counter.set(idx + 1); + + assert!(idx as usize == ctx.opaque_type_data.borrow().len()); + // this dance is to make sure the data is in the right + // place even if we encounter more opaque types while + // lowering the bounds + ctx.opaque_type_data + .borrow_mut() + .push(ReturnTypeImplTrait { bounds: Binders::new(1, Vec::new()) }); + // We don't want to lower the bounds inside the binders + // we're currently in, because they don't end up inside + // those binders. E.g. when we have `impl Trait>`, the `impl OtherTrait` can't refer + // to the self parameter from `impl Trait`, and the + // bounds aren't actually stored nested within each + // other, but separately. So if the `T` refers to a type + // parameter of the outer function, it's just one binder + // away instead of two. + let actual_opaque_type_data = ctx + .with_debruijn(DebruijnIndex::INNERMOST, |ctx| { + ReturnTypeImplTrait::from_hir(ctx, &bounds) + }); + ctx.opaque_type_data.borrow_mut()[idx as usize] = actual_opaque_type_data; + + let func = match ctx.resolver.generic_def() { + Some(GenericDefId::FunctionId(f)) => f, + _ => { + // this shouldn't happen + return (Ty::Unknown, None); + } + }; + let impl_trait_id = OpaqueTyId::ReturnTypeImplTrait(func, idx); + let generics = generics(ctx.db.upcast(), func.into()); + let parameters = Substs::bound_vars(&generics, ctx.in_binders); + Ty::Opaque(OpaqueTy { opaque_ty_id: impl_trait_id, parameters }) } ImplTraitLoweringMode::Param => { let idx = ctx.impl_trait_counter.get(); - ctx.impl_trait_counter.set(idx + 1); + // FIXME we're probably doing something wrong here + ctx.impl_trait_counter.set(idx + count_impl_traits(type_ref) as u16); if let Some(def) = ctx.resolver.generic_def() { let generics = generics(ctx.db.upcast(), def); let param = generics @@ -197,7 +250,8 @@ impl Ty { } ImplTraitLoweringMode::Variable => { let idx = ctx.impl_trait_counter.get(); - ctx.impl_trait_counter.set(idx + 1); + // FIXME we're probably doing something wrong here + ctx.impl_trait_counter.set(idx + count_impl_traits(type_ref) as u16); let (parent_params, self_params, list_params, _impl_trait_params) = if let Some(def) = ctx.resolver.generic_def() { let generics = generics(ctx.db.upcast(), def); @@ -663,6 +717,29 @@ fn assoc_type_bindings_from_type_bound<'a>( }) } +impl ReturnTypeImplTrait { + fn from_hir(ctx: &TyLoweringContext, bounds: &[TypeBound]) -> Self { + let self_ty = Ty::Bound(BoundVar::new(DebruijnIndex::INNERMOST, 0)); + let predicates = ctx.with_shifted_in(DebruijnIndex::ONE, |ctx| { + bounds + .iter() + .flat_map(|b| GenericPredicate::from_type_bound(ctx, b, self_ty.clone())) + .collect() + }); + ReturnTypeImplTrait { bounds: Binders::new(1, predicates) } + } +} + +fn count_impl_traits(type_ref: &TypeRef) -> usize { + let mut count = 0; + type_ref.walk(&mut |type_ref| { + if matches!(type_ref, TypeRef::ImplTrait(_)) { + count += 1; + } + }); + count +} + /// Build the signature of a callable item (function, struct or enum variant). pub fn callable_item_sig(db: &dyn HirDatabase, def: CallableDef) -> PolyFnSig { match def { @@ -864,7 +941,9 @@ fn fn_sig_for_fn(db: &dyn HirDatabase, def: FunctionId) -> PolyFnSig { .with_impl_trait_mode(ImplTraitLoweringMode::Variable) .with_type_param_mode(TypeParamLoweringMode::Variable); let params = data.params.iter().map(|tr| Ty::from_hir(&ctx_params, tr)).collect::>(); - let ctx_ret = ctx_params.with_impl_trait_mode(ImplTraitLoweringMode::Opaque); + let ctx_ret = TyLoweringContext::new(db, &resolver) + .with_impl_trait_mode(ImplTraitLoweringMode::Opaque) + .with_type_param_mode(TypeParamLoweringMode::Variable); let ret = Ty::from_hir(&ctx_ret, &data.ret_type); let generics = generics(db.upcast(), def.into()); let num_binders = generics.len(); @@ -1084,3 +1163,25 @@ pub(crate) fn impl_trait_query(db: &dyn HirDatabase, impl_id: ImplId) -> Option< TraitRef::from_hir(&ctx, target_trait, Some(self_ty.value))?, )) } + +pub(crate) fn return_type_impl_traits( + db: &impl HirDatabase, + def: hir_def::FunctionId, +) -> Option>> { + // FIXME unify with fn_sig_for_fn instead of doing lowering twice, maybe + let data = db.function_data(def); + let resolver = def.resolver(db.upcast()); + let ctx_ret = TyLoweringContext::new(db, &resolver) + .with_impl_trait_mode(ImplTraitLoweringMode::Opaque) + .with_type_param_mode(TypeParamLoweringMode::Variable); + let _ret = Ty::from_hir(&ctx_ret, &data.ret_type); + let generics = generics(db.upcast(), def.into()); + let num_binders = generics.len(); + let return_type_impl_traits = + ReturnTypeImplTraits { impl_traits: ctx_ret.opaque_type_data.into_inner() }; + if return_type_impl_traits.impl_traits.is_empty() { + None + } else { + Some(Arc::new(Binders::new(num_binders, return_type_impl_traits))) + } +} diff --git a/crates/ra_hir_ty/src/tests/traits.rs b/crates/ra_hir_ty/src/tests/traits.rs index e8778d419be7..d83dc6d79f40 100644 --- a/crates/ra_hir_ty/src/tests/traits.rs +++ b/crates/ra_hir_ty/src/tests/traits.rs @@ -1110,7 +1110,6 @@ fn test() { } #[test] -#[ignore] fn impl_trait() { assert_snapshot!( infer(r#" @@ -1160,6 +1159,52 @@ fn test(x: impl Trait, y: &impl Trait) { ); } +#[test] +fn return_pos_impl_trait() { + assert_snapshot!( + infer(r#" +trait Iterator { + type Item; + fn next(&mut self) -> Self::Item; +} +trait Trait { + fn foo(&self) -> T; +} +fn bar() -> (impl Iterator>, impl Trait) { loop {} } +fn baz(t: T) -> (impl Iterator>, impl Trait) { loop {} } + +fn test() { + // let (a, b) = bar(); + // a.next().foo(); + // b.foo(); + let (c, d) = baz(1u128); + c.next();//.foo(); + // d.foo(); +} +"#), + @r###" + 50..54 'self': &mut Self + 102..106 'self': &Self + 185..196 '{ loop {} }': ({unknown}, {unknown}) + 187..194 'loop {}': ! + 192..194 '{}': () + 207..208 't': T + 269..280 '{ loop {} }': ({unknown}, {unknown}) + 271..278 'loop {}': ! + 276..278 '{}': () + 292..429 '{ ...o(); }': () + 368..374 '(c, d)': (impl Iterator>, impl Trait) + 369..370 'c': impl Iterator> + 372..373 'd': impl Trait + 377..380 'baz': fn baz(u128) -> (impl Iterator>, impl Trait) + 377..387 'baz(1u128)': (impl Iterator>, impl Trait) + 381..386 '1u128': u128 + 393..394 'c': impl Iterator> + 393..401 'c.next()': impl Trait + "### + ); +} + #[test] fn dyn_trait() { assert_snapshot!( diff --git a/crates/ra_hir_ty/src/traits/chalk.rs b/crates/ra_hir_ty/src/traits/chalk.rs index 61de3cc30c66..a72a82f5a23a 100644 --- a/crates/ra_hir_ty/src/traits/chalk.rs +++ b/crates/ra_hir_ty/src/traits/chalk.rs @@ -4,7 +4,7 @@ use std::sync::Arc; use log::debug; use chalk_ir::{fold::shift::Shift, GenericArg, TypeName}; -use chalk_solve::rust_ir::{self, WellKnownTrait}; +use chalk_solve::rust_ir::{self, OpaqueTyDatumBound, WellKnownTrait}; use hir_def::{ lang_item::{lang_attr, LangItemTarget}, @@ -100,6 +100,7 @@ impl<'a> chalk_solve::RustIrDatabase for ChalkContext<'a> { fn associated_ty_value(&self, id: AssociatedTyValueId) -> Arc { self.db.associated_ty_value(self.krate, id) } + fn custom_clauses(&self) -> Vec> { vec![] } @@ -130,11 +131,34 @@ impl<'a> chalk_solve::RustIrDatabase for ChalkContext<'a> { self.db.program_clauses_for_chalk_env(self.krate, environment.clone()) } - fn opaque_ty_data( - &self, - _id: chalk_ir::OpaqueTyId, - ) -> Arc> { - unimplemented!() + fn opaque_ty_data(&self, id: chalk_ir::OpaqueTyId) -> Arc { + let interned_id = crate::db::InternedOpaqueTyId::from(id); + let full_id = self.db.lookup_intern_impl_trait_id(interned_id); + let (func, idx) = match full_id { + crate::OpaqueTyId::ReturnTypeImplTrait(func, idx) => (func, idx), + }; + let datas = + self.db.return_type_impl_traits(func).expect("impl trait id without impl traits"); + let data = &datas.value.impl_traits[idx as usize]; + let bound = OpaqueTyDatumBound { + bounds: make_binders( + data.bounds + .value + .iter() + .cloned() + .filter(|b| !b.is_error()) + .map(|b| b.to_chalk(self.db)) + .collect(), + 1, + ), + }; + let num_vars = datas.num_binders; + Arc::new(OpaqueTyDatum { opaque_ty_id: id, bound: make_binders(bound, num_vars) }) + } + + fn hidden_opaque_type(&self, _id: chalk_ir::OpaqueTyId) -> chalk_ir::Ty { + // FIXME: actually provide the hidden type; it is relevant for auto traits + Ty::Unknown.to_chalk(self.db) } fn force_impl_for( @@ -150,10 +174,6 @@ impl<'a> chalk_solve::RustIrDatabase for ChalkContext<'a> { // FIXME: implement actual object safety true } - - fn hidden_opaque_type(&self, _id: chalk_ir::OpaqueTyId) -> chalk_ir::Ty { - Ty::Unknown.to_chalk(self.db) - } } pub(crate) fn program_clauses_for_chalk_env_query( @@ -460,6 +480,18 @@ impl From for ImplId { } } +impl From for crate::db::InternedOpaqueTyId { + fn from(id: OpaqueTyId) -> Self { + InternKey::from_intern_id(id.0) + } +} + +impl From for OpaqueTyId { + fn from(id: crate::db::InternedOpaqueTyId) -> Self { + chalk_ir::OpaqueTyId(id.as_intern_id()) + } +} + impl From> for crate::traits::AssocTyValueId { fn from(id: rust_ir::AssociatedTyValueId) -> Self { Self::from_intern_id(id.0) diff --git a/crates/ra_hir_ty/src/traits/chalk/interner.rs b/crates/ra_hir_ty/src/traits/chalk/interner.rs index e27074ba6fad..56aab640c85f 100644 --- a/crates/ra_hir_ty/src/traits/chalk/interner.rs +++ b/crates/ra_hir_ty/src/traits/chalk/interner.rs @@ -22,6 +22,8 @@ pub type AssociatedTyValueId = chalk_solve::rust_ir::AssociatedTyValueId; pub type FnDefId = chalk_ir::FnDefId; pub type FnDefDatum = chalk_solve::rust_ir::FnDefDatum; +pub type OpaqueTyId = chalk_ir::OpaqueTyId; +pub type OpaqueTyDatum = chalk_solve::rust_ir::OpaqueTyDatum; impl chalk_ir::interner::Interner for Interner { type InternedType = Box>; // FIXME use Arc? diff --git a/crates/ra_hir_ty/src/traits/chalk/mapping.rs b/crates/ra_hir_ty/src/traits/chalk/mapping.rs index 5f6daf842be6..834360430318 100644 --- a/crates/ra_hir_ty/src/traits/chalk/mapping.rs +++ b/crates/ra_hir_ty/src/traits/chalk/mapping.rs @@ -16,8 +16,8 @@ use crate::{ db::HirDatabase, primitive::{FloatBitness, FloatTy, IntBitness, IntTy, Signedness, Uncertain}, traits::{builtin, AssocTyValue, Canonical, Impl, Obligation}, - ApplicationTy, CallableDef, GenericPredicate, InEnvironment, ProjectionPredicate, ProjectionTy, - Substs, TraitEnvironment, TraitRef, Ty, TypeCtor, + ApplicationTy, CallableDef, GenericPredicate, InEnvironment, OpaqueTy, OpaqueTyId, + ProjectionPredicate, ProjectionTy, Substs, TraitEnvironment, TraitRef, Ty, TypeCtor, }; use super::interner::*; @@ -68,7 +68,16 @@ impl ToChalk for Ty { let bounded_ty = chalk_ir::DynTy { bounds: make_binders(where_clauses, 1) }; chalk_ir::TyData::Dyn(bounded_ty).intern(&Interner) } - Ty::Opaque(_) | Ty::Unknown => { + Ty::Opaque(opaque_ty) => { + let opaque_ty_id = opaque_ty.opaque_ty_id.to_chalk(db); + let substitution = opaque_ty.parameters.to_chalk(db); + chalk_ir::TyData::Alias(chalk_ir::AliasTy::Opaque(chalk_ir::OpaqueTy { + opaque_ty_id, + substitution, + })) + .intern(&Interner) + } + Ty::Unknown => { let substitution = chalk_ir::Substitution::empty(&Interner); let name = TypeName::Error; chalk_ir::ApplicationTy { name, substitution }.cast(&Interner).intern(&Interner) @@ -98,7 +107,11 @@ impl ToChalk for Ty { let parameters = from_chalk(db, proj.substitution); Ty::Projection(ProjectionTy { associated_ty, parameters }) } - chalk_ir::TyData::Alias(chalk_ir::AliasTy::Opaque(_)) => unimplemented!(), + chalk_ir::TyData::Alias(chalk_ir::AliasTy::Opaque(opaque_ty)) => { + let impl_trait_id = from_chalk(db, opaque_ty.opaque_ty_id); + let parameters = from_chalk(db, opaque_ty.substitution); + Ty::Opaque(OpaqueTy { opaque_ty_id: impl_trait_id, parameters }) + } chalk_ir::TyData::Function(chalk_ir::Fn { num_binders: _, substitution }) => { let parameters: Substs = from_chalk(db, substitution); Ty::Apply(ApplicationTy { @@ -204,6 +217,21 @@ impl ToChalk for hir_def::TraitId { } } +impl ToChalk for OpaqueTyId { + type Chalk = chalk_ir::OpaqueTyId; + + fn to_chalk(self, db: &dyn HirDatabase) -> chalk_ir::OpaqueTyId { + db.intern_impl_trait_id(self).into() + } + + fn from_chalk( + db: &dyn HirDatabase, + opaque_ty_id: chalk_ir::OpaqueTyId, + ) -> OpaqueTyId { + db.lookup_intern_impl_trait_id(opaque_ty_id.into()) + } +} + impl ToChalk for TypeCtor { type Chalk = TypeName; @@ -214,6 +242,11 @@ impl ToChalk for TypeCtor { TypeName::AssociatedType(type_id) } + TypeCtor::OpaqueType(impl_trait_id) => { + let id = impl_trait_id.to_chalk(db); + TypeName::OpaqueType(id) + } + TypeCtor::Bool => TypeName::Scalar(Scalar::Bool), TypeCtor::Char => TypeName::Scalar(Scalar::Char), TypeCtor::Int(Uncertain::Known(int_ty)) => TypeName::Scalar(int_ty_to_chalk(int_ty)), @@ -252,7 +285,9 @@ impl ToChalk for TypeCtor { match type_name { TypeName::Adt(struct_id) => db.lookup_intern_type_ctor(struct_id.into()), TypeName::AssociatedType(type_id) => TypeCtor::AssociatedType(from_chalk(db, type_id)), - TypeName::OpaqueType(_) => unreachable!(), + TypeName::OpaqueType(opaque_type_id) => { + TypeCtor::OpaqueType(from_chalk(db, opaque_type_id)) + } TypeName::Scalar(Scalar::Bool) => TypeCtor::Bool, TypeName::Scalar(Scalar::Char) => TypeCtor::Char, diff --git a/crates/ra_hir_ty/src/traits/chalk/tls.rs b/crates/ra_hir_ty/src/traits/chalk/tls.rs index d88828c7c0a0..556af70989fe 100644 --- a/crates/ra_hir_ty/src/traits/chalk/tls.rs +++ b/crates/ra_hir_ty/src/traits/chalk/tls.rs @@ -69,6 +69,11 @@ impl DebugContext<'_> { let name = self.0.type_alias_data(type_alias).name.clone(); write!(f, "{}::{}", trait_name, name)?; } + TypeCtor::OpaqueType(opaque_ty_id) => match opaque_ty_id { + crate::OpaqueTyId::ReturnTypeImplTrait(func, idx) => { + write!(f, "{{impl trait {} of {:?}}}", idx, func)?; + } + }, TypeCtor::Closure { def, expr } => { write!(f, "{{closure {:?} in ", expr.into_raw())?; match def { From bbb40d746368eb6a38498649a723c7ead0ef8136 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Fri, 5 Jun 2020 16:45:20 +0200 Subject: [PATCH 091/172] Minimize FileLoader interface --- crates/ra_db/src/lib.rs | 53 ++++++++++------------- crates/ra_hir_def/src/test_db.rs | 12 +---- crates/ra_hir_expand/src/builtin_macro.rs | 18 +++----- crates/ra_hir_expand/src/test_db.rs | 9 +--- crates/ra_hir_ty/src/test_db.rs | 11 +---- crates/ra_ide_db/src/lib.rs | 11 +---- 6 files changed, 35 insertions(+), 79 deletions(-) diff --git a/crates/ra_db/src/lib.rs b/crates/ra_db/src/lib.rs index 2e63cb46e96f..7354b3832b19 100644 --- a/crates/ra_db/src/lib.rs +++ b/crates/ra_db/src/lib.rs @@ -89,14 +89,13 @@ pub const DEFAULT_LRU_CAP: usize = 128; pub trait FileLoader { /// Text of the file. fn file_text(&self, file_id: FileId) -> Arc; + /// Note that we intentionally accept a `&str` and not a `&Path` here. This + /// method exists to handle `#[path = "/some/path.rs"] mod foo;` and such, + /// so the input is guaranteed to be utf-8 string. We might introduce + /// `struct StrPath(str)` for clarity some day, but it's a bit messy, so we + /// get by with a `&str` for the time being. fn resolve_path(&self, anchor: FileId, path: &str) -> Option; fn relevant_crates(&self, file_id: FileId) -> Arc>; - - fn resolve_extern_path( - &self, - extern_id: ExternSourceId, - relative_path: &RelativePath, - ) -> Option; } /// Database which stores all significant input facts: source code and project @@ -154,34 +153,30 @@ impl FileLoader for FileLoaderDelegate<&'_ T> { fn file_text(&self, file_id: FileId) -> Arc { SourceDatabaseExt::file_text(self.0, file_id) } - /// Note that we intentionally accept a `&str` and not a `&Path` here. This - /// method exists to handle `#[path = "/some/path.rs"] mod foo;` and such, - /// so the input is guaranteed to be utf-8 string. We might introduce - /// `struct StrPath(str)` for clarity some day, but it's a bit messy, so we - /// get by with a `&str` for the time being. fn resolve_path(&self, anchor: FileId, path: &str) -> Option { - let rel_path = { - let mut rel_path = self.0.file_relative_path(anchor); - assert!(rel_path.pop()); - rel_path.push(path); - rel_path.normalize() - }; - let source_root = self.0.file_source_root(anchor); - let source_root = self.0.source_root(source_root); - source_root.file_by_relative_path(&rel_path) + // FIXME: this *somehow* should be platform agnostic... + if std::path::Path::new(path).is_absolute() { + let krate = *self.relevant_crates(anchor).get(0)?; + let (extern_source_id, relative_file) = + self.0.crate_graph()[krate].extern_source.extern_path(path)?; + + let source_root = self.0.source_root(SourceRootId(extern_source_id.0)); + source_root.file_by_relative_path(&relative_file) + } else { + let rel_path = { + let mut rel_path = self.0.file_relative_path(anchor); + assert!(rel_path.pop()); + rel_path.push(path); + rel_path.normalize() + }; + let source_root = self.0.file_source_root(anchor); + let source_root = self.0.source_root(source_root); + source_root.file_by_relative_path(&rel_path) + } } fn relevant_crates(&self, file_id: FileId) -> Arc> { let source_root = self.0.file_source_root(file_id); self.0.source_root_crates(source_root) } - - fn resolve_extern_path( - &self, - extern_id: ExternSourceId, - relative_path: &RelativePath, - ) -> Option { - let source_root = self.0.source_root(SourceRootId(extern_id.0)); - source_root.file_by_relative_path(&relative_path) - } } diff --git a/crates/ra_hir_def/src/test_db.rs b/crates/ra_hir_def/src/test_db.rs index e7a5182f03c0..bcfa66ac953c 100644 --- a/crates/ra_hir_def/src/test_db.rs +++ b/crates/ra_hir_def/src/test_db.rs @@ -6,9 +6,7 @@ use std::{ }; use hir_expand::db::AstDatabase; -use ra_db::{ - salsa, CrateId, ExternSourceId, FileId, FileLoader, FileLoaderDelegate, RelativePath, Upcast, -}; +use ra_db::{salsa, CrateId, FileId, FileLoader, FileLoaderDelegate, Upcast}; use crate::db::DefDatabase; @@ -64,14 +62,6 @@ impl FileLoader for TestDB { fn relevant_crates(&self, file_id: FileId) -> Arc> { FileLoaderDelegate(self).relevant_crates(file_id) } - - fn resolve_extern_path( - &self, - extern_id: ExternSourceId, - relative_path: &RelativePath, - ) -> Option { - FileLoaderDelegate(self).resolve_extern_path(extern_id, relative_path) - } } impl TestDB { diff --git a/crates/ra_hir_expand/src/builtin_macro.rs b/crates/ra_hir_expand/src/builtin_macro.rs index eec5fb8ebf85..7579546d2f3f 100644 --- a/crates/ra_hir_expand/src/builtin_macro.rs +++ b/crates/ra_hir_expand/src/builtin_macro.rs @@ -295,19 +295,13 @@ fn concat_expand( fn relative_file(db: &dyn AstDatabase, call_id: MacroCallId, path: &str) -> Option { let call_site = call_id.as_file().original_file(db); - - // Handle trivial case - if let Some(res) = db.resolve_path(call_site, path) { - // Prevent include itself - return if res == call_site { None } else { Some(res) }; + let res = db.resolve_path(call_site, path)?; + // Prevent include itself + if res == call_site { + None + } else { + Some(res) } - - // Extern paths ? - let krate = *db.relevant_crates(call_site).get(0)?; - let (extern_source_id, relative_file) = - db.crate_graph()[krate].extern_source.extern_path(path)?; - - db.resolve_extern_path(extern_source_id, &relative_file) } fn parse_string(tt: &tt::Subtree) -> Result { diff --git a/crates/ra_hir_expand/src/test_db.rs b/crates/ra_hir_expand/src/test_db.rs index 765a2f6d18e9..fdf225f55be9 100644 --- a/crates/ra_hir_expand/src/test_db.rs +++ b/crates/ra_hir_expand/src/test_db.rs @@ -5,7 +5,7 @@ use std::{ sync::{Arc, Mutex}, }; -use ra_db::{salsa, CrateId, ExternSourceId, FileId, FileLoader, FileLoaderDelegate, RelativePath}; +use ra_db::{salsa, CrateId, FileId, FileLoader, FileLoaderDelegate}; #[salsa::database( ra_db::SourceDatabaseExtStorage, @@ -47,11 +47,4 @@ impl FileLoader for TestDB { fn relevant_crates(&self, file_id: FileId) -> Arc> { FileLoaderDelegate(self).relevant_crates(file_id) } - fn resolve_extern_path( - &self, - anchor: ExternSourceId, - relative_path: &RelativePath, - ) -> Option { - FileLoaderDelegate(self).resolve_extern_path(anchor, relative_path) - } } diff --git a/crates/ra_hir_ty/src/test_db.rs b/crates/ra_hir_ty/src/test_db.rs index 21a3bdfd154f..e484968a099c 100644 --- a/crates/ra_hir_ty/src/test_db.rs +++ b/crates/ra_hir_ty/src/test_db.rs @@ -7,9 +7,7 @@ use std::{ use hir_def::{db::DefDatabase, AssocItemId, ModuleDefId, ModuleId}; use hir_expand::{db::AstDatabase, diagnostics::DiagnosticSink}; -use ra_db::{ - salsa, CrateId, FileId, FileLoader, FileLoaderDelegate, RelativePath, SourceDatabase, Upcast, -}; +use ra_db::{salsa, CrateId, FileId, FileLoader, FileLoaderDelegate, SourceDatabase, Upcast}; use stdx::format_to; use crate::{db::HirDatabase, diagnostics::Diagnostic, expr::ExprValidator}; @@ -78,13 +76,6 @@ impl FileLoader for TestDB { fn relevant_crates(&self, file_id: FileId) -> Arc> { FileLoaderDelegate(self).relevant_crates(file_id) } - fn resolve_extern_path( - &self, - extern_id: ra_db::ExternSourceId, - relative_path: &RelativePath, - ) -> Option { - FileLoaderDelegate(self).resolve_extern_path(extern_id, relative_path) - } } impl TestDB { diff --git a/crates/ra_ide_db/src/lib.rs b/crates/ra_ide_db/src/lib.rs index 93d5891a04b0..727d743b5fda 100644 --- a/crates/ra_ide_db/src/lib.rs +++ b/crates/ra_ide_db/src/lib.rs @@ -16,8 +16,8 @@ use std::sync::Arc; use hir::db::{AstDatabase, DefDatabase}; use ra_db::{ salsa::{self, Database, Durability}, - Canceled, CheckCanceled, CrateId, FileId, FileLoader, FileLoaderDelegate, RelativePath, - SourceDatabase, SourceRootId, Upcast, + Canceled, CheckCanceled, CrateId, FileId, FileLoader, FileLoaderDelegate, SourceDatabase, + SourceRootId, Upcast, }; use rustc_hash::FxHashMap; @@ -63,13 +63,6 @@ impl FileLoader for RootDatabase { fn relevant_crates(&self, file_id: FileId) -> Arc> { FileLoaderDelegate(self).relevant_crates(file_id) } - fn resolve_extern_path( - &self, - extern_id: ra_db::ExternSourceId, - relative_path: &RelativePath, - ) -> Option { - FileLoaderDelegate(self).resolve_extern_path(extern_id, relative_path) - } } impl salsa::Database for RootDatabase { From 0d2328f3eaf69c6a50fe6c1e946257bd3503d751 Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Fri, 5 Jun 2020 17:41:58 +0200 Subject: [PATCH 092/172] Review fixes --- crates/ra_hir/src/db.rs | 4 +- crates/ra_hir_ty/src/display.rs | 6 +-- crates/ra_hir_ty/src/lower.rs | 9 ++-- crates/ra_hir_ty/src/tests/traits.rs | 73 ++++++++++++++++++++++------ crates/ra_ide_db/src/change.rs | 1 + 5 files changed, 68 insertions(+), 25 deletions(-) diff --git a/crates/ra_hir/src/db.rs b/crates/ra_hir/src/db.rs index ec931b34fe87..693cd40cf311 100644 --- a/crates/ra_hir/src/db.rs +++ b/crates/ra_hir/src/db.rs @@ -18,8 +18,8 @@ pub use hir_ty::db::{ GenericDefaultsQuery, GenericPredicatesForParamQuery, GenericPredicatesQuery, HirDatabase, HirDatabaseStorage, ImplDatumQuery, ImplSelfTyQuery, ImplTraitQuery, ImplsForTraitQuery, ImplsInCrateQuery, InferQueryQuery, InternAssocTyValueQuery, InternChalkImplQuery, - InternTypeCtorQuery, InternTypeParamIdQuery, StructDatumQuery, TraitDatumQuery, - TraitSolveQuery, TyQuery, ValueTyQuery, + InternTypeCtorQuery, InternTypeParamIdQuery, ReturnTypeImplTraitsQuery, StructDatumQuery, + TraitDatumQuery, TraitSolveQuery, TyQuery, ValueTyQuery, }; #[test] diff --git a/crates/ra_hir_ty/src/display.rs b/crates/ra_hir_ty/src/display.rs index 3e63a2415d8c..3c97e13545e3 100644 --- a/crates/ra_hir_ty/src/display.rs +++ b/crates/ra_hir_ty/src/display.rs @@ -4,7 +4,7 @@ use std::fmt; use crate::{ db::HirDatabase, utils::generics, ApplicationTy, CallableDef, FnSig, GenericPredicate, - Obligation, ProjectionTy, Substs, TraitRef, Ty, TypeCtor, + Obligation, OpaqueTyId, ProjectionTy, Substs, TraitRef, Ty, TypeCtor, }; use hir_def::{ find_path, generics::TypeParamProvenance, item_scope::ItemInNs, AdtId, AssocContainerId, @@ -361,7 +361,7 @@ impl HirDisplay for ApplicationTy { } TypeCtor::OpaqueType(opaque_ty_id) => { let bounds = match opaque_ty_id { - crate::OpaqueTyId::ReturnTypeImplTrait(func, idx) => { + OpaqueTyId::ReturnTypeImplTrait(func, idx) => { let datas = f.db.return_type_impl_traits(func).expect("impl trait id without data"); let data = (*datas) @@ -448,7 +448,7 @@ impl HirDisplay for Ty { } Ty::Opaque(opaque_ty) => { let bounds = match opaque_ty.opaque_ty_id { - crate::OpaqueTyId::ReturnTypeImplTrait(func, idx) => { + OpaqueTyId::ReturnTypeImplTrait(func, idx) => { let datas = f.db.return_type_impl_traits(func).expect("impl trait id without data"); let data = (*datas) diff --git a/crates/ra_hir_ty/src/lower.rs b/crates/ra_hir_ty/src/lower.rs index dfc018b0bc5e..a05cbd7fcf00 100644 --- a/crates/ra_hir_ty/src/lower.rs +++ b/crates/ra_hir_ty/src/lower.rs @@ -21,8 +21,10 @@ use hir_def::{ HasModule, ImplId, LocalFieldId, Lookup, StaticId, StructId, TraitId, TypeAliasId, TypeParamId, UnionId, VariantId, }; +use hir_expand::name::Name; use ra_arena::map::ArenaMap; use ra_db::CrateId; +use test_utils::mark; use crate::{ db::HirDatabase, @@ -35,7 +37,6 @@ use crate::{ ProjectionPredicate, ProjectionTy, ReturnTypeImplTrait, ReturnTypeImplTraits, Substs, TraitEnvironment, TraitRef, Ty, TypeCtor, TypeWalk, }; -use hir_expand::name::Name; #[derive(Debug)] pub struct TyLoweringContext<'a> { @@ -220,10 +221,7 @@ impl Ty { let func = match ctx.resolver.generic_def() { Some(GenericDefId::FunctionId(f)) => f, - _ => { - // this shouldn't happen - return (Ty::Unknown, None); - } + _ => panic!("opaque impl trait lowering in non-function"), }; let impl_trait_id = OpaqueTyId::ReturnTypeImplTrait(func, idx); let generics = generics(ctx.db.upcast(), func.into()); @@ -719,6 +717,7 @@ fn assoc_type_bindings_from_type_bound<'a>( impl ReturnTypeImplTrait { fn from_hir(ctx: &TyLoweringContext, bounds: &[TypeBound]) -> Self { + mark::hit!(lower_rpit); let self_ty = Ty::Bound(BoundVar::new(DebruijnIndex::INNERMOST, 0)); let predicates = ctx.with_shifted_in(DebruijnIndex::ONE, |ctx| { bounds diff --git a/crates/ra_hir_ty/src/tests/traits.rs b/crates/ra_hir_ty/src/tests/traits.rs index d83dc6d79f40..0c538a62d920 100644 --- a/crates/ra_hir_ty/src/tests/traits.rs +++ b/crates/ra_hir_ty/src/tests/traits.rs @@ -1160,7 +1160,37 @@ fn test(x: impl Trait, y: &impl Trait) { } #[test] -fn return_pos_impl_trait() { +fn simple_return_pos_impl_trait() { + mark::check!(lower_rpit); + assert_snapshot!( + infer(r#" +trait Trait { + fn foo(&self) -> T; +} +fn bar() -> impl Trait { loop {} } + +fn test() { + let a = bar(); + a.foo(); +} +"#), + @r###" + 30..34 'self': &Self + 72..83 '{ loop {} }': ! + 74..81 'loop {}': ! + 79..81 '{}': () + 95..130 '{ ...o(); }': () + 105..106 'a': impl Trait + 109..112 'bar': fn bar() -> impl Trait + 109..114 'bar()': impl Trait + 120..121 'a': impl Trait + 120..127 'a.foo()': u64 + "### + ); +} + +#[test] +fn more_return_pos_impl_trait() { assert_snapshot!( infer(r#" trait Iterator { @@ -1174,12 +1204,12 @@ fn bar() -> (impl Iterator>, impl Trait) { loop {} } fn baz(t: T) -> (impl Iterator>, impl Trait) { loop {} } fn test() { - // let (a, b) = bar(); - // a.next().foo(); - // b.foo(); + let (a, b) = bar(); + a.next().foo(); + b.foo(); let (c, d) = baz(1u128); - c.next();//.foo(); - // d.foo(); + c.next().foo(); + d.foo(); } "#), @r###" @@ -1192,15 +1222,28 @@ fn test() { 269..280 '{ loop {} }': ({unknown}, {unknown}) 271..278 'loop {}': ! 276..278 '{}': () - 292..429 '{ ...o(); }': () - 368..374 '(c, d)': (impl Iterator>, impl Trait) - 369..370 'c': impl Iterator> - 372..373 'd': impl Trait - 377..380 'baz': fn baz(u128) -> (impl Iterator>, impl Trait) - 377..387 'baz(1u128)': (impl Iterator>, impl Trait) - 381..386 '1u128': u128 - 393..394 'c': impl Iterator> - 393..401 'c.next()': impl Trait + 292..414 '{ ...o(); }': () + 302..308 '(a, b)': (impl Iterator>, impl Trait) + 303..304 'a': impl Iterator> + 306..307 'b': impl Trait + 311..314 'bar': fn bar() -> (impl Iterator>, impl Trait) + 311..316 'bar()': (impl Iterator>, impl Trait) + 322..323 'a': impl Iterator> + 322..330 'a.next()': impl Trait + 322..336 'a.next().foo()': u32 + 342..343 'b': impl Trait + 342..349 'b.foo()': u64 + 359..365 '(c, d)': (impl Iterator>, impl Trait) + 360..361 'c': impl Iterator> + 363..364 'd': impl Trait + 368..371 'baz': fn baz(u128) -> (impl Iterator>, impl Trait) + 368..378 'baz(1u128)': (impl Iterator>, impl Trait) + 372..377 '1u128': u128 + 384..385 'c': impl Iterator> + 384..392 'c.next()': impl Trait + 384..398 'c.next().foo()': u128 + 404..405 'd': impl Trait + 404..411 'd.foo()': u128 "### ); } diff --git a/crates/ra_ide_db/src/change.rs b/crates/ra_ide_db/src/change.rs index 8446ef88e8c5..1f4d2c076570 100644 --- a/crates/ra_ide_db/src/change.rs +++ b/crates/ra_ide_db/src/change.rs @@ -369,6 +369,7 @@ impl RootDatabase { hir::db::ImplDatumQuery hir::db::AssociatedTyValueQuery hir::db::TraitSolveQuery + hir::db::ReturnTypeImplTraitsQuery // SymbolsDatabase crate::symbol_index::FileSymbolsQuery From a4a4a1854ebb53e1cdd7a5e3b308112bbbf3c676 Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Fri, 29 May 2020 19:14:04 +0200 Subject: [PATCH 093/172] Fix type parameter defaults They should not be applied in expression or pattern contexts, unless there are other explicitly given type args. --- .../src/handlers/add_explicit_type.rs | 4 +- crates/ra_hir_ty/src/infer.rs | 4 +- crates/ra_hir_ty/src/infer/path.rs | 3 +- crates/ra_hir_ty/src/lower.rs | 70 ++++++++---- .../src/tests/display_source_code.rs | 4 +- .../ra_hir_ty/src/tests/method_resolution.rs | 54 --------- crates/ra_hir_ty/src/tests/simple.rs | 108 ++++++++++++++++++ crates/ra_hir_ty/src/tests/traits.rs | 54 ++++----- crates/ra_ide/src/hover.rs | 2 +- crates/ra_ide/src/inlay_hints.rs | 4 +- 10 files changed, 192 insertions(+), 115 deletions(-) diff --git a/crates/ra_assists/src/handlers/add_explicit_type.rs b/crates/ra_assists/src/handlers/add_explicit_type.rs index ab20c66493c1..90b06a62595c 100644 --- a/crates/ra_assists/src/handlers/add_explicit_type.rs +++ b/crates/ra_assists/src/handlers/add_explicit_type.rs @@ -195,7 +195,7 @@ struct Test { } fn main() { - let test<|> = Test { t: 23, k: 33 }; + let test<|> = Test { t: 23u8, k: 33 }; }"#, r#" struct Test { @@ -204,7 +204,7 @@ struct Test { } fn main() { - let test: Test = Test { t: 23, k: 33 }; + let test: Test = Test { t: 23u8, k: 33 }; }"#, ); } diff --git a/crates/ra_hir_ty/src/infer.rs b/crates/ra_hir_ty/src/infer.rs index d9bf3c2f0f22..2e16e5120db0 100644 --- a/crates/ra_hir_ty/src/infer.rs +++ b/crates/ra_hir_ty/src/infer.rs @@ -439,13 +439,13 @@ impl<'a> InferenceContext<'a> { }; return match resolution { TypeNs::AdtId(AdtId::StructId(strukt)) => { - let substs = Ty::substs_from_path(&ctx, path, strukt.into()); + let substs = Ty::substs_from_path(&ctx, path, strukt.into(), true); let ty = self.db.ty(strukt.into()); let ty = self.insert_type_vars(ty.subst(&substs)); forbid_unresolved_segments((ty, Some(strukt.into())), unresolved) } TypeNs::EnumVariantId(var) => { - let substs = Ty::substs_from_path(&ctx, path, var.into()); + let substs = Ty::substs_from_path(&ctx, path, var.into(), true); let ty = self.db.ty(var.parent.into()); let ty = self.insert_type_vars(ty.subst(&substs)); forbid_unresolved_segments((ty, Some(var.into())), unresolved) diff --git a/crates/ra_hir_ty/src/infer/path.rs b/crates/ra_hir_ty/src/infer/path.rs index 1c2e56fb0d82..1ad0d8397166 100644 --- a/crates/ra_hir_ty/src/infer/path.rs +++ b/crates/ra_hir_ty/src/infer/path.rs @@ -95,7 +95,7 @@ impl<'a> InferenceContext<'a> { // self_subst is just for the parent let parent_substs = self_subst.unwrap_or_else(Substs::empty); let ctx = crate::lower::TyLoweringContext::new(self.db, &self.resolver); - let substs = Ty::substs_from_path(&ctx, path, typable); + let substs = Ty::substs_from_path(&ctx, path, typable, true); let full_substs = Substs::builder(substs.len()) .use_parent_substs(&parent_substs) .fill(substs.0[parent_substs.len()..].iter().cloned()) @@ -141,6 +141,7 @@ impl<'a> InferenceContext<'a> { def, resolved_segment, remaining_segments_for_ty, + true, ); if let Ty::Unknown = ty { return None; diff --git a/crates/ra_hir_ty/src/lower.rs b/crates/ra_hir_ty/src/lower.rs index a05cbd7fcf00..42713928f451 100644 --- a/crates/ra_hir_ty/src/lower.rs +++ b/crates/ra_hir_ty/src/lower.rs @@ -323,6 +323,7 @@ impl Ty { resolution: TypeNs, resolved_segment: PathSegment<'_>, remaining_segments: PathSegments<'_>, + infer_args: bool, ) -> (Ty, Option) { let ty = match resolution { TypeNs::TraitId(trait_) => { @@ -400,9 +401,15 @@ impl Ty { ctx.db.ty(adt.into()).subst(&substs) } - TypeNs::AdtId(it) => Ty::from_hir_path_inner(ctx, resolved_segment, it.into()), - TypeNs::BuiltinType(it) => Ty::from_hir_path_inner(ctx, resolved_segment, it.into()), - TypeNs::TypeAliasId(it) => Ty::from_hir_path_inner(ctx, resolved_segment, it.into()), + TypeNs::AdtId(it) => { + Ty::from_hir_path_inner(ctx, resolved_segment, it.into(), infer_args) + } + TypeNs::BuiltinType(it) => { + Ty::from_hir_path_inner(ctx, resolved_segment, it.into(), infer_args) + } + TypeNs::TypeAliasId(it) => { + Ty::from_hir_path_inner(ctx, resolved_segment, it.into(), infer_args) + } // FIXME: report error TypeNs::EnumVariantId(_) => return (Ty::Unknown, None), }; @@ -428,7 +435,13 @@ impl Ty { ), Some(i) => (path.segments().get(i - 1).unwrap(), path.segments().skip(i)), }; - Ty::from_partly_resolved_hir_path(ctx, resolution, resolved_segment, remaining_segments) + Ty::from_partly_resolved_hir_path( + ctx, + resolution, + resolved_segment, + remaining_segments, + false, + ) } fn select_associated_type( @@ -474,13 +487,14 @@ impl Ty { ctx: &TyLoweringContext<'_>, segment: PathSegment<'_>, typable: TyDefId, + infer_args: bool, ) -> Ty { let generic_def = match typable { TyDefId::BuiltinType(_) => None, TyDefId::AdtId(it) => Some(it.into()), TyDefId::TypeAliasId(it) => Some(it.into()), }; - let substs = substs_from_path_segment(ctx, segment, generic_def, false); + let substs = substs_from_path_segment(ctx, segment, generic_def, infer_args); ctx.db.ty(typable).subst(&substs) } @@ -493,6 +507,7 @@ impl Ty { // `ValueTyDefId` is just a convenient way to pass generics and // special-case enum variants resolved: ValueTyDefId, + infer_args: bool, ) -> Substs { let last = path.segments().last().expect("path should have at least one segment"); let (segment, generic_def) = match resolved { @@ -515,22 +530,27 @@ impl Ty { (segment, Some(var.parent.into())) } }; - substs_from_path_segment(ctx, segment, generic_def, false) + substs_from_path_segment(ctx, segment, generic_def, infer_args) } } -pub(super) fn substs_from_path_segment( +fn substs_from_path_segment( ctx: &TyLoweringContext<'_>, segment: PathSegment<'_>, def_generic: Option, - _add_self_param: bool, + infer_args: bool, ) -> Substs { let mut substs = Vec::new(); let def_generics = def_generic.map(|def| generics(ctx.db.upcast(), def)); let (parent_params, self_params, type_params, impl_trait_params) = def_generics.map_or((0, 0, 0, 0), |g| g.provenance_split()); + let total_len = parent_params + self_params + type_params + impl_trait_params; + substs.extend(iter::repeat(Ty::Unknown).take(parent_params)); + + let mut had_explicit_args = false; + if let Some(generic_args) = &segment.args_and_bindings { if !generic_args.has_self_type { substs.extend(iter::repeat(Ty::Unknown).take(self_params)); @@ -542,31 +562,35 @@ pub(super) fn substs_from_path_segment( for arg in generic_args.args.iter().skip(skip).take(expected_num) { match arg { GenericArg::Type(type_ref) => { + had_explicit_args = true; let ty = Ty::from_hir(ctx, type_ref); substs.push(ty); } } } } - let total_len = parent_params + self_params + type_params + impl_trait_params; + + // handle defaults. In expression or pattern path segments without + // explicitly specified type arguments, missing type arguments are inferred + // (i.e. defaults aren't used). + if !infer_args || had_explicit_args { + if let Some(def_generic) = def_generic { + let default_substs = ctx.db.generic_defaults(def_generic); + assert_eq!(total_len, default_substs.len()); + + for default_ty in default_substs.iter().skip(substs.len()) { + substs.push(default_ty.clone()); + } + } + } + // add placeholders for args that were not provided + // FIXME: emit diagnostics in contexts where this is not allowed for _ in substs.len()..total_len { substs.push(Ty::Unknown); } assert_eq!(substs.len(), total_len); - // handle defaults - if let Some(def_generic) = def_generic { - let default_substs = ctx.db.generic_defaults(def_generic); - assert_eq!(substs.len(), default_substs.len()); - - for (i, default_ty) in default_substs.iter().enumerate() { - if substs[i] == Ty::Unknown { - substs[i] = default_ty.clone(); - } - } - } - Substs(substs.into()) } @@ -615,9 +639,7 @@ impl TraitRef { segment: PathSegment<'_>, resolved: TraitId, ) -> Substs { - let has_self_param = - segment.args_and_bindings.as_ref().map(|a| a.has_self_type).unwrap_or(false); - substs_from_path_segment(ctx, segment, Some(resolved.into()), !has_self_param) + substs_from_path_segment(ctx, segment, Some(resolved.into()), false) } pub(crate) fn from_type_bound( diff --git a/crates/ra_hir_ty/src/tests/display_source_code.rs b/crates/ra_hir_ty/src/tests/display_source_code.rs index 4088b1d22d94..5dfa0a014599 100644 --- a/crates/ra_hir_ty/src/tests/display_source_code.rs +++ b/crates/ra_hir_ty/src/tests/display_source_code.rs @@ -29,7 +29,7 @@ fn omit_default_type_parameters() { //- /main.rs struct Foo { t: T } fn main() { - let foo = Foo { t: 5 }; + let foo = Foo { t: 5u8 }; foo<|>; } ", @@ -41,7 +41,7 @@ fn omit_default_type_parameters() { //- /main.rs struct Foo { k: K, t: T } fn main() { - let foo = Foo { k: 400, t: 5 }; + let foo = Foo { k: 400, t: 5u8 }; foo<|>; } ", diff --git a/crates/ra_hir_ty/src/tests/method_resolution.rs b/crates/ra_hir_ty/src/tests/method_resolution.rs index 558a70022dce..804297315c4d 100644 --- a/crates/ra_hir_ty/src/tests/method_resolution.rs +++ b/crates/ra_hir_ty/src/tests/method_resolution.rs @@ -183,60 +183,6 @@ fn test() { ); } -#[test] -fn infer_associated_method_generics_with_default_param() { - assert_snapshot!( - infer(r#" -struct Gen { - val: T -} - -impl Gen { - pub fn make() -> Gen { - loop { } - } -} - -fn test() { - let a = Gen::make(); -} -"#), - @r###" - 80..104 '{ ... }': Gen - 90..98 'loop { }': ! - 95..98 '{ }': () - 118..146 '{ ...e(); }': () - 128..129 'a': Gen - 132..141 'Gen::make': fn make() -> Gen - 132..143 'Gen::make()': Gen - "### - ); -} - -#[test] -fn infer_associated_method_generics_with_default_tuple_param() { - let t = type_at( - r#" -//- /main.rs -struct Gen { - val: T -} - -impl Gen { - pub fn make() -> Gen { - loop { } - } -} - -fn test() { - let a = Gen::make(); - a.val<|>; -} -"#, - ); - assert_eq!(t, "()"); -} - #[test] fn infer_associated_method_generics_without_args() { assert_snapshot!( diff --git a/crates/ra_hir_ty/src/tests/simple.rs b/crates/ra_hir_ty/src/tests/simple.rs index 88309157b78c..8a5031756fad 100644 --- a/crates/ra_hir_ty/src/tests/simple.rs +++ b/crates/ra_hir_ty/src/tests/simple.rs @@ -1997,3 +1997,111 @@ fn foo() { "### ); } + +#[test] +fn generic_default() { + assert_snapshot!( + infer(r#" +struct Thing { t: T } +enum OtherThing { + One { t: T }, + Two(T), +} + +fn test(t1: Thing, t2: OtherThing, t3: Thing, t4: OtherThing) { + t1.t; + t3.t; + match t2 { + OtherThing::One { t } => { t; }, + OtherThing::Two(t) => { t; }, + } + match t4 { + OtherThing::One { t } => { t; }, + OtherThing::Two(t) => { t; }, + } +} +"#), + @r###" + 98..100 't1': Thing<()> + 109..111 't2': OtherThing<()> + 125..127 't3': Thing + 141..143 't4': OtherThing + 162..385 '{ ... } }': () + 168..170 't1': Thing<()> + 168..172 't1.t': () + 178..180 't3': Thing + 178..182 't3.t': i32 + 188..283 'match ... }': () + 194..196 't2': OtherThing<()> + 207..228 'OtherT... { t }': OtherThing<()> + 225..226 't': () + 232..238 '{ t; }': () + 234..235 't': () + 248..266 'OtherT...Two(t)': OtherThing<()> + 264..265 't': () + 270..276 '{ t; }': () + 272..273 't': () + 288..383 'match ... }': () + 294..296 't4': OtherThing + 307..328 'OtherT... { t }': OtherThing + 325..326 't': i32 + 332..338 '{ t; }': () + 334..335 't': i32 + 348..366 'OtherT...Two(t)': OtherThing + 364..365 't': i32 + 370..376 '{ t; }': () + 372..373 't': i32 + "### + ); +} + +#[test] +fn generic_default_in_struct_literal() { + assert_snapshot!( + infer(r#" +struct Thing { t: T } +enum OtherThing { + One { t: T }, + Two(T), +} + +fn test() { + let x = Thing { t: loop {} }; + let y = Thing { t: () }; + let z = Thing { t: 1i32 }; + if let Thing { t } = z { + t; + } + + let a = OtherThing::One { t: 1i32 }; + let b = OtherThing::Two(1i32); +} +"#), + @r###" + 100..320 '{ ...32); }': () + 110..111 'x': Thing + 114..134 'Thing ...p {} }': Thing + 125..132 'loop {}': ! + 130..132 '{}': () + 144..145 'y': Thing<()> + 148..163 'Thing { t: () }': Thing<()> + 159..161 '()': () + 173..174 'z': Thing + 177..194 'Thing ...1i32 }': Thing + 188..192 '1i32': i32 + 200..241 'if let... }': () + 207..218 'Thing { t }': Thing + 215..216 't': i32 + 221..222 'z': Thing + 223..241 '{ ... }': () + 233..234 't': i32 + 251..252 'a': OtherThing + 255..282 'OtherT...1i32 }': OtherThing + 276..280 '1i32': i32 + 292..293 'b': OtherThing + 296..311 'OtherThing::Two': Two(i32) -> OtherThing + 296..317 'OtherT...(1i32)': OtherThing + 312..316 '1i32': i32 + "### + ); +} diff --git a/crates/ra_hir_ty/src/tests/traits.rs b/crates/ra_hir_ty/src/tests/traits.rs index 0c538a62d920..133fb5f398c9 100644 --- a/crates/ra_hir_ty/src/tests/traits.rs +++ b/crates/ra_hir_ty/src/tests/traits.rs @@ -1806,33 +1806,33 @@ fn test() { } "#), @r###" -65..69 'self': &Self -166..170 'self': Self -172..176 'args': Args -240..244 'self': &Foo -255..257 '{}': () -335..336 'f': F -355..357 '{}': () -444..690 '{ ...o(); }': () -454..459 'lazy1': Lazy T> -476..485 'Lazy::new': fn new T>(fn() -> T) -> Lazy T> -476..493 'Lazy::...| Foo)': Lazy T> -486..492 '|| Foo': || -> T -489..492 'Foo': Foo -503..505 'r1': {unknown} -508..513 'lazy1': Lazy T> -508..519 'lazy1.foo()': {unknown} -561..576 'make_foo_fn_ptr': fn() -> Foo -592..603 'make_foo_fn': fn make_foo_fn() -> Foo -613..618 'lazy2': Lazy T> -635..644 'Lazy::new': fn new T>(fn() -> T) -> Lazy T> -635..661 'Lazy::...n_ptr)': Lazy T> -645..660 'make_foo_fn_ptr': fn() -> Foo -671..673 'r2': {unknown} -676..681 'lazy2': Lazy T> -676..687 'lazy2.foo()': {unknown} -550..552 '{}': () -"### + 65..69 'self': &Self + 166..170 'self': Self + 172..176 'args': Args + 240..244 'self': &Foo + 255..257 '{}': () + 335..336 'f': F + 355..357 '{}': () + 444..690 '{ ...o(); }': () + 454..459 'lazy1': Lazy Foo> + 476..485 'Lazy::new': fn new Foo>(|| -> Foo) -> Lazy Foo> + 476..493 'Lazy::...| Foo)': Lazy Foo> + 486..492 '|| Foo': || -> Foo + 489..492 'Foo': Foo + 503..505 'r1': usize + 508..513 'lazy1': Lazy Foo> + 508..519 'lazy1.foo()': usize + 561..576 'make_foo_fn_ptr': fn() -> Foo + 592..603 'make_foo_fn': fn make_foo_fn() -> Foo + 613..618 'lazy2': Lazy Foo> + 635..644 'Lazy::new': fn new Foo>(fn() -> Foo) -> Lazy Foo> + 635..661 'Lazy::...n_ptr)': Lazy Foo> + 645..660 'make_foo_fn_ptr': fn() -> Foo + 671..673 'r2': {unknown} + 676..681 'lazy2': Lazy Foo> + 676..687 'lazy2.foo()': {unknown} + 550..552 '{}': () + "### ); } diff --git a/crates/ra_ide/src/hover.rs b/crates/ra_ide/src/hover.rs index 62df074594b5..846d8c69ba26 100644 --- a/crates/ra_ide/src/hover.rs +++ b/crates/ra_ide/src/hover.rs @@ -529,7 +529,7 @@ struct Test { } fn main() { - let zz<|> = Test { t: 23, k: 33 }; + let zz<|> = Test { t: 23u8, k: 33 }; }"#, &["Test"], ); diff --git a/crates/ra_ide/src/inlay_hints.rs b/crates/ra_ide/src/inlay_hints.rs index 49366de983be..7eb2cef735f4 100644 --- a/crates/ra_ide/src/inlay_hints.rs +++ b/crates/ra_ide/src/inlay_hints.rs @@ -415,7 +415,7 @@ struct Test { } fn main() { - let zz = Test { t: 23, k: 33 }; + let zz = Test { t: 23u8, k: 33 }; let zz_ref = &zz; }"#, ); @@ -428,7 +428,7 @@ fn main() { label: "Test", }, InlayHint { - range: 105..111, + range: 107..113, kind: TypeHint, label: "&Test", }, From b91fa7494ece3ca26e5797c4b23469d82b8e1322 Mon Sep 17 00:00:00 2001 From: vsrs Date: Sat, 6 Jun 2020 15:12:17 +0300 Subject: [PATCH 094/172] Fix Run lens. --- editors/code/src/run.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/editors/code/src/run.ts b/editors/code/src/run.ts index 5c790741f387..bb060cfe1554 100644 --- a/editors/code/src/run.ts +++ b/editors/code/src/run.ts @@ -110,7 +110,7 @@ export function createTask(runnable: ra.Runnable): vscode.Task { switch (runnable.kind) { case "cargo": command = toolchain.getPathForExecutable("cargo"); } - const args = runnable.args.cargoArgs; + const args = [...runnable.args.cargoArgs]; // should be a copy! if (runnable.args.executableArgs.length > 0) { args.push('--', ...runnable.args.executableArgs); } From de74c0dcab2efd50d68f70d15de3fced718e8c7a Mon Sep 17 00:00:00 2001 From: vsrs Date: Sat, 6 Jun 2020 12:00:46 +0300 Subject: [PATCH 095/172] Preliminary runnables refactoring --- crates/ra_ide/src/runnables.rs | 78 +++++++++++++++++ .../rust-analyzer/src/main_loop/handlers.rs | 86 +++++++++---------- crates/rust-analyzer/src/to_proto.rs | 12 +-- 3 files changed, 122 insertions(+), 54 deletions(-) diff --git a/crates/ra_ide/src/runnables.rs b/crates/ra_ide/src/runnables.rs index f32ce0d229b8..9f7b5edfd576 100644 --- a/crates/ra_ide/src/runnables.rs +++ b/crates/ra_ide/src/runnables.rs @@ -42,6 +42,42 @@ pub enum RunnableKind { Bin, } +#[derive(Debug, Eq, PartialEq)] +pub struct RunnableAction { + pub run_title: &'static str, + pub debugee: bool, +} + +const TEST: RunnableAction = RunnableAction { run_title: "▶\u{fe0e} Run Test", debugee: true }; +const DOCTEST: RunnableAction = + RunnableAction { run_title: "▶\u{fe0e} Run Doctest", debugee: false }; +const BENCH: RunnableAction = RunnableAction { run_title: "▶\u{fe0e} Run Bench", debugee: true }; +const BIN: RunnableAction = RunnableAction { run_title: "▶\u{fe0e} Run", debugee: true }; + +impl Runnable { + // test package::module::testname + pub fn label(&self, target: Option) -> String { + match &self.kind { + RunnableKind::Test { test_id, .. } => format!("test {}", test_id), + RunnableKind::TestMod { path } => format!("test-mod {}", path), + RunnableKind::Bench { test_id } => format!("bench {}", test_id), + RunnableKind::DocTest { test_id, .. } => format!("doctest {}", test_id), + RunnableKind::Bin => { + target.map_or_else(|| "run binary".to_string(), |t| format!("run {}", t)) + } + } + } + + pub fn action(&self) -> &'static RunnableAction { + match &self.kind { + RunnableKind::Test { .. } | RunnableKind::TestMod { .. } => &TEST, + RunnableKind::DocTest { .. } => &DOCTEST, + RunnableKind::Bench { .. } => &BENCH, + RunnableKind::Bin => &BIN, + } + } +} + // Feature: Run // // Shows a popup suggesting to run a test/benchmark/binary **at the current cursor @@ -207,6 +243,15 @@ mod tests { use crate::mock_analysis::analysis_and_position; + use super::{Runnable, RunnableAction, BENCH, BIN, DOCTEST, TEST}; + + fn assert_actions(runnables: &[Runnable], actions: &[&RunnableAction]) { + assert_eq!( + actions, + runnables.into_iter().map(|it| it.action()).collect::>().as_slice() + ); + } + #[test] fn test_runnables() { let (analysis, pos) = analysis_and_position( @@ -221,6 +266,9 @@ mod tests { #[test] #[ignore] fn test_foo() {} + + #[bench] + fn bench() {} "#, ); let runnables = analysis.runnables(pos.file_id).unwrap(); @@ -295,9 +343,32 @@ mod tests { }, cfg_exprs: [], }, + Runnable { + nav: NavigationTarget { + file_id: FileId( + 1, + ), + full_range: 82..104, + name: "bench", + kind: FN_DEF, + focus_range: Some( + 94..99, + ), + container_name: None, + description: None, + docs: None, + }, + kind: Bench { + test_id: Path( + "bench", + ), + }, + cfg_exprs: [], + }, ] "### ); + assert_actions(&runnables, &[&BIN, &TEST, &TEST, &BENCH]); } #[test] @@ -361,6 +432,7 @@ mod tests { ] "### ); + assert_actions(&runnables, &[&BIN, &DOCTEST]); } #[test] @@ -427,6 +499,7 @@ mod tests { ] "### ); + assert_actions(&runnables, &[&BIN, &DOCTEST]); } #[test] @@ -493,6 +566,7 @@ mod tests { ] "### ); + assert_actions(&runnables, &[&TEST, &TEST]); } #[test] @@ -561,6 +635,7 @@ mod tests { ] "### ); + assert_actions(&runnables, &[&TEST, &TEST]); } #[test] @@ -631,6 +706,7 @@ mod tests { ] "### ); + assert_actions(&runnables, &[&TEST, &TEST]); } #[test] @@ -681,6 +757,7 @@ mod tests { ] "### ); + assert_actions(&runnables, &[&TEST]); } #[test] @@ -739,6 +816,7 @@ mod tests { ] "### ); + assert_actions(&runnables, &[&TEST]); } #[test] diff --git a/crates/rust-analyzer/src/main_loop/handlers.rs b/crates/rust-analyzer/src/main_loop/handlers.rs index 3ff7797024cf..da16976d3daa 100644 --- a/crates/rust-analyzer/src/main_loop/handlers.rs +++ b/crates/rust-analyzer/src/main_loop/handlers.rs @@ -18,7 +18,7 @@ use lsp_types::{ TextDocumentIdentifier, Url, WorkspaceEdit, }; use ra_ide::{ - FileId, FilePosition, FileRange, HoverAction, Query, RangeInfo, RunnableKind, SearchScope, + FileId, FilePosition, FileRange, HoverAction, Query, RangeInfo, Runnable, RunnableKind, SearchScope, TextEdit, }; use ra_prof::profile; @@ -403,16 +403,11 @@ pub fn handle_runnables( if !runnable.nav.full_range().contains_inclusive(offset) { continue; } + } + if is_lib_target(&runnable, cargo_spec.as_ref()) { + continue; } - // Do not suggest binary run on other target than binary - if let RunnableKind::Bin = runnable.kind { - if let Some(spec) = &cargo_spec { - match spec.target_kind { - TargetKind::Bin => {} - _ => continue, - } - } - } + res.push(to_proto::runnable(&snap, file_id, runnable)?); } @@ -817,53 +812,26 @@ pub fn handle_code_lens( if snap.config.lens.runnable() { // Gather runnables for runnable in snap.analysis().runnables(file_id)? { - let (run_title, debugee) = match &runnable.kind { - RunnableKind::Test { .. } | RunnableKind::TestMod { .. } => { - ("▶\u{fe0e} Run Test", true) - } - RunnableKind::DocTest { .. } => { - // cargo does not support -no-run for doctests - ("▶\u{fe0e} Run Doctest", false) - } - RunnableKind::Bench { .. } => { - // Nothing wrong with bench debugging - ("Run Bench", true) - } - RunnableKind::Bin => { - // Do not suggest binary run on other target than binary - match &cargo_spec { - Some(spec) => match spec.target_kind { - TargetKind::Bin => ("Run", true), - _ => continue, - }, - None => continue, - } - } - }; + if is_lib_target(&runnable, cargo_spec.as_ref()) { + continue; + } + let action = runnable.action(); let range = to_proto::range(&line_index, runnable.nav.range()); let r = to_proto::runnable(&snap, file_id, runnable)?; if snap.config.lens.run { let lens = CodeLens { range, - command: Some(Command { - title: run_title.to_string(), - command: "rust-analyzer.runSingle".into(), - arguments: Some(vec![to_value(&r).unwrap()]), - }), + command: Some(run_single_command(&r, action.run_title)), data: None, }; lenses.push(lens); } - if debugee && snap.config.lens.debug { + if action.debugee && snap.config.lens.debug { let debug_lens = CodeLens { range, - command: Some(Command { - title: "Debug".into(), - command: "rust-analyzer.debugSingle".into(), - arguments: Some(vec![to_value(r).unwrap()]), - }), + command: Some(debug_single_command(r)), data: None, }; lenses.push(debug_lens); @@ -1169,6 +1137,22 @@ fn show_references_command( } } +fn run_single_command(runnable: &lsp_ext::Runnable, title: &str) -> Command { + Command { + title: title.to_string(), + command: "rust-analyzer.runSingle".into(), + arguments: Some(vec![to_value(runnable).unwrap()]), + } +} + +fn debug_single_command(runnable: lsp_ext::Runnable) -> Command { + Command { + title: "Debug".into(), + command: "rust-analyzer.debugSingle".into(), + arguments: Some(vec![to_value(runnable).unwrap()]), + } +} + fn to_command_link(command: Command, tooltip: String) -> lsp_ext::CommandLink { lsp_ext::CommandLink { tooltip: Some(tooltip), command } } @@ -1214,3 +1198,17 @@ fn prepare_hover_actions( }) .collect() } + +fn is_lib_target(runnable: &Runnable, cargo_spec: Option<&CargoTargetSpec>) -> bool { + // Do not suggest binary run on other target than binary + if let RunnableKind::Bin = runnable.kind { + if let Some(spec) = cargo_spec { + match spec.target_kind { + TargetKind::Bin => return true, + _ => () + } + } + } + + false +} \ No newline at end of file diff --git a/crates/rust-analyzer/src/to_proto.rs b/crates/rust-analyzer/src/to_proto.rs index 1da4d80eceab..710df1fbdec4 100644 --- a/crates/rust-analyzer/src/to_proto.rs +++ b/crates/rust-analyzer/src/to_proto.rs @@ -4,7 +4,7 @@ use ra_ide::{ Assist, CompletionItem, CompletionItemKind, Documentation, FileSystemEdit, Fold, FoldKind, FunctionSignature, Highlight, HighlightModifier, HighlightTag, HighlightedRange, Indel, InlayHint, InlayKind, InsertTextFormat, LineIndex, NavigationTarget, ReferenceAccess, - ResolvedAssist, Runnable, RunnableKind, Severity, SourceChange, SourceFileEdit, TextEdit, + ResolvedAssist, Runnable, Severity, SourceChange, SourceFileEdit, TextEdit, }; use ra_syntax::{SyntaxKind, TextRange, TextSize}; use ra_vfs::LineEndings; @@ -662,15 +662,7 @@ pub(crate) fn runnable( let target = spec.as_ref().map(|s| s.target.clone()); let (cargo_args, executable_args) = CargoTargetSpec::runnable_args(spec, &runnable.kind, &runnable.cfg_exprs)?; - let label = match &runnable.kind { - RunnableKind::Test { test_id, .. } => format!("test {}", test_id), - RunnableKind::TestMod { path } => format!("test-mod {}", path), - RunnableKind::Bench { test_id } => format!("bench {}", test_id), - RunnableKind::DocTest { test_id, .. } => format!("doctest {}", test_id), - RunnableKind::Bin => { - target.map_or_else(|| "run binary".to_string(), |t| format!("run {}", t)) - } - }; + let label = runnable.label(target); let location = location_link(snap, None, runnable.nav)?; Ok(lsp_ext::Runnable { From 3434f1dd2c47fff3df159b9d62115c2df3fd6401 Mon Sep 17 00:00:00 2001 From: vsrs Date: Sat, 6 Jun 2020 14:30:29 +0300 Subject: [PATCH 096/172] Add Run|Debug hover actions --- crates/ra_ide/src/hover.rs | 129 +++++++++++++++++- crates/ra_ide/src/runnables.rs | 14 +- crates/rust-analyzer/src/config.rs | 2 + .../rust-analyzer/src/main_loop/handlers.rs | 49 +++++-- crates/rust-analyzer/src/to_proto.rs | 4 +- editors/code/package.json | 10 ++ 6 files changed, 184 insertions(+), 24 deletions(-) diff --git a/crates/ra_ide/src/hover.rs b/crates/ra_ide/src/hover.rs index 846d8c69ba26..138a7a7a9cb2 100644 --- a/crates/ra_ide/src/hover.rs +++ b/crates/ra_ide/src/hover.rs @@ -14,34 +14,42 @@ use ra_syntax::{ast, match_ast, AstNode, SyntaxKind::*, SyntaxToken, TokenAtOffs use crate::{ display::{macro_label, rust_code_markup, rust_code_markup_with_doc, ShortLabel, ToNav}, - FilePosition, NavigationTarget, RangeInfo, + runnables::runnable, + FileId, FilePosition, NavigationTarget, RangeInfo, Runnable, }; #[derive(Clone, Debug, PartialEq, Eq)] pub struct HoverConfig { pub implementations: bool, + pub run: bool, + pub debug: bool, } impl Default for HoverConfig { fn default() -> Self { - Self { implementations: true } + Self { implementations: true, run: true, debug: true } } } impl HoverConfig { - pub const NO_ACTIONS: Self = Self { implementations: false }; + pub const NO_ACTIONS: Self = Self { implementations: false, run: false, debug: false }; pub fn any(&self) -> bool { - self.implementations + self.implementations || self.runnable() } pub fn none(&self) -> bool { !self.any() } + + pub fn runnable(&self) -> bool { + self.run || self.debug + } } #[derive(Debug, Clone)] pub enum HoverAction { + Runnable(Runnable), Implementaion(FilePosition), } @@ -125,6 +133,10 @@ pub(crate) fn hover(db: &RootDatabase, position: FilePosition) -> Option Option, + def: Definition, + file_id: FileId, +) -> Option { + match def { + Definition::ModuleDef(it) => match it { + ModuleDef::Module(it) => match it.definition_source(sema.db).value { + ModuleSource::Module(it) => runnable(&sema, it.syntax().clone(), file_id) + .map(|it| HoverAction::Runnable(it)), + _ => None, + }, + ModuleDef::Function(it) => { + runnable(&sema, it.source(sema.db).value.syntax().clone(), file_id) + .map(|it| HoverAction::Runnable(it)) + } + _ => None, + }, + _ => None, + } +} + fn hover_text( docs: Option, desc: Option, @@ -292,6 +326,7 @@ fn pick_best(tokens: TokenAtOffset) -> Option { #[cfg(test)] mod tests { use super::*; + use insta::assert_debug_snapshot; use ra_db::FileLoader; use ra_syntax::TextRange; @@ -309,6 +344,7 @@ mod tests { fn assert_impl_action(action: &HoverAction, position: u32) { let offset = match action { HoverAction::Implementaion(pos) => pos.offset, + it => panic!("Unexpected hover action: {:#?}", it), }; assert_eq!(offset, position.into()); } @@ -1176,4 +1212,89 @@ fn func(foo: i32) { if true { <|>foo; }; } ); assert_impl_action(&actions[0], 5); } + + #[test] + fn test_hover_test_has_action() { + let (_, actions) = check_hover_result( + " + //- /lib.rs + #[test] + fn foo_<|>test() {} + ", + &["fn foo_test()"], + ); + assert_debug_snapshot!(actions, + @r###" + [ + Runnable( + Runnable { + nav: NavigationTarget { + file_id: FileId( + 1, + ), + full_range: 0..24, + name: "foo_test", + kind: FN_DEF, + focus_range: Some( + 11..19, + ), + container_name: None, + description: None, + docs: None, + }, + kind: Test { + test_id: Path( + "foo_test", + ), + attr: TestAttr { + ignore: false, + }, + }, + cfg_exprs: [], + }, + ), + ] + "###); + } + + #[test] + fn test_hover_test_mod_has_action() { + let (_, actions) = check_hover_result( + " + //- /lib.rs + mod tests<|> { + #[test] + fn foo_test() {} + } + ", + &["mod tests"], + ); + assert_debug_snapshot!(actions, + @r###" + [ + Runnable( + Runnable { + nav: NavigationTarget { + file_id: FileId( + 1, + ), + full_range: 0..46, + name: "tests", + kind: MODULE, + focus_range: Some( + 4..9, + ), + container_name: None, + description: None, + docs: None, + }, + kind: TestMod { + path: "tests", + }, + cfg_exprs: [], + }, + ), + ] + "###); + } } diff --git a/crates/ra_ide/src/runnables.rs b/crates/ra_ide/src/runnables.rs index 9f7b5edfd576..fc57dc33d78d 100644 --- a/crates/ra_ide/src/runnables.rs +++ b/crates/ra_ide/src/runnables.rs @@ -11,14 +11,14 @@ use ra_syntax::{ use crate::{display::ToNav, FileId, NavigationTarget}; -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct Runnable { pub nav: NavigationTarget, pub kind: RunnableKind, pub cfg_exprs: Vec, } -#[derive(Debug)] +#[derive(Debug, Clone)] pub enum TestId { Name(String), Path(String), @@ -33,7 +33,7 @@ impl fmt::Display for TestId { } } -#[derive(Debug)] +#[derive(Debug, Clone)] pub enum RunnableKind { Test { test_id: TestId, attr: TestAttr }, TestMod { path: String }, @@ -95,7 +95,11 @@ pub(crate) fn runnables(db: &RootDatabase, file_id: FileId) -> Vec { source_file.syntax().descendants().filter_map(|i| runnable(&sema, i, file_id)).collect() } -fn runnable(sema: &Semantics, item: SyntaxNode, file_id: FileId) -> Option { +pub(crate) fn runnable( + sema: &Semantics, + item: SyntaxNode, + file_id: FileId, +) -> Option { match_ast! { match item { ast::FnDef(it) => runnable_fn(sema, it, file_id), @@ -171,7 +175,7 @@ fn runnable_fn( Some(Runnable { nav, kind, cfg_exprs }) } -#[derive(Debug)] +#[derive(Debug, Copy, Clone)] pub struct TestAttr { pub ignore: bool, } diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs index 8d6efdbe8cf9..17671f89ee41 100644 --- a/crates/rust-analyzer/src/config.rs +++ b/crates/rust-analyzer/src/config.rs @@ -285,6 +285,8 @@ impl Config { set(value, "/hoverActions/enable", &mut use_hover_actions); if use_hover_actions { set(value, "/hoverActions/implementations", &mut self.hover.implementations); + set(value, "/hoverActions/run", &mut self.hover.run); + set(value, "/hoverActions/debug", &mut self.hover.debug); } else { self.hover = HoverConfig::NO_ACTIONS; } diff --git a/crates/rust-analyzer/src/main_loop/handlers.rs b/crates/rust-analyzer/src/main_loop/handlers.rs index da16976d3daa..cae447eea872 100644 --- a/crates/rust-analyzer/src/main_loop/handlers.rs +++ b/crates/rust-analyzer/src/main_loop/handlers.rs @@ -18,8 +18,8 @@ use lsp_types::{ TextDocumentIdentifier, Url, WorkspaceEdit, }; use ra_ide::{ - FileId, FilePosition, FileRange, HoverAction, Query, RangeInfo, Runnable, RunnableKind, SearchScope, - TextEdit, + FileId, FilePosition, FileRange, HoverAction, Query, RangeInfo, Runnable, RunnableKind, + SearchScope, TextEdit, }; use ra_prof::profile; use ra_project_model::TargetKind; @@ -403,12 +403,12 @@ pub fn handle_runnables( if !runnable.nav.full_range().contains_inclusive(offset) { continue; } - } + } if is_lib_target(&runnable, cargo_spec.as_ref()) { continue; } - res.push(to_proto::runnable(&snap, file_id, runnable)?); + res.push(to_proto::runnable(&snap, file_id, &runnable)?); } // Add `cargo check` and `cargo test` for the whole package @@ -550,7 +550,7 @@ pub fn handle_hover( }), range: Some(range), }, - actions: prepare_hover_actions(&snap, info.info.actions()), + actions: prepare_hover_actions(&snap, position.file_id, info.info.actions()), }; Ok(Some(hover)) @@ -818,7 +818,7 @@ pub fn handle_code_lens( let action = runnable.action(); let range = to_proto::range(&line_index, runnable.nav.range()); - let r = to_proto::runnable(&snap, file_id, runnable)?; + let r = to_proto::runnable(&snap, file_id, &runnable)?; if snap.config.lens.run { let lens = CodeLens { range, @@ -829,11 +829,8 @@ pub fn handle_code_lens( } if action.debugee && snap.config.lens.debug { - let debug_lens = CodeLens { - range, - command: Some(debug_single_command(r)), - data: None, - }; + let debug_lens = + CodeLens { range, command: Some(debug_single_command(r)), data: None }; lenses.push(debug_lens); } } @@ -1183,8 +1180,33 @@ fn show_impl_command_link( None } +fn to_runnable_action( + snap: &GlobalStateSnapshot, + file_id: FileId, + runnable: &Runnable, +) -> Option { + to_proto::runnable(snap, file_id, runnable).ok().map(|r| { + let mut group = lsp_ext::CommandLinkGroup::default(); + + let action = runnable.action(); + if snap.config.hover.run { + let run_command = run_single_command(&r, action.run_title); + group.commands.push(to_command_link(run_command, r.label.clone())); + } + + if snap.config.hover.debug { + let hint = r.label.clone(); + let dbg_command = debug_single_command(r); + group.commands.push(to_command_link(dbg_command, hint)); + } + + group + }) +} + fn prepare_hover_actions( snap: &GlobalStateSnapshot, + file_id: FileId, actions: &[HoverAction], ) -> Vec { if snap.config.hover.none() || !snap.config.client_caps.hover_actions { @@ -1195,6 +1217,7 @@ fn prepare_hover_actions( .iter() .filter_map(|it| match it { HoverAction::Implementaion(position) => show_impl_command_link(snap, position), + HoverAction::Runnable(r) => to_runnable_action(snap, file_id, r), }) .collect() } @@ -1205,10 +1228,10 @@ fn is_lib_target(runnable: &Runnable, cargo_spec: Option<&CargoTargetSpec>) -> b if let Some(spec) = cargo_spec { match spec.target_kind { TargetKind::Bin => return true, - _ => () + _ => (), } } } false -} \ No newline at end of file +} diff --git a/crates/rust-analyzer/src/to_proto.rs b/crates/rust-analyzer/src/to_proto.rs index 710df1fbdec4..5daf037dabf3 100644 --- a/crates/rust-analyzer/src/to_proto.rs +++ b/crates/rust-analyzer/src/to_proto.rs @@ -656,14 +656,14 @@ pub(crate) fn resolved_code_action( pub(crate) fn runnable( snap: &GlobalStateSnapshot, file_id: FileId, - runnable: Runnable, + runnable: &Runnable, ) -> Result { let spec = CargoTargetSpec::for_file(snap, file_id)?; let target = spec.as_ref().map(|s| s.target.clone()); let (cargo_args, executable_args) = CargoTargetSpec::runnable_args(spec, &runnable.kind, &runnable.cfg_exprs)?; let label = runnable.label(target); - let location = location_link(snap, None, runnable.nav)?; + let location = location_link(snap, None, runnable.nav.clone())?; Ok(lsp_ext::Runnable { label, diff --git a/editors/code/package.json b/editors/code/package.json index b9c57db3bcf7..7fdb5c27d9d1 100644 --- a/editors/code/package.json +++ b/editors/code/package.json @@ -486,6 +486,16 @@ "type": "boolean", "default": true }, + "rust-analyzer.hoverActions.run": { + "markdownDescription": "Whether to show `Run` action. Only applies when `#rust-analyzer.hoverActions.enable#` is set.", + "type": "boolean", + "default": true + }, + "rust-analyzer.hoverActions.debug": { + "markdownDescription": "Whether to show `Debug` action. Only applies when `#rust-analyzer.hoverActions.enable#` is set.", + "type": "boolean", + "default": true + }, "rust-analyzer.linkedProjects": { "markdownDescription": [ "Disable project auto-discovery in favor of explicitly specified set of projects.", From d66daee84906358c0119ed2eb29386454f5e4350 Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Sat, 6 Jun 2020 17:52:00 +0200 Subject: [PATCH 097/172] Clean up handling of int/float literal types 'Unknown' int/float types actually never exist as such, they get replaced by type variables immediately. So the whole `Uncertain` thing was unnecessary and just led to a bunch of match branches that were never hit. --- crates/ra_hir_ty/src/infer.rs | 13 ++--- crates/ra_hir_ty/src/infer/expr.rs | 28 +++++----- crates/ra_hir_ty/src/lib.rs | 6 +-- crates/ra_hir_ty/src/method_resolution.rs | 12 ++--- crates/ra_hir_ty/src/primitive.rs | 54 -------------------- crates/ra_hir_ty/src/traits/chalk/mapping.rs | 24 ++++----- 6 files changed, 33 insertions(+), 104 deletions(-) diff --git a/crates/ra_hir_ty/src/infer.rs b/crates/ra_hir_ty/src/infer.rs index 2e16e5120db0..f965eb2b5c99 100644 --- a/crates/ra_hir_ty/src/infer.rs +++ b/crates/ra_hir_ty/src/infer.rs @@ -39,8 +39,7 @@ use ra_syntax::SmolStr; use super::{ primitive::{FloatTy, IntTy}, traits::{Guidance, Obligation, ProjectionPredicate, Solution}, - ApplicationTy, InEnvironment, ProjectionTy, Substs, TraitEnvironment, TraitRef, Ty, TypeCtor, - TypeWalk, Uncertain, + InEnvironment, ProjectionTy, Substs, TraitEnvironment, TraitRef, Ty, TypeCtor, TypeWalk, }; use crate::{ db::HirDatabase, infer::diagnostics::InferenceDiagnostic, lower::ImplTraitLoweringMode, @@ -312,12 +311,6 @@ impl<'a> InferenceContext<'a> { fn insert_type_vars_shallow(&mut self, ty: Ty) -> Ty { match ty { Ty::Unknown => self.table.new_type_var(), - Ty::Apply(ApplicationTy { ctor: TypeCtor::Int(Uncertain::Unknown), .. }) => { - self.table.new_integer_var() - } - Ty::Apply(ApplicationTy { ctor: TypeCtor::Float(Uncertain::Unknown), .. }) => { - self.table.new_float_var() - } _ => ty, } } @@ -664,8 +657,8 @@ impl InferTy { fn fallback_value(self) -> Ty { match self { InferTy::TypeVar(..) => Ty::Unknown, - InferTy::IntVar(..) => Ty::simple(TypeCtor::Int(Uncertain::Known(IntTy::i32()))), - InferTy::FloatVar(..) => Ty::simple(TypeCtor::Float(Uncertain::Known(FloatTy::f64()))), + InferTy::IntVar(..) => Ty::simple(TypeCtor::Int(IntTy::i32())), + InferTy::FloatVar(..) => Ty::simple(TypeCtor::Float(FloatTy::f64())), InferTy::MaybeNeverTypeVar(..) => Ty::simple(TypeCtor::Never), } } diff --git a/crates/ra_hir_ty/src/infer/expr.rs b/crates/ra_hir_ty/src/infer/expr.rs index 4a98e2debff5..9fd310f69a79 100644 --- a/crates/ra_hir_ty/src/infer/expr.rs +++ b/crates/ra_hir_ty/src/infer/expr.rs @@ -18,7 +18,7 @@ use crate::{ traits::InEnvironment, utils::{generics, variant_data, Generics}, ApplicationTy, Binders, CallableDef, InferTy, IntTy, Mutability, Obligation, Rawness, Substs, - TraitRef, Ty, TypeCtor, Uncertain, + TraitRef, Ty, TypeCtor, }; use super::{ @@ -426,15 +426,7 @@ impl<'a> InferenceContext<'a> { match &inner_ty { // Fast path for builtins Ty::Apply(ApplicationTy { - ctor: - TypeCtor::Int(Uncertain::Known(IntTy { - signedness: Signedness::Signed, - .. - })), - .. - }) - | Ty::Apply(ApplicationTy { - ctor: TypeCtor::Int(Uncertain::Unknown), + ctor: TypeCtor::Int(IntTy { signedness: Signedness::Signed, .. }), .. }) | Ty::Apply(ApplicationTy { ctor: TypeCtor::Float(_), .. }) @@ -577,9 +569,7 @@ impl<'a> InferenceContext<'a> { ); self.infer_expr( *repeat, - &Expectation::has_type(Ty::simple(TypeCtor::Int(Uncertain::Known( - IntTy::usize(), - )))), + &Expectation::has_type(Ty::simple(TypeCtor::Int(IntTy::usize()))), ); } } @@ -592,13 +582,19 @@ impl<'a> InferenceContext<'a> { Ty::apply_one(TypeCtor::Ref(Mutability::Shared), Ty::simple(TypeCtor::Str)) } Literal::ByteString(..) => { - let byte_type = Ty::simple(TypeCtor::Int(Uncertain::Known(IntTy::u8()))); + let byte_type = Ty::simple(TypeCtor::Int(IntTy::u8())); let array_type = Ty::apply_one(TypeCtor::Array, byte_type); Ty::apply_one(TypeCtor::Ref(Mutability::Shared), array_type) } Literal::Char(..) => Ty::simple(TypeCtor::Char), - Literal::Int(_v, ty) => Ty::simple(TypeCtor::Int((*ty).into())), - Literal::Float(_v, ty) => Ty::simple(TypeCtor::Float((*ty).into())), + Literal::Int(_v, ty) => match ty { + Some(int_ty) => Ty::simple(TypeCtor::Int((*int_ty).into())), + None => self.table.new_integer_var(), + }, + Literal::Float(_v, ty) => match ty { + Some(float_ty) => Ty::simple(TypeCtor::Float((*float_ty).into())), + None => self.table.new_float_var(), + }, }, }; // use a new type variable if we got Ty::Unknown here diff --git a/crates/ra_hir_ty/src/lib.rs b/crates/ra_hir_ty/src/lib.rs index 135976fcda9e..2b9372b4b1bc 100644 --- a/crates/ra_hir_ty/src/lib.rs +++ b/crates/ra_hir_ty/src/lib.rs @@ -58,7 +58,7 @@ use ra_db::{impl_intern_key, salsa, CrateId}; use crate::{ db::HirDatabase, - primitive::{FloatTy, IntTy, Uncertain}, + primitive::{FloatTy, IntTy}, utils::{generics, make_mut_slice, Generics}, }; use display::HirDisplay; @@ -87,10 +87,10 @@ pub enum TypeCtor { Char, /// A primitive integer type. For example, `i32`. - Int(Uncertain), + Int(IntTy), /// A primitive floating-point type. For example, `f64`. - Float(Uncertain), + Float(FloatTy), /// Structures, enumerations and unions. Adt(AdtId), diff --git a/crates/ra_hir_ty/src/method_resolution.rs b/crates/ra_hir_ty/src/method_resolution.rs index e19628fdf728..e83b394566b1 100644 --- a/crates/ra_hir_ty/src/method_resolution.rs +++ b/crates/ra_hir_ty/src/method_resolution.rs @@ -16,12 +16,8 @@ use rustc_hash::{FxHashMap, FxHashSet}; use super::Substs; use crate::{ - autoderef, - db::HirDatabase, - primitive::{FloatBitness, Uncertain}, - utils::all_super_traits, - ApplicationTy, Canonical, DebruijnIndex, InEnvironment, TraitEnvironment, TraitRef, Ty, - TypeCtor, TypeWalk, + autoderef, db::HirDatabase, primitive::FloatBitness, utils::all_super_traits, ApplicationTy, + Canonical, DebruijnIndex, InEnvironment, TraitEnvironment, TraitRef, Ty, TypeCtor, TypeWalk, }; /// This is used as a key for indexing impls. @@ -147,12 +143,12 @@ impl Ty { } TypeCtor::Bool => lang_item_crate!("bool"), TypeCtor::Char => lang_item_crate!("char"), - TypeCtor::Float(Uncertain::Known(f)) => match f.bitness { + TypeCtor::Float(f) => match f.bitness { // There are two lang items: one in libcore (fXX) and one in libstd (fXX_runtime) FloatBitness::X32 => lang_item_crate!("f32", "f32_runtime"), FloatBitness::X64 => lang_item_crate!("f64", "f64_runtime"), }, - TypeCtor::Int(Uncertain::Known(i)) => lang_item_crate!(i.ty_to_string()), + TypeCtor::Int(i) => lang_item_crate!(i.ty_to_string()), TypeCtor::Str => lang_item_crate!("str_alloc", "str"), TypeCtor::Slice => lang_item_crate!("slice_alloc", "slice"), TypeCtor::RawPtr(Mutability::Shared) => lang_item_crate!("const_ptr"), diff --git a/crates/ra_hir_ty/src/primitive.rs b/crates/ra_hir_ty/src/primitive.rs index 02a8179d94db..37966b709848 100644 --- a/crates/ra_hir_ty/src/primitive.rs +++ b/crates/ra_hir_ty/src/primitive.rs @@ -7,42 +7,6 @@ use std::fmt; pub use hir_def::builtin_type::{BuiltinFloat, BuiltinInt, FloatBitness, IntBitness, Signedness}; -#[derive(Clone, Copy, Eq, PartialEq, Hash, Debug)] -pub enum Uncertain { - Unknown, - Known(T), -} - -impl From for Uncertain { - fn from(ty: IntTy) -> Self { - Uncertain::Known(ty) - } -} - -impl fmt::Display for Uncertain { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match *self { - Uncertain::Unknown => write!(f, "{{integer}}"), - Uncertain::Known(ty) => write!(f, "{}", ty), - } - } -} - -impl From for Uncertain { - fn from(ty: FloatTy) -> Self { - Uncertain::Known(ty) - } -} - -impl fmt::Display for Uncertain { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match *self { - Uncertain::Unknown => write!(f, "{{float}}"), - Uncertain::Known(ty) => write!(f, "{}", ty), - } - } -} - #[derive(Copy, Clone, Eq, PartialEq, Hash)] pub struct IntTy { pub signedness: Signedness, @@ -173,21 +137,3 @@ impl From for FloatTy { FloatTy { bitness: t.bitness } } } - -impl From> for Uncertain { - fn from(t: Option) -> Self { - match t { - None => Uncertain::Unknown, - Some(t) => Uncertain::Known(t.into()), - } - } -} - -impl From> for Uncertain { - fn from(t: Option) -> Self { - match t { - None => Uncertain::Unknown, - Some(t) => Uncertain::Known(t.into()), - } - } -} diff --git a/crates/ra_hir_ty/src/traits/chalk/mapping.rs b/crates/ra_hir_ty/src/traits/chalk/mapping.rs index 28a5fbe3e896..18e5c9c16fca 100644 --- a/crates/ra_hir_ty/src/traits/chalk/mapping.rs +++ b/crates/ra_hir_ty/src/traits/chalk/mapping.rs @@ -14,7 +14,7 @@ use ra_db::salsa::InternKey; use crate::{ db::HirDatabase, - primitive::{FloatBitness, FloatTy, IntBitness, IntTy, Signedness, Uncertain}, + primitive::{FloatBitness, FloatTy, IntBitness, IntTy, Signedness}, traits::{builtin, AssocTyValue, Canonical, Impl, Obligation}, ApplicationTy, CallableDef, GenericPredicate, InEnvironment, OpaqueTy, OpaqueTyId, ProjectionPredicate, ProjectionTy, Substs, TraitEnvironment, TraitRef, Ty, TypeCtor, @@ -249,11 +249,11 @@ impl ToChalk for TypeCtor { TypeCtor::Bool => TypeName::Scalar(Scalar::Bool), TypeCtor::Char => TypeName::Scalar(Scalar::Char), - TypeCtor::Int(Uncertain::Known(int_ty)) => TypeName::Scalar(int_ty_to_chalk(int_ty)), - TypeCtor::Float(Uncertain::Known(FloatTy { bitness: FloatBitness::X32 })) => { + TypeCtor::Int(int_ty) => TypeName::Scalar(int_ty_to_chalk(int_ty)), + TypeCtor::Float(FloatTy { bitness: FloatBitness::X32 }) => { TypeName::Scalar(Scalar::Float(chalk_ir::FloatTy::F32)) } - TypeCtor::Float(Uncertain::Known(FloatTy { bitness: FloatBitness::X64 })) => { + TypeCtor::Float(FloatTy { bitness: FloatBitness::X64 }) => { TypeName::Scalar(Scalar::Float(chalk_ir::FloatTy::F64)) } @@ -268,9 +268,7 @@ impl ToChalk for TypeCtor { } TypeCtor::Never => TypeName::Never, - TypeCtor::Int(Uncertain::Unknown) - | TypeCtor::Float(Uncertain::Unknown) - | TypeCtor::Adt(_) + TypeCtor::Adt(_) | TypeCtor::Array | TypeCtor::FnPtr { .. } | TypeCtor::Closure { .. } => { @@ -291,19 +289,19 @@ impl ToChalk for TypeCtor { TypeName::Scalar(Scalar::Bool) => TypeCtor::Bool, TypeName::Scalar(Scalar::Char) => TypeCtor::Char, - TypeName::Scalar(Scalar::Int(int_ty)) => TypeCtor::Int(Uncertain::Known(IntTy { + TypeName::Scalar(Scalar::Int(int_ty)) => TypeCtor::Int(IntTy { signedness: Signedness::Signed, bitness: bitness_from_chalk_int(int_ty), - })), - TypeName::Scalar(Scalar::Uint(uint_ty)) => TypeCtor::Int(Uncertain::Known(IntTy { + }), + TypeName::Scalar(Scalar::Uint(uint_ty)) => TypeCtor::Int(IntTy { signedness: Signedness::Unsigned, bitness: bitness_from_chalk_uint(uint_ty), - })), + }), TypeName::Scalar(Scalar::Float(chalk_ir::FloatTy::F32)) => { - TypeCtor::Float(Uncertain::Known(FloatTy { bitness: FloatBitness::X32 })) + TypeCtor::Float(FloatTy { bitness: FloatBitness::X32 }) } TypeName::Scalar(Scalar::Float(chalk_ir::FloatTy::F64)) => { - TypeCtor::Float(Uncertain::Known(FloatTy { bitness: FloatBitness::X64 })) + TypeCtor::Float(FloatTy { bitness: FloatBitness::X64 }) } TypeName::Tuple(cardinality) => TypeCtor::Tuple { cardinality: cardinality as u16 }, TypeName::Raw(mutability) => TypeCtor::RawPtr(from_chalk(db, mutability)), From 46084f8a96dc94cad06c269d93269e7c5f7e4fad Mon Sep 17 00:00:00 2001 From: vsrs Date: Sat, 6 Jun 2020 20:10:36 +0300 Subject: [PATCH 098/172] Disable runnables lookup in macro-generated code. --- crates/ra_ide/src/hover.rs | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/crates/ra_ide/src/hover.rs b/crates/ra_ide/src/hover.rs index 138a7a7a9cb2..bbff5e81a44e 100644 --- a/crates/ra_ide/src/hover.rs +++ b/crates/ra_ide/src/hover.rs @@ -200,7 +200,16 @@ fn runnable_action( _ => None, }, ModuleDef::Function(it) => { - runnable(&sema, it.source(sema.db).value.syntax().clone(), file_id) + let src = it.source(sema.db); + if src.file_id != file_id.into() { + // Don't try to find runnables in a macro generated code. + // See tests below: + // test_hover_macro_generated_struct_fn_doc_comment + // test_hover_macro_generated_struct_fn_doc_attr + return None; + } + + runnable(&sema, src.value.syntax().clone(), file_id) .map(|it| HoverAction::Runnable(it)) } _ => None, From ae1acbd09c8e98e4e23f01f633ad551dabd5c578 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Sat, 6 Jun 2020 19:32:45 +0200 Subject: [PATCH 099/172] Document import style --- docs/dev/README.md | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/docs/dev/README.md b/docs/dev/README.md index 194a40e15c42..6f74d42236d4 100644 --- a/docs/dev/README.md +++ b/docs/dev/README.md @@ -184,6 +184,27 @@ use crate::{} use super::{} // but prefer `use crate::` ``` +## Import Style + +Items from `hir` and `ast` should be used qualified: + +```rust +// Good +use ra_syntax::ast; + +fn frobnicate(func: hir::Function, strukt: ast::StructDef) {} + +// Not as good +use hir::Function; +use ra_syntax::ast::StructDef; + +fn frobnicate(func: Function, strukt: StructDef) {} +``` + +Avoid local `use MyEnum::*` imports. + +Prefer `use crate::foo::bar` to `use super::bar`. + ## Order of Items Optimize for the reader who sees the file for the first time, and wants to get the general idea about what's going on. From 81ffe973ac265507419024048c166bbeef9aa275 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Sat, 6 Jun 2020 19:54:41 +0200 Subject: [PATCH 100/172] Document certain invariants --- docs/dev/README.md | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/docs/dev/README.md b/docs/dev/README.md index 6f74d42236d4..903cb4055a83 100644 --- a/docs/dev/README.md +++ b/docs/dev/README.md @@ -241,6 +241,33 @@ struct Foo { For `.md` and `.adoc` files, prefer a sentence-per-line format, don't wrap lines. If the line is too long, you want to split the sentence in two :-) +# Architecture Invariants + +This section tries to document high-level design constraints, which are not +always obvious from the low-level code. + +## Incomplete syntax trees + +Syntax trees are by design incomplete and do not enforce well-formedness. +If ast method returns an `Option`, it *can* be `None` at runtime, even if this is forbidden by the grammar. + +## LSP indenpendence + +rust-analyzer is independent from LSP. +It provides features for a hypothetical perfect Rust-specific IDE client. +Internal representations are lowered to LSP in the `rust-analyzer` crate (the only crate which is allowed to use LSP types). + +## IDE/Compiler split + +There's a semi-hard split between "compiler" and "IDE", at the `ra_hir` crate. +Compiler derives new facts about source code. +It explicitly acknowledges that not all info is available (ie, you can't look at types during name resolution). + +IDE assumes that all information is available at all times. + +IDE should use only types from `ra_hir`, and should not depend on the underling compiler types. +`ra_hir` is a facade. + # Logging Logging is done by both rust-analyzer and VS Code, so it might be tricky to From 9b4256dc4d9e0b633b73f5c07e6e0721bc1e9108 Mon Sep 17 00:00:00 2001 From: vsrs Date: Sat, 6 Jun 2020 22:11:17 +0300 Subject: [PATCH 101/172] Add lib target filtering. --- .../rust-analyzer/src/main_loop/handlers.rs | 29 ++++++++++++------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/crates/rust-analyzer/src/main_loop/handlers.rs b/crates/rust-analyzer/src/main_loop/handlers.rs index cae447eea872..eaa43f2bdc7b 100644 --- a/crates/rust-analyzer/src/main_loop/handlers.rs +++ b/crates/rust-analyzer/src/main_loop/handlers.rs @@ -404,7 +404,7 @@ pub fn handle_runnables( continue; } } - if is_lib_target(&runnable, cargo_spec.as_ref()) { + if should_skip_target(&runnable, cargo_spec.as_ref()) { continue; } @@ -812,7 +812,7 @@ pub fn handle_code_lens( if snap.config.lens.runnable() { // Gather runnables for runnable in snap.analysis().runnables(file_id)? { - if is_lib_target(&runnable, cargo_spec.as_ref()) { + if should_skip_target(&runnable, cargo_spec.as_ref()) { continue; } @@ -1185,6 +1185,11 @@ fn to_runnable_action( file_id: FileId, runnable: &Runnable, ) -> Option { + let cargo_spec = CargoTargetSpec::for_file(&snap, file_id).ok()?; + if should_skip_target(runnable, cargo_spec.as_ref()) { + return None; + } + to_proto::runnable(snap, file_id, runnable).ok().map(|r| { let mut group = lsp_ext::CommandLinkGroup::default(); @@ -1222,16 +1227,18 @@ fn prepare_hover_actions( .collect() } -fn is_lib_target(runnable: &Runnable, cargo_spec: Option<&CargoTargetSpec>) -> bool { - // Do not suggest binary run on other target than binary - if let RunnableKind::Bin = runnable.kind { - if let Some(spec) = cargo_spec { - match spec.target_kind { - TargetKind::Bin => return true, - _ => (), +fn should_skip_target(runnable: &Runnable, cargo_spec: Option<&CargoTargetSpec>) -> bool { + match runnable.kind { + RunnableKind::Bin => { + // Do not suggest binary run on other target than binary + match &cargo_spec { + Some(spec) => match spec.target_kind { + TargetKind::Bin => false, + _ => true, + }, + None => true, } } + _ => false, } - - false } From 73684a4ae2ff5251bbff35109d2c9ad40fe4ef01 Mon Sep 17 00:00:00 2001 From: unexge Date: Sat, 6 Jun 2020 22:16:59 +0300 Subject: [PATCH 102/172] Add goto def for enum variant field --- crates/ra_ide/src/goto_definition.rs | 19 +++++++++++++++++++ crates/ra_ide_db/src/defs.rs | 6 ++++++ 2 files changed, 25 insertions(+) diff --git a/crates/ra_ide/src/goto_definition.rs b/crates/ra_ide/src/goto_definition.rs index a6c86e99c95e..693344c3147d 100644 --- a/crates/ra_ide/src/goto_definition.rs +++ b/crates/ra_ide/src/goto_definition.rs @@ -886,4 +886,23 @@ mod tests { "x", ) } + + #[test] + fn goto_def_for_enum_variant_field() { + check_goto( + " + //- /lib.rs + enum Foo { + Bar { x: i32 } + } + fn baz(foo: Foo) { + match foo { + Foo::Bar { x<|> } => x + }; + } + ", + "x RECORD_FIELD_DEF FileId(1) 21..27 21..22", + "x: i32|x", + ); + } } diff --git a/crates/ra_ide_db/src/defs.rs b/crates/ra_ide_db/src/defs.rs index 1db60b87fb82..52233937ce9b 100644 --- a/crates/ra_ide_db/src/defs.rs +++ b/crates/ra_ide_db/src/defs.rs @@ -126,6 +126,12 @@ fn classify_name_inner(sema: &Semantics, name: &ast::Name) -> Opti Some(name_ref_class.definition()) }, ast::BindPat(it) => { + if let Some(record_field_pat) = it.syntax().parent().and_then(ast::RecordFieldPat::cast) { + return Some(Definition::Field( + sema.resolve_record_field_pat(&record_field_pat)? + )); + } + let local = sema.to_def(&it)?; Some(Definition::Local(local)) }, From 3162b9ed8f70701a1534acc47f8d0ea0ba8e3b2d Mon Sep 17 00:00:00 2001 From: Vincent Isambart Date: Sun, 7 Jun 2020 12:07:00 +0900 Subject: [PATCH 103/172] Fix VSCode settings --- editors/code/package.json | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/editors/code/package.json b/editors/code/package.json index b9c57db3bcf7..859ab4477263 100644 --- a/editors/code/package.json +++ b/editors/code/package.json @@ -487,10 +487,7 @@ "default": true }, "rust-analyzer.linkedProjects": { - "markdownDescription": [ - "Disable project auto-discovery in favor of explicitly specified set of projects.", - "Elements must be paths pointing to Cargo.toml, rust-project.json, or JSON objects in rust-project.json format" - ], + "markdownDescription": "Disable project auto-discovery in favor of explicitly specified set of projects. \nElements must be paths pointing to Cargo.toml, rust-project.json, or JSON objects in rust-project.json format", "type": "array", "items": { "type": [ From 65943c058583f912175f2cfde64ff2a0d92809b6 Mon Sep 17 00:00:00 2001 From: Leander Tentrup Date: Sun, 7 Jun 2020 14:50:02 +0200 Subject: [PATCH 104/172] Remove redundancy in syntax highlighting tests --- .../ra_ide/src/syntax_highlighting/tests.rs | 59 +++++++++---------- 1 file changed, 27 insertions(+), 32 deletions(-) diff --git a/crates/ra_ide/src/syntax_highlighting/tests.rs b/crates/ra_ide/src/syntax_highlighting/tests.rs index 36a1aa419bc3..5e42c5b55b42 100644 --- a/crates/ra_ide/src/syntax_highlighting/tests.rs +++ b/crates/ra_ide/src/syntax_highlighting/tests.rs @@ -7,9 +7,21 @@ use crate::{ FileRange, TextRange, }; +/// Highlights the code given by the `ra_fixture` argument, renders the +/// result as HTML, and compares it with the HTML file given as `snapshot`. +/// Note that the `snapshot` file is overwritten by the rendered HTML. +fn check_highlighting(ra_fixture: &str, snapshot: &str, rainbow: bool) { + let (analysis, file_id) = single_file(ra_fixture); + let dst_file = project_dir().join(snapshot); + let actual_html = &analysis.highlight_as_html(file_id, rainbow).unwrap(); + let expected_html = &read_text(&dst_file); + fs::write(dst_file, &actual_html).unwrap(); + assert_eq_text!(expected_html, actual_html); +} + #[test] fn test_highlighting() { - let (analysis, file_id) = single_file( + check_highlighting( r#" #[derive(Clone, Debug)] struct Foo { @@ -84,17 +96,14 @@ impl Option { } "# .trim(), + "crates/ra_ide/src/snapshots/highlighting.html", + false, ); - let dst_file = project_dir().join("crates/ra_ide/src/snapshots/highlighting.html"); - let actual_html = &analysis.highlight_as_html(file_id, false).unwrap(); - let expected_html = &read_text(&dst_file); - fs::write(dst_file, &actual_html).unwrap(); - assert_eq_text!(expected_html, actual_html); } #[test] fn test_rainbow_highlighting() { - let (analysis, file_id) = single_file( + check_highlighting( r#" fn main() { let hello = "hello"; @@ -110,12 +119,9 @@ fn bar() { } "# .trim(), + "crates/ra_ide/src/snapshots/rainbow_highlighting.html", + true, ); - let dst_file = project_dir().join("crates/ra_ide/src/snapshots/rainbow_highlighting.html"); - let actual_html = &analysis.highlight_as_html(file_id, true).unwrap(); - let expected_html = &read_text(&dst_file); - fs::write(dst_file, &actual_html).unwrap(); - assert_eq_text!(expected_html, actual_html); } #[test] @@ -153,7 +159,7 @@ fn test_ranges() { #[test] fn test_flattening() { - let (analysis, file_id) = single_file( + check_highlighting( r##" fn fixture(ra_fixture: &str) {} @@ -167,13 +173,9 @@ fn main() { ); }"## .trim(), + "crates/ra_ide/src/snapshots/highlight_injection.html", + false, ); - - let dst_file = project_dir().join("crates/ra_ide/src/snapshots/highlight_injection.html"); - let actual_html = &analysis.highlight_as_html(file_id, false).unwrap(); - let expected_html = &read_text(&dst_file); - fs::write(dst_file, &actual_html).unwrap(); - assert_eq_text!(expected_html, actual_html); } #[test] @@ -192,7 +194,7 @@ macro_rules! test {} fn test_string_highlighting() { // The format string detection is based on macro-expansion, // thus, we have to copy the macro definition from `std` - let (analysis, file_id) = single_file( + check_highlighting( r#" macro_rules! println { ($($arg:tt)*) => ({ @@ -250,18 +252,14 @@ fn main() { println!("{ничоси}", ничоси = 92); }"# .trim(), + "crates/ra_ide/src/snapshots/highlight_strings.html", + false, ); - - let dst_file = project_dir().join("crates/ra_ide/src/snapshots/highlight_strings.html"); - let actual_html = &analysis.highlight_as_html(file_id, false).unwrap(); - let expected_html = &read_text(&dst_file); - fs::write(dst_file, &actual_html).unwrap(); - assert_eq_text!(expected_html, actual_html); } #[test] fn test_unsafe_highlighting() { - let (analysis, file_id) = single_file( + check_highlighting( r#" unsafe fn unsafe_fn() {} @@ -282,10 +280,7 @@ fn main() { } "# .trim(), + "crates/ra_ide/src/snapshots/highlight_unsafe.html", + false, ); - let dst_file = project_dir().join("crates/ra_ide/src/snapshots/highlight_unsafe.html"); - let actual_html = &analysis.highlight_as_html(file_id, false).unwrap(); - let expected_html = &read_text(&dst_file); - fs::write(dst_file, &actual_html).unwrap(); - assert_eq_text!(expected_html, actual_html); } From 3937b225e7918ae6d75849a0959754af43fbf08c Mon Sep 17 00:00:00 2001 From: Paul Daniel Faria Date: Sun, 7 Jun 2020 10:29:03 -0400 Subject: [PATCH 105/172] Change management of test cfg to better support json projects --- crates/ra_project_model/src/lib.rs | 13 +++++++------ crates/rust-analyzer/src/cli/load_cargo.rs | 1 - crates/rust-analyzer/src/global_state.rs | 1 - 3 files changed, 7 insertions(+), 8 deletions(-) diff --git a/crates/ra_project_model/src/lib.rs b/crates/ra_project_model/src/lib.rs index 7ad941279506..ef443fc09e6e 100644 --- a/crates/ra_project_model/src/lib.rs +++ b/crates/ra_project_model/src/lib.rs @@ -349,11 +349,7 @@ impl ProjectWorkspace { let file_id = load(&sysroot[krate].root)?; // Crates from sysroot have `cfg(test)` disabled - let cfg_options = { - let mut opts = default_cfg_options.clone(); - opts.remove_atom("test"); - opts - }; + let cfg_options = default_cfg_options.clone(); let env = Env::default(); let extern_source = ExternSource::default(); @@ -404,7 +400,12 @@ impl ProjectWorkspace { if let Some(file_id) = load(root) { let edition = cargo[pkg].edition; let cfg_options = { - let mut opts = default_cfg_options.clone(); + let mut opts = { + let mut opts = default_cfg_options.clone(); + opts.insert_atom("test".into()); + opts + }; + for feature in cargo[pkg].features.iter() { opts.insert_key_value("feature".into(), feature.into()); } diff --git a/crates/rust-analyzer/src/cli/load_cargo.rs b/crates/rust-analyzer/src/cli/load_cargo.rs index c7e86fe0c48b..46181b677b5c 100644 --- a/crates/rust-analyzer/src/cli/load_cargo.rs +++ b/crates/rust-analyzer/src/cli/load_cargo.rs @@ -151,7 +151,6 @@ pub(crate) fn load( // FIXME: cfg options? let default_cfg_options = { let mut opts = get_rustc_cfg_options(None); - opts.insert_atom("test".into()); opts.insert_atom("debug_assertion".into()); opts }; diff --git a/crates/rust-analyzer/src/global_state.rs b/crates/rust-analyzer/src/global_state.rs index 0bebb5bf615a..4d871aa34013 100644 --- a/crates/rust-analyzer/src/global_state.rs +++ b/crates/rust-analyzer/src/global_state.rs @@ -138,7 +138,6 @@ impl GlobalState { // FIXME: Read default cfgs from config let default_cfg_options = { let mut opts = get_rustc_cfg_options(config.cargo.target.as_ref()); - opts.insert_atom("test".into()); opts.insert_atom("debug_assertion".into()); opts }; From 3aaaf924cb649ecc3b1a59653f47d1735fa7cb5d Mon Sep 17 00:00:00 2001 From: Leander Tentrup Date: Sun, 7 Jun 2020 22:57:24 +0200 Subject: [PATCH 106/172] Fix bug in lexer for format specifier where the `type` and `width` were not correctly distinguished --- .../src/snapshots/highlight_strings.html | 2 +- crates/ra_syntax/src/ast/tokens.rs | 37 ++++++++++++------- 2 files changed, 24 insertions(+), 15 deletions(-) diff --git a/crates/ra_ide/src/snapshots/highlight_strings.html b/crates/ra_ide/src/snapshots/highlight_strings.html index e97192b614a0..6a5cf0e7449b 100644 --- a/crates/ra_ide/src/snapshots/highlight_strings.html +++ b/crates/ra_ide/src/snapshots/highlight_strings.html @@ -63,7 +63,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd println!("Hello {:^5}!", "x"); println!("Hello {:>5}!", "x"); println!("Hello {:+}!", 5); - println!("{:#x}!", 27); + println!("{:#x}!", 27); println!("Hello {:05}!", 5); println!("Hello {:05}!", -5); println!("{:#010x}!", 27); diff --git a/crates/ra_syntax/src/ast/tokens.rs b/crates/ra_syntax/src/ast/tokens.rs index 04b0a4480375..56378385a1a9 100644 --- a/crates/ra_syntax/src/ast/tokens.rs +++ b/crates/ra_syntax/src/ast/tokens.rs @@ -335,16 +335,26 @@ pub trait HasFormatSpecifier: AstToken { } c if c == '_' || c.is_alphabetic() => { read_identifier(&mut chars, &mut callback); - if chars.peek().and_then(|next| next.1.as_ref().ok()).copied() - != Some('$') - { - continue; - } - skip_char_and_emit( - &mut chars, - FormatSpecifier::DollarSign, - &mut callback, - ); + // can be either width (indicated by dollar sign, or type in which case + // the next sign has to be `}`) + let next = + chars.peek().and_then(|next| next.1.as_ref().ok()).copied(); + match next { + Some('$') => skip_char_and_emit( + &mut chars, + FormatSpecifier::DollarSign, + &mut callback, + ), + Some('}') => { + skip_char_and_emit( + &mut chars, + FormatSpecifier::Close, + &mut callback, + ); + continue; + } + _ => continue, + }; } _ => {} } @@ -416,12 +426,11 @@ pub trait HasFormatSpecifier: AstToken { } } - let mut cloned = chars.clone().take(2); - let first = cloned.next().and_then(|next| next.1.as_ref().ok()).copied(); - if first != Some('}') { + if let Some((_, Ok('}'))) = chars.peek() { + skip_char_and_emit(&mut chars, FormatSpecifier::Close, &mut callback); + } else { continue; } - skip_char_and_emit(&mut chars, FormatSpecifier::Close, &mut callback); } _ => { while let Some((_, Ok(next_char))) = chars.peek() { From 0d01be7e186bf8c0676ddfa5ae719852315a936b Mon Sep 17 00:00:00 2001 From: Jeremy Kolb Date: Sun, 7 Jun 2020 18:21:38 -0400 Subject: [PATCH 107/172] Update quote --- Cargo.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 2e86b3fee195..df79334c96c9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -884,9 +884,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.6" +version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54a21852a652ad6f610c9510194f398ff6f8692e334fd1145fed931f7fbe44ea" +checksum = "aa563d17ecb180e500da1cfd2b028310ac758de548efdd203e18f283af693f37" dependencies = [ "proc-macro2", ] From ee8dec5dc11cfecf219b6510b0eadd9691a82ba5 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Mon, 8 Jun 2020 12:52:28 +0200 Subject: [PATCH 108/172] IDE API --- docs/dev/README.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/docs/dev/README.md b/docs/dev/README.md index 903cb4055a83..64d595b684a2 100644 --- a/docs/dev/README.md +++ b/docs/dev/README.md @@ -268,6 +268,13 @@ IDE assumes that all information is available at all times. IDE should use only types from `ra_hir`, and should not depend on the underling compiler types. `ra_hir` is a facade. +## IDE API + +The main IDE crate (`ra_ide`) uses "Plain Old Data" for the API. +Rather than talking in definitions and references, it talks in Strings and textual offsets. +In general, API is centered around UI concerns -- the result of the call is what the user sees in the editor, and not what the compiler sees underneath. +The results are 100% Rust specific though. + # Logging Logging is done by both rust-analyzer and VS Code, so it might be tricky to From cc07c82fefb2affc1772e12b8357471cccc8d578 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Mon, 8 Jun 2020 12:54:48 +0200 Subject: [PATCH 109/172] Preconditions style --- docs/dev/README.md | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/docs/dev/README.md b/docs/dev/README.md index 64d595b684a2..5a9c0a148a12 100644 --- a/docs/dev/README.md +++ b/docs/dev/README.md @@ -241,6 +241,26 @@ struct Foo { For `.md` and `.adoc` files, prefer a sentence-per-line format, don't wrap lines. If the line is too long, you want to split the sentence in two :-) +## Preconditions + +Function preconditions should generally be expressed in types and provided by the caller (rather than checked by callee): + +```rust +// Good +fn frbonicate(walrus: Walrus) { + ... +} + +// Not as good +fn frobnicate(walrus: Option) { + let walrus = match walrus { + Some(it) => it, + None => return, + }; + ... +} +``` + # Architecture Invariants This section tries to document high-level design constraints, which are not From 4968321706fc5c24d9f2c35cdbbd5e9047c97c96 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Mon, 8 Jun 2020 13:19:32 +0200 Subject: [PATCH 110/172] Don't @ people in commit messages --- docs/dev/README.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/docs/dev/README.md b/docs/dev/README.md index 5a9c0a148a12..46ee030fc686 100644 --- a/docs/dev/README.md +++ b/docs/dev/README.md @@ -261,6 +261,14 @@ fn frobnicate(walrus: Option) { } ``` +## Commit Style + +We don't have specific rules around git history hygiene. +Maintaining clean git history is encouraged, but not enforced. +We use rebase workflow, it's OK to rewrite history during PR review process. + +Avoid @mentioning people in commit messages, as such messages create a lot of duplicate notification traffic during rebases. + # Architecture Invariants This section tries to document high-level design constraints, which are not From b7db9f058ad51b7ba47db02b581a76b6756d74e8 Mon Sep 17 00:00:00 2001 From: vsrs Date: Mon, 8 Jun 2020 13:56:31 +0300 Subject: [PATCH 111/172] Apply suggestions from code review --- crates/ra_ide/src/hover.rs | 12 ++++++--- .../rust-analyzer/src/main_loop/handlers.rs | 26 ++++++++----------- crates/rust-analyzer/src/to_proto.rs | 4 +-- 3 files changed, 21 insertions(+), 21 deletions(-) diff --git a/crates/ra_ide/src/hover.rs b/crates/ra_ide/src/hover.rs index bbff5e81a44e..ad78b767120c 100644 --- a/crates/ra_ide/src/hover.rs +++ b/crates/ra_ide/src/hover.rs @@ -17,6 +17,7 @@ use crate::{ runnables::runnable, FileId, FilePosition, NavigationTarget, RangeInfo, Runnable, }; +use test_utils::mark; #[derive(Clone, Debug, PartialEq, Eq)] pub struct HoverConfig { @@ -202,10 +203,9 @@ fn runnable_action( ModuleDef::Function(it) => { let src = it.source(sema.db); if src.file_id != file_id.into() { - // Don't try to find runnables in a macro generated code. - // See tests below: - // test_hover_macro_generated_struct_fn_doc_comment - // test_hover_macro_generated_struct_fn_doc_attr + mark::hit!(hover_macro_generated_struct_fn_doc_comment); + mark::hit!(hover_macro_generated_struct_fn_doc_attr); + return None; } @@ -1121,6 +1121,8 @@ fn func(foo: i32) { if true { <|>foo; }; } #[test] fn test_hover_macro_generated_struct_fn_doc_comment() { + mark::check!(hover_macro_generated_struct_fn_doc_comment); + check_hover_result( r#" //- /lib.rs @@ -1147,6 +1149,8 @@ fn func(foo: i32) { if true { <|>foo; }; } #[test] fn test_hover_macro_generated_struct_fn_doc_attr() { + mark::check!(hover_macro_generated_struct_fn_doc_attr); + check_hover_result( r#" //- /lib.rs diff --git a/crates/rust-analyzer/src/main_loop/handlers.rs b/crates/rust-analyzer/src/main_loop/handlers.rs index eaa43f2bdc7b..a41adf8b0b51 100644 --- a/crates/rust-analyzer/src/main_loop/handlers.rs +++ b/crates/rust-analyzer/src/main_loop/handlers.rs @@ -408,7 +408,7 @@ pub fn handle_runnables( continue; } - res.push(to_proto::runnable(&snap, file_id, &runnable)?); + res.push(to_proto::runnable(&snap, file_id, runnable)?); } // Add `cargo check` and `cargo test` for the whole package @@ -818,7 +818,7 @@ pub fn handle_code_lens( let action = runnable.action(); let range = to_proto::range(&line_index, runnable.nav.range()); - let r = to_proto::runnable(&snap, file_id, &runnable)?; + let r = to_proto::runnable(&snap, file_id, runnable)?; if snap.config.lens.run { let lens = CodeLens { range, @@ -830,7 +830,7 @@ pub fn handle_code_lens( if action.debugee && snap.config.lens.debug { let debug_lens = - CodeLens { range, command: Some(debug_single_command(r)), data: None }; + CodeLens { range, command: Some(debug_single_command(&r)), data: None }; lenses.push(debug_lens); } } @@ -1142,7 +1142,7 @@ fn run_single_command(runnable: &lsp_ext::Runnable, title: &str) -> Command { } } -fn debug_single_command(runnable: lsp_ext::Runnable) -> Command { +fn debug_single_command(runnable: &lsp_ext::Runnable) -> Command { Command { title: "Debug".into(), command: "rust-analyzer.debugSingle".into(), @@ -1183,26 +1183,25 @@ fn show_impl_command_link( fn to_runnable_action( snap: &GlobalStateSnapshot, file_id: FileId, - runnable: &Runnable, + runnable: Runnable, ) -> Option { let cargo_spec = CargoTargetSpec::for_file(&snap, file_id).ok()?; - if should_skip_target(runnable, cargo_spec.as_ref()) { + if should_skip_target(&runnable, cargo_spec.as_ref()) { return None; } + let action: &'static _ = runnable.action(); to_proto::runnable(snap, file_id, runnable).ok().map(|r| { let mut group = lsp_ext::CommandLinkGroup::default(); - let action = runnable.action(); if snap.config.hover.run { let run_command = run_single_command(&r, action.run_title); group.commands.push(to_command_link(run_command, r.label.clone())); } if snap.config.hover.debug { - let hint = r.label.clone(); - let dbg_command = debug_single_command(r); - group.commands.push(to_command_link(dbg_command, hint)); + let dbg_command = debug_single_command(&r); + group.commands.push(to_command_link(dbg_command, r.label)); } group @@ -1222,7 +1221,7 @@ fn prepare_hover_actions( .iter() .filter_map(|it| match it { HoverAction::Implementaion(position) => show_impl_command_link(snap, position), - HoverAction::Runnable(r) => to_runnable_action(snap, file_id, r), + HoverAction::Runnable(r) => to_runnable_action(snap, file_id, r.clone()), }) .collect() } @@ -1232,10 +1231,7 @@ fn should_skip_target(runnable: &Runnable, cargo_spec: Option<&CargoTargetSpec>) RunnableKind::Bin => { // Do not suggest binary run on other target than binary match &cargo_spec { - Some(spec) => match spec.target_kind { - TargetKind::Bin => false, - _ => true, - }, + Some(spec) => spec.target_kind != TargetKind::Bin, None => true, } } diff --git a/crates/rust-analyzer/src/to_proto.rs b/crates/rust-analyzer/src/to_proto.rs index 5daf037dabf3..710df1fbdec4 100644 --- a/crates/rust-analyzer/src/to_proto.rs +++ b/crates/rust-analyzer/src/to_proto.rs @@ -656,14 +656,14 @@ pub(crate) fn resolved_code_action( pub(crate) fn runnable( snap: &GlobalStateSnapshot, file_id: FileId, - runnable: &Runnable, + runnable: Runnable, ) -> Result { let spec = CargoTargetSpec::for_file(snap, file_id)?; let target = spec.as_ref().map(|s| s.target.clone()); let (cargo_args, executable_args) = CargoTargetSpec::runnable_args(spec, &runnable.kind, &runnable.cfg_exprs)?; let label = runnable.label(target); - let location = location_link(snap, None, runnable.nav.clone())?; + let location = location_link(snap, None, runnable.nav)?; Ok(lsp_ext::Runnable { label, From c5d5d2185816d90ad1420fe544d93150cb9c9a48 Mon Sep 17 00:00:00 2001 From: unexge Date: Mon, 8 Jun 2020 14:46:58 +0300 Subject: [PATCH 112/172] Add `FieldShorthand` variant to `NameClass` --- crates/ra_ide/src/goto_definition.rs | 2 +- crates/ra_ide/src/syntax_highlighting.rs | 1 + crates/ra_ide_db/src/defs.rs | 56 +++++++++++++----------- 3 files changed, 32 insertions(+), 27 deletions(-) diff --git a/crates/ra_ide/src/goto_definition.rs b/crates/ra_ide/src/goto_definition.rs index 693344c3147d..620d2dedd3de 100644 --- a/crates/ra_ide/src/goto_definition.rs +++ b/crates/ra_ide/src/goto_definition.rs @@ -39,7 +39,7 @@ pub(crate) fn goto_definition( reference_definition(&sema, &name_ref).to_vec() }, ast::Name(name) => { - let def = classify_name(&sema, &name)?.definition(); + let def = classify_name(&sema, &name)?.into_definition()?; let nav = def.try_to_nav(sema.db)?; vec![nav] }, diff --git a/crates/ra_ide/src/syntax_highlighting.rs b/crates/ra_ide/src/syntax_highlighting.rs index 19ecd54d6cf2..f6b52c35d7f5 100644 --- a/crates/ra_ide/src/syntax_highlighting.rs +++ b/crates/ra_ide/src/syntax_highlighting.rs @@ -363,6 +363,7 @@ fn highlight_element( highlight_name(db, def) | HighlightModifier::Definition } Some(NameClass::ConstReference(def)) => highlight_name(db, def), + Some(NameClass::FieldShorthand { .. }) => HighlightTag::Field.into(), None => highlight_name_by_syntax(name) | HighlightModifier::Definition, } } diff --git a/crates/ra_ide_db/src/defs.rs b/crates/ra_ide_db/src/defs.rs index 52233937ce9b..853d856e790a 100644 --- a/crates/ra_ide_db/src/defs.rs +++ b/crates/ra_ide_db/src/defs.rs @@ -82,6 +82,10 @@ pub enum NameClass { Definition(Definition), /// `None` in `if let None = Some(82) {}` ConstReference(Definition), + FieldShorthand { + local: Local, + field: Definition, + }, } impl NameClass { @@ -89,12 +93,14 @@ impl NameClass { match self { NameClass::Definition(it) => Some(it), NameClass::ConstReference(_) => None, + NameClass::FieldShorthand { local: _, field } => Some(field), } } pub fn definition(self) -> Definition { match self { NameClass::Definition(it) | NameClass::ConstReference(it) => it, + NameClass::FieldShorthand { local, field: _ } => Definition::Local(local), } } } @@ -102,18 +108,14 @@ impl NameClass { pub fn classify_name(sema: &Semantics, name: &ast::Name) -> Option { let _p = profile("classify_name"); - if let Some(bind_pat) = name.syntax().parent().and_then(ast::BindPat::cast) { + let parent = name.syntax().parent()?; + + if let Some(bind_pat) = ast::BindPat::cast(parent.clone()) { if let Some(def) = sema.resolve_bind_pat_to_const(&bind_pat) { return Some(NameClass::ConstReference(Definition::ModuleDef(def))); } } - classify_name_inner(sema, name).map(NameClass::Definition) -} - -fn classify_name_inner(sema: &Semantics, name: &ast::Name) -> Option { - let parent = name.syntax().parent()?; - match_ast! { match parent { ast::Alias(it) => { @@ -123,69 +125,71 @@ fn classify_name_inner(sema: &Semantics, name: &ast::Name) -> Opti let name_ref = path_segment.name_ref()?; let name_ref_class = classify_name_ref(sema, &name_ref)?; - Some(name_ref_class.definition()) + Some(NameClass::Definition(name_ref_class.definition())) }, ast::BindPat(it) => { + let local = sema.to_def(&it)?; + if let Some(record_field_pat) = it.syntax().parent().and_then(ast::RecordFieldPat::cast) { - return Some(Definition::Field( - sema.resolve_record_field_pat(&record_field_pat)? - )); + if let Some(field) = sema.resolve_record_field_pat(&record_field_pat) { + let field = Definition::Field(field); + return Some(NameClass::FieldShorthand { local, field }); + } } - let local = sema.to_def(&it)?; - Some(Definition::Local(local)) + Some(NameClass::Definition(Definition::Local(local))) }, ast::RecordFieldDef(it) => { let field: hir::Field = sema.to_def(&it)?; - Some(Definition::Field(field)) + Some(NameClass::Definition(Definition::Field(field))) }, ast::Module(it) => { let def = sema.to_def(&it)?; - Some(Definition::ModuleDef(def.into())) + Some(NameClass::Definition(Definition::ModuleDef(def.into()))) }, ast::StructDef(it) => { let def: hir::Struct = sema.to_def(&it)?; - Some(Definition::ModuleDef(def.into())) + Some(NameClass::Definition(Definition::ModuleDef(def.into()))) }, ast::UnionDef(it) => { let def: hir::Union = sema.to_def(&it)?; - Some(Definition::ModuleDef(def.into())) + Some(NameClass::Definition(Definition::ModuleDef(def.into()))) }, ast::EnumDef(it) => { let def: hir::Enum = sema.to_def(&it)?; - Some(Definition::ModuleDef(def.into())) + Some(NameClass::Definition(Definition::ModuleDef(def.into()))) }, ast::TraitDef(it) => { let def: hir::Trait = sema.to_def(&it)?; - Some(Definition::ModuleDef(def.into())) + Some(NameClass::Definition(Definition::ModuleDef(def.into()))) }, ast::StaticDef(it) => { let def: hir::Static = sema.to_def(&it)?; - Some(Definition::ModuleDef(def.into())) + Some(NameClass::Definition(Definition::ModuleDef(def.into()))) }, ast::EnumVariant(it) => { let def: hir::EnumVariant = sema.to_def(&it)?; - Some(Definition::ModuleDef(def.into())) + Some(NameClass::Definition(Definition::ModuleDef(def.into()))) }, ast::FnDef(it) => { let def: hir::Function = sema.to_def(&it)?; - Some(Definition::ModuleDef(def.into())) + Some(NameClass::Definition(Definition::ModuleDef(def.into()))) }, ast::ConstDef(it) => { let def: hir::Const = sema.to_def(&it)?; - Some(Definition::ModuleDef(def.into())) + Some(NameClass::Definition(Definition::ModuleDef(def.into()))) }, ast::TypeAliasDef(it) => { let def: hir::TypeAlias = sema.to_def(&it)?; - Some(Definition::ModuleDef(def.into())) + Some(NameClass::Definition(Definition::ModuleDef(def.into()))) }, ast::MacroCall(it) => { let def = sema.to_def(&it)?; - Some(Definition::Macro(def)) + Some(NameClass::Definition(Definition::Macro(def))) }, ast::TypeParam(it) => { let def = sema.to_def(&it)?; - Some(Definition::TypeParam(def)) + Some(NameClass::Definition(Definition::TypeParam(def))) }, _ => None, } From 97ea2dfc4b6b49a175d4d0f85b37c77bd1ea3f3b Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Mon, 8 Jun 2020 13:58:54 +0200 Subject: [PATCH 113/172] Move to a dedicated file --- xtask/src/lib.rs | 57 ++---------------------------------------- xtask/src/main.rs | 5 ++-- xtask/src/release.rs | 59 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 64 insertions(+), 57 deletions(-) create mode 100644 xtask/src/release.rs diff --git a/xtask/src/lib.rs b/xtask/src/lib.rs index 739f49f7be81..747654c1fcb2 100644 --- a/xtask/src/lib.rs +++ b/xtask/src/lib.rs @@ -4,6 +4,7 @@ pub mod not_bash; pub mod install; +pub mod release; pub mod dist; pub mod pre_commit; @@ -19,7 +20,7 @@ use walkdir::{DirEntry, WalkDir}; use crate::{ codegen::Mode, - not_bash::{date_iso, fs2, pushd, pushenv, rm_rf, run}, + not_bash::{fs2, pushd, pushenv, rm_rf, run}, }; pub use anyhow::{bail, Context as _, Result}; @@ -153,60 +154,6 @@ pub fn run_pre_cache() -> Result<()> { Ok(()) } -pub fn run_release(dry_run: bool) -> Result<()> { - if !dry_run { - run!("git switch release")?; - run!("git fetch upstream --tags --force")?; - run!("git reset --hard tags/nightly")?; - run!("git push")?; - } - codegen::generate_assists_docs(Mode::Overwrite)?; - codegen::generate_feature_docs(Mode::Overwrite)?; - - let website_root = project_root().join("../rust-analyzer.github.io"); - let changelog_dir = website_root.join("./thisweek/_posts"); - - let today = date_iso()?; - let commit = run!("git rev-parse HEAD")?; - let changelog_n = fs2::read_dir(changelog_dir.as_path())?.count(); - - let contents = format!( - "\ -= Changelog #{} -:sectanchors: -:page-layout: post - -Commit: commit:{}[] + -Release: release:{}[] - -== New Features - -* pr:[] . - -== Fixes - -== Internal Improvements -", - changelog_n, commit, today - ); - - let path = changelog_dir.join(format!("{}-changelog-{}.adoc", today, changelog_n)); - fs2::write(&path, &contents)?; - - for &adoc in ["manual.adoc", "generated_features.adoc", "generated_assists.adoc"].iter() { - let src = project_root().join("./docs/user/").join(adoc); - let dst = website_root.join(adoc); - fs2::copy(src, dst)?; - } - - let tags = run!("git tag --list"; echo = false)?; - let prev_tag = tags.lines().filter(|line| is_release_tag(line)).last().unwrap(); - - println!("\n git log {}..HEAD --merges --reverse", prev_tag); - - Ok(()) -} - fn is_release_tag(tag: &str) -> bool { tag.len() == "2020-02-24".len() && tag.starts_with(|c: char| c.is_ascii_digit()) } diff --git a/xtask/src/main.rs b/xtask/src/main.rs index 81bb3a33f29e..701c60fc0230 100644 --- a/xtask/src/main.rs +++ b/xtask/src/main.rs @@ -16,8 +16,9 @@ use xtask::{ dist::run_dist, install::{ClientOpt, InstallCmd, ServerOpt}, not_bash::pushd, - pre_commit, project_root, run_clippy, run_fuzzer, run_pre_cache, run_release, run_rustfmt, - Result, + pre_commit, project_root, + release::run_release, + run_clippy, run_fuzzer, run_pre_cache, run_rustfmt, Result, }; fn main() -> Result<()> { diff --git a/xtask/src/release.rs b/xtask/src/release.rs new file mode 100644 index 000000000000..771a8cf718b6 --- /dev/null +++ b/xtask/src/release.rs @@ -0,0 +1,59 @@ +use crate::{ + codegen, is_release_tag, + not_bash::{date_iso, fs2, run}, + project_root, Mode, Result, +}; + +pub fn run_release(dry_run: bool) -> Result<()> { + if !dry_run { + run!("git switch release")?; + run!("git fetch upstream --tags --force")?; + run!("git reset --hard tags/nightly")?; + run!("git push")?; + } + codegen::generate_assists_docs(Mode::Overwrite)?; + codegen::generate_feature_docs(Mode::Overwrite)?; + + let website_root = project_root().join("../rust-analyzer.github.io"); + let changelog_dir = website_root.join("./thisweek/_posts"); + + let today = date_iso()?; + let commit = run!("git rev-parse HEAD")?; + let changelog_n = fs2::read_dir(changelog_dir.as_path())?.count(); + + let contents = format!( + "\ += Changelog #{} +:sectanchors: +:page-layout: post + +Commit: commit:{}[] + +Release: release:{}[] + +== New Features + +* pr:[] . + +== Fixes + +== Internal Improvements +", + changelog_n, commit, today + ); + + let path = changelog_dir.join(format!("{}-changelog-{}.adoc", today, changelog_n)); + fs2::write(&path, &contents)?; + + for &adoc in ["manual.adoc", "generated_features.adoc", "generated_assists.adoc"].iter() { + let src = project_root().join("./docs/user/").join(adoc); + let dst = website_root.join(adoc); + fs2::copy(src, dst)?; + } + + let tags = run!("git tag --list"; echo = false)?; + let prev_tag = tags.lines().filter(|line| is_release_tag(line)).last().unwrap(); + + println!("\n git log {}..HEAD --merges --reverse", prev_tag); + + Ok(()) +} From cbc5eb87386e132c63ec33ff496019fad7478fe8 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Mon, 8 Jun 2020 14:00:30 +0200 Subject: [PATCH 114/172] Refactor --- xtask/src/main.rs | 4 +- xtask/src/release.rs | 112 +++++++++++++++++++++++-------------------- 2 files changed, 62 insertions(+), 54 deletions(-) diff --git a/xtask/src/main.rs b/xtask/src/main.rs index 701c60fc0230..f7a79362d95f 100644 --- a/xtask/src/main.rs +++ b/xtask/src/main.rs @@ -17,7 +17,7 @@ use xtask::{ install::{ClientOpt, InstallCmd, ServerOpt}, not_bash::pushd, pre_commit, project_root, - release::run_release, + release::ReleaseCmd, run_clippy, run_fuzzer, run_pre_cache, run_rustfmt, Result, }; @@ -103,7 +103,7 @@ FLAGS: "release" => { let dry_run = args.contains("--dry-run"); args.finish()?; - run_release(dry_run) + ReleaseCmd { dry_run }.run() } "dist" => { let nightly = args.contains("--nightly"); diff --git a/xtask/src/release.rs b/xtask/src/release.rs index 771a8cf718b6..36c912184f0c 100644 --- a/xtask/src/release.rs +++ b/xtask/src/release.rs @@ -4,56 +4,64 @@ use crate::{ project_root, Mode, Result, }; -pub fn run_release(dry_run: bool) -> Result<()> { - if !dry_run { - run!("git switch release")?; - run!("git fetch upstream --tags --force")?; - run!("git reset --hard tags/nightly")?; - run!("git push")?; - } - codegen::generate_assists_docs(Mode::Overwrite)?; - codegen::generate_feature_docs(Mode::Overwrite)?; - - let website_root = project_root().join("../rust-analyzer.github.io"); - let changelog_dir = website_root.join("./thisweek/_posts"); - - let today = date_iso()?; - let commit = run!("git rev-parse HEAD")?; - let changelog_n = fs2::read_dir(changelog_dir.as_path())?.count(); - - let contents = format!( - "\ -= Changelog #{} -:sectanchors: -:page-layout: post - -Commit: commit:{}[] + -Release: release:{}[] - -== New Features - -* pr:[] . - -== Fixes - -== Internal Improvements -", - changelog_n, commit, today - ); - - let path = changelog_dir.join(format!("{}-changelog-{}.adoc", today, changelog_n)); - fs2::write(&path, &contents)?; - - for &adoc in ["manual.adoc", "generated_features.adoc", "generated_assists.adoc"].iter() { - let src = project_root().join("./docs/user/").join(adoc); - let dst = website_root.join(adoc); - fs2::copy(src, dst)?; - } - - let tags = run!("git tag --list"; echo = false)?; - let prev_tag = tags.lines().filter(|line| is_release_tag(line)).last().unwrap(); - - println!("\n git log {}..HEAD --merges --reverse", prev_tag); - - Ok(()) +pub struct ReleaseCmd { + pub dry_run: bool, +} + +impl ReleaseCmd { + pub fn run(self) -> Result<()> { + if !self.dry_run { + run!("git switch release")?; + run!("git fetch upstream --tags --force")?; + run!("git reset --hard tags/nightly")?; + run!("git push")?; + } + codegen::generate_assists_docs(Mode::Overwrite)?; + codegen::generate_feature_docs(Mode::Overwrite)?; + + let website_root = project_root().join("../rust-analyzer.github.io"); + let changelog_dir = website_root.join("./thisweek/_posts"); + + let today = date_iso()?; + let commit = run!("git rev-parse HEAD")?; + let changelog_n = fs2::read_dir(changelog_dir.as_path())?.count(); + + let contents = format!( + "\ + = Changelog #{} + :sectanchors: + :page-layout: post + + Commit: commit:{}[] + + Release: release:{}[] + + == New Features + + * pr:[] . + + == Fixes + + == Internal Improvements + ", + changelog_n, commit, today + ); + + let path = changelog_dir.join(format!("{}-changelog-{}.adoc", today, changelog_n)); + fs2::write(&path, &contents)?; + + for &adoc in ["manual.adoc", "generated_features.adoc", "generated_assists.adoc"].iter() { + let src = project_root().join("./docs/user/").join(adoc); + let dst = website_root.join(adoc); + fs2::copy(src, dst)?; + } + + let tags = run!("git tag --list"; echo = false)?; + let prev_tag = tags.lines().filter(|line| is_release_tag(line)).last().unwrap(); + + let git_log = run!("git log {}..HEAD --merges --reverse", prev_tag; echo = false)?; + let git_log_dst = website_root.join("git.log"); + fs2::write(git_log_dst, &git_log)?; + + Ok(()) + } } From 4a2efb2f42494f62891ac801e0a27d246bd36684 Mon Sep 17 00:00:00 2001 From: Leander Tentrup Date: Tue, 28 Apr 2020 11:01:51 +0200 Subject: [PATCH 115/172] Implement syntax highlighting for doctests --- .../src/snapshots/highlight_doctest.html | 70 ++++++++ crates/ra_ide/src/syntax_highlighting.rs | 126 ++++++++----- .../src/syntax_highlighting/injection.rs | 168 ++++++++++++++++++ .../ra_ide/src/syntax_highlighting/tests.rs | 50 ++++++ 4 files changed, 368 insertions(+), 46 deletions(-) create mode 100644 crates/ra_ide/src/snapshots/highlight_doctest.html create mode 100644 crates/ra_ide/src/syntax_highlighting/injection.rs diff --git a/crates/ra_ide/src/snapshots/highlight_doctest.html b/crates/ra_ide/src/snapshots/highlight_doctest.html new file mode 100644 index 000000000000..2f2d8c900ef6 --- /dev/null +++ b/crates/ra_ide/src/snapshots/highlight_doctest.html @@ -0,0 +1,70 @@ + + +
impl Foo {
+    /// Constructs a new `Foo`.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// # #![allow(unused_mut)]
+    /// let mut foo: Foo = Foo::new();
+    /// ```
+    pub const fn new() -> Foo {
+        Foo { }
+    }
+
+    /// `bar` method on `Foo`.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// let foo = Foo::new();
+    ///
+    /// // calls bar on foo
+    /// assert!(foo.bar());
+    ///
+    /// /* multi-line
+    ///        comment */
+    ///
+    /// let multi_line_string = "Foo
+    ///   bar
+    ///          ";
+    ///
+    /// ```
+    ///
+    /// ```
+    /// let foobar = Foo::new().bar();
+    /// ```
+    pub fn foo(&self) -> bool {
+        true
+    }
+}
\ No newline at end of file diff --git a/crates/ra_ide/src/syntax_highlighting.rs b/crates/ra_ide/src/syntax_highlighting.rs index 19ecd54d6cf2..6903403b2fc2 100644 --- a/crates/ra_ide/src/syntax_highlighting.rs +++ b/crates/ra_ide/src/syntax_highlighting.rs @@ -1,5 +1,6 @@ mod tags; mod html; +mod injection; #[cfg(test)] mod tests; @@ -10,14 +11,14 @@ use ra_ide_db::{ }; use ra_prof::profile; use ra_syntax::{ - ast::{self, HasFormatSpecifier, HasQuotes, HasStringValue}, + ast::{self, HasFormatSpecifier}, AstNode, AstToken, Direction, NodeOrToken, SyntaxElement, SyntaxKind::*, - SyntaxToken, TextRange, WalkEvent, T, + TextRange, WalkEvent, T, }; use rustc_hash::FxHashMap; -use crate::{call_info::ActiveParameter, Analysis, FileId}; +use crate::FileId; use ast::FormatSpecifier; pub(crate) use html::highlight_as_html; @@ -123,6 +124,23 @@ pub(crate) fn highlight( _ => (), } + // Check for Rust code in documentation + match &event { + WalkEvent::Leave(NodeOrToken::Node(node)) => { + if let Some((doctest, range_mapping, new_comments)) = + injection::extract_doc_comments(node) + { + injection::highlight_doc_comment( + doctest, + range_mapping, + new_comments, + &mut stack, + ); + } + } + _ => (), + } + let element = match event { WalkEvent::Enter(it) => it, WalkEvent::Leave(_) => continue, @@ -173,7 +191,7 @@ pub(crate) fn highlight( if let Some(token) = element.as_token().cloned().and_then(ast::RawString::cast) { let expanded = element_to_highlight.as_token().unwrap().clone(); - if highlight_injection(&mut stack, &sema, token, expanded).is_some() { + if injection::highlight_injection(&mut stack, &sema, token, expanded).is_some() { continue; } } @@ -259,9 +277,8 @@ impl HighlightedRangeStack { let mut parent = prev.pop().unwrap(); for ele in children { assert!(parent.range.contains_range(ele.range)); - let mut cloned = parent.clone(); - parent.range = TextRange::new(parent.range.start(), ele.range.start()); - cloned.range = TextRange::new(ele.range.end(), cloned.range.end()); + + let cloned = Self::intersect(&mut parent, &ele); if !parent.range.is_empty() { prev.push(parent); } @@ -274,6 +291,62 @@ impl HighlightedRangeStack { } } + /// Intersects the `HighlightedRange` `parent` with `child`. + /// `parent` is mutated in place, becoming the range before `child`. + /// Returns the range (of the same type as `parent`) *after* `child`. + fn intersect(parent: &mut HighlightedRange, child: &HighlightedRange) -> HighlightedRange { + assert!(parent.range.contains_range(child.range)); + + let mut cloned = parent.clone(); + parent.range = TextRange::new(parent.range.start(), child.range.start()); + cloned.range = TextRange::new(child.range.end(), cloned.range.end()); + + cloned + } + + /// Similar to `pop`, but can modify arbitrary prior ranges (where `pop`) + /// can only modify the last range currently on the stack. + /// Can be used to do injections that span multiple ranges, like the + /// doctest injection below. + /// If `delete` is set to true, the parent range is deleted instead of + /// intersected. + /// + /// Note that `pop` can be simulated by `pop_and_inject(false)` but the + /// latter is computationally more expensive. + fn pop_and_inject(&mut self, delete: bool) { + let mut children = self.stack.pop().unwrap(); + let prev = self.stack.last_mut().unwrap(); + children.sort_by_key(|range| range.range.start()); + prev.sort_by_key(|range| range.range.start()); + + for child in children { + if let Some(idx) = + prev.iter().position(|parent| parent.range.contains_range(child.range)) + { + let cloned = Self::intersect(&mut prev[idx], &child); + let insert_idx = if delete || prev[idx].range.is_empty() { + prev.remove(idx); + idx + } else { + idx + 1 + }; + prev.insert(insert_idx, child); + if !delete && !cloned.range.is_empty() { + prev.insert(insert_idx + 1, cloned); + } + } else if let Some(_idx) = + prev.iter().position(|parent| parent.range.contains(child.range.start())) + { + unreachable!("child range should be completely contained in parent range"); + } else { + let idx = prev + .binary_search_by_key(&child.range.start(), |range| range.range.start()) + .unwrap_or_else(|x| x); + prev.insert(idx, child); + } + } + } + fn add(&mut self, range: HighlightedRange) { self.stack .last_mut() @@ -539,42 +612,3 @@ fn highlight_name_by_syntax(name: ast::Name) -> Highlight { tag.into() } - -fn highlight_injection( - acc: &mut HighlightedRangeStack, - sema: &Semantics, - literal: ast::RawString, - expanded: SyntaxToken, -) -> Option<()> { - let active_parameter = ActiveParameter::at_token(&sema, expanded)?; - if !active_parameter.name.starts_with("ra_fixture") { - return None; - } - let value = literal.value()?; - let (analysis, tmp_file_id) = Analysis::from_single_file(value); - - if let Some(range) = literal.open_quote_text_range() { - acc.add(HighlightedRange { - range, - highlight: HighlightTag::StringLiteral.into(), - binding_hash: None, - }) - } - - for mut h in analysis.highlight(tmp_file_id).unwrap() { - if let Some(r) = literal.map_range_up(h.range) { - h.range = r; - acc.add(h) - } - } - - if let Some(range) = literal.close_quote_text_range() { - acc.add(HighlightedRange { - range, - highlight: HighlightTag::StringLiteral.into(), - binding_hash: None, - }) - } - - Some(()) -} diff --git a/crates/ra_ide/src/syntax_highlighting/injection.rs b/crates/ra_ide/src/syntax_highlighting/injection.rs new file mode 100644 index 000000000000..3575a0fc6e9f --- /dev/null +++ b/crates/ra_ide/src/syntax_highlighting/injection.rs @@ -0,0 +1,168 @@ +//! Syntax highlighting injections such as highlighting of documentation tests. + +use std::{collections::BTreeMap, convert::TryFrom}; + +use ast::{HasQuotes, HasStringValue}; +use hir::Semantics; +use ra_syntax::{ast, AstToken, SyntaxNode, SyntaxToken, TextRange, TextSize}; +use stdx::SepBy; + +use crate::{call_info::ActiveParameter, Analysis, HighlightTag, HighlightedRange, RootDatabase}; + +use super::HighlightedRangeStack; + +pub(super) fn highlight_injection( + acc: &mut HighlightedRangeStack, + sema: &Semantics, + literal: ast::RawString, + expanded: SyntaxToken, +) -> Option<()> { + let active_parameter = ActiveParameter::at_token(&sema, expanded)?; + if !active_parameter.name.starts_with("ra_fixture") { + return None; + } + let value = literal.value()?; + let (analysis, tmp_file_id) = Analysis::from_single_file(value); + + if let Some(range) = literal.open_quote_text_range() { + acc.add(HighlightedRange { + range, + highlight: HighlightTag::StringLiteral.into(), + binding_hash: None, + }) + } + + for mut h in analysis.highlight(tmp_file_id).unwrap() { + if let Some(r) = literal.map_range_up(h.range) { + h.range = r; + acc.add(h) + } + } + + if let Some(range) = literal.close_quote_text_range() { + acc.add(HighlightedRange { + range, + highlight: HighlightTag::StringLiteral.into(), + binding_hash: None, + }) + } + + Some(()) +} + +/// Mapping from extracted documentation code to original code +type RangesMap = BTreeMap; + +/// Extracts Rust code from documentation comments as well as a mapping from +/// the extracted source code back to the original source ranges. +/// Lastly, a vector of new comment highlight ranges (spanning only the +/// comment prefix) is returned which is used in the syntax highlighting +/// injection to replace the previous (line-spanning) comment ranges. +pub(super) fn extract_doc_comments( + node: &SyntaxNode, +) -> Option<(String, RangesMap, Vec)> { + // wrap the doctest into function body to get correct syntax highlighting + let prefix = "fn doctest() {\n"; + let suffix = "}\n"; + // Mapping from extracted documentation code to original code + let mut range_mapping: RangesMap = BTreeMap::new(); + let mut line_start = TextSize::try_from(prefix.len()).unwrap(); + let mut is_doctest = false; + // Replace the original, line-spanning comment ranges by new, only comment-prefix + // spanning comment ranges. + let mut new_comments = Vec::new(); + let doctest = node + .children_with_tokens() + .filter_map(|el| el.into_token().and_then(ast::Comment::cast)) + .filter(|comment| comment.kind().doc.is_some()) + .filter(|comment| { + if comment.text().contains("```") { + is_doctest = !is_doctest; + false + } else { + is_doctest + } + }) + .map(|comment| { + let prefix_len = comment.prefix().len(); + let line: &str = comment.text().as_str(); + let range = comment.syntax().text_range(); + + // whitespace after comment is ignored + let pos = if let Some(ws) = line.chars().nth(prefix_len).filter(|c| c.is_whitespace()) { + prefix_len + ws.len_utf8() + } else { + prefix_len + }; + + // lines marked with `#` should be ignored in output, we skip the `#` char + let pos = if let Some(ws) = line.chars().nth(pos).filter(|&c| c == '#') { + pos + ws.len_utf8() + } else { + pos + }; + + range_mapping.insert(line_start, range.start() + TextSize::try_from(pos).unwrap()); + new_comments.push(HighlightedRange { + range: TextRange::new( + range.start(), + range.start() + TextSize::try_from(pos).unwrap(), + ), + highlight: HighlightTag::Comment.into(), + binding_hash: None, + }); + line_start += range.len() - TextSize::try_from(pos).unwrap(); + line_start += TextSize::try_from('\n'.len_utf8()).unwrap(); + + line[pos..].to_owned() + }) + .sep_by("\n") + .to_string(); + + if doctest.is_empty() { + return None; + } + + let doctest = format!("{}{}{}", prefix, doctest, suffix); + Some((doctest, range_mapping, new_comments)) +} + +/// Injection of syntax highlighting of doctests. +pub(super) fn highlight_doc_comment( + text: String, + range_mapping: RangesMap, + new_comments: Vec, + stack: &mut HighlightedRangeStack, +) { + let (analysis, tmp_file_id) = Analysis::from_single_file(text); + + stack.push(); + for mut h in analysis.highlight(tmp_file_id).unwrap() { + // Determine start offset and end offset in case of multi-line ranges + let mut start_offset = None; + let mut end_offset = None; + for (line_start, orig_line_start) in range_mapping.range(..h.range.end()).rev() { + if line_start <= &h.range.start() { + start_offset.get_or_insert(orig_line_start - line_start); + break; + } else { + end_offset.get_or_insert(orig_line_start - line_start); + } + } + if let Some(start_offset) = start_offset { + h.range = TextRange::new( + h.range.start() + start_offset, + h.range.end() + end_offset.unwrap_or(start_offset), + ); + stack.add(h); + } + } + + // Inject the comment prefix highlight ranges + stack.push(); + for comment in new_comments { + stack.add(comment); + } + stack.pop_and_inject(false); + stack.pop_and_inject(true); +} diff --git a/crates/ra_ide/src/syntax_highlighting/tests.rs b/crates/ra_ide/src/syntax_highlighting/tests.rs index 5e42c5b55b42..ba345d90a039 100644 --- a/crates/ra_ide/src/syntax_highlighting/tests.rs +++ b/crates/ra_ide/src/syntax_highlighting/tests.rs @@ -284,3 +284,53 @@ fn main() { false, ); } + +#[test] +fn test_highlight_doctest() { + check_highlighting( + r#" +impl Foo { + /// Constructs a new `Foo`. + /// + /// # Examples + /// + /// ``` + /// # #![allow(unused_mut)] + /// let mut foo: Foo = Foo::new(); + /// ``` + pub const fn new() -> Foo { + Foo { } + } + + /// `bar` method on `Foo`. + /// + /// # Examples + /// + /// ``` + /// let foo = Foo::new(); + /// + /// // calls bar on foo + /// assert!(foo.bar()); + /// + /// /* multi-line + /// comment */ + /// + /// let multi_line_string = "Foo + /// bar + /// "; + /// + /// ``` + /// + /// ``` + /// let foobar = Foo::new().bar(); + /// ``` + pub fn foo(&self) -> bool { + true + } +} +"# + .trim(), + "crates/ra_ide/src/snapshots/highlight_doctest.html", + false, + ) +} From 4edf736eb23c2d94a8c65a83a83201590e847b3f Mon Sep 17 00:00:00 2001 From: unexge Date: Mon, 8 Jun 2020 15:37:12 +0300 Subject: [PATCH 116/172] Swap `into_definition` and `definition` semantics for `FieldShorthand` variant --- crates/ra_ide_db/src/defs.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/ra_ide_db/src/defs.rs b/crates/ra_ide_db/src/defs.rs index 853d856e790a..1826f3ac6517 100644 --- a/crates/ra_ide_db/src/defs.rs +++ b/crates/ra_ide_db/src/defs.rs @@ -93,14 +93,14 @@ impl NameClass { match self { NameClass::Definition(it) => Some(it), NameClass::ConstReference(_) => None, - NameClass::FieldShorthand { local: _, field } => Some(field), + NameClass::FieldShorthand { local, field: _ } => Some(Definition::Local(local)), } } pub fn definition(self) -> Definition { match self { NameClass::Definition(it) | NameClass::ConstReference(it) => it, - NameClass::FieldShorthand { local, field: _ } => Definition::Local(local), + NameClass::FieldShorthand { local: _, field } => field, } } } From 48b6dd0b332d43268796b30128502e618b5b975e Mon Sep 17 00:00:00 2001 From: unexge Date: Mon, 8 Jun 2020 15:38:10 +0300 Subject: [PATCH 117/172] Use explicit match for extracting def from `classify_name` --- crates/ra_ide/src/goto_definition.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/crates/ra_ide/src/goto_definition.rs b/crates/ra_ide/src/goto_definition.rs index 620d2dedd3de..0798d2c36170 100644 --- a/crates/ra_ide/src/goto_definition.rs +++ b/crates/ra_ide/src/goto_definition.rs @@ -1,6 +1,6 @@ use hir::Semantics; use ra_ide_db::{ - defs::{classify_name, classify_name_ref}, + defs::{classify_name, classify_name_ref, NameClass}, symbol_index, RootDatabase, }; use ra_syntax::{ @@ -39,7 +39,10 @@ pub(crate) fn goto_definition( reference_definition(&sema, &name_ref).to_vec() }, ast::Name(name) => { - let def = classify_name(&sema, &name)?.into_definition()?; + let def = match classify_name(&sema, &name)? { + NameClass::Definition(def) | NameClass::ConstReference(def) => def, + NameClass::FieldShorthand { local: _, field } => field, + }; let nav = def.try_to_nav(sema.db)?; vec![nav] }, From c476f71bdf83e930ebe5c2ad2754f24ee0925c32 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Mon, 8 Jun 2020 15:03:14 +0200 Subject: [PATCH 118/172] Highlight only the unsafe operator itself --- crates/ra_ide/src/syntax_highlighting.rs | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/crates/ra_ide/src/syntax_highlighting.rs b/crates/ra_ide/src/syntax_highlighting.rs index 9ff7356c94c1..ab45c364a81f 100644 --- a/crates/ra_ide/src/syntax_highlighting.rs +++ b/crates/ra_ide/src/syntax_highlighting.rs @@ -480,12 +480,8 @@ fn highlight_element( _ => h, } } - PREFIX_EXPR => { - let prefix_expr = element.into_node().and_then(ast::PrefixExpr::cast)?; - match prefix_expr.op_kind() { - Some(ast::PrefixOp::Deref) => {} - _ => return None, - } + T![*] => { + let prefix_expr = element.parent().and_then(ast::PrefixExpr::cast)?; let expr = prefix_expr.expr()?; let ty = sema.type_of_expr(&expr)?; From 3b4d000250ae142288408cfe2abe7c10d4495c92 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Mon, 8 Jun 2020 15:23:03 +0200 Subject: [PATCH 119/172] Better unsafe highlihgting tests --- crates/ra_ide/src/snapshots/highlight_doctest.html | 3 ++- crates/ra_ide/src/snapshots/highlight_injection.html | 3 ++- crates/ra_ide/src/snapshots/highlight_strings.html | 3 ++- crates/ra_ide/src/snapshots/highlight_unsafe.html | 5 +++-- crates/ra_ide/src/snapshots/highlighting.html | 3 ++- crates/ra_ide/src/snapshots/rainbow_highlighting.html | 3 ++- crates/ra_ide/src/syntax_highlighting/html.rs | 3 ++- crates/ra_ide/src/syntax_highlighting/tests.rs | 2 +- 8 files changed, 16 insertions(+), 9 deletions(-) diff --git a/crates/ra_ide/src/snapshots/highlight_doctest.html b/crates/ra_ide/src/snapshots/highlight_doctest.html index 2f2d8c900ef6..0ae8c7efcd6b 100644 --- a/crates/ra_ide/src/snapshots/highlight_doctest.html +++ b/crates/ra_ide/src/snapshots/highlight_doctest.html @@ -10,7 +10,8 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd .string_literal { color: #CC9393; } .field { color: #94BFF3; } .function { color: #93E0E3; } -.operator.unsafe { color: #E28C14; } +.function.unsafe { color: #BC8383; } +.operator.unsafe { color: #BC8383; } .parameter { color: #94BFF3; } .text { color: #DCDCCC; } .type { color: #7CB8BB; } diff --git a/crates/ra_ide/src/snapshots/highlight_injection.html b/crates/ra_ide/src/snapshots/highlight_injection.html index fcdc98201f2e..dec06eb518f5 100644 --- a/crates/ra_ide/src/snapshots/highlight_injection.html +++ b/crates/ra_ide/src/snapshots/highlight_injection.html @@ -10,7 +10,8 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd .string_literal { color: #CC9393; } .field { color: #94BFF3; } .function { color: #93E0E3; } -.operator.unsafe { color: #E28C14; } +.function.unsafe { color: #BC8383; } +.operator.unsafe { color: #BC8383; } .parameter { color: #94BFF3; } .text { color: #DCDCCC; } .type { color: #7CB8BB; } diff --git a/crates/ra_ide/src/snapshots/highlight_strings.html b/crates/ra_ide/src/snapshots/highlight_strings.html index 6a5cf0e7449b..849eb3b7349b 100644 --- a/crates/ra_ide/src/snapshots/highlight_strings.html +++ b/crates/ra_ide/src/snapshots/highlight_strings.html @@ -10,7 +10,8 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd .string_literal { color: #CC9393; } .field { color: #94BFF3; } .function { color: #93E0E3; } -.operator.unsafe { color: #E28C14; } +.function.unsafe { color: #BC8383; } +.operator.unsafe { color: #BC8383; } .parameter { color: #94BFF3; } .text { color: #DCDCCC; } .type { color: #7CB8BB; } diff --git a/crates/ra_ide/src/snapshots/highlight_unsafe.html b/crates/ra_ide/src/snapshots/highlight_unsafe.html index 17ffc727cd06..bd24e6e381ef 100644 --- a/crates/ra_ide/src/snapshots/highlight_unsafe.html +++ b/crates/ra_ide/src/snapshots/highlight_unsafe.html @@ -10,7 +10,8 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd .string_literal { color: #CC9393; } .field { color: #94BFF3; } .function { color: #93E0E3; } -.operator.unsafe { color: #E28C14; } +.function.unsafe { color: #BC8383; } +.operator.unsafe { color: #BC8383; } .parameter { color: #94BFF3; } .text { color: #DCDCCC; } .type { color: #7CB8BB; } @@ -42,7 +43,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd unsafe { unsafe_fn(); HasUnsafeFn.unsafe_method(); - let y = *x; + let y = *(x); let z = -x; } } \ 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 42c5f3e5515e..e34ff5a7da3c 100644 --- a/crates/ra_ide/src/snapshots/highlighting.html +++ b/crates/ra_ide/src/snapshots/highlighting.html @@ -10,7 +10,8 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd .string_literal { color: #CC9393; } .field { color: #94BFF3; } .function { color: #93E0E3; } -.operator.unsafe { color: #E28C14; } +.function.unsafe { color: #BC8383; } +.operator.unsafe { color: #BC8383; } .parameter { color: #94BFF3; } .text { color: #DCDCCC; } .type { color: #7CB8BB; } diff --git a/crates/ra_ide/src/snapshots/rainbow_highlighting.html b/crates/ra_ide/src/snapshots/rainbow_highlighting.html index 2dd61d20d691..1ab06182c040 100644 --- a/crates/ra_ide/src/snapshots/rainbow_highlighting.html +++ b/crates/ra_ide/src/snapshots/rainbow_highlighting.html @@ -10,7 +10,8 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd .string_literal { color: #CC9393; } .field { color: #94BFF3; } .function { color: #93E0E3; } -.operator.unsafe { color: #E28C14; } +.function.unsafe { color: #BC8383; } +.operator.unsafe { color: #BC8383; } .parameter { color: #94BFF3; } .text { color: #DCDCCC; } .type { color: #7CB8BB; } diff --git a/crates/ra_ide/src/syntax_highlighting/html.rs b/crates/ra_ide/src/syntax_highlighting/html.rs index 7d946c98dae7..5bada6252c0f 100644 --- a/crates/ra_ide/src/syntax_highlighting/html.rs +++ b/crates/ra_ide/src/syntax_highlighting/html.rs @@ -69,7 +69,8 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd .string_literal { color: #CC9393; } .field { color: #94BFF3; } .function { color: #93E0E3; } -.operator.unsafe { color: #E28C14; } +.function.unsafe { color: #BC8383; } +.operator.unsafe { color: #BC8383; } .parameter { color: #94BFF3; } .text { color: #DCDCCC; } .type { color: #7CB8BB; } diff --git a/crates/ra_ide/src/syntax_highlighting/tests.rs b/crates/ra_ide/src/syntax_highlighting/tests.rs index ba345d90a039..021f8e7e2d41 100644 --- a/crates/ra_ide/src/syntax_highlighting/tests.rs +++ b/crates/ra_ide/src/syntax_highlighting/tests.rs @@ -274,7 +274,7 @@ fn main() { unsafe { unsafe_fn(); HasUnsafeFn.unsafe_method(); - let y = *x; + let y = *(x); let z = -x; } } From 9c35f135b9c872e904ee1e838cfa69fc5745c45f Mon Sep 17 00:00:00 2001 From: Paul Daniel Faria Date: Mon, 8 Jun 2020 10:23:20 -0400 Subject: [PATCH 120/172] Remove default_cfg_options, pass target instead so it can be used for building cargo workspaces --- crates/ra_project_model/src/lib.rs | 26 +++++++++++----------- crates/rust-analyzer/src/cli/load_cargo.rs | 20 ++++------------- crates/rust-analyzer/src/global_state.rs | 11 ++------- 3 files changed, 19 insertions(+), 38 deletions(-) diff --git a/crates/ra_project_model/src/lib.rs b/crates/ra_project_model/src/lib.rs index ef443fc09e6e..4ef2e6f856f3 100644 --- a/crates/ra_project_model/src/lib.rs +++ b/crates/ra_project_model/src/lib.rs @@ -250,7 +250,7 @@ impl ProjectWorkspace { pub fn to_crate_graph( &self, - default_cfg_options: &CfgOptions, + target: Option<&String>, extern_source_roots: &FxHashMap, proc_macro_client: &ProcMacroClient, load: &mut dyn FnMut(&Path) -> Option, @@ -269,7 +269,7 @@ impl ProjectWorkspace { json_project::Edition::Edition2018 => Edition::Edition2018, }; let cfg_options = { - let mut opts = default_cfg_options.clone(); + let mut opts = CfgOptions::default(); for cfg in &krate.cfg { match cfg.find('=') { None => opts.insert_atom(cfg.into()), @@ -343,14 +343,13 @@ impl ProjectWorkspace { } } ProjectWorkspace::Cargo { cargo, sysroot } => { + let mut cfg_options = get_rustc_cfg_options(target); + let sysroot_crates: FxHashMap<_, _> = sysroot .crates() .filter_map(|krate| { let file_id = load(&sysroot[krate].root)?; - // Crates from sysroot have `cfg(test)` disabled - let cfg_options = default_cfg_options.clone(); - let env = Env::default(); let extern_source = ExternSource::default(); let proc_macro = vec![]; @@ -361,7 +360,7 @@ impl ProjectWorkspace { file_id, Edition::Edition2018, Some(crate_name), - cfg_options, + cfg_options.clone(), env, extern_source, proc_macro, @@ -392,6 +391,10 @@ impl ProjectWorkspace { let mut pkg_to_lib_crate = FxHashMap::default(); let mut pkg_crates = FxHashMap::default(); + + // Add test cfg for non-sysroot crates + cfg_options.insert_atom("test".into()); + // Next, create crates for each package, target pair for pkg in cargo.packages() { let mut lib_tgt = None; @@ -400,12 +403,7 @@ impl ProjectWorkspace { if let Some(file_id) = load(root) { let edition = cargo[pkg].edition; let cfg_options = { - let mut opts = { - let mut opts = default_cfg_options.clone(); - opts.insert_atom("test".into()); - opts - }; - + let mut opts = cfg_options.clone(); for feature in cargo[pkg].features.iter() { opts.insert_key_value("feature".into(), feature.into()); } @@ -562,7 +560,7 @@ impl ProjectWorkspace { } } -pub fn get_rustc_cfg_options(target: Option<&String>) -> CfgOptions { +fn get_rustc_cfg_options(target: Option<&String>) -> CfgOptions { let mut cfg_options = CfgOptions::default(); // Some nightly-only cfgs, which are required for stdlib @@ -602,6 +600,8 @@ pub fn get_rustc_cfg_options(target: Option<&String>) -> CfgOptions { Err(e) => log::error!("failed to get rustc cfgs: {:#}", e), } + cfg_options.insert_atom("debug_assertion".into()); + cfg_options } diff --git a/crates/rust-analyzer/src/cli/load_cargo.rs b/crates/rust-analyzer/src/cli/load_cargo.rs index 46181b677b5c..8f2aeac7714e 100644 --- a/crates/rust-analyzer/src/cli/load_cargo.rs +++ b/crates/rust-analyzer/src/cli/load_cargo.rs @@ -8,8 +8,7 @@ use crossbeam_channel::{unbounded, Receiver}; use ra_db::{ExternSourceId, FileId, SourceRootId}; use ra_ide::{AnalysisChange, AnalysisHost}; use ra_project_model::{ - get_rustc_cfg_options, CargoConfig, PackageRoot, ProcMacroClient, ProjectManifest, - ProjectWorkspace, + CargoConfig, PackageRoot, ProcMacroClient, ProjectManifest, ProjectWorkspace, }; use ra_vfs::{RootEntry, Vfs, VfsChange, VfsTask, Watch}; use rustc_hash::{FxHashMap, FxHashSet}; @@ -148,25 +147,14 @@ pub(crate) fn load( } } - // FIXME: cfg options? - let default_cfg_options = { - let mut opts = get_rustc_cfg_options(None); - opts.insert_atom("debug_assertion".into()); - opts - }; - - let crate_graph = ws.to_crate_graph( - &default_cfg_options, - &extern_source_roots, - proc_macro_client, - &mut |path: &Path| { + let crate_graph = + ws.to_crate_graph(None, &extern_source_roots, proc_macro_client, &mut |path: &Path| { // Some path from metadata will be non canonicalized, e.g. /foo/../bar/lib.rs let path = path.canonicalize().ok()?; let vfs_file = vfs.load(&path); log::debug!("vfs file {:?} -> {:?}", path, vfs_file); vfs_file.map(vfs_file_to_id) - }, - ); + }); log::debug!("crate graph: {:?}", crate_graph); analysis_change.set_crate_graph(crate_graph); diff --git a/crates/rust-analyzer/src/global_state.rs b/crates/rust-analyzer/src/global_state.rs index 4d871aa34013..0b52030cf525 100644 --- a/crates/rust-analyzer/src/global_state.rs +++ b/crates/rust-analyzer/src/global_state.rs @@ -15,7 +15,7 @@ use ra_flycheck::{Flycheck, FlycheckConfig}; use ra_ide::{ Analysis, AnalysisChange, AnalysisHost, CrateGraph, FileId, LibraryData, SourceRootId, }; -use ra_project_model::{get_rustc_cfg_options, ProcMacroClient, ProjectWorkspace}; +use ra_project_model::{ProcMacroClient, ProjectWorkspace}; use ra_vfs::{LineEndings, RootEntry, Vfs, VfsChange, VfsFile, VfsRoot, VfsTask, Watch}; use relative_path::RelativePathBuf; use stdx::format_to; @@ -135,13 +135,6 @@ impl GlobalState { } } - // FIXME: Read default cfgs from config - let default_cfg_options = { - let mut opts = get_rustc_cfg_options(config.cargo.target.as_ref()); - opts.insert_atom("debug_assertion".into()); - opts - }; - let proc_macro_client = match &config.proc_macro_srv { None => ProcMacroClient::dummy(), Some((path, args)) => match ProcMacroClient::extern_process(path.into(), args) { @@ -167,7 +160,7 @@ impl GlobalState { }; for ws in workspaces.iter() { crate_graph.extend(ws.to_crate_graph( - &default_cfg_options, + config.cargo.target.as_ref(), &extern_source_roots, &proc_macro_client, &mut load, From e3663d60bfe1e8505e6db8bffab997b87be03913 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lauren=C8=9Biu=20Nicola?= Date: Mon, 8 Jun 2020 18:50:27 +0300 Subject: [PATCH 121/172] Dev docs nits --- docs/dev/README.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/docs/dev/README.md b/docs/dev/README.md index 46ee030fc686..0330939b6ef0 100644 --- a/docs/dev/README.md +++ b/docs/dev/README.md @@ -55,7 +55,7 @@ You can run `cargo xtask install-pre-commit-hook` to install git-hook to run rus All Rust code lives in the `crates` top-level directory, and is organized as a single Cargo workspace. The `editors` top-level directory contains code for integrating with editors. Currently, it contains the plugin for VS Code (in -typescript). The `docs` top-level directory contains both developer and user +TypeScript). The `docs` top-level directory contains both developer and user documentation. We have some automation infra in Rust in the `xtask` package. It contains @@ -79,8 +79,8 @@ possible. There's **"Run Extension (Debug Build)"** launch configuration for thi In general, I use one of the following workflows for fixing bugs and implementing features. -If the problem concerns only internal parts of rust-analyzer (ie, I don't need -to touch `rust-analyzer` crate or typescript code), there is a unit-test for it. +If the problem concerns only internal parts of rust-analyzer (i.e. I don't need +to touch `rust-analyzer` crate or TypeScript code), there is a unit-test for it. So, I use **Rust Analyzer: Run** action in VS Code to run this single test, and then just do printf-driven development/debugging. As a sanity check after I'm done, I use `cargo xtask install --server` and **Reload Window** action in VS @@ -279,7 +279,7 @@ always obvious from the low-level code. Syntax trees are by design incomplete and do not enforce well-formedness. If ast method returns an `Option`, it *can* be `None` at runtime, even if this is forbidden by the grammar. -## LSP indenpendence +## LSP independence rust-analyzer is independent from LSP. It provides features for a hypothetical perfect Rust-specific IDE client. @@ -289,7 +289,7 @@ Internal representations are lowered to LSP in the `rust-analyzer` crate (the on There's a semi-hard split between "compiler" and "IDE", at the `ra_hir` crate. Compiler derives new facts about source code. -It explicitly acknowledges that not all info is available (ie, you can't look at types during name resolution). +It explicitly acknowledges that not all info is available (i.e. you can't look at types during name resolution). IDE assumes that all information is available at all times. @@ -345,8 +345,8 @@ There's also two VS Code commands which might be of interest: rust code that it refers to and the rust editor will also highlight the proper text range. - If you press Ctrl (i.e. trigger goto definition) in the inspected - Rust source file the syntax tree read-only editor should scroll to and select the + If you trigger Go to Definition in the inspected Rust source file, + the syntax tree read-only editor should scroll to and select the appropriate syntax node token. ![demo](https://user-images.githubusercontent.com/36276403/78225773-6636a480-74d3-11ea-9d9f-1c9d42da03b0.png) From dbceaf522b717bd0dda89f6af1684ebd8e033aee Mon Sep 17 00:00:00 2001 From: Paul Daniel Faria Date: Mon, 8 Jun 2020 12:10:23 -0400 Subject: [PATCH 122/172] Use Option<&str> for target instead of Option<&String> --- crates/ra_project_model/src/lib.rs | 6 +++--- crates/rust-analyzer/src/global_state.rs | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/crates/ra_project_model/src/lib.rs b/crates/ra_project_model/src/lib.rs index 4ef2e6f856f3..fe03b509e4ff 100644 --- a/crates/ra_project_model/src/lib.rs +++ b/crates/ra_project_model/src/lib.rs @@ -250,7 +250,7 @@ impl ProjectWorkspace { pub fn to_crate_graph( &self, - target: Option<&String>, + target: Option<&str>, extern_source_roots: &FxHashMap, proc_macro_client: &ProcMacroClient, load: &mut dyn FnMut(&Path) -> Option, @@ -560,7 +560,7 @@ impl ProjectWorkspace { } } -fn get_rustc_cfg_options(target: Option<&String>) -> CfgOptions { +fn get_rustc_cfg_options(target: Option<&str>) -> CfgOptions { let mut cfg_options = CfgOptions::default(); // Some nightly-only cfgs, which are required for stdlib @@ -578,7 +578,7 @@ fn get_rustc_cfg_options(target: Option<&String>) -> CfgOptions { let mut cmd = Command::new(ra_toolchain::rustc()); cmd.args(&["--print", "cfg", "-O"]); if let Some(target) = target { - cmd.args(&["--target", target.as_str()]); + cmd.args(&["--target", target]); } let output = output(cmd)?; Ok(String::from_utf8(output.stdout)?) diff --git a/crates/rust-analyzer/src/global_state.rs b/crates/rust-analyzer/src/global_state.rs index 0b52030cf525..73b0f881d369 100644 --- a/crates/rust-analyzer/src/global_state.rs +++ b/crates/rust-analyzer/src/global_state.rs @@ -160,7 +160,7 @@ impl GlobalState { }; for ws in workspaces.iter() { crate_graph.extend(ws.to_crate_graph( - config.cargo.target.as_ref(), + config.cargo.target.as_deref(), &extern_source_roots, &proc_macro_client, &mut load, From 2a42904680041664621034c22c3f600a47107a5a Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Mon, 8 Jun 2020 21:44:42 +0200 Subject: [PATCH 123/172] Simplify --- crates/ra_ide/src/diagnostics.rs | 4 ++-- crates/ra_ide/src/lib.rs | 2 +- crates/ra_ide/src/references/rename.rs | 6 +++--- crates/ra_ide/src/typing.rs | 6 +++--- crates/ra_ide_db/src/source_change.rs | 23 +++++++---------------- 5 files changed, 16 insertions(+), 25 deletions(-) diff --git a/crates/ra_ide/src/diagnostics.rs b/crates/ra_ide/src/diagnostics.rs index 15dc50cf143d..bf14a467f8a2 100644 --- a/crates/ra_ide/src/diagnostics.rs +++ b/crates/ra_ide/src/diagnostics.rs @@ -21,7 +21,7 @@ use ra_syntax::{ }; use ra_text_edit::{TextEdit, TextEditBuilder}; -use crate::{Diagnostic, FileId, FileSystemEdit, Fix, SourceChange, SourceFileEdit}; +use crate::{Diagnostic, FileId, FileSystemEdit, Fix, SourceFileEdit}; #[derive(Debug, Copy, Clone)] pub enum Severity { @@ -115,7 +115,7 @@ pub(crate) fn diagnostics(db: &RootDatabase, file_id: FileId) -> Vec let node = d.ast(db); let replacement = format!("Ok({})", node.syntax()); let edit = TextEdit::replace(node.syntax().text_range(), replacement); - let source_change = SourceChange::source_file_edit_from(file_id, edit); + let source_change = SourceFileEdit { file_id, edit }.into(); let fix = Fix::new("Wrap with ok", source_change); res.borrow_mut().push(Diagnostic { range: sema.diagnostics_range(d).range, diff --git a/crates/ra_ide/src/lib.rs b/crates/ra_ide/src/lib.rs index a56718d3f595..28f6867672c0 100644 --- a/crates/ra_ide/src/lib.rs +++ b/crates/ra_ide/src/lib.rs @@ -503,7 +503,7 @@ impl Analysis { ) -> Cancelable> { self.with_db(|db| { let edits = ssr::parse_search_replace(query, parse_only, db)?; - Ok(SourceChange::source_file_edits(edits)) + Ok(SourceChange::from(edits)) }) } diff --git a/crates/ra_ide/src/references/rename.rs b/crates/ra_ide/src/references/rename.rs index 28c6349b1b4e..915d4f4d3b56 100644 --- a/crates/ra_ide/src/references/rename.rs +++ b/crates/ra_ide/src/references/rename.rs @@ -171,7 +171,7 @@ fn rename_to_self(db: &RootDatabase, position: FilePosition) -> Option Option { diff --git a/crates/ra_ide_db/src/source_change.rs b/crates/ra_ide_db/src/source_change.rs index e713f4b7e80c..f40ae8304a42 100644 --- a/crates/ra_ide_db/src/source_change.rs +++ b/crates/ra_ide_db/src/source_change.rs @@ -22,17 +22,6 @@ impl SourceChange { ) -> Self { SourceChange { source_file_edits, file_system_edits, is_snippet: false } } - - /// Creates a new SourceChange with the given label, - /// containing only the given `SourceFileEdits`. - pub fn source_file_edits(edits: Vec) -> Self { - SourceChange { source_file_edits: edits, file_system_edits: vec![], is_snippet: false } - } - /// Creates a new SourceChange with the given label - /// from the given `FileId` and `TextEdit` - pub fn source_file_edit_from(file_id: FileId, edit: TextEdit) -> Self { - SourceFileEdit { file_id, edit }.into() - } } #[derive(Debug, Clone)] @@ -43,11 +32,13 @@ pub struct SourceFileEdit { impl From for SourceChange { fn from(edit: SourceFileEdit) -> SourceChange { - SourceChange { - source_file_edits: vec![edit], - file_system_edits: Vec::new(), - is_snippet: false, - } + vec![edit].into() + } +} + +impl From> for SourceChange { + fn from(source_file_edits: Vec) -> SourceChange { + SourceChange { source_file_edits, file_system_edits: Vec::new(), is_snippet: false } } } From e38685cb48a44c3321922f5f7228072b503d2973 Mon Sep 17 00:00:00 2001 From: Avi Dessauer Date: Mon, 8 Jun 2020 17:49:06 -0400 Subject: [PATCH 124/172] Parse default unsafe fn --- crates/ra_parser/src/grammar/items.rs | 8 +++- .../inline/ok/0163_default_unsafe_fn.rast | 40 +++++++++++++++++++ .../inline/ok/0163_default_unsafe_fn.rs | 3 ++ 3 files changed, 50 insertions(+), 1 deletion(-) create mode 100644 crates/ra_syntax/test_data/parser/inline/ok/0163_default_unsafe_fn.rast create mode 100644 crates/ra_syntax/test_data/parser/inline/ok/0163_default_unsafe_fn.rs diff --git a/crates/ra_parser/src/grammar/items.rs b/crates/ra_parser/src/grammar/items.rs index 9c14b954af4b..56cfb509da0f 100644 --- a/crates/ra_parser/src/grammar/items.rs +++ b/crates/ra_parser/src/grammar/items.rs @@ -121,7 +121,13 @@ pub(super) fn maybe_item(p: &mut Parser, m: Marker, flavor: ItemFlavor) -> Resul T![unsafe] => { // test default_unsafe_impl // default unsafe impl Foo {} - if p.nth(2) == T![impl] { + + // test default_unsafe_fn + // impl T for Foo { + // default unsafe fn foo() {} + // } + let sk = p.nth(2); + if sk == T![impl] || sk == T![fn] { p.bump_remap(T![default]); p.bump(T![unsafe]); has_mods = true; diff --git a/crates/ra_syntax/test_data/parser/inline/ok/0163_default_unsafe_fn.rast b/crates/ra_syntax/test_data/parser/inline/ok/0163_default_unsafe_fn.rast new file mode 100644 index 000000000000..adb6159f4725 --- /dev/null +++ b/crates/ra_syntax/test_data/parser/inline/ok/0163_default_unsafe_fn.rast @@ -0,0 +1,40 @@ +SOURCE_FILE@0..50 + IMPL_DEF@0..49 + IMPL_KW@0..4 "impl" + WHITESPACE@4..5 " " + PATH_TYPE@5..6 + PATH@5..6 + PATH_SEGMENT@5..6 + NAME_REF@5..6 + IDENT@5..6 "T" + WHITESPACE@6..7 " " + FOR_KW@7..10 "for" + WHITESPACE@10..11 " " + PATH_TYPE@11..14 + PATH@11..14 + PATH_SEGMENT@11..14 + NAME_REF@11..14 + IDENT@11..14 "Foo" + WHITESPACE@14..15 " " + ITEM_LIST@15..49 + L_CURLY@15..16 "{" + WHITESPACE@16..21 "\n " + FN_DEF@21..47 + DEFAULT_KW@21..28 "default" + WHITESPACE@28..29 " " + UNSAFE_KW@29..35 "unsafe" + WHITESPACE@35..36 " " + FN_KW@36..38 "fn" + WHITESPACE@38..39 " " + NAME@39..42 + IDENT@39..42 "foo" + PARAM_LIST@42..44 + L_PAREN@42..43 "(" + R_PAREN@43..44 ")" + WHITESPACE@44..45 " " + BLOCK_EXPR@45..47 + L_CURLY@45..46 "{" + R_CURLY@46..47 "}" + WHITESPACE@47..48 "\n" + R_CURLY@48..49 "}" + WHITESPACE@49..50 "\n" diff --git a/crates/ra_syntax/test_data/parser/inline/ok/0163_default_unsafe_fn.rs b/crates/ra_syntax/test_data/parser/inline/ok/0163_default_unsafe_fn.rs new file mode 100644 index 000000000000..12926cd8a1b4 --- /dev/null +++ b/crates/ra_syntax/test_data/parser/inline/ok/0163_default_unsafe_fn.rs @@ -0,0 +1,3 @@ +impl T for Foo { + default unsafe fn foo() {} +} From 38fa4d17fb9622044ee0f0bc50d6c71d5aa46dd1 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Tue, 9 Jun 2020 00:01:40 +0200 Subject: [PATCH 125/172] Simplify API --- crates/ra_assists/src/assist_context.rs | 99 +++++-------------- .../ra_assists/src/handlers/add_function.rs | 2 +- .../extract_struct_from_enum_variant.rs | 66 ++++++------- .../ra_assists/src/handlers/fix_visibility.rs | 4 +- 4 files changed, 60 insertions(+), 111 deletions(-) diff --git a/crates/ra_assists/src/assist_context.rs b/crates/ra_assists/src/assist_context.rs index 1925db8b2754..edd8255f44a1 100644 --- a/crates/ra_assists/src/assist_context.rs +++ b/crates/ra_assists/src/assist_context.rs @@ -1,5 +1,7 @@ //! See `AssistContext` +use std::mem; + use algo::find_covering_element; use hir::Semantics; use ra_db::{FileId, FileRange}; @@ -19,7 +21,6 @@ use crate::{ assist_config::{AssistConfig, SnippetCap}, Assist, AssistId, GroupLabel, ResolvedAssist, }; -use rustc_hash::FxHashMap; /// `AssistContext` allows to apply an assist or check if it could be applied. /// @@ -139,16 +140,6 @@ impl Assists { let label = Assist::new(id, label.into(), None, target); self.add_impl(label, f) } - pub(crate) fn add_in_multiple_files( - &mut self, - id: AssistId, - label: impl Into, - target: TextRange, - f: impl FnOnce(&mut AssistDirector), - ) -> Option<()> { - let label = Assist::new(id, label.into(), None, target); - self.add_impl_multiple_files(label, f) - } pub(crate) fn add_group( &mut self, group: &GroupLabel, @@ -173,31 +164,6 @@ impl Assists { Some(()) } - fn add_impl_multiple_files( - &mut self, - label: Assist, - f: impl FnOnce(&mut AssistDirector), - ) -> Option<()> { - if !self.resolve { - self.buf.push((label, None)); - return None; - } - let mut director = AssistDirector::default(); - f(&mut director); - let changes = director.finish(); - let file_edits: Vec = - changes.into_iter().map(|mut change| change.source_file_edits.pop().unwrap()).collect(); - - let source_change = SourceChange { - source_file_edits: file_edits, - file_system_edits: vec![], - is_snippet: false, - }; - - self.buf.push((label, Some(source_change))); - Some(()) - } - fn finish(mut self) -> Vec<(Assist, Option)> { self.buf.sort_by_key(|(label, _edit)| label.target.len()); self.buf @@ -206,13 +172,32 @@ impl Assists { pub(crate) struct AssistBuilder { edit: TextEditBuilder, - file: FileId, + file_id: FileId, is_snippet: bool, + edits: Vec, } impl AssistBuilder { - pub(crate) fn new(file: FileId) -> AssistBuilder { - AssistBuilder { edit: TextEditBuilder::default(), file, is_snippet: false } + pub(crate) fn new(file_id: FileId) -> AssistBuilder { + AssistBuilder { + edit: TextEditBuilder::default(), + file_id, + is_snippet: false, + edits: Vec::new(), + } + } + + pub(crate) fn edit_file(&mut self, file_id: FileId) { + self.file_id = file_id; + } + + fn commit(&mut self) { + let edit = mem::take(&mut self.edit).finish(); + if !edit.is_empty() { + let new_edit = SourceFileEdit { file_id: self.file_id, edit }; + assert!(!self.edits.iter().any(|it| it.file_id == new_edit.file_id)); + self.edits.push(new_edit); + } } /// Remove specified `range` of text. @@ -270,48 +255,18 @@ impl AssistBuilder { algo::diff(&node, &new).into_text_edit(&mut self.edit) } - // FIXME: better API - pub(crate) fn set_file(&mut self, assist_file: FileId) { - self.file = assist_file; - } - // FIXME: kill this API /// Get access to the raw `TextEditBuilder`. pub(crate) fn text_edit_builder(&mut self) -> &mut TextEditBuilder { &mut self.edit } - fn finish(self) -> SourceChange { - let edit = self.edit.finish(); - let source_file_edit = SourceFileEdit { file_id: self.file, edit }; - let mut res: SourceChange = source_file_edit.into(); + fn finish(mut self) -> SourceChange { + self.commit(); + let mut res: SourceChange = mem::take(&mut self.edits).into(); if self.is_snippet { res.is_snippet = true; } res } } - -pub(crate) struct AssistDirector { - builders: FxHashMap, -} - -impl AssistDirector { - pub(crate) fn perform(&mut self, file_id: FileId, f: impl FnOnce(&mut AssistBuilder)) { - let mut builder = self.builders.entry(file_id).or_insert(AssistBuilder::new(file_id)); - f(&mut builder); - } - - fn finish(self) -> Vec { - self.builders - .into_iter() - .map(|(_, builder)| builder.finish()) - .collect::>() - } -} - -impl Default for AssistDirector { - fn default() -> Self { - AssistDirector { builders: FxHashMap::default() } - } -} diff --git a/crates/ra_assists/src/handlers/add_function.rs b/crates/ra_assists/src/handlers/add_function.rs index 24f931a85e19..1cfbd75aa11c 100644 --- a/crates/ra_assists/src/handlers/add_function.rs +++ b/crates/ra_assists/src/handlers/add_function.rs @@ -64,7 +64,7 @@ pub(crate) fn add_function(acc: &mut Assists, ctx: &AssistContext) -> Option<()> let target = call.syntax().text_range(); acc.add(AssistId("add_function"), "Add function", target, |builder| { let function_template = function_builder.render(); - builder.set_file(function_template.file); + builder.edit_file(function_template.file); let new_fn = function_template.to_string(ctx.config.snippet_cap); match ctx.config.snippet_cap { Some(cap) => builder.insert_snippet(cap, function_template.insert_offset, new_fn), diff --git a/crates/ra_assists/src/handlers/extract_struct_from_enum_variant.rs b/crates/ra_assists/src/handlers/extract_struct_from_enum_variant.rs index 2c455a1fd367..44db7917a60e 100644 --- a/crates/ra_assists/src/handlers/extract_struct_from_enum_variant.rs +++ b/crates/ra_assists/src/handlers/extract_struct_from_enum_variant.rs @@ -1,21 +1,18 @@ -use ra_ide_db::{defs::Definition, search::Reference, RootDatabase}; -use ra_syntax::{ - algo::find_node_at_offset, - ast::{self, AstNode, NameOwner}, - SourceFile, SyntaxNode, TextRange, TextSize, -}; - -use crate::{ - assist_context::{AssistBuilder, AssistDirector}, - utils::insert_use_statement, - AssistContext, AssistId, Assists, -}; -use ast::{ArgListOwner, VisibilityOwner}; use hir::{EnumVariant, Module, ModuleDef, Name}; use ra_db::FileId; use ra_fmt::leading_indent; +use ra_ide_db::{defs::Definition, search::Reference, RootDatabase}; +use ra_syntax::{ + algo::find_node_at_offset, + ast::{self, ArgListOwner, AstNode, NameOwner, VisibilityOwner}, + SourceFile, SyntaxNode, TextRange, TextSize, +}; use rustc_hash::FxHashSet; +use crate::{ + assist_context::AssistBuilder, utils::insert_use_statement, AssistContext, AssistId, Assists, +}; + // Assist: extract_struct_from_enum_variant // // Extracts a struct from enum variant. @@ -50,11 +47,11 @@ pub(crate) fn extract_struct_from_enum_variant( let enum_module_def = ModuleDef::from(enum_hir); let current_module = enum_hir.module(ctx.db); let target = variant.syntax().text_range(); - acc.add_in_multiple_files( + acc.add( AssistId("extract_struct_from_enum_variant"), "Extract struct from enum variant", target, - |edit| { + |builder| { let definition = Definition::ModuleDef(ModuleDef::EnumVariant(variant_hir)); let res = definition.find_usages(&ctx.db, None); let start_offset = variant.parent_enum().syntax().text_range().start(); @@ -64,7 +61,7 @@ pub(crate) fn extract_struct_from_enum_variant( let source_file = ctx.sema.parse(reference.file_range.file_id); update_reference( ctx, - edit, + builder, reference, &source_file, &enum_module_def, @@ -73,7 +70,7 @@ pub(crate) fn extract_struct_from_enum_variant( ); } extract_struct_def( - edit, + builder, enum_ast.syntax(), &variant_name, &field_list.to_string(), @@ -82,7 +79,7 @@ pub(crate) fn extract_struct_from_enum_variant( &visibility, ); let list_range = field_list.syntax().text_range(); - update_variant(edit, &variant_name, ctx.frange.file_id, list_range); + update_variant(builder, &variant_name, ctx.frange.file_id, list_range); }, ) } @@ -115,7 +112,7 @@ fn insert_import( } fn extract_struct_def( - edit: &mut AssistDirector, + builder: &mut AssistBuilder, enum_ast: &SyntaxNode, variant_name: &str, variant_list: &str, @@ -142,14 +139,13 @@ fn extract_struct_def( list_with_visibility(variant_list), indent ); - edit.perform(file_id, |builder| { - builder.insert(start_offset, struct_def); - }); + builder.edit_file(file_id); + builder.insert(start_offset, struct_def); Some(()) } fn update_variant( - edit: &mut AssistDirector, + builder: &mut AssistBuilder, variant_name: &str, file_id: FileId, list_range: TextRange, @@ -158,15 +154,14 @@ fn update_variant( list_range.start().checked_add(TextSize::from(1))?, list_range.end().checked_sub(TextSize::from(1))?, ); - edit.perform(file_id, |builder| { - builder.replace(inside_variant_range, variant_name); - }); + builder.edit_file(file_id); + builder.replace(inside_variant_range, variant_name); Some(()) } fn update_reference( ctx: &AssistContext, - edit: &mut AssistDirector, + builder: &mut AssistBuilder, reference: Reference, source_file: &SourceFile, enum_module_def: &ModuleDef, @@ -186,16 +181,15 @@ fn update_reference( list_range.start().checked_add(TextSize::from(1))?, list_range.end().checked_sub(TextSize::from(1))?, ); - edit.perform(reference.file_range.file_id, |builder| { - if !visited_modules_set.contains(&module) { - if insert_import(ctx, builder, &path_expr, &module, enum_module_def, variant_hir_name) - .is_some() - { - visited_modules_set.insert(module); - } + builder.edit_file(reference.file_range.file_id); + if !visited_modules_set.contains(&module) { + if insert_import(ctx, builder, &path_expr, &module, enum_module_def, variant_hir_name) + .is_some() + { + visited_modules_set.insert(module); } - builder.replace(inside_list_range, format!("{}{}", segment, list)); - }); + } + builder.replace(inside_list_range, format!("{}{}", segment, list)); Some(()) } diff --git a/crates/ra_assists/src/handlers/fix_visibility.rs b/crates/ra_assists/src/handlers/fix_visibility.rs index 9ec42f568c59..531b3560f2e4 100644 --- a/crates/ra_assists/src/handlers/fix_visibility.rs +++ b/crates/ra_assists/src/handlers/fix_visibility.rs @@ -63,7 +63,7 @@ fn add_vis_to_referenced_module_def(acc: &mut Assists, ctx: &AssistContext) -> O }; acc.add(AssistId("fix_visibility"), assist_label, target, |builder| { - builder.set_file(target_file); + builder.edit_file(target_file); match ctx.config.snippet_cap { Some(cap) => builder.insert_snippet(cap, offset, format!("$0{} ", missing_visibility)), None => builder.insert(offset, format!("{} ", missing_visibility)), @@ -106,7 +106,7 @@ fn add_vis_to_referenced_record_field(acc: &mut Assists, ctx: &AssistContext) -> format!("Change visibility of {}.{} to {}", parent_name, target_name, missing_visibility); acc.add(AssistId("fix_visibility"), assist_label, target, |builder| { - builder.set_file(target_file); + builder.edit_file(target_file); match ctx.config.snippet_cap { Some(cap) => builder.insert_snippet(cap, offset, format!("$0{} ", missing_visibility)), None => builder.insert(offset, format!("{} ", missing_visibility)), From 055b9b64bce2808ed31eb4547cea16ec282d6897 Mon Sep 17 00:00:00 2001 From: Aaron Wood Date: Wed, 3 Jun 2020 14:54:05 -0700 Subject: [PATCH 126/172] Finish transition to cfgs from the separate atoms and features. --- crates/ra_project_model/src/json_project.rs | 42 +-------------------- crates/ra_project_model/src/lib.rs | 6 --- 2 files changed, 1 insertion(+), 47 deletions(-) diff --git a/crates/ra_project_model/src/json_project.rs b/crates/ra_project_model/src/json_project.rs index 09c06fef9359..ee2de4c25691 100644 --- a/crates/ra_project_model/src/json_project.rs +++ b/crates/ra_project_model/src/json_project.rs @@ -2,7 +2,7 @@ use std::path::PathBuf; -use rustc_hash::{FxHashMap, FxHashSet}; +use rustc_hash::FxHashSet; use serde::Deserialize; /// Roots and crates that compose this Rust project. @@ -28,16 +28,9 @@ pub struct Crate { pub(crate) edition: Edition, pub(crate) deps: Vec, - // This is the preferred method of providing cfg options. #[serde(default)] pub(crate) cfg: FxHashSet, - // These two are here for transition only. - #[serde(default)] - pub(crate) atom_cfgs: FxHashSet, - #[serde(default)] - pub(crate) key_value_cfgs: FxHashMap, - pub(crate) out_dir: Option, pub(crate) proc_macro_dylib_path: Option, } @@ -99,37 +92,4 @@ mod tests { assert!(krate.cfg.contains(&"feature=feature_2".to_string())); assert!(krate.cfg.contains(&"other=value".to_string())); } - - #[test] - fn test_crate_deserialization_old_json() { - let raw_json = json!( { - "crate_id": 2, - "root_module": "this/is/a/file/path.rs", - "deps": [ - { - "crate": 1, - "name": "some_dep_crate" - }, - ], - "edition": "2015", - "atom_cfgs": [ - "atom_1", - "atom_2", - ], - "key_value_cfgs": { - "feature": "feature_1", - "feature": "feature_2", - "other": "value", - }, - }); - - let krate: Crate = serde_json::from_value(raw_json).unwrap(); - - assert!(krate.atom_cfgs.contains(&"atom_1".to_string())); - assert!(krate.atom_cfgs.contains(&"atom_2".to_string())); - assert!(krate.key_value_cfgs.contains_key(&"feature".to_string())); - assert_eq!(krate.key_value_cfgs.get("feature"), Some(&"feature_2".to_string())); - assert!(krate.key_value_cfgs.contains_key(&"other".to_string())); - assert_eq!(krate.key_value_cfgs.get("other"), Some(&"value".to_string())); - } } diff --git a/crates/ra_project_model/src/lib.rs b/crates/ra_project_model/src/lib.rs index fe03b509e4ff..47fa34ddfb84 100644 --- a/crates/ra_project_model/src/lib.rs +++ b/crates/ra_project_model/src/lib.rs @@ -280,12 +280,6 @@ impl ProjectWorkspace { } } } - for name in &krate.atom_cfgs { - opts.insert_atom(name.into()); - } - for (key, value) in &krate.key_value_cfgs { - opts.insert_key_value(key.into(), value.into()); - } opts }; From 2785362a1f9a3436072152e5499ac5d7c4d98cc4 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Tue, 9 Jun 2020 10:50:25 +0200 Subject: [PATCH 127/172] Update crates/ra_parser/src/grammar/items.rs --- crates/ra_parser/src/grammar/items.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/crates/ra_parser/src/grammar/items.rs b/crates/ra_parser/src/grammar/items.rs index 56cfb509da0f..97642bc24499 100644 --- a/crates/ra_parser/src/grammar/items.rs +++ b/crates/ra_parser/src/grammar/items.rs @@ -126,8 +126,7 @@ pub(super) fn maybe_item(p: &mut Parser, m: Marker, flavor: ItemFlavor) -> Resul // impl T for Foo { // default unsafe fn foo() {} // } - let sk = p.nth(2); - if sk == T![impl] || sk == T![fn] { + if p.nth(2) == T![impl] || p.nth(2) == T![fn] { p.bump_remap(T![default]); p.bump(T![unsafe]); has_mods = true; From 5233766ce51d7593bb02d041bd63fa3aad44f666 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Tue, 9 Jun 2020 11:33:28 +0200 Subject: [PATCH 128/172] Simplify unwrapping of blocks --- .../ra_assists/src/handlers/unwrap_block.rs | 120 +++++++----------- 1 file changed, 45 insertions(+), 75 deletions(-) diff --git a/crates/ra_assists/src/handlers/unwrap_block.rs b/crates/ra_assists/src/handlers/unwrap_block.rs index 8440c7d0f43e..c48ecaae8a3e 100644 --- a/crates/ra_assists/src/handlers/unwrap_block.rs +++ b/crates/ra_assists/src/handlers/unwrap_block.rs @@ -1,8 +1,5 @@ use ra_fmt::unwrap_trivial_block; -use ra_syntax::{ - ast::{self, ElseBranch, Expr, LoopBodyOwner}, - match_ast, AstNode, TextRange, T, -}; +use ra_syntax::{ast, AstNode, TextRange, T}; use crate::{AssistContext, AssistId, Assists}; @@ -29,89 +26,62 @@ pub(crate) fn unwrap_block(acc: &mut Assists, ctx: &AssistContext) -> Option<()> let parent = block.syntax().parent()?; let assist_id = AssistId("unwrap_block"); let assist_label = "Unwrap block"; + let parent = ast::Expr::cast(parent)?; - let (expr, expr_to_unwrap) = match_ast! { - match parent { - ast::ForExpr(for_expr) => { - let block_expr = for_expr.loop_body()?; - let expr_to_unwrap = extract_expr(ctx.frange.range, block_expr)?; - (ast::Expr::ForExpr(for_expr), expr_to_unwrap) - }, - ast::WhileExpr(while_expr) => { - let block_expr = while_expr.loop_body()?; - let expr_to_unwrap = extract_expr(ctx.frange.range, block_expr)?; - (ast::Expr::WhileExpr(while_expr), expr_to_unwrap) - }, - ast::LoopExpr(loop_expr) => { - let block_expr = loop_expr.loop_body()?; - let expr_to_unwrap = extract_expr(ctx.frange.range, block_expr)?; - (ast::Expr::LoopExpr(loop_expr), expr_to_unwrap) - }, - ast::IfExpr(if_expr) => { - let mut resp = None; + match parent.clone() { + ast::Expr::ForExpr(_) | ast::Expr::WhileExpr(_) | ast::Expr::LoopExpr(_) => (), + ast::Expr::IfExpr(if_expr) => { + let then_branch = if_expr.then_branch()?; + if then_branch == block { + if let Some(ancestor) = if_expr.syntax().parent().and_then(ast::IfExpr::cast) { + // For `else if` blocks + let ancestor_then_branch = ancestor.then_branch()?; - let then_branch = if_expr.then_branch()?; - if then_branch.l_curly_token()?.text_range().contains_range(ctx.frange.range) { - if let Some(ancestor) = if_expr.syntax().parent().and_then(ast::IfExpr::cast) { - // For `else if` blocks - let ancestor_then_branch = ancestor.then_branch()?; - let l_curly_token = then_branch.l_curly_token()?; + let target = then_branch.syntax().text_range(); + return acc.add(assist_id, assist_label, target, |edit| { + let range_to_del_else_if = TextRange::new( + ancestor_then_branch.syntax().text_range().end(), + l_curly_token.text_range().start(), + ); + let range_to_del_rest = TextRange::new( + then_branch.syntax().text_range().end(), + if_expr.syntax().text_range().end(), + ); - let target = then_branch.syntax().text_range(); - return acc.add(assist_id, assist_label, target, |edit| { - let range_to_del_else_if = TextRange::new(ancestor_then_branch.syntax().text_range().end(), l_curly_token.text_range().start()); - let range_to_del_rest = TextRange::new(then_branch.syntax().text_range().end(), if_expr.syntax().text_range().end()); - - edit.delete(range_to_del_rest); - edit.delete(range_to_del_else_if); - edit.replace(target, update_expr_string(then_branch.to_string(), &[' ', '{'])); - }); - } else { - resp = Some((ast::Expr::IfExpr(if_expr.clone()), Expr::BlockExpr(then_branch))); - } - } else if let Some(else_branch) = if_expr.else_branch() { - match else_branch { - ElseBranch::Block(else_block) => { - let l_curly_token = else_block.l_curly_token()?; - if l_curly_token.text_range().contains_range(ctx.frange.range) { - let target = else_block.syntax().text_range(); - return acc.add(assist_id, assist_label, target, |edit| { - let range_to_del = TextRange::new(then_branch.syntax().text_range().end(), l_curly_token.text_range().start()); - - edit.delete(range_to_del); - edit.replace(target, update_expr_string(else_block.to_string(), &[' ', '{'])); - }); - } - }, - ElseBranch::IfExpr(_) => {}, - } + edit.delete(range_to_del_rest); + edit.delete(range_to_del_else_if); + edit.replace( + target, + update_expr_string(then_branch.to_string(), &[' ', '{']), + ); + }); } + } else { + let target = block.syntax().text_range(); + return acc.add(assist_id, assist_label, target, |edit| { + let range_to_del = TextRange::new( + then_branch.syntax().text_range().end(), + l_curly_token.text_range().start(), + ); - resp? - }, - _ => return None, + edit.delete(range_to_del); + edit.replace(target, update_expr_string(block.to_string(), &[' ', '{'])); + }); + } } + _ => return None, }; - let target = expr_to_unwrap.syntax().text_range(); - acc.add(assist_id, assist_label, target, |edit| { - edit.replace( - expr.syntax().text_range(), - update_expr_string(expr_to_unwrap.to_string(), &[' ', '{', '\n']), + let unwrapped = unwrap_trivial_block(block); + let target = unwrapped.syntax().text_range(); + acc.add(assist_id, assist_label, target, |builder| { + builder.replace( + parent.syntax().text_range(), + update_expr_string(unwrapped.to_string(), &[' ', '{', '\n']), ); }) } -fn extract_expr(cursor_range: TextRange, block: ast::BlockExpr) -> Option { - let cursor_in_range = block.l_curly_token()?.text_range().contains_range(cursor_range); - - if cursor_in_range { - Some(unwrap_trivial_block(block)) - } else { - None - } -} - fn update_expr_string(expr_str: String, trim_start_pat: &[char]) -> String { let expr_string = expr_str.trim_start_matches(trim_start_pat); let mut expr_string_lines: Vec<&str> = expr_string.lines().collect(); From 53cc2c16e7a53660fb7322d96284ef199699e250 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Tue, 9 Jun 2020 11:52:45 +0200 Subject: [PATCH 129/172] Unwrap block works with match arms --- .../ra_assists/src/handlers/unwrap_block.rs | 44 +++++++++++++++++-- 1 file changed, 40 insertions(+), 4 deletions(-) diff --git a/crates/ra_assists/src/handlers/unwrap_block.rs b/crates/ra_assists/src/handlers/unwrap_block.rs index c48ecaae8a3e..1fb13f481419 100644 --- a/crates/ra_assists/src/handlers/unwrap_block.rs +++ b/crates/ra_assists/src/handlers/unwrap_block.rs @@ -1,5 +1,11 @@ use ra_fmt::unwrap_trivial_block; -use ra_syntax::{ast, AstNode, TextRange, T}; +use ra_syntax::{ + ast::{ + self, + edit::{AstNodeEdit, IndentLevel}, + }, + AstNode, TextRange, T, +}; use crate::{AssistContext, AssistId, Assists}; @@ -21,15 +27,21 @@ use crate::{AssistContext, AssistId, Assists}; // } // ``` pub(crate) fn unwrap_block(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { - let l_curly_token = ctx.find_token_at_offset(T!['{'])?; - let block = ast::BlockExpr::cast(l_curly_token.parent())?; - let parent = block.syntax().parent()?; let assist_id = AssistId("unwrap_block"); let assist_label = "Unwrap block"; + + let l_curly_token = ctx.find_token_at_offset(T!['{'])?; + let mut block = ast::BlockExpr::cast(l_curly_token.parent())?; + let mut parent = block.syntax().parent()?; + if ast::MatchArm::can_cast(parent.kind()) { + parent = parent.ancestors().find(|it| ast::MatchExpr::can_cast(it.kind()))? + } + let parent = ast::Expr::cast(parent)?; match parent.clone() { ast::Expr::ForExpr(_) | ast::Expr::WhileExpr(_) | ast::Expr::LoopExpr(_) => (), + ast::Expr::MatchExpr(_) => block = block.dedent(IndentLevel(1)), ast::Expr::IfExpr(if_expr) => { let then_branch = if_expr.then_branch()?; if then_branch == block { @@ -459,6 +471,30 @@ mod tests { ); } + #[test] + fn unwrap_match_arm() { + check_assist( + unwrap_block, + r#" +fn main() { + match rel_path { + Ok(rel_path) => {<|> + let rel_path = RelativePathBuf::from_path(rel_path).ok()?; + Some((*id, rel_path)) + } + Err(_) => None, + } +} +"#, + r#" +fn main() { + let rel_path = RelativePathBuf::from_path(rel_path).ok()?; + Some((*id, rel_path)) +} +"#, + ); + } + #[test] fn simple_if_in_while_bad_cursor_position() { check_assist_not_applicable( From 8cad7d1a2b2546986aa8f42207875b1c8eed948a Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Tue, 9 Jun 2020 12:38:47 +0200 Subject: [PATCH 130/172] Use correct indent when replacing with match --- .../ra_assists/src/handlers/early_return.rs | 2 +- .../src/handlers/replace_if_let_with_match.rs | 37 ++++++++++++++++++- crates/ra_syntax/src/ast/edit.rs | 13 +++++-- 3 files changed, 45 insertions(+), 7 deletions(-) diff --git a/crates/ra_assists/src/handlers/early_return.rs b/crates/ra_assists/src/handlers/early_return.rs index 4cc75a7ce2dc..dfade743215c 100644 --- a/crates/ra_assists/src/handlers/early_return.rs +++ b/crates/ra_assists/src/handlers/early_return.rs @@ -154,7 +154,7 @@ pub(crate) fn convert_to_guarded_return(acc: &mut Assists, ctx: &AssistContext) parent_block: &ast::BlockExpr, if_expr: &ast::IfExpr, ) -> SyntaxNode { - let then_block_items = then_block.dedent(IndentLevel::from(1)); + let then_block_items = then_block.dedent(IndentLevel(1)); let end_of_then = then_block_items.syntax().last_child_or_token().unwrap(); let end_of_then = if end_of_then.prev_sibling_or_token().map(|n| n.kind()) == Some(WHITESPACE) { diff --git a/crates/ra_assists/src/handlers/replace_if_let_with_match.rs b/crates/ra_assists/src/handlers/replace_if_let_with_match.rs index e016f51c3eb4..dfcd787de585 100644 --- a/crates/ra_assists/src/handlers/replace_if_let_with_match.rs +++ b/crates/ra_assists/src/handlers/replace_if_let_with_match.rs @@ -51,6 +51,7 @@ pub(crate) fn replace_if_let_with_match(acc: &mut Assists, ctx: &AssistContext) acc.add(AssistId("replace_if_let_with_match"), "Replace with match", target, move |edit| { let match_expr = { let then_arm = { + let then_block = then_block.reset_indent().indent(IndentLevel(1)); let then_expr = unwrap_trivial_block(then_block); make::match_arm(vec![pat.clone()], then_expr) }; @@ -64,8 +65,8 @@ pub(crate) fn replace_if_let_with_match(acc: &mut Assists, ctx: &AssistContext) let else_expr = unwrap_trivial_block(else_block); make::match_arm(vec![pattern], else_expr) }; - make::expr_match(expr, make::match_arm_list(vec![then_arm, else_arm])) - .indent(IndentLevel::from_node(if_expr.syntax())) + let match_expr = make::expr_match(expr, make::match_arm_list(vec![then_arm, else_arm])); + match_expr.indent(IndentLevel::from_node(if_expr.syntax())) }; edit.replace_ast::(if_expr.into(), match_expr); @@ -213,4 +214,36 @@ fn foo(x: Result) { "#, ); } + + #[test] + fn nested_indent() { + check_assist( + replace_if_let_with_match, + r#" +fn main() { + if true { + <|>if let Ok(rel_path) = path.strip_prefix(root_path) { + let rel_path = RelativePathBuf::from_path(rel_path).ok()?; + Some((*id, rel_path)) + } else { + None + } + } +} +"#, + r#" +fn main() { + if true { + match path.strip_prefix(root_path) { + Ok(rel_path) => { + let rel_path = RelativePathBuf::from_path(rel_path).ok()?; + Some((*id, rel_path)) + } + _ => None, + } + } +} +"#, + ) + } } diff --git a/crates/ra_syntax/src/ast/edit.rs b/crates/ra_syntax/src/ast/edit.rs index 29eb3fcb9ccc..2ef173a03970 100644 --- a/crates/ra_syntax/src/ast/edit.rs +++ b/crates/ra_syntax/src/ast/edit.rs @@ -579,12 +579,17 @@ pub trait AstNodeEdit: AstNode + Clone + Sized { rewriter.rewrite_ast(self) } #[must_use] - fn indent(&self, indent: IndentLevel) -> Self { - Self::cast(indent.increase_indent(self.syntax().clone())).unwrap() + fn indent(&self, level: IndentLevel) -> Self { + Self::cast(level.increase_indent(self.syntax().clone())).unwrap() } #[must_use] - fn dedent(&self, indent: IndentLevel) -> Self { - Self::cast(indent.decrease_indent(self.syntax().clone())).unwrap() + fn dedent(&self, level: IndentLevel) -> Self { + Self::cast(level.decrease_indent(self.syntax().clone())).unwrap() + } + #[must_use] + fn reset_indent(&self) -> Self { + let level = IndentLevel::from_node(self.syntax()); + self.dedent(level) } } From d8571e076c0bbb3219fb3ed250279b158d75e683 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Tue, 9 Jun 2020 13:17:22 +0200 Subject: [PATCH 131/172] Simplify --- crates/ra_db/src/input.rs | 12 ++++-------- crates/ra_db/src/lib.rs | 2 +- 2 files changed, 5 insertions(+), 9 deletions(-) diff --git a/crates/ra_db/src/input.rs b/crates/ra_db/src/input.rs index 4d2d3b48a0c0..a8d6466eac15 100644 --- a/crates/ra_db/src/input.rs +++ b/crates/ra_db/src/input.rs @@ -337,15 +337,11 @@ impl Env { } impl ExternSource { - pub fn extern_path(&self, path: impl AsRef) -> Option<(ExternSourceId, RelativePathBuf)> { - let path = path.as_ref(); + pub fn extern_path(&self, path: &Path) -> Option<(ExternSourceId, RelativePathBuf)> { self.extern_paths.iter().find_map(|(root_path, id)| { - if let Ok(rel_path) = path.strip_prefix(root_path) { - let rel_path = RelativePathBuf::from_path(rel_path).ok()?; - Some((*id, rel_path)) - } else { - None - } + let rel_path = path.strip_prefix(root_path).ok()?; + let rel_path = RelativePathBuf::from_path(rel_path).ok()?; + Some((*id, rel_path)) }) } diff --git a/crates/ra_db/src/lib.rs b/crates/ra_db/src/lib.rs index 91e0ee619479..2ab3148840fa 100644 --- a/crates/ra_db/src/lib.rs +++ b/crates/ra_db/src/lib.rs @@ -158,7 +158,7 @@ impl FileLoader for FileLoaderDelegate<&'_ T> { if std::path::Path::new(path).is_absolute() { let krate = *self.relevant_crates(anchor).get(0)?; let (extern_source_id, relative_file) = - self.0.crate_graph()[krate].extern_source.extern_path(path)?; + self.0.crate_graph()[krate].extern_source.extern_path(path.as_ref())?; let source_root = self.0.source_root(SourceRootId(extern_source_id.0)); source_root.file_by_relative_path(&relative_file) From 16943e533c68427e3381e03b49de29e0d6c0450c Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Tue, 9 Jun 2020 13:36:08 +0200 Subject: [PATCH 132/172] Minor, use `T!` --- crates/ra_parser/src/grammar/paths.rs | 2 +- crates/ra_parser/src/grammar/patterns.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/ra_parser/src/grammar/paths.rs b/crates/ra_parser/src/grammar/paths.rs index 332acc1a018a..428aa711e161 100644 --- a/crates/ra_parser/src/grammar/paths.rs +++ b/crates/ra_parser/src/grammar/paths.rs @@ -3,7 +3,7 @@ use super::*; pub(super) const PATH_FIRST: TokenSet = - token_set![IDENT, SELF_KW, SUPER_KW, CRATE_KW, COLON, L_ANGLE]; + token_set![IDENT, T![self], T![super], T![crate], T![:], T![<]]; pub(super) fn is_path_start(p: &Parser) -> bool { is_use_path_start(p) || p.at(T![<]) diff --git a/crates/ra_parser/src/grammar/patterns.rs b/crates/ra_parser/src/grammar/patterns.rs index 68fb2fc7337a..264cf262ec84 100644 --- a/crates/ra_parser/src/grammar/patterns.rs +++ b/crates/ra_parser/src/grammar/patterns.rs @@ -4,7 +4,7 @@ use super::*; pub(super) const PATTERN_FIRST: TokenSet = expressions::LITERAL_FIRST .union(paths::PATH_FIRST) - .union(token_set![BOX_KW, REF_KW, MUT_KW, L_PAREN, L_BRACK, AMP, UNDERSCORE, MINUS, DOT]); + .union(token_set![T![box], T![ref], T![mut], T!['('], T!['['], T![&], T![_], T![-], T![.]]); pub(crate) fn pattern(p: &mut Parser) { pattern_r(p, PAT_RECOVERY_SET); From e8d50578ab2702ff5f0955285c979c6923f14f80 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Tue, 9 Jun 2020 13:45:18 +0200 Subject: [PATCH 133/172] Correctly parse <_> paths in patterns closes #3659 --- crates/ra_parser/src/grammar/patterns.rs | 6 +- .../parser/err/0024_many_type_parens.rast | 74 +++++++++---------- .../inline/ok/0164_type_path_in_pattern.rast | 38 ++++++++++ .../inline/ok/0164_type_path_in_pattern.rs | 1 + 4 files changed, 80 insertions(+), 39 deletions(-) create mode 100644 crates/ra_syntax/test_data/parser/inline/ok/0164_type_path_in_pattern.rast create mode 100644 crates/ra_syntax/test_data/parser/inline/ok/0164_type_path_in_pattern.rs diff --git a/crates/ra_parser/src/grammar/patterns.rs b/crates/ra_parser/src/grammar/patterns.rs index 264cf262ec84..427c0eb49c2f 100644 --- a/crates/ra_parser/src/grammar/patterns.rs +++ b/crates/ra_parser/src/grammar/patterns.rs @@ -88,7 +88,9 @@ fn atom_pat(p: &mut Parser, recovery_set: TokenSet) -> Option { _ => bind_pat(p, true), }, - _ if paths::is_use_path_start(p) => path_or_macro_pat(p), + // test type_path_in_pattern + // fn main() { let <_>::Foo = (); } + _ if paths::is_path_start(p) => path_or_macro_pat(p), _ if is_literal_pat_start(p) => literal_pat(p), T![.] if p.at(T![..]) => dot_dot_pat(p), @@ -138,7 +140,7 @@ fn literal_pat(p: &mut Parser) -> CompletedMarker { // let Bar(..) = (); // } fn path_or_macro_pat(p: &mut Parser) -> CompletedMarker { - assert!(paths::is_use_path_start(p)); + assert!(paths::is_path_start(p)); let m = p.start(); paths::expr_path(p); let kind = match p.current() { diff --git a/crates/ra_syntax/test_data/parser/err/0024_many_type_parens.rast b/crates/ra_syntax/test_data/parser/err/0024_many_type_parens.rast index 7c957fdde236..48610a5ebbe2 100644 --- a/crates/ra_syntax/test_data/parser/err/0024_many_type_parens.rast +++ b/crates/ra_syntax/test_data/parser/err/0024_many_type_parens.rast @@ -180,44 +180,45 @@ SOURCE_FILE@0..240 EXPR_STMT@150..180 TUPLE_EXPR@150..180 L_PAREN@150..151 "(" - BIN_EXPR@151..180 - BIN_EXPR@151..178 - BIN_EXPR@151..169 - BIN_EXPR@151..167 - BIN_EXPR@151..164 - FOR_EXPR@151..157 - FOR_KW@151..154 "for" - ERROR@154..155 - L_ANGLE@154..155 "<" - ERROR@155..157 - LIFETIME@155..157 "\'a" - R_ANGLE@157..158 ">" - WHITESPACE@158..159 " " + FOR_EXPR@151..180 + FOR_KW@151..154 "for" + PATH_PAT@154..158 + PATH@154..158 + PATH_SEGMENT@154..158 + L_ANGLE@154..155 "<" + ERROR@155..157 + LIFETIME@155..157 "\'a" + R_ANGLE@157..158 ">" + WHITESPACE@158..159 " " + BIN_EXPR@159..180 + BIN_EXPR@159..178 + BIN_EXPR@159..169 + BIN_EXPR@159..167 PATH_EXPR@159..164 PATH@159..164 PATH_SEGMENT@159..164 NAME_REF@159..164 IDENT@159..164 "Trait" - L_ANGLE@164..165 "<" - ERROR@165..167 - LIFETIME@165..167 "\'a" - R_ANGLE@167..168 ">" - ERROR@168..169 - R_PAREN@168..169 ")" - WHITESPACE@169..170 " " - PLUS@170..171 "+" - WHITESPACE@171..172 " " - PAREN_EXPR@172..178 - L_PAREN@172..173 "(" - PATH_EXPR@173..177 - PATH@173..177 - PATH_SEGMENT@173..177 - NAME_REF@173..177 - IDENT@173..177 "Copy" - R_PAREN@177..178 ")" - R_ANGLE@178..179 ">" - ERROR@179..180 - SEMICOLON@179..180 ";" + L_ANGLE@164..165 "<" + ERROR@165..167 + LIFETIME@165..167 "\'a" + R_ANGLE@167..168 ">" + ERROR@168..169 + R_PAREN@168..169 ")" + WHITESPACE@169..170 " " + PLUS@170..171 "+" + WHITESPACE@171..172 " " + PAREN_EXPR@172..178 + L_PAREN@172..173 "(" + PATH_EXPR@173..177 + PATH@173..177 + PATH_SEGMENT@173..177 + NAME_REF@173..177 + IDENT@173..177 "Copy" + R_PAREN@177..178 ")" + R_ANGLE@178..179 ">" + ERROR@179..180 + SEMICOLON@179..180 ";" WHITESPACE@180..185 "\n " LET_STMT@185..235 LET_KW@185..188 "let" @@ -302,13 +303,12 @@ error 146..146: expected expression error 147..147: expected SEMICOLON error 148..148: expected expression error 149..149: expected SEMICOLON -error 154..154: expected pattern -error 155..155: expected IN_KW -error 155..155: expected expression -error 157..157: expected a block +error 155..155: expected type +error 158..158: expected IN_KW error 165..165: expected expression error 168..168: expected expression error 179..179: expected expression +error 180..180: expected a block error 180..180: expected COMMA error 180..180: expected expression error 180..180: expected R_PAREN diff --git a/crates/ra_syntax/test_data/parser/inline/ok/0164_type_path_in_pattern.rast b/crates/ra_syntax/test_data/parser/inline/ok/0164_type_path_in_pattern.rast new file mode 100644 index 000000000000..8688992750d6 --- /dev/null +++ b/crates/ra_syntax/test_data/parser/inline/ok/0164_type_path_in_pattern.rast @@ -0,0 +1,38 @@ +SOURCE_FILE@0..33 + FN_DEF@0..32 + FN_KW@0..2 "fn" + WHITESPACE@2..3 " " + NAME@3..7 + IDENT@3..7 "main" + PARAM_LIST@7..9 + L_PAREN@7..8 "(" + R_PAREN@8..9 ")" + WHITESPACE@9..10 " " + BLOCK_EXPR@10..32 + L_CURLY@10..11 "{" + WHITESPACE@11..12 " " + LET_STMT@12..30 + LET_KW@12..15 "let" + WHITESPACE@15..16 " " + PATH_PAT@16..24 + PATH@16..24 + PATH@16..19 + PATH_SEGMENT@16..19 + L_ANGLE@16..17 "<" + PLACEHOLDER_TYPE@17..18 + UNDERSCORE@17..18 "_" + R_ANGLE@18..19 ">" + COLON2@19..21 "::" + PATH_SEGMENT@21..24 + NAME_REF@21..24 + IDENT@21..24 "Foo" + WHITESPACE@24..25 " " + EQ@25..26 "=" + WHITESPACE@26..27 " " + TUPLE_EXPR@27..29 + L_PAREN@27..28 "(" + R_PAREN@28..29 ")" + SEMICOLON@29..30 ";" + WHITESPACE@30..31 " " + R_CURLY@31..32 "}" + WHITESPACE@32..33 "\n" diff --git a/crates/ra_syntax/test_data/parser/inline/ok/0164_type_path_in_pattern.rs b/crates/ra_syntax/test_data/parser/inline/ok/0164_type_path_in_pattern.rs new file mode 100644 index 000000000000..ebe26834d883 --- /dev/null +++ b/crates/ra_syntax/test_data/parser/inline/ok/0164_type_path_in_pattern.rs @@ -0,0 +1 @@ +fn main() { let <_>::Foo = (); } From 684b6fa1b8cd41b03ba485084690f78991820645 Mon Sep 17 00:00:00 2001 From: Clemens Wasser Date: Tue, 9 Jun 2020 21:47:54 +0200 Subject: [PATCH 134/172] flycheck now uses the configured features --- crates/ra_flycheck/src/lib.rs | 24 +++++++++++++++++++++--- crates/rust-analyzer/src/config.rs | 4 +++- 2 files changed, 24 insertions(+), 4 deletions(-) diff --git a/crates/ra_flycheck/src/lib.rs b/crates/ra_flycheck/src/lib.rs index 041e38a9ff4c..6c41705298bd 100644 --- a/crates/ra_flycheck/src/lib.rs +++ b/crates/ra_flycheck/src/lib.rs @@ -18,8 +18,17 @@ pub use cargo_metadata::diagnostic::{ #[derive(Clone, Debug, PartialEq, Eq)] pub enum FlycheckConfig { - CargoCommand { command: String, all_targets: bool, all_features: bool, extra_args: Vec }, - CustomCommand { command: String, args: Vec }, + CargoCommand { + command: String, + all_targets: bool, + all_features: bool, + features: Vec, + extra_args: Vec, + }, + CustomCommand { + command: String, + args: Vec, + }, } /// Flycheck wraps the shared state and communication machinery used for @@ -188,7 +197,13 @@ impl FlycheckThread { self.check_process = None; let mut cmd = match &self.config { - FlycheckConfig::CargoCommand { command, all_targets, all_features, extra_args } => { + FlycheckConfig::CargoCommand { + command, + all_targets, + all_features, + extra_args, + features, + } => { let mut cmd = Command::new(ra_toolchain::cargo()); cmd.arg(command); cmd.args(&["--workspace", "--message-format=json", "--manifest-path"]) @@ -198,6 +213,9 @@ impl FlycheckThread { } if *all_features { cmd.arg("--all-features"); + } else if !features.is_empty() { + cmd.arg("--features"); + cmd.arg(features.join(" ")); } cmd.args(extra_args); cmd diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs index 17671f89ee41..5d5f7d66a798 100644 --- a/crates/rust-analyzer/src/config.rs +++ b/crates/rust-analyzer/src/config.rs @@ -147,6 +147,7 @@ impl Default for Config { all_targets: true, all_features: false, extra_args: Vec::new(), + features: Vec::new(), }), inlay_hints: InlayHintsConfig { @@ -234,13 +235,14 @@ impl Config { } // otherwise configure command customizations _ => { - if let Some(FlycheckConfig::CargoCommand { command, extra_args, all_targets, all_features }) + if let Some(FlycheckConfig::CargoCommand { command, extra_args, all_targets, all_features, features }) = &mut self.check { set(value, "/checkOnSave/extraArgs", extra_args); set(value, "/checkOnSave/command", command); set(value, "/checkOnSave/allTargets", all_targets); set(value, "/checkOnSave/allFeatures", all_features); + *features = self.cargo.features.clone(); } } }; From f4ed2da966d1e568a67a7cae68880b2eb717c2cd Mon Sep 17 00:00:00 2001 From: Aaron Wood Date: Tue, 9 Jun 2020 19:58:39 -0700 Subject: [PATCH 135/172] Correct "debug_assertion" to "debug_assertions" to match the cfg option that the rust debug assert macros use. --- crates/ra_project_model/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/ra_project_model/src/lib.rs b/crates/ra_project_model/src/lib.rs index fe03b509e4ff..6604f5092389 100644 --- a/crates/ra_project_model/src/lib.rs +++ b/crates/ra_project_model/src/lib.rs @@ -600,7 +600,7 @@ fn get_rustc_cfg_options(target: Option<&str>) -> CfgOptions { Err(e) => log::error!("failed to get rustc cfgs: {:#}", e), } - cfg_options.insert_atom("debug_assertion".into()); + cfg_options.insert_atom("debug_assertions".into()); cfg_options } From 47ef544fa57ca1833b466e491315e54a88780b4d Mon Sep 17 00:00:00 2001 From: Clemens Wasser Date: Wed, 10 Jun 2020 08:51:11 +0200 Subject: [PATCH 136/172] Added the rust-analyzer.checkOnSave.features option. --- crates/rust-analyzer/src/config.rs | 5 ++++- editors/code/package.json | 8 ++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs index 5d5f7d66a798..320414ecf356 100644 --- a/crates/rust-analyzer/src/config.rs +++ b/crates/rust-analyzer/src/config.rs @@ -242,7 +242,10 @@ impl Config { set(value, "/checkOnSave/command", command); set(value, "/checkOnSave/allTargets", all_targets); set(value, "/checkOnSave/allFeatures", all_features); - *features = self.cargo.features.clone(); + set(value, "/checkOnSave/features", features); + if features.is_empty() && !self.cargo.features.is_empty() { + *features = self.cargo.features.clone(); + } } } }; diff --git a/editors/code/package.json b/editors/code/package.json index 779d7e1b8716..6389499e9af0 100644 --- a/editors/code/package.json +++ b/editors/code/package.json @@ -322,6 +322,14 @@ "default": false, "markdownDescription": "Check with all features (will be passed as `--all-features`)" }, + "rust-analyzer.checkOnSave.features": { + "type": "array", + "items": { + "type": "string" + }, + "default": [], + "description": "List of features to activate. Set to `rust-analyzer.cargo.features` if empty." + }, "rust-analyzer.inlayHints.enable": { "type": "boolean", "default": true, From 33b905883819038ad67476fe14b7b48212a73f93 Mon Sep 17 00:00:00 2001 From: Clemens Wasser Date: Wed, 10 Jun 2020 09:27:25 +0200 Subject: [PATCH 137/172] Most of the checkOnSafe options now default to the cargo equivalent. --- crates/rust-analyzer/src/config.rs | 6 +++++- editors/code/package.json | 11 +++++++---- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs index 320414ecf356..617612dc3a87 100644 --- a/crates/rust-analyzer/src/config.rs +++ b/crates/rust-analyzer/src/config.rs @@ -241,7 +241,11 @@ impl Config { set(value, "/checkOnSave/extraArgs", extra_args); set(value, "/checkOnSave/command", command); set(value, "/checkOnSave/allTargets", all_targets); - set(value, "/checkOnSave/allFeatures", all_features); + if let Some(new_all_features) = get(value, "/checkOnSave/allFeatures") { + *all_features = new_all_features; + } else { + *all_features = self.cargo.all_features; + } set(value, "/checkOnSave/features", features); if features.is_empty() && !self.cargo.features.is_empty() { *features = self.cargo.features.clone(); diff --git a/editors/code/package.json b/editors/code/package.json index 6389499e9af0..647c83685b7d 100644 --- a/editors/code/package.json +++ b/editors/code/package.json @@ -318,9 +318,12 @@ "markdownDescription": "Check all targets and tests (will be passed as `--all-targets`)" }, "rust-analyzer.checkOnSave.allFeatures": { - "type": "boolean", - "default": false, - "markdownDescription": "Check with all features (will be passed as `--all-features`)" + "type": [ + "null", + "boolean" + ], + "default": null, + "markdownDescription": "Check with all features (will be passed as `--all-features`). Defaults to `rust-analyzer.cargo.allFeatures`." }, "rust-analyzer.checkOnSave.features": { "type": "array", @@ -328,7 +331,7 @@ "type": "string" }, "default": [], - "description": "List of features to activate. Set to `rust-analyzer.cargo.features` if empty." + "description": "List of features to activate. Defaults to `rust-analyzer.cargo.features`." }, "rust-analyzer.inlayHints.enable": { "type": "boolean", From fe21fc2d259cbe2a32bfee3432f2c51ade079083 Mon Sep 17 00:00:00 2001 From: Clemens Wasser Date: Wed, 10 Jun 2020 09:37:26 +0200 Subject: [PATCH 138/172] checkOnSafe.features and checkOnSafe.allFeatures now work identically. --- crates/rust-analyzer/src/config.rs | 11 ++--------- editors/code/package.json | 7 +++++-- 2 files changed, 7 insertions(+), 11 deletions(-) diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs index 617612dc3a87..1253db836162 100644 --- a/crates/rust-analyzer/src/config.rs +++ b/crates/rust-analyzer/src/config.rs @@ -241,15 +241,8 @@ impl Config { set(value, "/checkOnSave/extraArgs", extra_args); set(value, "/checkOnSave/command", command); set(value, "/checkOnSave/allTargets", all_targets); - if let Some(new_all_features) = get(value, "/checkOnSave/allFeatures") { - *all_features = new_all_features; - } else { - *all_features = self.cargo.all_features; - } - set(value, "/checkOnSave/features", features); - if features.is_empty() && !self.cargo.features.is_empty() { - *features = self.cargo.features.clone(); - } + *all_features = get(value, "/checkOnSave/allFeatures").unwrap_or(self.cargo.all_features); + *features = get(value, "/checkOnSave/features").unwrap_or(self.cargo.features.clone()); } } }; diff --git a/editors/code/package.json b/editors/code/package.json index 647c83685b7d..e2027970db86 100644 --- a/editors/code/package.json +++ b/editors/code/package.json @@ -326,11 +326,14 @@ "markdownDescription": "Check with all features (will be passed as `--all-features`). Defaults to `rust-analyzer.cargo.allFeatures`." }, "rust-analyzer.checkOnSave.features": { - "type": "array", + "type": [ + "null", + "array" + ], "items": { "type": "string" }, - "default": [], + "default": null, "description": "List of features to activate. Defaults to `rust-analyzer.cargo.features`." }, "rust-analyzer.inlayHints.enable": { From 27ebe5d33e08d92c1a032dc27f19094571bd19cd Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Wed, 10 Jun 2020 12:08:35 +0200 Subject: [PATCH 139/172] Reduce OUT_DIR special casing --- crates/ra_project_model/src/lib.rs | 20 ++++-------- crates/rust-analyzer/src/cli/load_cargo.rs | 38 +++++++++++----------- crates/rust-analyzer/src/global_state.rs | 35 ++++++++++---------- 3 files changed, 43 insertions(+), 50 deletions(-) diff --git a/crates/ra_project_model/src/lib.rs b/crates/ra_project_model/src/lib.rs index c1f7e3ac5f48..cb0e27dce217 100644 --- a/crates/ra_project_model/src/lib.rs +++ b/crates/ra_project_model/src/lib.rs @@ -47,17 +47,21 @@ pub struct PackageRoot { path: PathBuf, /// Is a member of the current workspace is_member: bool, + out_dir: Option, } impl PackageRoot { pub fn new_member(path: PathBuf) -> PackageRoot { - Self { path, is_member: true } + Self { path, is_member: true, out_dir: None } } pub fn new_non_member(path: PathBuf) -> PackageRoot { - Self { path, is_member: false } + Self { path, is_member: false, out_dir: None } } pub fn path(&self) -> &Path { &self.path } + pub fn out_dir(&self) -> Option<&Path> { + self.out_dir.as_deref() + } pub fn is_member(&self) -> bool { self.is_member } @@ -204,6 +208,7 @@ impl ProjectWorkspace { .map(|pkg| PackageRoot { path: cargo[pkg].root().to_path_buf(), is_member: cargo[pkg].is_member, + out_dir: cargo[pkg].out_dir.clone(), }) .chain(sysroot.crates().map(|krate| { PackageRoot::new_non_member(sysroot[krate].root_dir().to_path_buf()) @@ -212,17 +217,6 @@ impl ProjectWorkspace { } } - pub fn out_dirs(&self) -> Vec { - match self { - ProjectWorkspace::Json { project } => { - project.crates.iter().filter_map(|krate| krate.out_dir.as_ref()).cloned().collect() - } - ProjectWorkspace::Cargo { cargo, sysroot: _ } => { - cargo.packages().filter_map(|pkg| cargo[pkg].out_dir.as_ref()).cloned().collect() - } - } - } - pub fn proc_macro_dylib_paths(&self) -> Vec { match self { ProjectWorkspace::Json { project } => project diff --git a/crates/rust-analyzer/src/cli/load_cargo.rs b/crates/rust-analyzer/src/cli/load_cargo.rs index 8f2aeac7714e..45af963179b3 100644 --- a/crates/rust-analyzer/src/cli/load_cargo.rs +++ b/crates/rust-analyzer/src/cli/load_cargo.rs @@ -36,28 +36,28 @@ pub fn load_cargo( )?; let mut extern_dirs = FxHashSet::default(); - extern_dirs.extend(ws.out_dirs()); - - let mut project_roots = ws.to_roots(); - project_roots.extend(extern_dirs.iter().cloned().map(PackageRoot::new_non_member)); let (sender, receiver) = unbounded(); let sender = Box::new(move |t| sender.send(t).unwrap()); - let (mut vfs, roots) = Vfs::new( - project_roots - .iter() - .map(|pkg_root| { - RootEntry::new( - pkg_root.path().to_owned(), - RustPackageFilterBuilder::default() - .set_member(pkg_root.is_member()) - .into_vfs_filter(), - ) - }) - .collect(), - sender, - Watch(false), - ); + + let mut roots = Vec::new(); + let project_roots = ws.to_roots(); + for root in &project_roots { + roots.push(RootEntry::new( + root.path().to_owned(), + RustPackageFilterBuilder::default().set_member(root.is_member()).into_vfs_filter(), + )); + + if let Some(out_dir) = root.out_dir() { + extern_dirs.insert(out_dir.to_path_buf()); + roots.push(RootEntry::new( + out_dir.to_owned(), + RustPackageFilterBuilder::default().set_member(root.is_member()).into_vfs_filter(), + )) + } + } + + let (mut vfs, roots) = Vfs::new(roots, sender, Watch(false)); let source_roots = roots .into_iter() diff --git a/crates/rust-analyzer/src/global_state.rs b/crates/rust-analyzer/src/global_state.rs index 73b0f881d369..96d91b12d924 100644 --- a/crates/rust-analyzer/src/global_state.rs +++ b/crates/rust-analyzer/src/global_state.rs @@ -89,8 +89,7 @@ impl GlobalState { ) -> GlobalState { let mut change = AnalysisChange::new(); - let extern_dirs: FxHashSet<_> = - workspaces.iter().flat_map(ProjectWorkspace::out_dirs).collect(); + let mut extern_dirs: FxHashSet = FxHashSet::default(); let mut local_roots = Vec::new(); let roots: Vec<_> = { @@ -100,22 +99,22 @@ impl GlobalState { .exclude(exclude_globs.iter().cloned()) .into_vfs_filter() }; - workspaces - .iter() - .flat_map(ProjectWorkspace::to_roots) - .map(|pkg_root| { - let path = pkg_root.path().to_owned(); - if pkg_root.is_member() { - local_roots.push(path.clone()); - } - RootEntry::new(path, create_filter(pkg_root.is_member())) - }) - .chain( - extern_dirs - .iter() - .map(|path| RootEntry::new(path.to_owned(), create_filter(false))), - ) - .collect() + let mut roots = Vec::new(); + for root in workspaces.iter().flat_map(ProjectWorkspace::to_roots) { + let path = root.path().to_owned(); + if root.is_member() { + local_roots.push(path.clone()); + } + roots.push(RootEntry::new(path, create_filter(root.is_member()))); + if let Some(out_dir) = root.out_dir() { + extern_dirs.insert(out_dir.to_path_buf()); + roots.push(RootEntry::new( + out_dir.to_path_buf(), + create_filter(root.is_member()), + )) + } + } + roots }; let (task_sender, task_receiver) = unbounded(); From 506e1ddbfa5213f254923da9bbf0efddc6f1fc34 Mon Sep 17 00:00:00 2001 From: Matthew Jasper Date: Wed, 10 Jun 2020 11:30:48 +0100 Subject: [PATCH 140/172] Separating parsing of `for` in predicates and types --- crates/ra_parser/src/grammar/type_params.rs | 22 +- crates/ra_parser/src/grammar/types.rs | 15 +- crates/ra_syntax/src/ast.rs | 6 +- crates/ra_syntax/src/ast/generated/nodes.rs | 2 + .../parser/err/0027_incomplere_where_for.rast | 15 +- .../parser/err/0044_unexpected_for_type.rast | 88 +++++ .../parser/err/0044_unexpected_for_type.rs | 3 + .../parser/inline/ok/0003_where_pred_for.rast | 367 +++++++++++++++--- .../parser/inline/ok/0003_where_pred_for.rs | 18 +- .../parser/inline/ok/0081_for_type.rast | 256 +++--------- .../parser/inline/ok/0081_for_type.rs | 4 +- xtask/src/ast_src.rs | 2 +- 12 files changed, 507 insertions(+), 291 deletions(-) create mode 100644 crates/ra_syntax/test_data/parser/err/0044_unexpected_for_type.rast create mode 100644 crates/ra_syntax/test_data/parser/err/0044_unexpected_for_type.rs diff --git a/crates/ra_parser/src/grammar/type_params.rs b/crates/ra_parser/src/grammar/type_params.rs index 50e4900c31a7..b3508c732e9b 100644 --- a/crates/ra_parser/src/grammar/type_params.rs +++ b/crates/ra_parser/src/grammar/type_params.rs @@ -191,10 +191,30 @@ fn where_predicate(p: &mut Parser) { } _ => { // test where_pred_for - // fn test() + // fn for_trait() // where // for<'a> F: Fn(&'a str) // { } + // fn for_ref() + // where + // for<'a> &'a F: Debug + // { } + // fn for_parens() + // where + // for<'a> (&'a F): Fn(&'a str) + // { } + // fn for_slice() + // where + // for<'a> [&'a F]: Eq + // { } + // fn for_qpath(_t: &T) + // where + // for<'a> <&'a T as Baz>::Foo: Iterator + // { } + if p.at(T![for]) { + types::for_binder(p); + } + types::type_(p); if p.at(T![:]) { diff --git a/crates/ra_parser/src/grammar/types.rs b/crates/ra_parser/src/grammar/types.rs index fe1a039cbf64..63dd3774f11f 100644 --- a/crates/ra_parser/src/grammar/types.rs +++ b/crates/ra_parser/src/grammar/types.rs @@ -216,19 +216,20 @@ pub(super) fn for_binder(p: &mut Parser) { // test for_type // type A = for<'a> fn() -> (); -// fn foo(_t: &T) where for<'a> &'a T: Iterator {} -// fn bar(_t: &T) where for<'a> &'a mut T: Iterator {} -// fn baz(_t: &T) where for<'a> <&'a T as Baz>::Foo: Iterator {} +// type B = for<'a> unsafe extern "C" fn(&'a ()) -> (); pub(super) fn for_type(p: &mut Parser) { assert!(p.at(T![for])); let m = p.start(); for_binder(p); match p.current() { - T![fn] | T![unsafe] | T![extern] => fn_pointer_type(p), - T![&] => reference_type(p), - _ if paths::is_path_start(p) => path_type_(p, false), - _ => p.error("expected a path"), + T![fn] | T![unsafe] | T![extern] => {} + // OK: legacy trait object format + _ if paths::is_use_path_start(p) => {} + _ => { + p.error("expected a function pointer or path"); + } } + type_no_bounds(p); m.complete(p, FOR_TYPE); } diff --git a/crates/ra_syntax/src/ast.rs b/crates/ra_syntax/src/ast.rs index eddc807d5a50..9d02aeef3fa2 100644 --- a/crates/ra_syntax/src/ast.rs +++ b/crates/ra_syntax/src/ast.rs @@ -285,6 +285,8 @@ where let pred = predicates.next().unwrap(); let mut bounds = pred.type_bound_list().unwrap().bounds(); + assert!(pred.for_token().is_none()); + assert!(pred.type_param_list().is_none()); assert_eq!("T", pred.type_ref().unwrap().syntax().text().to_string()); assert_bound("Clone", bounds.next()); assert_bound("Copy", bounds.next()); @@ -322,6 +324,8 @@ where let pred = predicates.next().unwrap(); let mut bounds = pred.type_bound_list().unwrap().bounds(); - assert_eq!("for<'a> F", pred.type_ref().unwrap().syntax().text().to_string()); + assert!(pred.for_token().is_some()); + assert_eq!("<'a>", pred.type_param_list().unwrap().syntax().text().to_string()); + assert_eq!("F", pred.type_ref().unwrap().syntax().text().to_string()); assert_bound("Fn(&'a str)", bounds.next()); } diff --git a/crates/ra_syntax/src/ast/generated/nodes.rs b/crates/ra_syntax/src/ast/generated/nodes.rs index cb430ca01352..40081ebb16ea 100644 --- a/crates/ra_syntax/src/ast/generated/nodes.rs +++ b/crates/ra_syntax/src/ast/generated/nodes.rs @@ -2052,6 +2052,8 @@ pub struct WherePred { } impl ast::TypeBoundsOwner for WherePred {} impl WherePred { + pub fn for_token(&self) -> Option { support::token(&self.syntax, T![for]) } + pub fn type_param_list(&self) -> Option { support::child(&self.syntax) } pub fn lifetime_token(&self) -> Option { support::token(&self.syntax, T![lifetime]) } diff --git a/crates/ra_syntax/test_data/parser/err/0027_incomplere_where_for.rast b/crates/ra_syntax/test_data/parser/err/0027_incomplere_where_for.rast index 568a4cc028eb..4d6461d1e195 100644 --- a/crates/ra_syntax/test_data/parser/err/0027_incomplere_where_for.rast +++ b/crates/ra_syntax/test_data/parser/err/0027_incomplere_where_for.rast @@ -12,17 +12,16 @@ SOURCE_FILE@0..30 WHERE_KW@13..18 "where" WHITESPACE@18..19 " " WHERE_PRED@19..26 - FOR_TYPE@19..26 - FOR_KW@19..22 "for" - TYPE_PARAM_LIST@22..26 - L_ANGLE@22..23 "<" - LIFETIME_PARAM@23..25 - LIFETIME@23..25 "\'a" - R_ANGLE@25..26 ">" + FOR_KW@19..22 "for" + TYPE_PARAM_LIST@22..26 + L_ANGLE@22..23 "<" + LIFETIME_PARAM@23..25 + LIFETIME@23..25 "\'a" + R_ANGLE@25..26 ">" WHITESPACE@26..27 "\n" BLOCK_EXPR@27..29 L_CURLY@27..28 "{" R_CURLY@28..29 "}" WHITESPACE@29..30 "\n" -error 26..26: expected a path +error 26..26: expected type error 26..26: expected colon diff --git a/crates/ra_syntax/test_data/parser/err/0044_unexpected_for_type.rast b/crates/ra_syntax/test_data/parser/err/0044_unexpected_for_type.rast new file mode 100644 index 000000000000..3400beff03ee --- /dev/null +++ b/crates/ra_syntax/test_data/parser/err/0044_unexpected_for_type.rast @@ -0,0 +1,88 @@ +SOURCE_FILE@0..79 + TYPE_ALIAS_DEF@0..25 + TYPE_KW@0..4 "type" + WHITESPACE@4..5 " " + NAME@5..6 + IDENT@5..6 "A" + WHITESPACE@6..7 " " + EQ@7..8 "=" + WHITESPACE@8..9 " " + FOR_TYPE@9..24 + FOR_KW@9..12 "for" + TYPE_PARAM_LIST@12..16 + L_ANGLE@12..13 "<" + LIFETIME_PARAM@13..15 + LIFETIME@13..15 "\'a" + R_ANGLE@15..16 ">" + WHITESPACE@16..17 " " + REFERENCE_TYPE@17..24 + AMP@17..18 "&" + LIFETIME@18..20 "\'a" + WHITESPACE@20..21 " " + PATH_TYPE@21..24 + PATH@21..24 + PATH_SEGMENT@21..24 + NAME_REF@21..24 + IDENT@21..24 "u32" + SEMICOLON@24..25 ";" + WHITESPACE@25..26 "\n" + TYPE_ALIAS_DEF@26..54 + TYPE_KW@26..30 "type" + WHITESPACE@30..31 " " + NAME@31..32 + IDENT@31..32 "B" + WHITESPACE@32..33 " " + EQ@33..34 "=" + WHITESPACE@34..35 " " + FOR_TYPE@35..53 + FOR_KW@35..38 "for" + TYPE_PARAM_LIST@38..42 + L_ANGLE@38..39 "<" + LIFETIME_PARAM@39..41 + LIFETIME@39..41 "\'a" + R_ANGLE@41..42 ">" + WHITESPACE@42..43 " " + TUPLE_TYPE@43..53 + L_PAREN@43..44 "(" + REFERENCE_TYPE@44..51 + AMP@44..45 "&" + LIFETIME@45..47 "\'a" + WHITESPACE@47..48 " " + PATH_TYPE@48..51 + PATH@48..51 + PATH_SEGMENT@48..51 + NAME_REF@48..51 + IDENT@48..51 "u32" + COMMA@51..52 "," + R_PAREN@52..53 ")" + SEMICOLON@53..54 ";" + WHITESPACE@54..55 "\n" + TYPE_ALIAS_DEF@55..78 + TYPE_KW@55..59 "type" + WHITESPACE@59..60 " " + NAME@60..61 + IDENT@60..61 "B" + WHITESPACE@61..62 " " + EQ@62..63 "=" + WHITESPACE@63..64 " " + FOR_TYPE@64..77 + FOR_KW@64..67 "for" + TYPE_PARAM_LIST@67..71 + L_ANGLE@67..68 "<" + LIFETIME_PARAM@68..70 + LIFETIME@68..70 "\'a" + R_ANGLE@70..71 ">" + WHITESPACE@71..72 " " + SLICE_TYPE@72..77 + L_BRACK@72..73 "[" + PATH_TYPE@73..76 + PATH@73..76 + PATH_SEGMENT@73..76 + NAME_REF@73..76 + IDENT@73..76 "u32" + R_BRACK@76..77 "]" + SEMICOLON@77..78 ";" + WHITESPACE@78..79 "\n" +error 16..16: expected a function pointer or path +error 42..42: expected a function pointer or path +error 71..71: expected a function pointer or path diff --git a/crates/ra_syntax/test_data/parser/err/0044_unexpected_for_type.rs b/crates/ra_syntax/test_data/parser/err/0044_unexpected_for_type.rs new file mode 100644 index 000000000000..f34ac7fc578f --- /dev/null +++ b/crates/ra_syntax/test_data/parser/err/0044_unexpected_for_type.rs @@ -0,0 +1,3 @@ +type A = for<'a> &'a u32; +type B = for<'a> (&'a u32,); +type B = for<'a> [u32]; diff --git a/crates/ra_syntax/test_data/parser/inline/ok/0003_where_pred_for.rast b/crates/ra_syntax/test_data/parser/inline/ok/0003_where_pred_for.rast index 9dc473e43c7f..4f88bfe434e8 100644 --- a/crates/ra_syntax/test_data/parser/inline/ok/0003_where_pred_for.rast +++ b/crates/ra_syntax/test_data/parser/inline/ok/0003_where_pred_for.rast @@ -1,61 +1,310 @@ -SOURCE_FILE@0..49 - FN_DEF@0..48 +SOURCE_FILE@0..292 + FN_DEF@0..53 FN_KW@0..2 "fn" WHITESPACE@2..3 " " - NAME@3..7 - IDENT@3..7 "test" - TYPE_PARAM_LIST@7..10 - L_ANGLE@7..8 "<" - TYPE_PARAM@8..9 - NAME@8..9 - IDENT@8..9 "F" - R_ANGLE@9..10 ">" - PARAM_LIST@10..12 - L_PAREN@10..11 "(" - R_PAREN@11..12 ")" - WHITESPACE@12..13 "\n" - WHERE_CLAUSE@13..44 - WHERE_KW@13..18 "where" - WHITESPACE@18..22 "\n " - WHERE_PRED@22..44 - FOR_TYPE@22..31 - FOR_KW@22..25 "for" - TYPE_PARAM_LIST@25..29 - L_ANGLE@25..26 "<" - LIFETIME_PARAM@26..28 - LIFETIME@26..28 "\'a" - R_ANGLE@28..29 ">" - WHITESPACE@29..30 " " - PATH_TYPE@30..31 - PATH@30..31 - PATH_SEGMENT@30..31 - NAME_REF@30..31 - IDENT@30..31 "F" - COLON@31..32 ":" - WHITESPACE@32..33 " " - TYPE_BOUND_LIST@33..44 - TYPE_BOUND@33..44 - PATH_TYPE@33..44 - PATH@33..44 - PATH_SEGMENT@33..44 - NAME_REF@33..35 - IDENT@33..35 "Fn" - PARAM_LIST@35..44 - L_PAREN@35..36 "(" - PARAM@36..43 - REFERENCE_TYPE@36..43 - AMP@36..37 "&" - LIFETIME@37..39 "\'a" - WHITESPACE@39..40 " " - PATH_TYPE@40..43 - PATH@40..43 - PATH_SEGMENT@40..43 - NAME_REF@40..43 - IDENT@40..43 "str" - R_PAREN@43..44 ")" - WHITESPACE@44..45 "\n" - BLOCK_EXPR@45..48 - L_CURLY@45..46 "{" - WHITESPACE@46..47 " " - R_CURLY@47..48 "}" - WHITESPACE@48..49 "\n" + NAME@3..12 + IDENT@3..12 "for_trait" + TYPE_PARAM_LIST@12..15 + L_ANGLE@12..13 "<" + TYPE_PARAM@13..14 + NAME@13..14 + IDENT@13..14 "F" + R_ANGLE@14..15 ">" + PARAM_LIST@15..17 + L_PAREN@15..16 "(" + R_PAREN@16..17 ")" + WHITESPACE@17..18 "\n" + WHERE_CLAUSE@18..49 + WHERE_KW@18..23 "where" + WHITESPACE@23..27 "\n " + WHERE_PRED@27..49 + FOR_KW@27..30 "for" + TYPE_PARAM_LIST@30..34 + L_ANGLE@30..31 "<" + LIFETIME_PARAM@31..33 + LIFETIME@31..33 "\'a" + R_ANGLE@33..34 ">" + WHITESPACE@34..35 " " + PATH_TYPE@35..36 + PATH@35..36 + PATH_SEGMENT@35..36 + NAME_REF@35..36 + IDENT@35..36 "F" + COLON@36..37 ":" + WHITESPACE@37..38 " " + TYPE_BOUND_LIST@38..49 + TYPE_BOUND@38..49 + PATH_TYPE@38..49 + PATH@38..49 + PATH_SEGMENT@38..49 + NAME_REF@38..40 + IDENT@38..40 "Fn" + PARAM_LIST@40..49 + L_PAREN@40..41 "(" + PARAM@41..48 + REFERENCE_TYPE@41..48 + AMP@41..42 "&" + LIFETIME@42..44 "\'a" + WHITESPACE@44..45 " " + PATH_TYPE@45..48 + PATH@45..48 + PATH_SEGMENT@45..48 + NAME_REF@45..48 + IDENT@45..48 "str" + R_PAREN@48..49 ")" + WHITESPACE@49..50 "\n" + BLOCK_EXPR@50..53 + L_CURLY@50..51 "{" + WHITESPACE@51..52 " " + R_CURLY@52..53 "}" + WHITESPACE@53..54 "\n" + FN_DEF@54..103 + FN_KW@54..56 "fn" + WHITESPACE@56..57 " " + NAME@57..64 + IDENT@57..64 "for_ref" + TYPE_PARAM_LIST@64..67 + L_ANGLE@64..65 "<" + TYPE_PARAM@65..66 + NAME@65..66 + IDENT@65..66 "F" + R_ANGLE@66..67 ">" + PARAM_LIST@67..69 + L_PAREN@67..68 "(" + R_PAREN@68..69 ")" + WHITESPACE@69..70 "\n" + WHERE_CLAUSE@70..99 + WHERE_KW@70..75 "where" + WHITESPACE@75..79 "\n " + WHERE_PRED@79..99 + FOR_KW@79..82 "for" + TYPE_PARAM_LIST@82..86 + L_ANGLE@82..83 "<" + LIFETIME_PARAM@83..85 + LIFETIME@83..85 "\'a" + R_ANGLE@85..86 ">" + WHITESPACE@86..87 " " + REFERENCE_TYPE@87..92 + AMP@87..88 "&" + LIFETIME@88..90 "\'a" + WHITESPACE@90..91 " " + PATH_TYPE@91..92 + PATH@91..92 + PATH_SEGMENT@91..92 + NAME_REF@91..92 + IDENT@91..92 "F" + COLON@92..93 ":" + WHITESPACE@93..94 " " + TYPE_BOUND_LIST@94..99 + TYPE_BOUND@94..99 + PATH_TYPE@94..99 + PATH@94..99 + PATH_SEGMENT@94..99 + NAME_REF@94..99 + IDENT@94..99 "Debug" + WHITESPACE@99..100 "\n" + BLOCK_EXPR@100..103 + L_CURLY@100..101 "{" + WHITESPACE@101..102 " " + R_CURLY@102..103 "}" + WHITESPACE@103..104 "\n" + FN_DEF@104..164 + FN_KW@104..106 "fn" + WHITESPACE@106..107 " " + NAME@107..117 + IDENT@107..117 "for_parens" + TYPE_PARAM_LIST@117..120 + L_ANGLE@117..118 "<" + TYPE_PARAM@118..119 + NAME@118..119 + IDENT@118..119 "F" + R_ANGLE@119..120 ">" + PARAM_LIST@120..122 + L_PAREN@120..121 "(" + R_PAREN@121..122 ")" + WHITESPACE@122..123 "\n" + WHERE_CLAUSE@123..160 + WHERE_KW@123..128 "where" + WHITESPACE@128..132 "\n " + WHERE_PRED@132..160 + FOR_KW@132..135 "for" + TYPE_PARAM_LIST@135..139 + L_ANGLE@135..136 "<" + LIFETIME_PARAM@136..138 + LIFETIME@136..138 "\'a" + R_ANGLE@138..139 ">" + WHITESPACE@139..140 " " + PAREN_TYPE@140..147 + L_PAREN@140..141 "(" + REFERENCE_TYPE@141..146 + AMP@141..142 "&" + LIFETIME@142..144 "\'a" + WHITESPACE@144..145 " " + PATH_TYPE@145..146 + PATH@145..146 + PATH_SEGMENT@145..146 + NAME_REF@145..146 + IDENT@145..146 "F" + R_PAREN@146..147 ")" + COLON@147..148 ":" + WHITESPACE@148..149 " " + TYPE_BOUND_LIST@149..160 + TYPE_BOUND@149..160 + PATH_TYPE@149..160 + PATH@149..160 + PATH_SEGMENT@149..160 + NAME_REF@149..151 + IDENT@149..151 "Fn" + PARAM_LIST@151..160 + L_PAREN@151..152 "(" + PARAM@152..159 + REFERENCE_TYPE@152..159 + AMP@152..153 "&" + LIFETIME@153..155 "\'a" + WHITESPACE@155..156 " " + PATH_TYPE@156..159 + PATH@156..159 + PATH_SEGMENT@156..159 + NAME_REF@156..159 + IDENT@156..159 "str" + R_PAREN@159..160 ")" + WHITESPACE@160..161 "\n" + BLOCK_EXPR@161..164 + L_CURLY@161..162 "{" + WHITESPACE@162..163 " " + R_CURLY@163..164 "}" + WHITESPACE@164..165 "\n" + FN_DEF@165..215 + FN_KW@165..167 "fn" + WHITESPACE@167..168 " " + NAME@168..177 + IDENT@168..177 "for_slice" + TYPE_PARAM_LIST@177..180 + L_ANGLE@177..178 "<" + TYPE_PARAM@178..179 + NAME@178..179 + IDENT@178..179 "F" + R_ANGLE@179..180 ">" + PARAM_LIST@180..182 + L_PAREN@180..181 "(" + R_PAREN@181..182 ")" + WHITESPACE@182..183 "\n" + WHERE_CLAUSE@183..211 + WHERE_KW@183..188 "where" + WHITESPACE@188..192 "\n " + WHERE_PRED@192..211 + FOR_KW@192..195 "for" + TYPE_PARAM_LIST@195..199 + L_ANGLE@195..196 "<" + LIFETIME_PARAM@196..198 + LIFETIME@196..198 "\'a" + R_ANGLE@198..199 ">" + WHITESPACE@199..200 " " + SLICE_TYPE@200..207 + L_BRACK@200..201 "[" + REFERENCE_TYPE@201..206 + AMP@201..202 "&" + LIFETIME@202..204 "\'a" + WHITESPACE@204..205 " " + PATH_TYPE@205..206 + PATH@205..206 + PATH_SEGMENT@205..206 + NAME_REF@205..206 + IDENT@205..206 "F" + R_BRACK@206..207 "]" + COLON@207..208 ":" + WHITESPACE@208..209 " " + TYPE_BOUND_LIST@209..211 + TYPE_BOUND@209..211 + PATH_TYPE@209..211 + PATH@209..211 + PATH_SEGMENT@209..211 + NAME_REF@209..211 + IDENT@209..211 "Eq" + WHITESPACE@211..212 "\n" + BLOCK_EXPR@212..215 + L_CURLY@212..213 "{" + WHITESPACE@213..214 " " + R_CURLY@214..215 "}" + WHITESPACE@215..216 "\n" + FN_DEF@216..291 + FN_KW@216..218 "fn" + WHITESPACE@218..219 " " + NAME@219..228 + IDENT@219..228 "for_qpath" + TYPE_PARAM_LIST@228..231 + L_ANGLE@228..229 "<" + TYPE_PARAM@229..230 + NAME@229..230 + IDENT@229..230 "T" + R_ANGLE@230..231 ">" + PARAM_LIST@231..239 + L_PAREN@231..232 "(" + PARAM@232..238 + BIND_PAT@232..234 + NAME@232..234 + IDENT@232..234 "_t" + COLON@234..235 ":" + WHITESPACE@235..236 " " + REFERENCE_TYPE@236..238 + AMP@236..237 "&" + PATH_TYPE@237..238 + PATH@237..238 + PATH_SEGMENT@237..238 + NAME_REF@237..238 + IDENT@237..238 "T" + R_PAREN@238..239 ")" + WHITESPACE@239..240 "\n" + WHERE_CLAUSE@240..287 + WHERE_KW@240..245 "where" + WHITESPACE@245..250 "\n " + WHERE_PRED@250..287 + FOR_KW@250..253 "for" + TYPE_PARAM_LIST@253..257 + L_ANGLE@253..254 "<" + LIFETIME_PARAM@254..256 + LIFETIME@254..256 "\'a" + R_ANGLE@256..257 ">" + WHITESPACE@257..258 " " + PATH_TYPE@258..277 + PATH@258..277 + PATH@258..272 + PATH_SEGMENT@258..272 + L_ANGLE@258..259 "<" + REFERENCE_TYPE@259..264 + AMP@259..260 "&" + LIFETIME@260..262 "\'a" + WHITESPACE@262..263 " " + PATH_TYPE@263..264 + PATH@263..264 + PATH_SEGMENT@263..264 + NAME_REF@263..264 + IDENT@263..264 "T" + WHITESPACE@264..265 " " + AS_KW@265..267 "as" + WHITESPACE@267..268 " " + PATH_TYPE@268..271 + PATH@268..271 + PATH_SEGMENT@268..271 + NAME_REF@268..271 + IDENT@268..271 "Baz" + R_ANGLE@271..272 ">" + COLON2@272..274 "::" + PATH_SEGMENT@274..277 + NAME_REF@274..277 + IDENT@274..277 "Foo" + COLON@277..278 ":" + WHITESPACE@278..279 " " + TYPE_BOUND_LIST@279..287 + TYPE_BOUND@279..287 + PATH_TYPE@279..287 + PATH@279..287 + PATH_SEGMENT@279..287 + NAME_REF@279..287 + IDENT@279..287 "Iterator" + WHITESPACE@287..288 "\n" + BLOCK_EXPR@288..291 + L_CURLY@288..289 "{" + WHITESPACE@289..290 " " + R_CURLY@290..291 "}" + WHITESPACE@291..292 "\n" diff --git a/crates/ra_syntax/test_data/parser/inline/ok/0003_where_pred_for.rs b/crates/ra_syntax/test_data/parser/inline/ok/0003_where_pred_for.rs index b448c6178087..2d47596be39b 100644 --- a/crates/ra_syntax/test_data/parser/inline/ok/0003_where_pred_for.rs +++ b/crates/ra_syntax/test_data/parser/inline/ok/0003_where_pred_for.rs @@ -1,4 +1,20 @@ -fn test() +fn for_trait() where for<'a> F: Fn(&'a str) { } +fn for_ref() +where + for<'a> &'a F: Debug +{ } +fn for_parens() +where + for<'a> (&'a F): Fn(&'a str) +{ } +fn for_slice() +where + for<'a> [&'a F]: Eq +{ } +fn for_qpath(_t: &T) +where + for<'a> <&'a T as Baz>::Foo: Iterator +{ } diff --git a/crates/ra_syntax/test_data/parser/inline/ok/0081_for_type.rast b/crates/ra_syntax/test_data/parser/inline/ok/0081_for_type.rast index dfb8d57ad844..26a80017aea7 100644 --- a/crates/ra_syntax/test_data/parser/inline/ok/0081_for_type.rast +++ b/crates/ra_syntax/test_data/parser/inline/ok/0081_for_type.rast @@ -1,4 +1,4 @@ -SOURCE_FILE@0..200 +SOURCE_FILE@0..82 TYPE_ALIAS_DEF@0..28 TYPE_KW@0..4 "type" WHITESPACE@4..5 " " @@ -29,212 +29,48 @@ SOURCE_FILE@0..200 R_PAREN@26..27 ")" SEMICOLON@27..28 ";" WHITESPACE@28..29 "\n" - FN_DEF@29..79 - FN_KW@29..31 "fn" - WHITESPACE@31..32 " " - NAME@32..35 - IDENT@32..35 "foo" - TYPE_PARAM_LIST@35..38 - L_ANGLE@35..36 "<" - TYPE_PARAM@36..37 - NAME@36..37 - IDENT@36..37 "T" - R_ANGLE@37..38 ">" - PARAM_LIST@38..46 - L_PAREN@38..39 "(" - PARAM@39..45 - BIND_PAT@39..41 - NAME@39..41 - IDENT@39..41 "_t" - COLON@41..42 ":" - WHITESPACE@42..43 " " - REFERENCE_TYPE@43..45 - AMP@43..44 "&" - PATH_TYPE@44..45 - PATH@44..45 - PATH_SEGMENT@44..45 - NAME_REF@44..45 - IDENT@44..45 "T" - R_PAREN@45..46 ")" - WHITESPACE@46..47 " " - WHERE_CLAUSE@47..76 - WHERE_KW@47..52 "where" - WHITESPACE@52..53 " " - WHERE_PRED@53..76 - FOR_TYPE@53..66 - FOR_KW@53..56 "for" - TYPE_PARAM_LIST@56..60 - L_ANGLE@56..57 "<" - LIFETIME_PARAM@57..59 - LIFETIME@57..59 "\'a" - R_ANGLE@59..60 ">" - WHITESPACE@60..61 " " - REFERENCE_TYPE@61..66 - AMP@61..62 "&" - LIFETIME@62..64 "\'a" - WHITESPACE@64..65 " " - PATH_TYPE@65..66 - PATH@65..66 - PATH_SEGMENT@65..66 - NAME_REF@65..66 - IDENT@65..66 "T" - COLON@66..67 ":" - WHITESPACE@67..68 " " - TYPE_BOUND_LIST@68..76 - TYPE_BOUND@68..76 - PATH_TYPE@68..76 - PATH@68..76 - PATH_SEGMENT@68..76 - NAME_REF@68..76 - IDENT@68..76 "Iterator" - WHITESPACE@76..77 " " - BLOCK_EXPR@77..79 - L_CURLY@77..78 "{" - R_CURLY@78..79 "}" - WHITESPACE@79..80 "\n" - FN_DEF@80..134 - FN_KW@80..82 "fn" - WHITESPACE@82..83 " " - NAME@83..86 - IDENT@83..86 "bar" - TYPE_PARAM_LIST@86..89 - L_ANGLE@86..87 "<" - TYPE_PARAM@87..88 - NAME@87..88 - IDENT@87..88 "T" - R_ANGLE@88..89 ">" - PARAM_LIST@89..97 - L_PAREN@89..90 "(" - PARAM@90..96 - BIND_PAT@90..92 - NAME@90..92 - IDENT@90..92 "_t" - COLON@92..93 ":" - WHITESPACE@93..94 " " - REFERENCE_TYPE@94..96 - AMP@94..95 "&" - PATH_TYPE@95..96 - PATH@95..96 - PATH_SEGMENT@95..96 - NAME_REF@95..96 - IDENT@95..96 "T" - R_PAREN@96..97 ")" - WHITESPACE@97..98 " " - WHERE_CLAUSE@98..131 - WHERE_KW@98..103 "where" - WHITESPACE@103..104 " " - WHERE_PRED@104..131 - FOR_TYPE@104..121 - FOR_KW@104..107 "for" - TYPE_PARAM_LIST@107..111 - L_ANGLE@107..108 "<" - LIFETIME_PARAM@108..110 - LIFETIME@108..110 "\'a" - R_ANGLE@110..111 ">" - WHITESPACE@111..112 " " - REFERENCE_TYPE@112..121 - AMP@112..113 "&" - LIFETIME@113..115 "\'a" - WHITESPACE@115..116 " " - MUT_KW@116..119 "mut" - WHITESPACE@119..120 " " - PATH_TYPE@120..121 - PATH@120..121 - PATH_SEGMENT@120..121 - NAME_REF@120..121 - IDENT@120..121 "T" - COLON@121..122 ":" - WHITESPACE@122..123 " " - TYPE_BOUND_LIST@123..131 - TYPE_BOUND@123..131 - PATH_TYPE@123..131 - PATH@123..131 - PATH_SEGMENT@123..131 - NAME_REF@123..131 - IDENT@123..131 "Iterator" - WHITESPACE@131..132 " " - BLOCK_EXPR@132..134 - L_CURLY@132..133 "{" - R_CURLY@133..134 "}" - WHITESPACE@134..135 "\n" - FN_DEF@135..199 - FN_KW@135..137 "fn" - WHITESPACE@137..138 " " - NAME@138..141 - IDENT@138..141 "baz" - TYPE_PARAM_LIST@141..144 - L_ANGLE@141..142 "<" - TYPE_PARAM@142..143 - NAME@142..143 - IDENT@142..143 "T" - R_ANGLE@143..144 ">" - PARAM_LIST@144..152 - L_PAREN@144..145 "(" - PARAM@145..151 - BIND_PAT@145..147 - NAME@145..147 - IDENT@145..147 "_t" - COLON@147..148 ":" - WHITESPACE@148..149 " " - REFERENCE_TYPE@149..151 - AMP@149..150 "&" - PATH_TYPE@150..151 - PATH@150..151 - PATH_SEGMENT@150..151 - NAME_REF@150..151 - IDENT@150..151 "T" - R_PAREN@151..152 ")" - WHITESPACE@152..153 " " - WHERE_CLAUSE@153..196 - WHERE_KW@153..158 "where" - WHITESPACE@158..159 " " - WHERE_PRED@159..196 - FOR_TYPE@159..186 - FOR_KW@159..162 "for" - TYPE_PARAM_LIST@162..166 - L_ANGLE@162..163 "<" - LIFETIME_PARAM@163..165 - LIFETIME@163..165 "\'a" - R_ANGLE@165..166 ">" - WHITESPACE@166..167 " " - PATH_TYPE@167..186 - PATH@167..186 - PATH@167..181 - PATH_SEGMENT@167..181 - L_ANGLE@167..168 "<" - REFERENCE_TYPE@168..173 - AMP@168..169 "&" - LIFETIME@169..171 "\'a" - WHITESPACE@171..172 " " - PATH_TYPE@172..173 - PATH@172..173 - PATH_SEGMENT@172..173 - NAME_REF@172..173 - IDENT@172..173 "T" - WHITESPACE@173..174 " " - AS_KW@174..176 "as" - WHITESPACE@176..177 " " - PATH_TYPE@177..180 - PATH@177..180 - PATH_SEGMENT@177..180 - NAME_REF@177..180 - IDENT@177..180 "Baz" - R_ANGLE@180..181 ">" - COLON2@181..183 "::" - PATH_SEGMENT@183..186 - NAME_REF@183..186 - IDENT@183..186 "Foo" - COLON@186..187 ":" - WHITESPACE@187..188 " " - TYPE_BOUND_LIST@188..196 - TYPE_BOUND@188..196 - PATH_TYPE@188..196 - PATH@188..196 - PATH_SEGMENT@188..196 - NAME_REF@188..196 - IDENT@188..196 "Iterator" - WHITESPACE@196..197 " " - BLOCK_EXPR@197..199 - L_CURLY@197..198 "{" - R_CURLY@198..199 "}" - WHITESPACE@199..200 "\n" + TYPE_ALIAS_DEF@29..81 + TYPE_KW@29..33 "type" + WHITESPACE@33..34 " " + NAME@34..35 + IDENT@34..35 "B" + WHITESPACE@35..36 " " + EQ@36..37 "=" + WHITESPACE@37..38 " " + FOR_TYPE@38..80 + FOR_KW@38..41 "for" + TYPE_PARAM_LIST@41..45 + L_ANGLE@41..42 "<" + LIFETIME_PARAM@42..44 + LIFETIME@42..44 "\'a" + R_ANGLE@44..45 ">" + WHITESPACE@45..46 " " + FN_POINTER_TYPE@46..80 + UNSAFE_KW@46..52 "unsafe" + WHITESPACE@52..53 " " + ABI@53..63 + EXTERN_KW@53..59 "extern" + WHITESPACE@59..60 " " + STRING@60..63 "\"C\"" + WHITESPACE@63..64 " " + FN_KW@64..66 "fn" + PARAM_LIST@66..74 + L_PAREN@66..67 "(" + PARAM@67..73 + REFERENCE_TYPE@67..73 + AMP@67..68 "&" + LIFETIME@68..70 "\'a" + WHITESPACE@70..71 " " + TUPLE_TYPE@71..73 + L_PAREN@71..72 "(" + R_PAREN@72..73 ")" + R_PAREN@73..74 ")" + WHITESPACE@74..75 " " + RET_TYPE@75..80 + THIN_ARROW@75..77 "->" + WHITESPACE@77..78 " " + TUPLE_TYPE@78..80 + L_PAREN@78..79 "(" + R_PAREN@79..80 ")" + SEMICOLON@80..81 ";" + WHITESPACE@81..82 "\n" diff --git a/crates/ra_syntax/test_data/parser/inline/ok/0081_for_type.rs b/crates/ra_syntax/test_data/parser/inline/ok/0081_for_type.rs index d6774d438bc6..457e8744fe10 100644 --- a/crates/ra_syntax/test_data/parser/inline/ok/0081_for_type.rs +++ b/crates/ra_syntax/test_data/parser/inline/ok/0081_for_type.rs @@ -1,4 +1,2 @@ type A = for<'a> fn() -> (); -fn foo(_t: &T) where for<'a> &'a T: Iterator {} -fn bar(_t: &T) where for<'a> &'a mut T: Iterator {} -fn baz(_t: &T) where for<'a> <&'a T as Baz>::Foo: Iterator {} +type B = for<'a> unsafe extern "C" fn(&'a ()) -> (); diff --git a/xtask/src/ast_src.rs b/xtask/src/ast_src.rs index f60f0fb16ce1..392648d7133d 100644 --- a/xtask/src/ast_src.rs +++ b/xtask/src/ast_src.rs @@ -1707,7 +1707,7 @@ pub(crate) const AST_SRC: AstSrc = AstSrc { /// ``` /// /// [Reference](https://doc.rust-lang.org/reference/items/generics.html#where-clauses) - struct WherePred: TypeBoundsOwner { T![lifetime], TypeRef } + struct WherePred: TypeBoundsOwner { T![for], TypeParamList, T![lifetime], TypeRef } /// Where clause. /// From db1cadd4447f7c580c5436a4368a4de3c67fa37a Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Wed, 10 Jun 2020 12:34:23 +0200 Subject: [PATCH 141/172] In field patterns, don't highlight local binding as a field --- crates/ra_ide/src/snapshots/highlighting.html | 4 ++- .../ra_ide/src/syntax_highlighting/tests.rs | 26 ++++++++++--------- crates/ra_ide_db/src/defs.rs | 9 ++++--- 3 files changed, 23 insertions(+), 16 deletions(-) diff --git a/crates/ra_ide/src/snapshots/highlighting.html b/crates/ra_ide/src/snapshots/highlighting.html index e34ff5a7da3c..33548d43ccd5 100644 --- a/crates/ra_ide/src/snapshots/highlighting.html +++ b/crates/ra_ide/src/snapshots/highlighting.html @@ -84,7 +84,9 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd let y = &mut x; let z = &y; - y; + let Foo { x: z, y } = Foo { x: z, y }; + + y; } enum Option<T> { diff --git a/crates/ra_ide/src/syntax_highlighting/tests.rs b/crates/ra_ide/src/syntax_highlighting/tests.rs index 021f8e7e2d41..949bf59a0d24 100644 --- a/crates/ra_ide/src/syntax_highlighting/tests.rs +++ b/crates/ra_ide/src/syntax_highlighting/tests.rs @@ -7,18 +7,6 @@ use crate::{ FileRange, TextRange, }; -/// Highlights the code given by the `ra_fixture` argument, renders the -/// result as HTML, and compares it with the HTML file given as `snapshot`. -/// Note that the `snapshot` file is overwritten by the rendered HTML. -fn check_highlighting(ra_fixture: &str, snapshot: &str, rainbow: bool) { - let (analysis, file_id) = single_file(ra_fixture); - let dst_file = project_dir().join(snapshot); - let actual_html = &analysis.highlight_as_html(file_id, rainbow).unwrap(); - let expected_html = &read_text(&dst_file); - fs::write(dst_file, &actual_html).unwrap(); - assert_eq_text!(expected_html, actual_html); -} - #[test] fn test_highlighting() { check_highlighting( @@ -77,6 +65,8 @@ fn main() { let y = &mut x; let z = &y; + let Foo { x: z, y } = Foo { x: z, y }; + y; } @@ -334,3 +324,15 @@ impl Foo { false, ) } + +/// Highlights the code given by the `ra_fixture` argument, renders the +/// result as HTML, and compares it with the HTML file given as `snapshot`. +/// Note that the `snapshot` file is overwritten by the rendered HTML. +fn check_highlighting(ra_fixture: &str, snapshot: &str, rainbow: bool) { + let (analysis, file_id) = single_file(ra_fixture); + let dst_file = project_dir().join(snapshot); + let actual_html = &analysis.highlight_as_html(file_id, rainbow).unwrap(); + let expected_html = &read_text(&dst_file); + fs::write(dst_file, &actual_html).unwrap(); + assert_eq_text!(expected_html, actual_html); +} diff --git a/crates/ra_ide_db/src/defs.rs b/crates/ra_ide_db/src/defs.rs index 1826f3ac6517..3ef5e74b6997 100644 --- a/crates/ra_ide_db/src/defs.rs +++ b/crates/ra_ide_db/src/defs.rs @@ -78,6 +78,7 @@ impl Definition { } } +#[derive(Debug)] pub enum NameClass { Definition(Definition), /// `None` in `if let None = Some(82) {}` @@ -131,9 +132,11 @@ pub fn classify_name(sema: &Semantics, name: &ast::Name) -> Option let local = sema.to_def(&it)?; if let Some(record_field_pat) = it.syntax().parent().and_then(ast::RecordFieldPat::cast) { - if let Some(field) = sema.resolve_record_field_pat(&record_field_pat) { - let field = Definition::Field(field); - return Some(NameClass::FieldShorthand { local, field }); + if record_field_pat.name_ref().is_none() { + if let Some(field) = sema.resolve_record_field_pat(&record_field_pat) { + let field = Definition::Field(field); + return Some(NameClass::FieldShorthand { local, field }); + } } } From d50a1a0fe9055bf03b9746cb341b251af8e7b326 Mon Sep 17 00:00:00 2001 From: Jonas Schievink Date: Mon, 8 Jun 2020 13:00:31 +0200 Subject: [PATCH 142/172] Profile `world_symbols` --- crates/ra_ide_db/src/symbol_index.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/crates/ra_ide_db/src/symbol_index.rs b/crates/ra_ide_db/src/symbol_index.rs index acc31fe3b51b..299b47c6a93b 100644 --- a/crates/ra_ide_db/src/symbol_index.rs +++ b/crates/ra_ide_db/src/symbol_index.rs @@ -132,6 +132,8 @@ fn file_symbols(db: &impl SymbolsDatabase, file_id: FileId) -> Arc // | VS Code | kbd:[Ctrl+T] // |=== pub fn world_symbols(db: &RootDatabase, query: Query) -> Vec { + let _p = ra_prof::profile("world_symbols").detail(|| query.query.clone()); + /// Need to wrap Snapshot to provide `Clone` impl for `map_with` struct Snap(salsa::Snapshot); impl Clone for Snap { From 54936e8aa212ea5fdf737d8e1b0a02c231ed89eb Mon Sep 17 00:00:00 2001 From: Jonas Schievink Date: Mon, 8 Jun 2020 13:00:53 +0200 Subject: [PATCH 143/172] Fix the symbol query limit --- crates/ra_ide_db/src/symbol_index.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/crates/ra_ide_db/src/symbol_index.rs b/crates/ra_ide_db/src/symbol_index.rs index 299b47c6a93b..78c7143567cb 100644 --- a/crates/ra_ide_db/src/symbol_index.rs +++ b/crates/ra_ide_db/src/symbol_index.rs @@ -300,9 +300,6 @@ impl Query { let mut stream = op.union(); let mut res = Vec::new(); while let Some((_, indexed_values)) = stream.next() { - if res.len() >= self.limit { - break; - } for indexed_value in indexed_values { let symbol_index = &indices[indexed_value.index]; let (start, end) = SymbolIndex::map_value_to_range(indexed_value.value); @@ -314,6 +311,10 @@ impl Query { if self.exact && symbol.name != self.query { continue; } + + if res.len() >= self.limit { + return res; + } res.push(symbol.clone()); } } From 4bcf8c8c68bd791f295aa06ef7903c006be3f356 Mon Sep 17 00:00:00 2001 From: Jonas Schievink Date: Tue, 9 Jun 2020 17:32:42 +0200 Subject: [PATCH 144/172] Add an FST index to `ImportMap` --- Cargo.lock | 2 + crates/ra_hir_def/Cargo.toml | 2 + crates/ra_hir_def/src/import_map.rs | 253 +++++++++++++++++++++++++++- crates/ra_hir_expand/src/name.rs | 7 + 4 files changed, 261 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index df79334c96c9..4c55519681eb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -981,7 +981,9 @@ dependencies = [ "anymap", "drop_bomb", "either", + "fst", "insta", + "itertools", "log", "once_cell", "ra_arena", diff --git a/crates/ra_hir_def/Cargo.toml b/crates/ra_hir_def/Cargo.toml index b853583088cf..bd69abfc752e 100644 --- a/crates/ra_hir_def/Cargo.toml +++ b/crates/ra_hir_def/Cargo.toml @@ -14,6 +14,8 @@ rustc-hash = "1.1.0" either = "1.5.3" anymap = "0.12.1" drop_bomb = "0.1.4" +fst = { version = "0.4", default-features = false } +itertools = "0.9.0" stdx = { path = "../stdx" } diff --git a/crates/ra_hir_def/src/import_map.rs b/crates/ra_hir_def/src/import_map.rs index 4284a0a9120a..e9b2fe26ed27 100644 --- a/crates/ra_hir_def/src/import_map.rs +++ b/crates/ra_hir_def/src/import_map.rs @@ -1,7 +1,10 @@ //! A map of all publicly exported items in a crate. +use std::cmp::Ordering; use std::{collections::hash_map::Entry, fmt, sync::Arc}; +use fst::{self, Streamer}; +use itertools::Itertools; use ra_db::CrateId; use rustc_hash::FxHashMap; @@ -21,9 +24,17 @@ use crate::{ /// /// Note that all paths are relative to the containing crate's root, so the crate name still needs /// to be prepended to the `ModPath` before the path is valid. -#[derive(Eq, PartialEq)] pub struct ImportMap { map: FxHashMap, + + /// List of keys stored in `map`, sorted lexicographically by their `ModPath`. Indexed by the + /// values returned by running `fst`. + /// + /// Since a path can refer to multiple items due to namespacing, we store all items with the + /// same path right after each other. This allows us to find all items after the FST gives us + /// the index of the first one. + importables: Vec, + fst: fst::Map>, } impl ImportMap { @@ -88,7 +99,34 @@ impl ImportMap { } } - Arc::new(Self { map: import_map }) + let mut importables = import_map.iter().collect::>(); + + importables.sort_by(cmp); + + // Build the FST, taking care not to insert duplicate values. + + let mut builder = fst::MapBuilder::memory(); + let mut last_batch_start = 0; + + for idx in 0..importables.len() { + if let Some(next_item) = importables.get(idx + 1) { + if cmp(&importables[last_batch_start], next_item) == Ordering::Equal { + continue; + } + } + + let start = last_batch_start; + last_batch_start = idx + 1; + + let key: String = fst_path(&importables[start].1).collect(); + + builder.insert(key, start as u64).unwrap(); + } + + let fst = fst::Map::new(builder.into_inner().unwrap()).unwrap(); + let importables = importables.iter().map(|(item, _)| **item).collect(); + + Arc::new(Self { map: import_map, fst, importables }) } /// Returns the `ModPath` needed to import/mention `item`, relative to this crate's root. @@ -97,6 +135,14 @@ impl ImportMap { } } +impl PartialEq for ImportMap { + fn eq(&self, other: &Self) -> bool { + self.importables == other.importables + } +} + +impl Eq for ImportMap {} + impl fmt::Debug for ImportMap { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let mut importable_paths: Vec<_> = self @@ -117,13 +163,97 @@ impl fmt::Debug for ImportMap { } } +fn fst_path(path: &ModPath) -> impl Iterator + '_ { + path.segments + .iter() + .map(|name| name.as_text().unwrap()) + .intersperse("::") + .flat_map(|s| s.chars().map(|c| c.to_ascii_lowercase())) +} + +fn cmp((_, lhs): &(&ItemInNs, &ModPath), (_, rhs): &(&ItemInNs, &ModPath)) -> Ordering { + let lhs_chars = fst_path(lhs); + let rhs_chars = fst_path(rhs); + lhs_chars.cmp(rhs_chars) +} + +#[derive(Debug)] +pub struct Query { + query: String, + anchor_end: bool, +} + +impl Query { + pub fn new(query: impl AsRef) -> Self { + Self { query: query.as_ref().to_lowercase(), anchor_end: false } + } + + /// Only returns items whose paths end with the (case-insensitive) query string as their last + /// segment. + pub fn anchor_end(self) -> Self { + Self { anchor_end: true, ..self } + } +} + +/// Searches dependencies of `krate` for an importable path matching `query`. +/// +/// This returns all items that could be imported from within `krate`, excluding paths inside +/// `krate` itself. +pub fn search_dependencies<'a>( + db: &'a dyn DefDatabase, + krate: CrateId, + query: Query, +) -> Vec { + let _p = ra_prof::profile("import_map::global_search").detail(|| format!("{:?}", query)); + + let graph = db.crate_graph(); + let import_maps: Vec<_> = + graph[krate].dependencies.iter().map(|dep| db.import_map(dep.crate_id)).collect(); + + let automaton = fst::automaton::Subsequence::new(&query.query); + + let mut op = fst::map::OpBuilder::new(); + for map in &import_maps { + op = op.add(map.fst.search(&automaton)); + } + + let mut stream = op.union(); + let mut res = Vec::new(); + while let Some((_, indexed_values)) = stream.next() { + for indexed_value in indexed_values { + let import_map = &import_maps[indexed_value.index]; + let importables = &import_map.importables[indexed_value.value as usize..]; + + // Path shared by the importable items in this group. + let path = &import_map.map[&importables[0]]; + + if query.anchor_end { + // Last segment must match query. + let last = path.segments.last().unwrap().to_string(); + if last.to_lowercase() != query.query { + continue; + } + } + + // Add the items from this `ModPath` group. Those are all subsequent items in + // `importables` whose paths match `path`. + res.extend(importables.iter().copied().take_while(|item| { + let item_path = &import_map.map[item]; + fst_path(item_path).eq(fst_path(path)) + })); + } + } + + res +} + #[cfg(test)] mod tests { use super::*; use crate::test_db::TestDB; use insta::assert_snapshot; use ra_db::fixture::WithFixture; - use ra_db::SourceDatabase; + use ra_db::{SourceDatabase, Upcast}; fn import_map(ra_fixture: &str) -> String { let db = TestDB::with_files(ra_fixture); @@ -144,6 +274,40 @@ mod tests { import_maps.join("\n") } + fn search_dependencies_of(ra_fixture: &str, krate_name: &str, query: Query) -> String { + let db = TestDB::with_files(ra_fixture); + let crate_graph = db.crate_graph(); + let krate = crate_graph + .iter() + .find(|krate| { + crate_graph[*krate].display_name.as_ref().map(|n| n.to_string()) + == Some(krate_name.to_string()) + }) + .unwrap(); + + search_dependencies(db.upcast(), krate, query) + .into_iter() + .filter_map(|item| { + let mark = match item { + ItemInNs::Types(_) => "t", + ItemInNs::Values(_) => "v", + ItemInNs::Macros(_) => "m", + }; + item.krate(db.upcast()).map(|krate| { + let map = db.import_map(krate); + let path = map.path_of(item).unwrap(); + format!( + "{}::{} ({})", + crate_graph[krate].display_name.as_ref().unwrap(), + path, + mark + ) + }) + }) + .collect::>() + .join("\n") + } + #[test] fn smoke() { let map = import_map( @@ -328,4 +492,87 @@ mod tests { lib: "###); } + + #[test] + fn namespacing() { + let map = import_map( + r" + //- /lib.rs crate:lib + pub struct Thing; // t + v + #[macro_export] + macro_rules! Thing { // m + () => {}; + } + ", + ); + + assert_snapshot!(map, @r###" + lib: + - Thing (m) + - Thing (t) + - Thing (v) + "###); + + let map = import_map( + r" + //- /lib.rs crate:lib + pub mod Thing {} // t + #[macro_export] + macro_rules! Thing { // m + () => {}; + } + ", + ); + + assert_snapshot!(map, @r###" + lib: + - Thing (m) + - Thing (t) + "###); + } + + #[test] + fn search() { + let ra_fixture = r#" + //- /main.rs crate:main deps:dep + //- /dep.rs crate:dep deps:tdep + use tdep::fmt as fmt_dep; + pub mod fmt { + pub trait Display { + fn fmt(); + } + } + #[macro_export] + macro_rules! Fmt { + () => {}; + } + pub struct Fmt; + + pub fn format() {} + pub fn no() {} + + //- /tdep.rs crate:tdep + pub mod fmt { + pub struct NotImportableFromMain; + } + "#; + + let res = search_dependencies_of(ra_fixture, "main", Query::new("fmt")); + assert_snapshot!(res, @r###" + dep::Fmt (v) + dep::fmt (t) + dep::Fmt (t) + dep::Fmt (m) + dep::fmt::Display (t) + dep::format (v) + "###); + + let res = search_dependencies_of(ra_fixture, "main", Query::new("fmt").anchor_end()); + assert_snapshot!(res, @r###" + dep::Fmt (v) + dep::fmt (t) + dep::Fmt (t) + dep::Fmt (m) + "###); + } } diff --git a/crates/ra_hir_expand/src/name.rs b/crates/ra_hir_expand/src/name.rs index 660bdfe3365b..f306d96412b1 100644 --- a/crates/ra_hir_expand/src/name.rs +++ b/crates/ra_hir_expand/src/name.rs @@ -67,6 +67,13 @@ impl Name { _ => None, } } + + pub fn as_text(&self) -> Option<&str> { + match &self.0 { + Repr::Text(s) => Some(s.as_str()), + _ => None, + } + } } pub trait AsName { From 6463d3ac63a479e33d923593e720696b38a1a54c Mon Sep 17 00:00:00 2001 From: Jonas Schievink Date: Tue, 9 Jun 2020 18:47:14 +0200 Subject: [PATCH 145/172] symbol_index: allow querying a single crate --- crates/ra_ide_db/src/symbol_index.rs | 43 ++++++++++++++++++++++------ 1 file changed, 34 insertions(+), 9 deletions(-) diff --git a/crates/ra_ide_db/src/symbol_index.rs b/crates/ra_ide_db/src/symbol_index.rs index 78c7143567cb..c974891eaefa 100644 --- a/crates/ra_ide_db/src/symbol_index.rs +++ b/crates/ra_ide_db/src/symbol_index.rs @@ -29,9 +29,10 @@ use std::{ }; use fst::{self, Streamer}; +use hir::db::DefDatabase; use ra_db::{ salsa::{self, ParallelDatabase}, - FileId, SourceDatabaseExt, SourceRootId, + CrateId, FileId, SourceDatabaseExt, SourceRootId, }; use ra_syntax::{ ast::{self, NameOwner}, @@ -110,6 +111,14 @@ fn file_symbols(db: &impl SymbolsDatabase, file_id: FileId) -> Arc Arc::new(SymbolIndex::new(symbols)) } +/// Need to wrap Snapshot to provide `Clone` impl for `map_with` +struct Snap(salsa::Snapshot); +impl Clone for Snap { + fn clone(&self) -> Snap { + Snap(self.0.snapshot()) + } +} + // Feature: Workspace Symbol // // Uses fuzzy-search to find types, modules and functions by name across your @@ -134,14 +143,6 @@ fn file_symbols(db: &impl SymbolsDatabase, file_id: FileId) -> Arc pub fn world_symbols(db: &RootDatabase, query: Query) -> Vec { let _p = ra_prof::profile("world_symbols").detail(|| query.query.clone()); - /// Need to wrap Snapshot to provide `Clone` impl for `map_with` - struct Snap(salsa::Snapshot); - impl Clone for Snap { - fn clone(&self) -> Snap { - Snap(self.0.snapshot()) - } - } - let buf: Vec> = if query.libs { let snap = Snap(db.snapshot()); #[cfg(not(feature = "wasm"))] @@ -175,6 +176,30 @@ pub fn world_symbols(db: &RootDatabase, query: Query) -> Vec { query.search(&buf) } +pub fn crate_symbols(db: &RootDatabase, krate: CrateId, query: Query) -> Vec { + let def_map = db.crate_def_map(krate); + let mut files = Vec::new(); + let mut modules = vec![def_map.root]; + while let Some(module) = modules.pop() { + let data = &def_map[module]; + files.extend(data.origin.file_id()); + modules.extend(data.children.values()); + } + + let snap = Snap(db.snapshot()); + + #[cfg(not(feature = "wasm"))] + let buf = files + .par_iter() + .map_with(snap, |db, &file_id| db.0.file_symbols(file_id)) + .collect::>(); + + #[cfg(feature = "wasm")] + let buf = files.iter().map(|&file_id| snap.0.file_symbols(file_id)).collect::>(); + + query.search(&buf) +} + pub fn index_resolve(db: &RootDatabase, name_ref: &ast::NameRef) -> Vec { let name = name_ref.text(); let mut query = Query::new(name.to_string()); From b01fb22494eaf64c02c17fc38598a3a2dbd8e980 Mon Sep 17 00:00:00 2001 From: Jonas Schievink Date: Tue, 9 Jun 2020 18:48:00 +0200 Subject: [PATCH 146/172] ra_hir: expose `import_map::search_dependencies` --- crates/ra_hir/src/code_model.rs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/crates/ra_hir/src/code_model.rs b/crates/ra_hir/src/code_model.rs index 4a06f3bcddb6..c8329d971e50 100644 --- a/crates/ra_hir/src/code_model.rs +++ b/crates/ra_hir/src/code_model.rs @@ -9,6 +9,7 @@ use hir_def::{ builtin_type::BuiltinType, docs::Documentation, expr::{BindingAnnotation, Pat, PatId}, + import_map, per_ns::PerNs, resolver::{HasResolver, Resolver}, type_ref::{Mutability, TypeRef}, @@ -98,6 +99,19 @@ impl Crate { db.crate_graph()[self.id].display_name.as_ref().cloned() } + pub fn query_external_importables( + self, + db: &dyn DefDatabase, + query: &str, + ) -> impl Iterator> { + import_map::search_dependencies(db, self.into(), import_map::Query::new(query).anchor_end()) + .into_iter() + .map(|item| match item { + ItemInNs::Types(mod_id) | ItemInNs::Values(mod_id) => Either::Left(mod_id.into()), + ItemInNs::Macros(mac_id) => Either::Right(mac_id.into()), + }) + } + pub fn all(db: &dyn HirDatabase) -> Vec { db.crate_graph().iter().map(|id| Crate { id }).collect() } From a70a0ca73ceac339de4e1df6d561894e485ba5ee Mon Sep 17 00:00:00 2001 From: Jonas Schievink Date: Tue, 9 Jun 2020 18:48:44 +0200 Subject: [PATCH 147/172] ImportsLocator: use ImportMap for non-local crates --- crates/ra_assists/src/handlers/auto_import.rs | 47 ++++++++++++++++++- crates/ra_ide_db/src/imports_locator.rs | 46 +++++++++--------- 2 files changed, 70 insertions(+), 23 deletions(-) diff --git a/crates/ra_assists/src/handlers/auto_import.rs b/crates/ra_assists/src/handlers/auto_import.rs index edf96d50ec14..1f4142747328 100644 --- a/crates/ra_assists/src/handlers/auto_import.rs +++ b/crates/ra_assists/src/handlers/auto_import.rs @@ -130,7 +130,7 @@ impl AutoImportAssets { fn search_for_imports(&self, db: &RootDatabase) -> BTreeSet { let _p = profile("auto_import::search_for_imports"); let current_crate = self.module_with_name_to_import.krate(); - ImportsLocator::new(db) + ImportsLocator::new(db, current_crate) .find_imports(&self.get_search_query()) .into_iter() .filter_map(|candidate| match &self.import_candidate { @@ -841,4 +841,49 @@ fn main() { ", ) } + + #[test] + fn dep_import() { + check_assist( + auto_import, + r" + //- /lib.rs crate:dep + pub struct Struct; + + //- /main.rs crate:main deps:dep + fn main() { + Struct<|> + }", + r"use dep::Struct; + +fn main() { + Struct +} +", + ); + } + + #[test] + fn whole_segment() { + check_assist( + auto_import, + r" + //- /lib.rs crate:dep + pub mod fmt { + pub trait Display {} + } + + pub fn panic_fmt() {} + + //- /main.rs crate:main deps:dep + struct S; + + impl f<|>mt::Display for S {}", + r"use dep::fmt; + +struct S; +impl fmt::Display for S {} +", + ); + } } diff --git a/crates/ra_ide_db/src/imports_locator.rs b/crates/ra_ide_db/src/imports_locator.rs index bf0d8db6067b..fff112e66103 100644 --- a/crates/ra_ide_db/src/imports_locator.rs +++ b/crates/ra_ide_db/src/imports_locator.rs @@ -1,7 +1,7 @@ //! This module contains an import search funcionality that is provided to the ra_assists module. //! Later, this should be moved away to a separate crate that is accessible from the ra_assists module. -use hir::{MacroDef, ModuleDef, Semantics}; +use hir::{Crate, MacroDef, ModuleDef, Semantics}; use ra_prof::profile; use ra_syntax::{ast, AstNode, SyntaxKind::NAME}; @@ -11,44 +11,46 @@ use crate::{ RootDatabase, }; use either::Either; +use rustc_hash::FxHashSet; pub struct ImportsLocator<'a> { sema: Semantics<'a, RootDatabase>, + krate: Crate, } impl<'a> ImportsLocator<'a> { - pub fn new(db: &'a RootDatabase) -> Self { - Self { sema: Semantics::new(db) } + pub fn new(db: &'a RootDatabase, krate: Crate) -> Self { + Self { sema: Semantics::new(db), krate } } pub fn find_imports(&mut self, name_to_import: &str) -> Vec> { let _p = profile("search_for_imports"); let db = self.sema.db; - let project_results = { + // Query dependencies first. + let mut candidates: FxHashSet<_> = + self.krate.query_external_importables(db, name_to_import).collect(); + + // Query the local crate using the symbol index. + let local_results = { let mut query = Query::new(name_to_import.to_string()); query.exact(); query.limit(40); - symbol_index::world_symbols(db, query) - }; - let lib_results = { - let mut query = Query::new(name_to_import.to_string()); - query.libs(); - query.exact(); - query.limit(40); - symbol_index::world_symbols(db, query) + symbol_index::crate_symbols(db, self.krate.into(), query) }; - project_results - .into_iter() - .chain(lib_results.into_iter()) - .filter_map(|import_candidate| self.get_name_definition(&import_candidate)) - .filter_map(|name_definition_to_import| match name_definition_to_import { - Definition::ModuleDef(module_def) => Some(Either::Left(module_def)), - Definition::Macro(macro_def) => Some(Either::Right(macro_def)), - _ => None, - }) - .collect() + candidates.extend( + local_results + .into_iter() + .filter_map(|import_candidate| self.get_name_definition(&import_candidate)) + .filter_map(|name_definition_to_import| match name_definition_to_import { + Definition::ModuleDef(module_def) => Some(Either::Left(module_def)), + Definition::Macro(macro_def) => Some(Either::Right(macro_def)), + _ => None, + }), + ); + + candidates.into_iter().collect() } fn get_name_definition(&mut self, import_candidate: &FileSymbol) -> Option { From 781b514e5886cbaff88483cdeddc504effef299c Mon Sep 17 00:00:00 2001 From: Jonas Schievink Date: Wed, 10 Jun 2020 11:39:06 +0200 Subject: [PATCH 148/172] Add test for macro generated items --- crates/ra_assists/src/handlers/auto_import.rs | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/crates/ra_assists/src/handlers/auto_import.rs b/crates/ra_assists/src/handlers/auto_import.rs index 1f4142747328..86a173ff5889 100644 --- a/crates/ra_assists/src/handlers/auto_import.rs +++ b/crates/ra_assists/src/handlers/auto_import.rs @@ -865,6 +865,7 @@ fn main() { #[test] fn whole_segment() { + // Tests that only imports whose last segment matches the identifier get suggested. check_assist( auto_import, r" @@ -883,6 +884,36 @@ fn main() { struct S; impl fmt::Display for S {} +", + ); + } + + #[test] + fn macro_generated() { + // Tests that macro-generated items are suggested from external crates. + check_assist( + auto_import, + r" + //- /lib.rs crate:dep + + macro_rules! mac { + () => { + pub struct Cheese; + }; + } + + mac!(); + + //- /main.rs crate:main deps:dep + + fn main() { + Cheese<|>; + }", + r"use dep::Cheese; + +fn main() { + Cheese; +} ", ); } From bcf875f46ae5142c42ddac8094e1b6652182d4be Mon Sep 17 00:00:00 2001 From: Jonas Schievink Date: Wed, 10 Jun 2020 11:52:00 +0200 Subject: [PATCH 149/172] Clean up import_map.rs --- crates/ra_hir_def/src/import_map.rs | 45 +++++++++++++---------------- crates/ra_hir_expand/src/name.rs | 7 ----- 2 files changed, 20 insertions(+), 32 deletions(-) diff --git a/crates/ra_hir_def/src/import_map.rs b/crates/ra_hir_def/src/import_map.rs index e9b2fe26ed27..f2e4ca2db545 100644 --- a/crates/ra_hir_def/src/import_map.rs +++ b/crates/ra_hir_def/src/import_map.rs @@ -1,10 +1,8 @@ //! A map of all publicly exported items in a crate. -use std::cmp::Ordering; -use std::{collections::hash_map::Entry, fmt, sync::Arc}; +use std::{cmp::Ordering, collections::hash_map::Entry, fmt, sync::Arc}; use fst::{self, Streamer}; -use itertools::Itertools; use ra_db::CrateId; use rustc_hash::FxHashMap; @@ -118,7 +116,7 @@ impl ImportMap { let start = last_batch_start; last_batch_start = idx + 1; - let key: String = fst_path(&importables[start].1).collect(); + let key = fst_path(&importables[start].1); builder.insert(key, start as u64).unwrap(); } @@ -137,7 +135,8 @@ impl ImportMap { impl PartialEq for ImportMap { fn eq(&self, other: &Self) -> bool { - self.importables == other.importables + // `fst` and `importables` are built from `map`, so we don't need to compare them. + self.map == other.map } } @@ -163,18 +162,16 @@ impl fmt::Debug for ImportMap { } } -fn fst_path(path: &ModPath) -> impl Iterator + '_ { - path.segments - .iter() - .map(|name| name.as_text().unwrap()) - .intersperse("::") - .flat_map(|s| s.chars().map(|c| c.to_ascii_lowercase())) +fn fst_path(path: &ModPath) -> String { + let mut s = path.to_string(); + s.make_ascii_lowercase(); + s } fn cmp((_, lhs): &(&ItemInNs, &ModPath), (_, rhs): &(&ItemInNs, &ModPath)) -> Ordering { - let lhs_chars = fst_path(lhs); - let rhs_chars = fst_path(rhs); - lhs_chars.cmp(rhs_chars) + let lhs_str = fst_path(lhs); + let rhs_str = fst_path(rhs); + lhs_str.cmp(&rhs_str) } #[derive(Debug)] @@ -184,8 +181,8 @@ pub struct Query { } impl Query { - pub fn new(query: impl AsRef) -> Self { - Self { query: query.as_ref().to_lowercase(), anchor_end: false } + pub fn new(query: &str) -> Self { + Self { query: query.to_lowercase(), anchor_end: false } } /// Only returns items whose paths end with the (case-insensitive) query string as their last @@ -197,14 +194,13 @@ impl Query { /// Searches dependencies of `krate` for an importable path matching `query`. /// -/// This returns all items that could be imported from within `krate`, excluding paths inside -/// `krate` itself. +/// This returns a list of items that could be imported from dependencies of `krate`. pub fn search_dependencies<'a>( db: &'a dyn DefDatabase, krate: CrateId, query: Query, ) -> Vec { - let _p = ra_prof::profile("import_map::global_search").detail(|| format!("{:?}", query)); + let _p = ra_prof::profile("search_dependencies").detail(|| format!("{:?}", query)); let graph = db.crate_graph(); let import_maps: Vec<_> = @@ -239,7 +235,7 @@ pub fn search_dependencies<'a>( // `importables` whose paths match `path`. res.extend(importables.iter().copied().take_while(|item| { let item_path = &import_map.map[item]; - fst_path(item_path).eq(fst_path(path)) + fst_path(item_path) == fst_path(path) })); } } @@ -252,6 +248,7 @@ mod tests { use super::*; use crate::test_db::TestDB; use insta::assert_snapshot; + use itertools::Itertools; use ra_db::fixture::WithFixture; use ra_db::{SourceDatabase, Upcast}; @@ -259,7 +256,7 @@ mod tests { let db = TestDB::with_files(ra_fixture); let crate_graph = db.crate_graph(); - let import_maps: Vec<_> = crate_graph + let s = crate_graph .iter() .filter_map(|krate| { let cdata = &crate_graph[krate]; @@ -269,9 +266,8 @@ mod tests { Some(format!("{}:\n{:?}", name, map)) }) - .collect(); - - import_maps.join("\n") + .join("\n"); + s } fn search_dependencies_of(ra_fixture: &str, krate_name: &str, query: Query) -> String { @@ -304,7 +300,6 @@ mod tests { ) }) }) - .collect::>() .join("\n") } diff --git a/crates/ra_hir_expand/src/name.rs b/crates/ra_hir_expand/src/name.rs index f306d96412b1..660bdfe3365b 100644 --- a/crates/ra_hir_expand/src/name.rs +++ b/crates/ra_hir_expand/src/name.rs @@ -67,13 +67,6 @@ impl Name { _ => None, } } - - pub fn as_text(&self) -> Option<&str> { - match &self.0 { - Repr::Text(s) => Some(s.as_str()), - _ => None, - } - } } pub trait AsName { From 56c7145993f94a12bf923f08cbd62d963e62bbd1 Mon Sep 17 00:00:00 2001 From: Jonas Schievink Date: Wed, 10 Jun 2020 12:30:33 +0200 Subject: [PATCH 150/172] Limit import map queries --- crates/ra_hir/src/code_model.rs | 16 ++++++----- crates/ra_hir_def/src/import_map.rs | 42 ++++++++++++++++++++++++++++- 2 files changed, 51 insertions(+), 7 deletions(-) diff --git a/crates/ra_hir/src/code_model.rs b/crates/ra_hir/src/code_model.rs index c8329d971e50..a55fe03a699c 100644 --- a/crates/ra_hir/src/code_model.rs +++ b/crates/ra_hir/src/code_model.rs @@ -104,12 +104,16 @@ impl Crate { db: &dyn DefDatabase, query: &str, ) -> impl Iterator> { - import_map::search_dependencies(db, self.into(), import_map::Query::new(query).anchor_end()) - .into_iter() - .map(|item| match item { - ItemInNs::Types(mod_id) | ItemInNs::Values(mod_id) => Either::Left(mod_id.into()), - ItemInNs::Macros(mac_id) => Either::Right(mac_id.into()), - }) + import_map::search_dependencies( + db, + self.into(), + import_map::Query::new(query).anchor_end().limit(40), + ) + .into_iter() + .map(|item| match item { + ItemInNs::Types(mod_id) | ItemInNs::Values(mod_id) => Either::Left(mod_id.into()), + ItemInNs::Macros(mac_id) => Either::Right(mac_id.into()), + }) } pub fn all(db: &dyn HirDatabase) -> Vec { diff --git a/crates/ra_hir_def/src/import_map.rs b/crates/ra_hir_def/src/import_map.rs index f2e4ca2db545..70368d8df5f4 100644 --- a/crates/ra_hir_def/src/import_map.rs +++ b/crates/ra_hir_def/src/import_map.rs @@ -178,11 +178,12 @@ fn cmp((_, lhs): &(&ItemInNs, &ModPath), (_, rhs): &(&ItemInNs, &ModPath)) -> Or pub struct Query { query: String, anchor_end: bool, + limit: usize, } impl Query { pub fn new(query: &str) -> Self { - Self { query: query.to_lowercase(), anchor_end: false } + Self { query: query.to_lowercase(), anchor_end: false, limit: usize::max_value() } } /// Only returns items whose paths end with the (case-insensitive) query string as their last @@ -190,6 +191,11 @@ impl Query { pub fn anchor_end(self) -> Self { Self { anchor_end: true, ..self } } + + /// Limits the returned number of items to `limit`. + pub fn limit(self, limit: usize) -> Self { + Self { limit, ..self } + } } /// Searches dependencies of `krate` for an importable path matching `query`. @@ -237,6 +243,11 @@ pub fn search_dependencies<'a>( let item_path = &import_map.map[item]; fst_path(item_path) == fst_path(path) })); + + if res.len() >= query.limit { + res.truncate(query.limit); + return res; + } } } @@ -570,4 +581,33 @@ mod tests { dep::Fmt (m) "###); } + + #[test] + fn search_limit() { + let res = search_dependencies_of( + r#" + //- /main.rs crate:main deps:dep + //- /dep.rs crate:dep + pub mod fmt { + pub trait Display { + fn fmt(); + } + } + #[macro_export] + macro_rules! Fmt { + () => {}; + } + pub struct Fmt; + + pub fn format() {} + pub fn no() {} + "#, + "main", + Query::new("").limit(2), + ); + assert_snapshot!(res, @r###" + dep::fmt (t) + dep::Fmt (t) + "###); + } } From ed2817e599a9c0e812af26587badad6da7a4d949 Mon Sep 17 00:00:00 2001 From: Jonas Schievink Date: Wed, 10 Jun 2020 12:37:00 +0200 Subject: [PATCH 151/172] Move limit check down --- crates/ra_ide_db/src/symbol_index.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/ra_ide_db/src/symbol_index.rs b/crates/ra_ide_db/src/symbol_index.rs index c974891eaefa..ac0a201df315 100644 --- a/crates/ra_ide_db/src/symbol_index.rs +++ b/crates/ra_ide_db/src/symbol_index.rs @@ -337,10 +337,10 @@ impl Query { continue; } + res.push(symbol.clone()); if res.len() >= self.limit { return res; } - res.push(symbol.clone()); } } } From 1538206609dbb88f10ff2525523096bab0c65740 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Wed, 10 Jun 2020 14:12:08 +0200 Subject: [PATCH 152/172] Explain inline tests --- docs/dev/README.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/docs/dev/README.md b/docs/dev/README.md index 0330939b6ef0..cf93135ee9b8 100644 --- a/docs/dev/README.md +++ b/docs/dev/README.md @@ -303,6 +303,17 @@ Rather than talking in definitions and references, it talks in Strings and textu In general, API is centered around UI concerns -- the result of the call is what the user sees in the editor, and not what the compiler sees underneath. The results are 100% Rust specific though. +## Parser Tests + +Test for parser (`ra_parser`) live in `ra_syntax` crate (see `test_data` direcotory). +There are two kinds of tests: + +* Manually written test cases in `parser/ok` and `parser/error` +* "Inline" tests in `parser/inline` (these are generated) from comments in `ra_parser` crate. + +The purpose of inline tests is not to achieve full coverage by test cases, but to explain to the reader of the code what each particular `if` and `match` is responsible for. +If you are tempted to add a large inline test, it might be a good idea to leave only the simplest example in place, and move the test to a manual `parser/ok` test. + # Logging Logging is done by both rust-analyzer and VS Code, so it might be tricky to From 7e83ed99a887f959bd4cf97357faf373a09f9269 Mon Sep 17 00:00:00 2001 From: Jonas Schievink Date: Wed, 10 Jun 2020 16:04:55 +0200 Subject: [PATCH 153/172] Respect casing when searching for imports --- crates/ra_assists/src/handlers/auto_import.rs | 25 ++++++++ crates/ra_hir/src/code_model.rs | 2 +- crates/ra_hir_def/src/import_map.rs | 60 +++++++++++++++++-- 3 files changed, 81 insertions(+), 6 deletions(-) diff --git a/crates/ra_assists/src/handlers/auto_import.rs b/crates/ra_assists/src/handlers/auto_import.rs index 86a173ff5889..5092bf336671 100644 --- a/crates/ra_assists/src/handlers/auto_import.rs +++ b/crates/ra_assists/src/handlers/auto_import.rs @@ -914,6 +914,31 @@ impl fmt::Display for S {} fn main() { Cheese; } +", + ); + } + + #[test] + fn casing() { + // Tests that differently cased names don't interfere and we only suggest the matching one. + check_assist( + auto_import, + r" + //- /lib.rs crate:dep + + pub struct FMT; + pub struct fmt; + + //- /main.rs crate:main deps:dep + + fn main() { + FMT<|>; + }", + r"use dep::FMT; + +fn main() { + FMT; +} ", ); } diff --git a/crates/ra_hir/src/code_model.rs b/crates/ra_hir/src/code_model.rs index a55fe03a699c..1a9f6cc768b6 100644 --- a/crates/ra_hir/src/code_model.rs +++ b/crates/ra_hir/src/code_model.rs @@ -107,7 +107,7 @@ impl Crate { import_map::search_dependencies( db, self.into(), - import_map::Query::new(query).anchor_end().limit(40), + import_map::Query::new(query).anchor_end().case_sensitive().limit(40), ) .into_iter() .map(|item| match item { diff --git a/crates/ra_hir_def/src/import_map.rs b/crates/ra_hir_def/src/import_map.rs index 70368d8df5f4..a55d7d83b6b5 100644 --- a/crates/ra_hir_def/src/import_map.rs +++ b/crates/ra_hir_def/src/import_map.rs @@ -177,13 +177,21 @@ fn cmp((_, lhs): &(&ItemInNs, &ModPath), (_, rhs): &(&ItemInNs, &ModPath)) -> Or #[derive(Debug)] pub struct Query { query: String, + lowercased: String, anchor_end: bool, + case_sensitive: bool, limit: usize, } impl Query { pub fn new(query: &str) -> Self { - Self { query: query.to_lowercase(), anchor_end: false, limit: usize::max_value() } + Self { + lowercased: query.to_lowercase(), + query: query.to_string(), + anchor_end: false, + case_sensitive: false, + limit: usize::max_value(), + } } /// Only returns items whose paths end with the (case-insensitive) query string as their last @@ -196,6 +204,11 @@ impl Query { pub fn limit(self, limit: usize) -> Self { Self { limit, ..self } } + + /// Respect casing of the query string when matching. + pub fn case_sensitive(self) -> Self { + Self { case_sensitive: true, ..self } + } } /// Searches dependencies of `krate` for an importable path matching `query`. @@ -212,7 +225,7 @@ pub fn search_dependencies<'a>( let import_maps: Vec<_> = graph[krate].dependencies.iter().map(|dep| db.import_map(dep.crate_id)).collect(); - let automaton = fst::automaton::Subsequence::new(&query.query); + let automaton = fst::automaton::Subsequence::new(&query.lowercased); let mut op = fst::map::OpBuilder::new(); for map in &import_maps { @@ -232,17 +245,27 @@ pub fn search_dependencies<'a>( if query.anchor_end { // Last segment must match query. let last = path.segments.last().unwrap().to_string(); - if last.to_lowercase() != query.query { + if last.to_lowercase() != query.lowercased { continue; } } // Add the items from this `ModPath` group. Those are all subsequent items in // `importables` whose paths match `path`. - res.extend(importables.iter().copied().take_while(|item| { + let iter = importables.iter().copied().take_while(|item| { let item_path = &import_map.map[item]; fst_path(item_path) == fst_path(path) - })); + }); + + if query.case_sensitive { + // FIXME: This does not do a subsequence match. + res.extend(iter.filter(|item| { + let item_path = &import_map.map[item]; + item_path.to_string().contains(&query.query) + })); + } else { + res.extend(iter); + } if res.len() >= query.limit { res.truncate(query.limit); @@ -582,6 +605,33 @@ mod tests { "###); } + #[test] + fn search_casing() { + let ra_fixture = r#" + //- /main.rs crate:main deps:dep + //- /dep.rs crate:dep + + pub struct fmt; + pub struct FMT; + "#; + + let res = search_dependencies_of(ra_fixture, "main", Query::new("FMT")); + + assert_snapshot!(res, @r###" + dep::FMT (v) + dep::FMT (t) + dep::fmt (t) + dep::fmt (v) + "###); + + let res = search_dependencies_of(ra_fixture, "main", Query::new("FMT").case_sensitive()); + + assert_snapshot!(res, @r###" + dep::FMT (v) + dep::FMT (t) + "###); + } + #[test] fn search_limit() { let res = search_dependencies_of( From dd22657407bb0ab24d141275fd4f0d87269262c8 Mon Sep 17 00:00:00 2001 From: Jonas Schievink Date: Wed, 10 Jun 2020 16:15:49 +0200 Subject: [PATCH 154/172] ImportMap: use IndexMap internally It iterates in insertion order, which makes the ordering more predictable. --- Cargo.lock | 1 + crates/ra_hir_def/Cargo.toml | 1 + crates/ra_hir_def/src/import_map.rs | 21 ++++++++++++--------- 3 files changed, 14 insertions(+), 9 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 4c55519681eb..e6338e3160b8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -982,6 +982,7 @@ dependencies = [ "drop_bomb", "either", "fst", + "indexmap", "insta", "itertools", "log", diff --git a/crates/ra_hir_def/Cargo.toml b/crates/ra_hir_def/Cargo.toml index bd69abfc752e..ef1f65ee0655 100644 --- a/crates/ra_hir_def/Cargo.toml +++ b/crates/ra_hir_def/Cargo.toml @@ -16,6 +16,7 @@ anymap = "0.12.1" drop_bomb = "0.1.4" fst = { version = "0.4", default-features = false } itertools = "0.9.0" +indexmap = "1.4.0" stdx = { path = "../stdx" } diff --git a/crates/ra_hir_def/src/import_map.rs b/crates/ra_hir_def/src/import_map.rs index a55d7d83b6b5..36b4fdd81606 100644 --- a/crates/ra_hir_def/src/import_map.rs +++ b/crates/ra_hir_def/src/import_map.rs @@ -1,10 +1,11 @@ //! A map of all publicly exported items in a crate. -use std::{cmp::Ordering, collections::hash_map::Entry, fmt, sync::Arc}; +use std::{cmp::Ordering, fmt, hash::BuildHasherDefault, sync::Arc}; use fst::{self, Streamer}; +use indexmap::{map::Entry, IndexMap}; use ra_db::CrateId; -use rustc_hash::FxHashMap; +use rustc_hash::FxHasher; use crate::{ db::DefDatabase, @@ -14,6 +15,8 @@ use crate::{ ModuleDefId, ModuleId, }; +type FxIndexMap = IndexMap>; + /// A map from publicly exported items to the path needed to import/name them from a downstream /// crate. /// @@ -23,7 +26,7 @@ use crate::{ /// Note that all paths are relative to the containing crate's root, so the crate name still needs /// to be prepended to the `ModPath` before the path is valid. pub struct ImportMap { - map: FxHashMap, + map: FxIndexMap, /// List of keys stored in `map`, sorted lexicographically by their `ModPath`. Indexed by the /// values returned by running `fst`. @@ -39,7 +42,7 @@ impl ImportMap { pub fn import_map_query(db: &dyn DefDatabase, krate: CrateId) -> Arc { let _p = ra_prof::profile("import_map_query"); let def_map = db.crate_def_map(krate); - let mut import_map = FxHashMap::with_capacity_and_hasher(64, Default::default()); + let mut import_map = FxIndexMap::with_capacity_and_hasher(64, Default::default()); // We look only into modules that are public(ly reexported), starting with the crate root. let empty = ModPath { kind: PathKind::Plain, segments: vec![] }; @@ -588,9 +591,9 @@ mod tests { let res = search_dependencies_of(ra_fixture, "main", Query::new("fmt")); assert_snapshot!(res, @r###" - dep::Fmt (v) dep::fmt (t) dep::Fmt (t) + dep::Fmt (v) dep::Fmt (m) dep::fmt::Display (t) dep::format (v) @@ -598,9 +601,9 @@ mod tests { let res = search_dependencies_of(ra_fixture, "main", Query::new("fmt").anchor_end()); assert_snapshot!(res, @r###" - dep::Fmt (v) dep::fmt (t) dep::Fmt (t) + dep::Fmt (v) dep::Fmt (m) "###); } @@ -618,17 +621,17 @@ mod tests { let res = search_dependencies_of(ra_fixture, "main", Query::new("FMT")); assert_snapshot!(res, @r###" - dep::FMT (v) - dep::FMT (t) dep::fmt (t) dep::fmt (v) + dep::FMT (t) + dep::FMT (v) "###); let res = search_dependencies_of(ra_fixture, "main", Query::new("FMT").case_sensitive()); assert_snapshot!(res, @r###" - dep::FMT (v) dep::FMT (t) + dep::FMT (v) "###); } From f280407d7350c2abc4cc35389d389acdc5d93e28 Mon Sep 17 00:00:00 2001 From: Jacek Generowicz Date: Wed, 10 Jun 2020 20:32:29 +0200 Subject: [PATCH 155/172] Fix parser test directory name in dev docs --- docs/dev/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/dev/README.md b/docs/dev/README.md index cf93135ee9b8..ef5ffbf597ae 100644 --- a/docs/dev/README.md +++ b/docs/dev/README.md @@ -308,7 +308,7 @@ The results are 100% Rust specific though. Test for parser (`ra_parser`) live in `ra_syntax` crate (see `test_data` direcotory). There are two kinds of tests: -* Manually written test cases in `parser/ok` and `parser/error` +* Manually written test cases in `parser/ok` and `parser/err` * "Inline" tests in `parser/inline` (these are generated) from comments in `ra_parser` crate. The purpose of inline tests is not to achieve full coverage by test cases, but to explain to the reader of the code what each particular `if` and `match` is responsible for. From d8a5d39c2d05fb59b6c243935111714e18334599 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Thu, 11 Jun 2020 11:30:06 +0200 Subject: [PATCH 156/172] Make relevant_crates return a Set --- Cargo.lock | 1 + crates/ra_db/src/input.rs | 8 +++----- crates/ra_db/src/lib.rs | 20 +++++++++++++------- crates/ra_hir_def/src/test_db.rs | 3 ++- crates/ra_hir_expand/Cargo.toml | 1 + crates/ra_hir_expand/src/builtin_derive.rs | 7 +++---- crates/ra_hir_expand/src/builtin_macro.rs | 5 ++--- crates/ra_hir_expand/src/lib.rs | 5 +++++ crates/ra_hir_expand/src/test_db.rs | 3 ++- crates/ra_hir_ty/src/test_db.rs | 3 ++- crates/ra_ide_db/src/lib.rs | 4 ++-- 11 files changed, 36 insertions(+), 24 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index df79334c96c9..22483516ad3b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1010,6 +1010,7 @@ dependencies = [ "ra_prof", "ra_syntax", "ra_tt", + "rustc-hash", "test_utils", ] diff --git a/crates/ra_db/src/input.rs b/crates/ra_db/src/input.rs index a8d6466eac15..bf26048f2adb 100644 --- a/crates/ra_db/src/input.rs +++ b/crates/ra_db/src/input.rs @@ -15,12 +15,10 @@ use std::{ use ra_cfg::CfgOptions; use ra_syntax::SmolStr; -use rustc_hash::FxHashMap; -use rustc_hash::FxHashSet; +use ra_tt::TokenExpander; +use rustc_hash::{FxHashMap, FxHashSet}; use crate::{RelativePath, RelativePathBuf}; -use fmt::Display; -use ra_tt::TokenExpander; /// `FileId` is an integer which uniquely identifies a file. File paths are /// messy and system-dependent, so most of the code should work directly with @@ -111,7 +109,7 @@ impl CrateName { } } -impl Display for CrateName { +impl fmt::Display for CrateName { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{}", self.0) } diff --git a/crates/ra_db/src/lib.rs b/crates/ra_db/src/lib.rs index 2ab3148840fa..80ddb6058afb 100644 --- a/crates/ra_db/src/lib.rs +++ b/crates/ra_db/src/lib.rs @@ -7,6 +7,7 @@ use std::{panic, sync::Arc}; use ra_prof::profile; use ra_syntax::{ast, Parse, SourceFile, TextRange, TextSize}; +use rustc_hash::FxHashSet; pub use crate::{ cancellation::Canceled, @@ -95,7 +96,7 @@ pub trait FileLoader { /// `struct StrPath(str)` for clarity some day, but it's a bit messy, so we /// get by with a `&str` for the time being. fn resolve_path(&self, anchor: FileId, path: &str) -> Option; - fn relevant_crates(&self, file_id: FileId) -> Arc>; + fn relevant_crates(&self, file_id: FileId) -> Arc>; } /// Database which stores all significant input facts: source code and project @@ -133,16 +134,21 @@ pub trait SourceDatabaseExt: SourceDatabase { #[salsa::input] fn source_root(&self, id: SourceRootId) -> Arc; - fn source_root_crates(&self, id: SourceRootId) -> Arc>; + fn source_root_crates(&self, id: SourceRootId) -> Arc>; } fn source_root_crates( db: &(impl SourceDatabaseExt + SourceDatabase), id: SourceRootId, -) -> Arc> { - let root = db.source_root(id); +) -> Arc> { let graph = db.crate_graph(); - let res = root.walk().filter_map(|it| graph.crate_id_for_crate_root(it)).collect::>(); + let res = graph + .iter() + .filter(|&krate| { + let root_file = graph[krate].root_file_id; + db.file_source_root(root_file) == id + }) + .collect::>(); Arc::new(res) } @@ -156,7 +162,7 @@ impl FileLoader for FileLoaderDelegate<&'_ T> { fn resolve_path(&self, anchor: FileId, path: &str) -> Option { // FIXME: this *somehow* should be platform agnostic... if std::path::Path::new(path).is_absolute() { - let krate = *self.relevant_crates(anchor).get(0)?; + let krate = *self.relevant_crates(anchor).iter().next()?; let (extern_source_id, relative_file) = self.0.crate_graph()[krate].extern_source.extern_path(path.as_ref())?; @@ -175,7 +181,7 @@ impl FileLoader for FileLoaderDelegate<&'_ T> { } } - fn relevant_crates(&self, file_id: FileId) -> Arc> { + fn relevant_crates(&self, file_id: FileId) -> Arc> { let source_root = self.0.file_source_root(file_id); self.0.source_root_crates(source_root) } diff --git a/crates/ra_hir_def/src/test_db.rs b/crates/ra_hir_def/src/test_db.rs index bcfa66ac953c..4581d87453b2 100644 --- a/crates/ra_hir_def/src/test_db.rs +++ b/crates/ra_hir_def/src/test_db.rs @@ -7,6 +7,7 @@ use std::{ use hir_expand::db::AstDatabase; use ra_db::{salsa, CrateId, FileId, FileLoader, FileLoaderDelegate, Upcast}; +use rustc_hash::FxHashSet; use crate::db::DefDatabase; @@ -59,7 +60,7 @@ impl FileLoader for TestDB { fn resolve_path(&self, anchor: FileId, path: &str) -> Option { FileLoaderDelegate(self).resolve_path(anchor, path) } - fn relevant_crates(&self, file_id: FileId) -> Arc> { + fn relevant_crates(&self, file_id: FileId) -> Arc> { FileLoaderDelegate(self).relevant_crates(file_id) } } diff --git a/crates/ra_hir_expand/Cargo.toml b/crates/ra_hir_expand/Cargo.toml index 2cd522766066..e5c9f3e997a7 100644 --- a/crates/ra_hir_expand/Cargo.toml +++ b/crates/ra_hir_expand/Cargo.toml @@ -10,6 +10,7 @@ doctest = false [dependencies] log = "0.4.8" either = "1.5.3" +rustc-hash = "1.0.0" ra_arena = { path = "../ra_arena" } ra_db = { path = "../ra_db" } diff --git a/crates/ra_hir_expand/src/builtin_derive.rs b/crates/ra_hir_expand/src/builtin_derive.rs index 1dc9cac6651e..967d1f3a16ee 100644 --- a/crates/ra_hir_expand/src/builtin_derive.rs +++ b/crates/ra_hir_expand/src/builtin_derive.rs @@ -9,7 +9,7 @@ use ra_syntax::{ }; use crate::db::AstDatabase; -use crate::{name, quote, LazyMacroId, MacroCallId, MacroDefId, MacroDefKind}; +use crate::{guess_crate, name, quote, LazyMacroId, MacroCallId, MacroDefId, MacroDefKind}; macro_rules! register_builtin { ( $($trait:ident => $expand:ident),* ) => { @@ -160,8 +160,7 @@ fn find_builtin_crate(db: &dyn AstDatabase, id: LazyMacroId) -> tt::TokenTree { let m: MacroCallId = id.into(); let file_id = m.as_file().original_file(db); let cg = db.crate_graph(); - let krates = db.relevant_crates(file_id); - let krate = match krates.get(0) { + let krate = match guess_crate(db, file_id) { Some(krate) => krate, None => { let tt = quote! { core }; @@ -172,7 +171,7 @@ fn find_builtin_crate(db: &dyn AstDatabase, id: LazyMacroId) -> tt::TokenTree { // XXX // All crates except core itself should have a dependency on core, // We detect `core` by seeing whether it doesn't have such a dependency. - let tt = if cg[*krate].dependencies.iter().any(|dep| dep.name == "core") { + let tt = if cg[krate].dependencies.iter().any(|dep| dep.name == "core") { quote! { core } } else { quote! { crate } diff --git a/crates/ra_hir_expand/src/builtin_macro.rs b/crates/ra_hir_expand/src/builtin_macro.rs index 7579546d2f3f..93da3f149d27 100644 --- a/crates/ra_hir_expand/src/builtin_macro.rs +++ b/crates/ra_hir_expand/src/builtin_macro.rs @@ -5,7 +5,7 @@ use crate::{ name, AstId, CrateId, MacroDefId, MacroDefKind, TextSize, }; -use crate::{quote, EagerMacroId, LazyMacroId, MacroCallId}; +use crate::{guess_crate, quote, EagerMacroId, LazyMacroId, MacroCallId}; use either::Either; use mbe::parse_to_token_tree; use ra_db::FileId; @@ -335,8 +335,7 @@ fn include_expand( fn get_env_inner(db: &dyn AstDatabase, arg_id: EagerMacroId, key: &str) -> Option { let call_id: MacroCallId = arg_id.into(); let original_file = call_id.as_file().original_file(db); - - let krate = *db.relevant_crates(original_file).get(0)?; + let krate = guess_crate(db, original_file)?; db.crate_graph()[krate].env.get(key) } diff --git a/crates/ra_hir_expand/src/lib.rs b/crates/ra_hir_expand/src/lib.rs index f440c073ba8a..dc4d7f000e1e 100644 --- a/crates/ra_hir_expand/src/lib.rs +++ b/crates/ra_hir_expand/src/lib.rs @@ -424,3 +424,8 @@ impl InFile { self.with_value(self.value.syntax()) } } + +// FIXME: this is obviously wrong, there shouldn't be any guesing here +fn guess_crate(db: &dyn db::AstDatabase, file_id: FileId) -> Option { + db.relevant_crates(file_id).iter().next().copied() +} diff --git a/crates/ra_hir_expand/src/test_db.rs b/crates/ra_hir_expand/src/test_db.rs index fdf225f55be9..09fc18c360f5 100644 --- a/crates/ra_hir_expand/src/test_db.rs +++ b/crates/ra_hir_expand/src/test_db.rs @@ -6,6 +6,7 @@ use std::{ }; use ra_db::{salsa, CrateId, FileId, FileLoader, FileLoaderDelegate}; +use rustc_hash::FxHashSet; #[salsa::database( ra_db::SourceDatabaseExtStorage, @@ -44,7 +45,7 @@ impl FileLoader for TestDB { fn resolve_path(&self, anchor: FileId, path: &str) -> Option { FileLoaderDelegate(self).resolve_path(anchor, path) } - fn relevant_crates(&self, file_id: FileId) -> Arc> { + fn relevant_crates(&self, file_id: FileId) -> Arc> { FileLoaderDelegate(self).relevant_crates(file_id) } } diff --git a/crates/ra_hir_ty/src/test_db.rs b/crates/ra_hir_ty/src/test_db.rs index e484968a099c..ad04e3e0f908 100644 --- a/crates/ra_hir_ty/src/test_db.rs +++ b/crates/ra_hir_ty/src/test_db.rs @@ -8,6 +8,7 @@ use std::{ use hir_def::{db::DefDatabase, AssocItemId, ModuleDefId, ModuleId}; use hir_expand::{db::AstDatabase, diagnostics::DiagnosticSink}; use ra_db::{salsa, CrateId, FileId, FileLoader, FileLoaderDelegate, SourceDatabase, Upcast}; +use rustc_hash::FxHashSet; use stdx::format_to; use crate::{db::HirDatabase, diagnostics::Diagnostic, expr::ExprValidator}; @@ -73,7 +74,7 @@ impl FileLoader for TestDB { fn resolve_path(&self, anchor: FileId, path: &str) -> Option { FileLoaderDelegate(self).resolve_path(anchor, path) } - fn relevant_crates(&self, file_id: FileId) -> Arc> { + fn relevant_crates(&self, file_id: FileId) -> Arc> { FileLoaderDelegate(self).relevant_crates(file_id) } } diff --git a/crates/ra_ide_db/src/lib.rs b/crates/ra_ide_db/src/lib.rs index 727d743b5fda..480fd45764e9 100644 --- a/crates/ra_ide_db/src/lib.rs +++ b/crates/ra_ide_db/src/lib.rs @@ -19,7 +19,7 @@ use ra_db::{ Canceled, CheckCanceled, CrateId, FileId, FileLoader, FileLoaderDelegate, SourceDatabase, SourceRootId, Upcast, }; -use rustc_hash::FxHashMap; +use rustc_hash::{FxHashMap, FxHashSet}; use crate::{line_index::LineIndex, symbol_index::SymbolsDatabase}; @@ -60,7 +60,7 @@ impl FileLoader for RootDatabase { fn resolve_path(&self, anchor: FileId, path: &str) -> Option { FileLoaderDelegate(self).resolve_path(anchor, path) } - fn relevant_crates(&self, file_id: FileId) -> Arc> { + fn relevant_crates(&self, file_id: FileId) -> Arc> { FileLoaderDelegate(self).relevant_crates(file_id) } } From 6766a6b0e189f47d7a405c872598bca9a2395360 Mon Sep 17 00:00:00 2001 From: Jonas Schievink Date: Thu, 11 Jun 2020 12:03:08 +0200 Subject: [PATCH 157/172] Add symbol index FIXME --- crates/ra_ide_db/src/symbol_index.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/crates/ra_ide_db/src/symbol_index.rs b/crates/ra_ide_db/src/symbol_index.rs index ac0a201df315..aab91897320a 100644 --- a/crates/ra_ide_db/src/symbol_index.rs +++ b/crates/ra_ide_db/src/symbol_index.rs @@ -177,6 +177,9 @@ pub fn world_symbols(db: &RootDatabase, query: Query) -> Vec { } pub fn crate_symbols(db: &RootDatabase, krate: CrateId, query: Query) -> Vec { + // FIXME(#4842): This now depends on CrateDefMap, why not build the entire symbol index from + // that instead? + let def_map = db.crate_def_map(krate); let mut files = Vec::new(); let mut modules = vec![def_map.root]; From fac7b0e252ab305f5c8d69b04c46c587ee021aa9 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Thu, 11 Jun 2020 12:08:24 +0200 Subject: [PATCH 158/172] Don't guess macro expansion crate --- crates/ra_hir/src/semantics.rs | 3 +- crates/ra_hir/src/source_analyzer.rs | 3 +- crates/ra_hir_def/src/body.rs | 2 +- crates/ra_hir_def/src/lib.rs | 11 ++++-- crates/ra_hir_def/src/nameres/collector.rs | 39 ++++++++++++---------- crates/ra_hir_expand/src/builtin_derive.rs | 28 +++++++--------- crates/ra_hir_expand/src/builtin_macro.rs | 17 +++++----- crates/ra_hir_expand/src/eager.rs | 32 +++++++++++++----- crates/ra_hir_expand/src/lib.rs | 16 +++++---- 9 files changed, 89 insertions(+), 62 deletions(-) diff --git a/crates/ra_hir/src/semantics.rs b/crates/ra_hir/src/semantics.rs index 7c1f79f279b6..a232a58567ce 100644 --- a/crates/ra_hir/src/semantics.rs +++ b/crates/ra_hir/src/semantics.rs @@ -122,8 +122,9 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> { let macro_call = self.find_file(actual_macro_call.syntax().clone()).with_value(actual_macro_call); let sa = self.analyze2(macro_call.map(|it| it.syntax()), None); + let krate = sa.resolver.krate()?; let macro_call_id = macro_call - .as_call_id(self.db, |path| sa.resolver.resolve_path_as_macro(self.db, &path))?; + .as_call_id(self.db, krate, |path| sa.resolver.resolve_path_as_macro(self.db, &path))?; hir_expand::db::expand_hypothetical(self.db, macro_call_id, hypothetical_args, token_to_map) } diff --git a/crates/ra_hir/src/source_analyzer.rs b/crates/ra_hir/src/source_analyzer.rs index 4b509f07c6b7..7c6bbea13c29 100644 --- a/crates/ra_hir/src/source_analyzer.rs +++ b/crates/ra_hir/src/source_analyzer.rs @@ -307,7 +307,8 @@ impl SourceAnalyzer { db: &dyn HirDatabase, macro_call: InFile<&ast::MacroCall>, ) -> Option { - let macro_call_id = macro_call.as_call_id(db.upcast(), |path| { + let krate = self.resolver.krate()?; + let macro_call_id = macro_call.as_call_id(db.upcast(), krate, |path| { self.resolver.resolve_path_as_macro(db.upcast(), &path) })?; Some(macro_call_id.as_file()) diff --git a/crates/ra_hir_def/src/body.rs b/crates/ra_hir_def/src/body.rs index 273036cee1cf..4f2350915dcb 100644 --- a/crates/ra_hir_def/src/body.rs +++ b/crates/ra_hir_def/src/body.rs @@ -97,7 +97,7 @@ impl Expander { let macro_call = InFile::new(self.current_file_id, ¯o_call); - if let Some(call_id) = macro_call.as_call_id(db, |path| { + if let Some(call_id) = macro_call.as_call_id(db, self.crate_def_map.krate, |path| { if let Some(local_scope) = local_scope { if let Some(def) = path.as_ident().and_then(|n| local_scope.get_legacy_macro(n)) { return Some(def); diff --git a/crates/ra_hir_def/src/lib.rs b/crates/ra_hir_def/src/lib.rs index de490fcc58af..edc59e5a80f4 100644 --- a/crates/ra_hir_def/src/lib.rs +++ b/crates/ra_hir_def/src/lib.rs @@ -417,6 +417,7 @@ pub trait AsMacroCall { fn as_call_id( &self, db: &dyn db::DefDatabase, + krate: CrateId, resolver: impl Fn(path::ModPath) -> Option, ) -> Option; } @@ -425,13 +426,14 @@ impl AsMacroCall for InFile<&ast::MacroCall> { fn as_call_id( &self, db: &dyn db::DefDatabase, + krate: CrateId, resolver: impl Fn(path::ModPath) -> Option, ) -> Option { let ast_id = AstId::new(self.file_id, db.ast_id_map(self.file_id).ast_id(self.value)); let h = Hygiene::new(db.upcast(), self.file_id); let path = path::ModPath::from_src(self.value.path()?, &h)?; - AstIdWithPath::new(ast_id.file_id, ast_id.value, path).as_call_id(db, resolver) + AstIdWithPath::new(ast_id.file_id, ast_id.value, path).as_call_id(db, krate, resolver) } } @@ -452,6 +454,7 @@ impl AsMacroCall for AstIdWithPath { fn as_call_id( &self, db: &dyn db::DefDatabase, + krate: CrateId, resolver: impl Fn(path::ModPath) -> Option, ) -> Option { let def: MacroDefId = resolver(self.path.clone())?; @@ -461,13 +464,13 @@ impl AsMacroCall for AstIdWithPath { let hygiene = Hygiene::new(db.upcast(), self.ast_id.file_id); Some( - expand_eager_macro(db.upcast(), macro_call, def, &|path: ast::Path| { + expand_eager_macro(db.upcast(), krate, macro_call, def, &|path: ast::Path| { resolver(path::ModPath::from_src(path, &hygiene)?) })? .into(), ) } else { - Some(def.as_lazy_macro(db.upcast(), MacroCallKind::FnLike(self.ast_id)).into()) + Some(def.as_lazy_macro(db.upcast(), krate, MacroCallKind::FnLike(self.ast_id)).into()) } } } @@ -476,12 +479,14 @@ impl AsMacroCall for AstIdWithPath { fn as_call_id( &self, db: &dyn db::DefDatabase, + krate: CrateId, resolver: impl Fn(path::ModPath) -> Option, ) -> Option { let def = resolver(self.path.clone())?; Some( def.as_lazy_macro( db.upcast(), + krate, MacroCallKind::Attr(self.ast_id, self.path.segments.last()?.to_string()), ) .into(), diff --git a/crates/ra_hir_def/src/nameres/collector.rs b/crates/ra_hir_def/src/nameres/collector.rs index 353a31ad47a1..976e5e5850f1 100644 --- a/crates/ra_hir_def/src/nameres/collector.rs +++ b/crates/ra_hir_def/src/nameres/collector.rs @@ -571,16 +571,18 @@ impl DefCollector<'_> { return false; } - if let Some(call_id) = directive.ast_id.as_call_id(self.db, |path| { - let resolved_res = self.def_map.resolve_path_fp_with_macro( - self.db, - ResolveMode::Other, - directive.module_id, - &path, - BuiltinShadowMode::Module, - ); - resolved_res.resolved_def.take_macros() - }) { + if let Some(call_id) = + directive.ast_id.as_call_id(self.db, self.def_map.krate, |path| { + let resolved_res = self.def_map.resolve_path_fp_with_macro( + self.db, + ResolveMode::Other, + directive.module_id, + &path, + BuiltinShadowMode::Module, + ); + resolved_res.resolved_def.take_macros() + }) + { resolved.push((directive.module_id, call_id, directive.depth)); res = ReachedFixedPoint::No; return false; @@ -589,9 +591,10 @@ impl DefCollector<'_> { true }); attribute_macros.retain(|directive| { - if let Some(call_id) = directive - .ast_id - .as_call_id(self.db, |path| self.resolve_attribute_macro(&directive, &path)) + if let Some(call_id) = + directive.ast_id.as_call_id(self.db, self.def_map.krate, |path| { + self.resolve_attribute_macro(&directive, &path) + }) { resolved.push((directive.module_id, call_id, 0)); res = ReachedFixedPoint::No; @@ -957,11 +960,13 @@ impl ModCollector<'_, '_> { } // Case 2: try to resolve in legacy scope and expand macro_rules - if let Some(macro_call_id) = ast_id.as_call_id(self.def_collector.db, |path| { - path.as_ident().and_then(|name| { - self.def_collector.def_map[self.module_id].scope.get_legacy_macro(&name) + if let Some(macro_call_id) = + ast_id.as_call_id(self.def_collector.db, self.def_collector.def_map.krate, |path| { + path.as_ident().and_then(|name| { + self.def_collector.def_map[self.module_id].scope.get_legacy_macro(&name) + }) }) - }) { + { self.def_collector.unexpanded_macros.push(MacroDirective { module_id: self.module_id, ast_id, diff --git a/crates/ra_hir_expand/src/builtin_derive.rs b/crates/ra_hir_expand/src/builtin_derive.rs index 967d1f3a16ee..26b667b55715 100644 --- a/crates/ra_hir_expand/src/builtin_derive.rs +++ b/crates/ra_hir_expand/src/builtin_derive.rs @@ -8,8 +8,7 @@ use ra_syntax::{ match_ast, }; -use crate::db::AstDatabase; -use crate::{guess_crate, name, quote, LazyMacroId, MacroCallId, MacroDefId, MacroDefKind}; +use crate::{db::AstDatabase, name, quote, LazyMacroId, MacroDefId, MacroDefKind}; macro_rules! register_builtin { ( $($trait:ident => $expand:ident),* ) => { @@ -156,17 +155,8 @@ fn expand_simple_derive( fn find_builtin_crate(db: &dyn AstDatabase, id: LazyMacroId) -> tt::TokenTree { // FIXME: make hygiene works for builtin derive macro // such that $crate can be used here. - - let m: MacroCallId = id.into(); - let file_id = m.as_file().original_file(db); let cg = db.crate_graph(); - let krate = match guess_crate(db, file_id) { - Some(krate) => krate, - None => { - let tt = quote! { core }; - return tt.token_trees[0].clone(); - } - }; + let krate = db.lookup_intern_macro(id).krate; // XXX // All crates except core itself should have a dependency on core, @@ -263,10 +253,12 @@ fn partial_ord_expand( #[cfg(test)] mod tests { - use super::*; - use crate::{test_db::TestDB, AstId, MacroCallId, MacroCallKind, MacroCallLoc}; use name::{known, Name}; - use ra_db::{fixture::WithFixture, SourceDatabase}; + use ra_db::{fixture::WithFixture, CrateId, SourceDatabase}; + + use crate::{test_db::TestDB, AstId, MacroCallId, MacroCallKind, MacroCallLoc}; + + use super::*; fn expand_builtin_derive(s: &str, name: Name) -> String { let def = find_builtin_derive(&name).unwrap(); @@ -290,7 +282,11 @@ mod tests { let attr_id = AstId::new(file_id.into(), ast_id_map.ast_id(&items[0])); - let loc = MacroCallLoc { def, kind: MacroCallKind::Attr(attr_id, name.to_string()) }; + let loc = MacroCallLoc { + def, + krate: CrateId(0), + kind: MacroCallKind::Attr(attr_id, name.to_string()), + }; let id: MacroCallId = db.intern_macro(loc).into(); let parsed = db.parse_or_expand(id.as_file()).unwrap(); diff --git a/crates/ra_hir_expand/src/builtin_macro.rs b/crates/ra_hir_expand/src/builtin_macro.rs index 93da3f149d27..b50eb347c8e4 100644 --- a/crates/ra_hir_expand/src/builtin_macro.rs +++ b/crates/ra_hir_expand/src/builtin_macro.rs @@ -1,15 +1,14 @@ //! Builtin macro -use crate::db::AstDatabase; use crate::{ - ast::{self, AstToken, HasStringValue}, - name, AstId, CrateId, MacroDefId, MacroDefKind, TextSize, + db::AstDatabase, name, quote, AstId, CrateId, EagerMacroId, LazyMacroId, MacroCallId, + MacroDefId, MacroDefKind, TextSize, }; -use crate::{guess_crate, quote, EagerMacroId, LazyMacroId, MacroCallId}; use either::Either; use mbe::parse_to_token_tree; use ra_db::FileId; use ra_parser::FragmentKind; +use ra_syntax::ast::{self, AstToken, HasStringValue}; macro_rules! register_builtin { ( LAZY: $(($name:ident, $kind: ident) => $expand:ident),* , EAGER: $(($e_name:ident, $e_kind: ident) => $e_expand:ident),* ) => { @@ -333,9 +332,7 @@ fn include_expand( } fn get_env_inner(db: &dyn AstDatabase, arg_id: EagerMacroId, key: &str) -> Option { - let call_id: MacroCallId = arg_id.into(); - let original_file = call_id.as_file().original_file(db); - let krate = guess_crate(db, original_file)?; + let krate = db.lookup_intern_eager_expansion(arg_id).krate; db.crate_graph()[krate].env.get(key) } @@ -394,6 +391,7 @@ mod tests { let expander = find_by_name(¯o_calls[0].name().unwrap().as_name()).unwrap(); + let krate = CrateId(0); let file_id = match expander { Either::Left(expander) => { // the first one should be a macro_rules @@ -406,6 +404,7 @@ mod tests { let loc = MacroCallLoc { def, + krate, kind: MacroCallKind::FnLike(AstId::new( file_id.into(), ast_id_map.ast_id(¯o_calls[1]), @@ -418,7 +417,7 @@ mod tests { Either::Right(expander) => { // the first one should be a macro_rules let def = MacroDefId { - krate: Some(CrateId(0)), + krate: Some(krate), ast_id: Some(AstId::new(file_id.into(), ast_id_map.ast_id(¯o_calls[0]))), kind: MacroDefKind::BuiltInEager(expander), local_inner: false, @@ -432,6 +431,7 @@ mod tests { def, fragment: FragmentKind::Expr, subtree: Arc::new(parsed_args.clone()), + krate, file_id: file_id.into(), } }); @@ -441,6 +441,7 @@ mod tests { def, fragment, subtree: Arc::new(subtree), + krate, file_id: file_id.into(), }; diff --git a/crates/ra_hir_expand/src/eager.rs b/crates/ra_hir_expand/src/eager.rs index 932f47c30989..302d2b3e099c 100644 --- a/crates/ra_hir_expand/src/eager.rs +++ b/crates/ra_hir_expand/src/eager.rs @@ -25,12 +25,14 @@ use crate::{ EagerCallLoc, EagerMacroId, InFile, MacroCallId, MacroCallKind, MacroDefId, MacroDefKind, }; +use ra_db::CrateId; use ra_parser::FragmentKind; use ra_syntax::{algo::SyntaxRewriter, SyntaxNode}; use std::sync::Arc; pub fn expand_eager_macro( db: &dyn AstDatabase, + krate: CrateId, macro_call: InFile, def: MacroDefId, resolver: &dyn Fn(ast::Path) -> Option, @@ -47,6 +49,7 @@ pub fn expand_eager_macro( def, fragment: FragmentKind::Expr, subtree: Arc::new(parsed_args.clone()), + krate, file_id: macro_call.file_id, } }); @@ -56,14 +59,20 @@ pub fn expand_eager_macro( let result = eager_macro_recur( db, InFile::new(arg_file_id.as_file(), parsed_args.syntax_node()), + krate, resolver, )?; let subtree = to_subtree(&result)?; if let MacroDefKind::BuiltInEager(eager) = def.kind { let (subtree, fragment) = eager.expand(db, arg_id, &subtree).ok()?; - let eager = - EagerCallLoc { def, fragment, subtree: Arc::new(subtree), file_id: macro_call.file_id }; + let eager = EagerCallLoc { + def, + fragment, + subtree: Arc::new(subtree), + krate, + file_id: macro_call.file_id, + }; Some(db.intern_eager_expansion(eager)) } else { @@ -81,11 +90,12 @@ fn lazy_expand( db: &dyn AstDatabase, def: &MacroDefId, macro_call: InFile, + krate: CrateId, ) -> Option> { let ast_id = db.ast_id_map(macro_call.file_id).ast_id(¯o_call.value); let id: MacroCallId = - def.as_lazy_macro(db, MacroCallKind::FnLike(macro_call.with_value(ast_id))).into(); + def.as_lazy_macro(db, krate, MacroCallKind::FnLike(macro_call.with_value(ast_id))).into(); db.parse_or_expand(id.as_file()).map(|node| InFile::new(id.as_file(), node)) } @@ -93,6 +103,7 @@ fn lazy_expand( fn eager_macro_recur( db: &dyn AstDatabase, curr: InFile, + krate: CrateId, macro_resolver: &dyn Fn(ast::Path) -> Option, ) -> Option { let original = curr.value.clone(); @@ -105,18 +116,23 @@ fn eager_macro_recur( let def: MacroDefId = macro_resolver(child.path()?)?; let insert = match def.kind { MacroDefKind::BuiltInEager(_) => { - let id: MacroCallId = - expand_eager_macro(db, curr.with_value(child.clone()), def, macro_resolver)? - .into(); + let id: MacroCallId = expand_eager_macro( + db, + krate, + curr.with_value(child.clone()), + def, + macro_resolver, + )? + .into(); db.parse_or_expand(id.as_file())? } MacroDefKind::Declarative | MacroDefKind::BuiltIn(_) | MacroDefKind::BuiltInDerive(_) | MacroDefKind::CustomDerive(_) => { - let expanded = lazy_expand(db, &def, curr.with_value(child.clone()))?; + let expanded = lazy_expand(db, &def, curr.with_value(child.clone()), krate)?; // replace macro inside - eager_macro_recur(db, expanded, macro_resolver)? + eager_macro_recur(db, expanded, krate, macro_resolver)? } }; diff --git a/crates/ra_hir_expand/src/lib.rs b/crates/ra_hir_expand/src/lib.rs index dc4d7f000e1e..5eac2605b9c1 100644 --- a/crates/ra_hir_expand/src/lib.rs +++ b/crates/ra_hir_expand/src/lib.rs @@ -209,8 +209,13 @@ pub struct MacroDefId { } impl MacroDefId { - pub fn as_lazy_macro(self, db: &dyn db::AstDatabase, kind: MacroCallKind) -> LazyMacroId { - db.intern_macro(MacroCallLoc { def: self, kind }) + pub fn as_lazy_macro( + self, + db: &dyn db::AstDatabase, + krate: CrateId, + kind: MacroCallKind, + ) -> LazyMacroId { + db.intern_macro(MacroCallLoc { def: self, krate, kind }) } } @@ -227,6 +232,7 @@ pub enum MacroDefKind { #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct MacroCallLoc { pub(crate) def: MacroDefId, + pub(crate) krate: CrateId, pub(crate) kind: MacroCallKind, } @@ -274,6 +280,7 @@ pub struct EagerCallLoc { pub(crate) def: MacroDefId, pub(crate) fragment: FragmentKind, pub(crate) subtree: Arc, + pub(crate) krate: CrateId, pub(crate) file_id: HirFileId, } @@ -424,8 +431,3 @@ impl InFile { self.with_value(self.value.syntax()) } } - -// FIXME: this is obviously wrong, there shouldn't be any guesing here -fn guess_crate(db: &dyn db::AstDatabase, file_id: FileId) -> Option { - db.relevant_crates(file_id).iter().next().copied() -} From 663ce0e99d8aa11707dd2ca22956552859b6ea12 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Thu, 11 Jun 2020 13:26:48 +0200 Subject: [PATCH 159/172] Remove dead code --- crates/ra_ide_db/src/change.rs | 9 +-------- crates/ra_ide_db/src/lib.rs | 18 ++---------------- crates/rust-analyzer/src/cli/load_cargo.rs | 4 ---- crates/rust-analyzer/src/global_state.rs | 1 - 4 files changed, 3 insertions(+), 29 deletions(-) diff --git a/crates/ra_ide_db/src/change.rs b/crates/ra_ide_db/src/change.rs index 5dbe1c1b7e0f..2fc796a85922 100644 --- a/crates/ra_ide_db/src/change.rs +++ b/crates/ra_ide_db/src/change.rs @@ -16,7 +16,7 @@ use rustc_hash::FxHashMap; use crate::{ symbol_index::{SymbolIndex, SymbolsDatabase}, - DebugData, RootDatabase, + RootDatabase, }; #[derive(Default)] @@ -26,7 +26,6 @@ pub struct AnalysisChange { files_changed: Vec<(FileId, Arc)>, libraries_added: Vec, crate_graph: Option, - debug_data: DebugData, } impl fmt::Debug for AnalysisChange { @@ -87,10 +86,6 @@ impl AnalysisChange { pub fn set_crate_graph(&mut self, graph: CrateGraph) { self.crate_graph = Some(graph); } - - pub fn set_debug_root_path(&mut self, source_root_id: SourceRootId, path: String) { - self.debug_data.root_paths.insert(source_root_id, path); - } } #[derive(Debug)] @@ -218,8 +213,6 @@ impl RootDatabase { if let Some(crate_graph) = change.crate_graph { self.set_crate_graph_with_durability(Arc::new(crate_graph), Durability::HIGH) } - - Arc::make_mut(&mut self.debug_data).merge(change.debug_data) } fn apply_root_change(&mut self, root_id: SourceRootId, root_change: RootChange) { diff --git a/crates/ra_ide_db/src/lib.rs b/crates/ra_ide_db/src/lib.rs index 480fd45764e9..a808de4f1135 100644 --- a/crates/ra_ide_db/src/lib.rs +++ b/crates/ra_ide_db/src/lib.rs @@ -17,9 +17,9 @@ use hir::db::{AstDatabase, DefDatabase}; use ra_db::{ salsa::{self, Database, Durability}, Canceled, CheckCanceled, CrateId, FileId, FileLoader, FileLoaderDelegate, SourceDatabase, - SourceRootId, Upcast, + Upcast, }; -use rustc_hash::{FxHashMap, FxHashSet}; +use rustc_hash::FxHashSet; use crate::{line_index::LineIndex, symbol_index::SymbolsDatabase}; @@ -36,7 +36,6 @@ use crate::{line_index::LineIndex, symbol_index::SymbolsDatabase}; #[derive(Debug)] pub struct RootDatabase { runtime: salsa::Runtime, - pub(crate) debug_data: Arc, pub last_gc: crate::wasm_shims::Instant, pub last_gc_check: crate::wasm_shims::Instant, } @@ -98,7 +97,6 @@ impl RootDatabase { runtime: salsa::Runtime::default(), last_gc: crate::wasm_shims::Instant::now(), last_gc_check: crate::wasm_shims::Instant::now(), - debug_data: Default::default(), }; db.set_crate_graph_with_durability(Default::default(), Durability::HIGH); db.set_local_roots_with_durability(Default::default(), Durability::HIGH); @@ -121,7 +119,6 @@ impl salsa::ParallelDatabase for RootDatabase { runtime: self.runtime.snapshot(self), last_gc: self.last_gc, last_gc_check: self.last_gc_check, - debug_data: Arc::clone(&self.debug_data), }) } } @@ -135,14 +132,3 @@ fn line_index(db: &impl LineIndexDatabase, file_id: FileId) -> Arc { let text = db.file_text(file_id); Arc::new(LineIndex::new(&*text)) } - -#[derive(Debug, Default, Clone)] -pub(crate) struct DebugData { - pub(crate) root_paths: FxHashMap, -} - -impl DebugData { - pub(crate) fn merge(&mut self, other: DebugData) { - self.root_paths.extend(other.root_paths.into_iter()); - } -} diff --git a/crates/rust-analyzer/src/cli/load_cargo.rs b/crates/rust-analyzer/src/cli/load_cargo.rs index 45af963179b3..97367d7c60e9 100644 --- a/crates/rust-analyzer/src/cli/load_cargo.rs +++ b/crates/rust-analyzer/src/cli/load_cargo.rs @@ -111,10 +111,6 @@ pub(crate) fn load( vfs.root2path(root) ); analysis_change.add_root(source_root_id, is_local); - analysis_change.set_debug_root_path( - source_root_id, - source_roots[&source_root_id].path().display().to_string(), - ); let vfs_root_path = vfs.root2path(root); if extern_dirs.contains(&vfs_root_path) { diff --git a/crates/rust-analyzer/src/global_state.rs b/crates/rust-analyzer/src/global_state.rs index 96d91b12d924..21116e165e72 100644 --- a/crates/rust-analyzer/src/global_state.rs +++ b/crates/rust-analyzer/src/global_state.rs @@ -126,7 +126,6 @@ impl GlobalState { let vfs_root_path = vfs.root2path(r); let is_local = local_roots.iter().any(|it| vfs_root_path.starts_with(it)); change.add_root(SourceRootId(r.0), is_local); - change.set_debug_root_path(SourceRootId(r.0), vfs_root_path.display().to_string()); // FIXME: add path2root in vfs to simpily this logic if extern_dirs.contains(&vfs_root_path) { From b2aefc2e025c54c3dc56e1a8003a6bb366da5f4b Mon Sep 17 00:00:00 2001 From: kjeremy Date: Thu, 11 Jun 2020 08:43:35 -0400 Subject: [PATCH 160/172] Updated yanked crate --- Cargo.lock | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 2cb34fa207ad..329e0dd7c67e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -247,12 +247,13 @@ dependencies = [ [[package]] name = "crossbeam-queue" -version = "0.2.2" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab6bffe714b6bb07e42f201352c34f51fefd355ace793f9e638ebd52d23f98d2" +checksum = "774ba60a54c213d409d5353bda12d49cd68d14e45036a285234c8d6f91f92570" dependencies = [ "cfg-if", "crossbeam-utils", + "maybe-uninit", ] [[package]] From c383bc8094874a5aac4b3029088654c988ee1d07 Mon Sep 17 00:00:00 2001 From: kjeremy Date: Thu, 11 Jun 2020 08:55:41 -0400 Subject: [PATCH 161/172] Update crates --- Cargo.lock | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 329e0dd7c67e..308e36836fa8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -566,9 +566,9 @@ dependencies = [ [[package]] name = "jod-thread" -version = "0.1.1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4022656272c3e564a7cdebcaaba6518d844b0d0c1836597196efb5bfeb98bb49" +checksum = "8b23360e99b8717f20aaa4598f5a6541efbe30630039fbc7706cf954a87947ae" [[package]] name = "kernel32-sys" @@ -1521,9 +1521,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.53" +version = "1.0.55" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "993948e75b189211a9b31a7528f950c6adc21f9720b6438ff80a7fa2f864cea2" +checksum = "ec2c5d7e739bc07a3e73381a39d61fdb5f671c60c1df26a130690665803d8226" dependencies = [ "itoa", "ryu", @@ -1543,9 +1543,9 @@ dependencies = [ [[package]] name = "serde_yaml" -version = "0.8.12" +version = "0.8.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16c7a592a1ec97c9c1c68d75b6e537dcbf60c7618e038e7841e00af1d9ccf0c4" +checksum = "ae3e2dd40a7cdc18ca80db804b7f461a39bb721160a85c9a1fa30134bf3c02a5" dependencies = [ "dtoa", "linked-hash-map", @@ -1586,9 +1586,9 @@ checksum = "ab16ced94dbd8a46c82fd81e3ed9a8727dac2977ea869d217bcc4ea1f122e81f" [[package]] name = "syn" -version = "1.0.30" +version = "1.0.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93a56fabc59dce20fe48b6c832cc249c713e7ed88fa28b0ee0a3bfcaae5fe4e2" +checksum = "b5304cfdf27365b7585c25d4af91b35016ed21ef88f17ced89c7093b43dba8b6" dependencies = [ "proc-macro2", "quote", @@ -1597,9 +1597,9 @@ dependencies = [ [[package]] name = "synstructure" -version = "0.12.3" +version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67656ea1dc1b41b1451851562ea232ec2e5a80242139f7e679ceccfb5d61f545" +checksum = "b834f2d66f734cb897113e34aaff2f1ab4719ca946f9a7358dba8f8064148701" dependencies = [ "proc-macro2", "quote", From 90331ea0350eaea281d35bd0aa13df7f20a8600d Mon Sep 17 00:00:00 2001 From: Jonas Schievink Date: Thu, 11 Jun 2020 16:22:31 +0200 Subject: [PATCH 162/172] Make known paths use `core` instead of `std` --- crates/ra_hir_def/src/data.rs | 2 +- crates/ra_hir_def/src/path.rs | 20 +++++++++---------- crates/ra_hir_ty/src/expr.rs | 10 ++++++---- crates/ra_hir_ty/src/infer.rs | 16 +++++++-------- crates/ra_hir_ty/src/tests/simple.rs | 4 ++-- crates/ra_hir_ty/src/tests/traits.rs | 30 +++++++++++++++------------- crates/ra_ide/src/diagnostics.rs | 18 ++++++++--------- 7 files changed, 52 insertions(+), 48 deletions(-) diff --git a/crates/ra_hir_def/src/data.rs b/crates/ra_hir_def/src/data.rs index 807195d25ad9..53599e74a266 100644 --- a/crates/ra_hir_def/src/data.rs +++ b/crates/ra_hir_def/src/data.rs @@ -99,7 +99,7 @@ impl FunctionData { } fn desugar_future_path(orig: TypeRef) -> Path { - let path = path![std::future::Future]; + let path = path![core::future::Future]; let mut generic_args: Vec<_> = std::iter::repeat(None).take(path.segments.len() - 1).collect(); let mut last = GenericArgs::empty(); last.bindings.push(AssociatedTypeBinding { diff --git a/crates/ra_hir_def/src/path.rs b/crates/ra_hir_def/src/path.rs index bfa921de2220..ba16442bd517 100644 --- a/crates/ra_hir_def/src/path.rs +++ b/crates/ra_hir_def/src/path.rs @@ -323,16 +323,16 @@ pub use hir_expand::name as __name; #[macro_export] macro_rules! __known_path { - (std::iter::IntoIterator) => {}; - (std::result::Result) => {}; - (std::ops::Range) => {}; - (std::ops::RangeFrom) => {}; - (std::ops::RangeFull) => {}; - (std::ops::RangeTo) => {}; - (std::ops::RangeToInclusive) => {}; - (std::ops::RangeInclusive) => {}; - (std::future::Future) => {}; - (std::ops::Try) => {}; + (core::iter::IntoIterator) => {}; + (core::result::Result) => {}; + (core::ops::Range) => {}; + (core::ops::RangeFrom) => {}; + (core::ops::RangeFull) => {}; + (core::ops::RangeTo) => {}; + (core::ops::RangeToInclusive) => {}; + (core::ops::RangeInclusive) => {}; + (core::future::Future) => {}; + (core::ops::Try) => {}; ($path:path) => { compile_error!("Please register your known path in the path module") }; diff --git a/crates/ra_hir_ty/src/expr.rs b/crates/ra_hir_ty/src/expr.rs index f04968e145bd..7db928dded28 100644 --- a/crates/ra_hir_ty/src/expr.rs +++ b/crates/ra_hir_ty/src/expr.rs @@ -226,17 +226,19 @@ impl<'a, 'b> ExprValidator<'a, 'b> { None => return, }; - let std_result_path = path![std::result::Result]; + let core_result_path = path![core::result::Result]; let resolver = self.func.resolver(db.upcast()); - let std_result_enum = match resolver.resolve_known_enum(db.upcast(), &std_result_path) { + let core_result_enum = match resolver.resolve_known_enum(db.upcast(), &core_result_path) { Some(it) => it, _ => return, }; - let std_result_ctor = TypeCtor::Adt(AdtId::EnumId(std_result_enum)); + let core_result_ctor = TypeCtor::Adt(AdtId::EnumId(core_result_enum)); let params = match &mismatch.expected { - Ty::Apply(ApplicationTy { ctor, parameters }) if ctor == &std_result_ctor => parameters, + Ty::Apply(ApplicationTy { ctor, parameters }) if ctor == &core_result_ctor => { + parameters + } _ => return, }; diff --git a/crates/ra_hir_ty/src/infer.rs b/crates/ra_hir_ty/src/infer.rs index f965eb2b5c99..3719f76a6e21 100644 --- a/crates/ra_hir_ty/src/infer.rs +++ b/crates/ra_hir_ty/src/infer.rs @@ -555,13 +555,13 @@ impl<'a> InferenceContext<'a> { } fn resolve_into_iter_item(&self) -> Option { - let path = path![std::iter::IntoIterator]; + let path = path![core::iter::IntoIterator]; let trait_ = self.resolver.resolve_known_trait(self.db.upcast(), &path)?; self.db.trait_data(trait_).associated_type_by_name(&name![Item]) } fn resolve_ops_try_ok(&self) -> Option { - let path = path![std::ops::Try]; + let path = path![core::ops::Try]; let trait_ = self.resolver.resolve_known_trait(self.db.upcast(), &path)?; self.db.trait_data(trait_).associated_type_by_name(&name![Ok]) } @@ -587,37 +587,37 @@ impl<'a> InferenceContext<'a> { } fn resolve_range_full(&self) -> Option { - let path = path![std::ops::RangeFull]; + let path = path![core::ops::RangeFull]; let struct_ = self.resolver.resolve_known_struct(self.db.upcast(), &path)?; Some(struct_.into()) } fn resolve_range(&self) -> Option { - let path = path![std::ops::Range]; + let path = path![core::ops::Range]; let struct_ = self.resolver.resolve_known_struct(self.db.upcast(), &path)?; Some(struct_.into()) } fn resolve_range_inclusive(&self) -> Option { - let path = path![std::ops::RangeInclusive]; + let path = path![core::ops::RangeInclusive]; let struct_ = self.resolver.resolve_known_struct(self.db.upcast(), &path)?; Some(struct_.into()) } fn resolve_range_from(&self) -> Option { - let path = path![std::ops::RangeFrom]; + let path = path![core::ops::RangeFrom]; let struct_ = self.resolver.resolve_known_struct(self.db.upcast(), &path)?; Some(struct_.into()) } fn resolve_range_to(&self) -> Option { - let path = path![std::ops::RangeTo]; + let path = path![core::ops::RangeTo]; let struct_ = self.resolver.resolve_known_struct(self.db.upcast(), &path)?; Some(struct_.into()) } fn resolve_range_to_inclusive(&self) -> Option { - let path = path![std::ops::RangeToInclusive]; + let path = path![core::ops::RangeToInclusive]; let struct_ = self.resolver.resolve_known_struct(self.db.upcast(), &path)?; Some(struct_.into()) } diff --git a/crates/ra_hir_ty/src/tests/simple.rs b/crates/ra_hir_ty/src/tests/simple.rs index 8a5031756fad..37659cd02c81 100644 --- a/crates/ra_hir_ty/src/tests/simple.rs +++ b/crates/ra_hir_ty/src/tests/simple.rs @@ -95,7 +95,7 @@ fn foo() { fn infer_ranges() { let (db, pos) = TestDB::with_position( r#" -//- /main.rs crate:main deps:std +//- /main.rs crate:main deps:core fn test() { let a = ..; let b = 1..; @@ -108,7 +108,7 @@ fn test() { t<|>; } -//- /std.rs crate:std +//- /core.rs crate:core #[prelude_import] use prelude::*; mod prelude {} diff --git a/crates/ra_hir_ty/src/tests/traits.rs b/crates/ra_hir_ty/src/tests/traits.rs index 133fb5f398c9..e81193a3c30a 100644 --- a/crates/ra_hir_ty/src/tests/traits.rs +++ b/crates/ra_hir_ty/src/tests/traits.rs @@ -10,7 +10,7 @@ use super::{infer, infer_with_mismatches, type_at, type_at_pos}; fn infer_await() { let (db, pos) = TestDB::with_position( r#" -//- /main.rs crate:main deps:std +//- /main.rs crate:main deps:core struct IntFuture; @@ -24,7 +24,7 @@ fn test() { v<|>; } -//- /std.rs crate:std +//- /core.rs crate:core #[prelude_import] use future::*; mod future { #[lang = "future_trait"] @@ -42,7 +42,7 @@ mod future { fn infer_async() { let (db, pos) = TestDB::with_position( r#" -//- /main.rs crate:main deps:std +//- /main.rs crate:main deps:core async fn foo() -> u64 { 128 @@ -54,7 +54,7 @@ fn test() { v<|>; } -//- /std.rs crate:std +//- /core.rs crate:core #[prelude_import] use future::*; mod future { #[lang = "future_trait"] @@ -72,7 +72,7 @@ mod future { fn infer_desugar_async() { let (db, pos) = TestDB::with_position( r#" -//- /main.rs crate:main deps:std +//- /main.rs crate:main deps:core async fn foo() -> u64 { 128 @@ -83,7 +83,7 @@ fn test() { r<|>; } -//- /std.rs crate:std +//- /core.rs crate:core #[prelude_import] use future::*; mod future { trait Future { @@ -100,7 +100,7 @@ mod future { fn infer_try() { let (db, pos) = TestDB::with_position( r#" -//- /main.rs crate:main deps:std +//- /main.rs crate:main deps:core fn test() { let r: Result = Result::Ok(1); @@ -108,7 +108,7 @@ fn test() { v<|>; } -//- /std.rs crate:std +//- /core.rs crate:core #[prelude_import] use ops::*; mod ops { @@ -140,9 +140,9 @@ mod result { fn infer_for_loop() { let (db, pos) = TestDB::with_position( r#" -//- /main.rs crate:main deps:std +//- /main.rs crate:main deps:core,alloc -use std::collections::Vec; +use alloc::collections::Vec; fn test() { let v = Vec::new(); @@ -152,7 +152,7 @@ fn test() { } } -//- /std.rs crate:std +//- /core.rs crate:core #[prelude_import] use iter::*; mod iter { @@ -161,6 +161,8 @@ mod iter { } } +//- /alloc.rs crate:alloc deps:core + mod collections { struct Vec {} impl Vec { @@ -168,7 +170,7 @@ mod collections { fn push(&mut self, t: T) { } } - impl crate::iter::IntoIterator for Vec { + impl IntoIterator for Vec { type Item=T; } } @@ -2846,12 +2848,12 @@ fn test() { fn integer_range_iterate() { let t = type_at( r#" -//- /main.rs crate:main deps:std +//- /main.rs crate:main deps:core fn test() { for x in 0..100 { x<|>; } } -//- /std.rs crate:std +//- /core.rs crate:core pub mod ops { pub struct Range { pub start: Idx, diff --git a/crates/ra_ide/src/diagnostics.rs b/crates/ra_ide/src/diagnostics.rs index bf14a467f8a2..0f1cb0bcc445 100644 --- a/crates/ra_ide/src/diagnostics.rs +++ b/crates/ra_ide/src/diagnostics.rs @@ -321,7 +321,7 @@ mod tests { fn test_wrap_return_type() { let before = r#" //- /main.rs - use std::{string::String, result::Result::{self, Ok, Err}}; + use core::{string::String, result::Result::{self, Ok, Err}}; fn div(x: i32, y: i32) -> Result { if y == 0 { @@ -330,7 +330,7 @@ mod tests { x / y<|> } - //- /std/lib.rs + //- /core/lib.rs pub mod string { pub struct String { } } @@ -339,7 +339,7 @@ mod tests { } "#; let after = r#" - use std::{string::String, result::Result::{self, Ok, Err}}; + use core::{string::String, result::Result::{self, Ok, Err}}; fn div(x: i32, y: i32) -> Result { if y == 0 { @@ -355,7 +355,7 @@ mod tests { fn test_wrap_return_type_handles_generic_functions() { let before = r#" //- /main.rs - use std::result::Result::{self, Ok, Err}; + use core::result::Result::{self, Ok, Err}; fn div(x: T) -> Result { if x == 0 { @@ -364,13 +364,13 @@ mod tests { <|>x } - //- /std/lib.rs + //- /core/lib.rs pub mod result { pub enum Result { Ok(T), Err(E) } } "#; let after = r#" - use std::result::Result::{self, Ok, Err}; + use core::result::Result::{self, Ok, Err}; fn div(x: T) -> Result { if x == 0 { @@ -386,7 +386,7 @@ mod tests { fn test_wrap_return_type_handles_type_aliases() { let before = r#" //- /main.rs - use std::{string::String, result::Result::{self, Ok, Err}}; + use core::{string::String, result::Result::{self, Ok, Err}}; type MyResult = Result; @@ -397,7 +397,7 @@ mod tests { x <|>/ y } - //- /std/lib.rs + //- /core/lib.rs pub mod string { pub struct String { } } @@ -406,7 +406,7 @@ mod tests { } "#; let after = r#" - use std::{string::String, result::Result::{self, Ok, Err}}; + use core::{string::String, result::Result::{self, Ok, Err}}; type MyResult = Result; fn div(x: i32, y: i32) -> MyResult { From 279a1ae5646aa474f6e6b6d2a0b9c248e7e0460c Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Thu, 11 Jun 2020 17:13:24 +0200 Subject: [PATCH 163/172] Indent chain `.` even if there's more stuff afterwards --- crates/ra_ide/src/typing.rs | 38 +++++++++++++++++++++++++++++++------ 1 file changed, 32 insertions(+), 6 deletions(-) diff --git a/crates/ra_ide/src/typing.rs b/crates/ra_ide/src/typing.rs index 533306e2e734..83776d2b6d34 100644 --- a/crates/ra_ide/src/typing.rs +++ b/crates/ra_ide/src/typing.rs @@ -21,7 +21,9 @@ use ra_ide_db::{source_change::SourceFileEdit, RootDatabase}; use ra_syntax::{ algo::find_node_at_offset, ast::{self, AstToken}, - AstNode, SourceFile, TextRange, TextSize, + AstNode, SourceFile, + SyntaxKind::{FIELD_EXPR, METHOD_CALL_EXPR}, + TextRange, TextSize, }; use ra_text_edit::TextEdit; @@ -98,9 +100,12 @@ fn on_dot_typed(file: &SourceFile, offset: TextSize) -> Option { }; let current_indent_len = TextSize::of(current_indent); + let parent = whitespace.syntax().parent(); // Make sure dot is a part of call chain - let field_expr = ast::FieldExpr::cast(whitespace.syntax().parent())?; - let prev_indent = leading_indent(field_expr.syntax())?; + if !matches!(parent.kind(), FIELD_EXPR | METHOD_CALL_EXPR) { + return None; + } + let prev_indent = leading_indent(&parent)?; let target_indent = format!(" {}", prev_indent); let target_indent_len = TextSize::of(&target_indent); if current_indent_len == target_indent_len { @@ -143,11 +148,11 @@ mod tests { }) } - fn type_char(char_typed: char, before: &str, after: &str) { - let actual = do_type_char(char_typed, before) + fn type_char(char_typed: char, ra_fixture_before: &str, ra_fixture_after: &str) { + let actual = do_type_char(char_typed, ra_fixture_before) .unwrap_or_else(|| panic!("typing `{}` did nothing", char_typed)); - assert_eq_text!(after, &actual); + assert_eq_text!(ra_fixture_after, &actual); } fn type_char_noop(char_typed: char, before: &str) { @@ -248,6 +253,27 @@ fn foo() { ) } + #[test] + fn indents_new_chain_call_with_let() { + type_char( + '.', + r#" +fn main() { + let _ = foo + <|> + bar() +} +"#, + r#" +fn main() { + let _ = foo + . + bar() +} +"#, + ); + } + #[test] fn indents_continued_chain_call() { type_char( From 215e229dd1a495cda6541371c75145ba03f62de5 Mon Sep 17 00:00:00 2001 From: Jonas Schievink Date: Thu, 11 Jun 2020 17:28:32 +0200 Subject: [PATCH 164/172] Update wrap return tests Update "no diagnostic" tests, use `()` instead of `String` --- crates/ra_ide/src/diagnostics.rs | 46 ++++++++++++-------------------- 1 file changed, 17 insertions(+), 29 deletions(-) diff --git a/crates/ra_ide/src/diagnostics.rs b/crates/ra_ide/src/diagnostics.rs index 0f1cb0bcc445..f44feaf697b7 100644 --- a/crates/ra_ide/src/diagnostics.rs +++ b/crates/ra_ide/src/diagnostics.rs @@ -321,29 +321,26 @@ mod tests { fn test_wrap_return_type() { let before = r#" //- /main.rs - use core::{string::String, result::Result::{self, Ok, Err}}; + use core::result::Result::{self, Ok, Err}; - fn div(x: i32, y: i32) -> Result { + fn div(x: i32, y: i32) -> Result { if y == 0 { - return Err("div by zero".into()); + return Err(()); } x / y<|> } //- /core/lib.rs - pub mod string { - pub struct String { } - } pub mod result { pub enum Result { Ok(T), Err(E) } } "#; let after = r#" - use core::{string::String, result::Result::{self, Ok, Err}}; + use core::result::Result::{self, Ok, Err}; - fn div(x: i32, y: i32) -> Result { + fn div(x: i32, y: i32) -> Result { if y == 0 { - return Err("div by zero".into()); + return Err(()); } Ok(x / y) } @@ -386,32 +383,29 @@ mod tests { fn test_wrap_return_type_handles_type_aliases() { let before = r#" //- /main.rs - use core::{string::String, result::Result::{self, Ok, Err}}; + use core::result::Result::{self, Ok, Err}; - type MyResult = Result; + type MyResult = Result; fn div(x: i32, y: i32) -> MyResult { if y == 0 { - return Err("div by zero".into()); + return Err(()); } x <|>/ y } //- /core/lib.rs - pub mod string { - pub struct String { } - } pub mod result { pub enum Result { Ok(T), Err(E) } } "#; let after = r#" - use core::{string::String, result::Result::{self, Ok, Err}}; + use core::result::Result::{self, Ok, Err}; - type MyResult = Result; + type MyResult = Result; fn div(x: i32, y: i32) -> MyResult { if y == 0 { - return Err("div by zero".into()); + return Err(()); } Ok(x / y) } @@ -423,16 +417,13 @@ mod tests { fn test_wrap_return_type_not_applicable_when_expr_type_does_not_match_ok_type() { let content = r#" //- /main.rs - use std::{string::String, result::Result::{self, Ok, Err}}; + use core::result::Result::{self, Ok, Err}; - fn foo() -> Result { + fn foo() -> Result<(), i32> { 0<|> } - //- /std/lib.rs - pub mod string { - pub struct String { } - } + //- /core/lib.rs pub mod result { pub enum Result { Ok(T), Err(E) } } @@ -444,7 +435,7 @@ mod tests { fn test_wrap_return_type_not_applicable_when_return_type_is_not_result() { let content = r#" //- /main.rs - use std::{string::String, result::Result::{self, Ok, Err}}; + use core::result::Result::{self, Ok, Err}; enum SomeOtherEnum { Ok(i32), @@ -455,10 +446,7 @@ mod tests { 0<|> } - //- /std/lib.rs - pub mod string { - pub struct String { } - } + //- /core/lib.rs pub mod result { pub enum Result { Ok(T), Err(E) } } From 879693e63c19b474153473e11f96ab9c321739db Mon Sep 17 00:00:00 2001 From: Matthew Jasper Date: Thu, 11 Jun 2020 18:14:57 +0100 Subject: [PATCH 165/172] Move complex inline test to own file --- crates/ra_parser/src/grammar/type_params.rs | 16 - .../parser/err/0044_unexpected_for_type.rast | 324 +++++++++++---- .../parser/err/0044_unexpected_for_type.rs | 12 +- .../parser/inline/ok/0003_where_pred_for.rast | 252 +---------- .../parser/inline/ok/0003_where_pred_for.rs | 16 - .../parser/ok/0067_where_for_pred.rast | 392 ++++++++++++++++++ .../parser/ok/0067_where_for_pred.rs | 30 ++ 7 files changed, 670 insertions(+), 372 deletions(-) create mode 100644 crates/ra_syntax/test_data/parser/ok/0067_where_for_pred.rast create mode 100644 crates/ra_syntax/test_data/parser/ok/0067_where_for_pred.rs diff --git a/crates/ra_parser/src/grammar/type_params.rs b/crates/ra_parser/src/grammar/type_params.rs index b3508c732e9b..325d566adee0 100644 --- a/crates/ra_parser/src/grammar/type_params.rs +++ b/crates/ra_parser/src/grammar/type_params.rs @@ -195,22 +195,6 @@ fn where_predicate(p: &mut Parser) { // where // for<'a> F: Fn(&'a str) // { } - // fn for_ref() - // where - // for<'a> &'a F: Debug - // { } - // fn for_parens() - // where - // for<'a> (&'a F): Fn(&'a str) - // { } - // fn for_slice() - // where - // for<'a> [&'a F]: Eq - // { } - // fn for_qpath(_t: &T) - // where - // for<'a> <&'a T as Baz>::Foo: Iterator - // { } if p.at(T![for]) { types::for_binder(p); } diff --git a/crates/ra_syntax/test_data/parser/err/0044_unexpected_for_type.rast b/crates/ra_syntax/test_data/parser/err/0044_unexpected_for_type.rast index 3400beff03ee..cb90f28bc74a 100644 --- a/crates/ra_syntax/test_data/parser/err/0044_unexpected_for_type.rast +++ b/crates/ra_syntax/test_data/parser/err/0044_unexpected_for_type.rast @@ -1,88 +1,240 @@ -SOURCE_FILE@0..79 - TYPE_ALIAS_DEF@0..25 +SOURCE_FILE@0..239 + TYPE_ALIAS_DEF@0..30 TYPE_KW@0..4 "type" WHITESPACE@4..5 " " - NAME@5..6 - IDENT@5..6 "A" - WHITESPACE@6..7 " " - EQ@7..8 "=" - WHITESPACE@8..9 " " - FOR_TYPE@9..24 - FOR_KW@9..12 "for" - TYPE_PARAM_LIST@12..16 - L_ANGLE@12..13 "<" - LIFETIME_PARAM@13..15 - LIFETIME@13..15 "\'a" - R_ANGLE@15..16 ">" - WHITESPACE@16..17 " " - REFERENCE_TYPE@17..24 - AMP@17..18 "&" - LIFETIME@18..20 "\'a" - WHITESPACE@20..21 " " - PATH_TYPE@21..24 - PATH@21..24 - PATH_SEGMENT@21..24 - NAME_REF@21..24 - IDENT@21..24 "u32" - SEMICOLON@24..25 ";" - WHITESPACE@25..26 "\n" - TYPE_ALIAS_DEF@26..54 - TYPE_KW@26..30 "type" - WHITESPACE@30..31 " " - NAME@31..32 - IDENT@31..32 "B" - WHITESPACE@32..33 " " - EQ@33..34 "=" - WHITESPACE@34..35 " " - FOR_TYPE@35..53 - FOR_KW@35..38 "for" - TYPE_PARAM_LIST@38..42 - L_ANGLE@38..39 "<" - LIFETIME_PARAM@39..41 - LIFETIME@39..41 "\'a" - R_ANGLE@41..42 ">" - WHITESPACE@42..43 " " - TUPLE_TYPE@43..53 - L_PAREN@43..44 "(" - REFERENCE_TYPE@44..51 - AMP@44..45 "&" - LIFETIME@45..47 "\'a" - WHITESPACE@47..48 " " - PATH_TYPE@48..51 - PATH@48..51 - PATH_SEGMENT@48..51 - NAME_REF@48..51 - IDENT@48..51 "u32" - COMMA@51..52 "," - R_PAREN@52..53 ")" - SEMICOLON@53..54 ";" - WHITESPACE@54..55 "\n" - TYPE_ALIAS_DEF@55..78 - TYPE_KW@55..59 "type" - WHITESPACE@59..60 " " - NAME@60..61 - IDENT@60..61 "B" - WHITESPACE@61..62 " " - EQ@62..63 "=" - WHITESPACE@63..64 " " - FOR_TYPE@64..77 - FOR_KW@64..67 "for" - TYPE_PARAM_LIST@67..71 - L_ANGLE@67..68 "<" - LIFETIME_PARAM@68..70 - LIFETIME@68..70 "\'a" - R_ANGLE@70..71 ">" - WHITESPACE@71..72 " " - SLICE_TYPE@72..77 - L_BRACK@72..73 "[" - PATH_TYPE@73..76 - PATH@73..76 - PATH_SEGMENT@73..76 - NAME_REF@73..76 - IDENT@73..76 "u32" - R_BRACK@76..77 "]" - SEMICOLON@77..78 ";" - WHITESPACE@78..79 "\n" -error 16..16: expected a function pointer or path -error 42..42: expected a function pointer or path -error 71..71: expected a function pointer or path + NAME@5..11 + IDENT@5..11 "ForRef" + WHITESPACE@11..12 " " + EQ@12..13 "=" + WHITESPACE@13..14 " " + FOR_TYPE@14..29 + FOR_KW@14..17 "for" + TYPE_PARAM_LIST@17..21 + L_ANGLE@17..18 "<" + LIFETIME_PARAM@18..20 + LIFETIME@18..20 "\'a" + R_ANGLE@20..21 ">" + WHITESPACE@21..22 " " + REFERENCE_TYPE@22..29 + AMP@22..23 "&" + LIFETIME@23..25 "\'a" + WHITESPACE@25..26 " " + PATH_TYPE@26..29 + PATH@26..29 + PATH_SEGMENT@26..29 + NAME_REF@26..29 + IDENT@26..29 "u32" + SEMICOLON@29..30 ";" + WHITESPACE@30..31 "\n" + TYPE_ALIAS_DEF@31..64 + TYPE_KW@31..35 "type" + WHITESPACE@35..36 " " + NAME@36..42 + IDENT@36..42 "ForTup" + WHITESPACE@42..43 " " + EQ@43..44 "=" + WHITESPACE@44..45 " " + FOR_TYPE@45..63 + FOR_KW@45..48 "for" + TYPE_PARAM_LIST@48..52 + L_ANGLE@48..49 "<" + LIFETIME_PARAM@49..51 + LIFETIME@49..51 "\'a" + R_ANGLE@51..52 ">" + WHITESPACE@52..53 " " + TUPLE_TYPE@53..63 + L_PAREN@53..54 "(" + REFERENCE_TYPE@54..61 + AMP@54..55 "&" + LIFETIME@55..57 "\'a" + WHITESPACE@57..58 " " + PATH_TYPE@58..61 + PATH@58..61 + PATH_SEGMENT@58..61 + NAME_REF@58..61 + IDENT@58..61 "u32" + COMMA@61..62 "," + R_PAREN@62..63 ")" + SEMICOLON@63..64 ";" + WHITESPACE@64..65 "\n" + TYPE_ALIAS_DEF@65..95 + TYPE_KW@65..69 "type" + WHITESPACE@69..70 " " + NAME@70..78 + IDENT@70..78 "ForSlice" + WHITESPACE@78..79 " " + EQ@79..80 "=" + WHITESPACE@80..81 " " + FOR_TYPE@81..94 + FOR_KW@81..84 "for" + TYPE_PARAM_LIST@84..88 + L_ANGLE@84..85 "<" + LIFETIME_PARAM@85..87 + LIFETIME@85..87 "\'a" + R_ANGLE@87..88 ">" + WHITESPACE@88..89 " " + SLICE_TYPE@89..94 + L_BRACK@89..90 "[" + PATH_TYPE@90..93 + PATH@90..93 + PATH_SEGMENT@90..93 + NAME_REF@90..93 + IDENT@90..93 "u32" + R_BRACK@93..94 "]" + SEMICOLON@94..95 ";" + WHITESPACE@95..96 "\n" + TYPE_ALIAS_DEF@96..149 + TYPE_KW@96..100 "type" + WHITESPACE@100..101 " " + NAME@101..109 + IDENT@101..109 "ForForFn" + WHITESPACE@109..110 " " + EQ@110..111 "=" + WHITESPACE@111..112 " " + FOR_TYPE@112..148 + FOR_KW@112..115 "for" + TYPE_PARAM_LIST@115..119 + L_ANGLE@115..116 "<" + LIFETIME_PARAM@116..118 + LIFETIME@116..118 "\'a" + R_ANGLE@118..119 ">" + WHITESPACE@119..120 " " + FOR_TYPE@120..148 + FOR_KW@120..123 "for" + TYPE_PARAM_LIST@123..127 + L_ANGLE@123..124 "<" + LIFETIME_PARAM@124..126 + LIFETIME@124..126 "\'b" + R_ANGLE@126..127 ">" + WHITESPACE@127..128 " " + FN_POINTER_TYPE@128..148 + FN_KW@128..130 "fn" + PARAM_LIST@130..148 + L_PAREN@130..131 "(" + PARAM@131..138 + REFERENCE_TYPE@131..138 + AMP@131..132 "&" + LIFETIME@132..134 "\'a" + WHITESPACE@134..135 " " + PATH_TYPE@135..138 + PATH@135..138 + PATH_SEGMENT@135..138 + NAME_REF@135..138 + IDENT@135..138 "i32" + COMMA@138..139 "," + WHITESPACE@139..140 " " + PARAM@140..147 + REFERENCE_TYPE@140..147 + AMP@140..141 "&" + LIFETIME@141..143 "\'b" + WHITESPACE@143..144 " " + PATH_TYPE@144..147 + PATH@144..147 + PATH_SEGMENT@144..147 + NAME_REF@144..147 + IDENT@144..147 "i32" + R_PAREN@147..148 ")" + SEMICOLON@148..149 ";" + WHITESPACE@149..150 "\n" + FN_DEF@150..238 + FN_KW@150..152 "fn" + WHITESPACE@152..153 " " + NAME@153..164 + IDENT@153..164 "for_for_for" + TYPE_PARAM_LIST@164..167 + L_ANGLE@164..165 "<" + TYPE_PARAM@165..166 + NAME@165..166 + IDENT@165..166 "T" + R_ANGLE@166..167 ">" + PARAM_LIST@167..169 + L_PAREN@167..168 "(" + R_PAREN@168..169 ")" + WHITESPACE@169..170 "\n" + WHERE_CLAUSE@170..234 + WHERE_KW@170..175 "where" + WHITESPACE@175..180 "\n " + WHERE_PRED@180..233 + FOR_KW@180..183 "for" + TYPE_PARAM_LIST@183..187 + L_ANGLE@183..184 "<" + LIFETIME_PARAM@184..186 + LIFETIME@184..186 "\'a" + R_ANGLE@186..187 ">" + WHITESPACE@187..188 " " + FOR_TYPE@188..227 + FOR_KW@188..191 "for" + TYPE_PARAM_LIST@191..195 + L_ANGLE@191..192 "<" + LIFETIME_PARAM@192..194 + LIFETIME@192..194 "\'b" + R_ANGLE@194..195 ">" + WHITESPACE@195..196 " " + FOR_TYPE@196..227 + FOR_KW@196..199 "for" + TYPE_PARAM_LIST@199..203 + L_ANGLE@199..200 "<" + LIFETIME_PARAM@200..202 + LIFETIME@200..202 "\'c" + R_ANGLE@202..203 ">" + WHITESPACE@203..204 " " + FN_POINTER_TYPE@204..227 + FN_KW@204..206 "fn" + PARAM_LIST@206..227 + L_PAREN@206..207 "(" + PARAM@207..212 + REFERENCE_TYPE@207..212 + AMP@207..208 "&" + LIFETIME@208..210 "\'a" + WHITESPACE@210..211 " " + PATH_TYPE@211..212 + PATH@211..212 + PATH_SEGMENT@211..212 + NAME_REF@211..212 + IDENT@211..212 "T" + COMMA@212..213 "," + WHITESPACE@213..214 " " + PARAM@214..219 + REFERENCE_TYPE@214..219 + AMP@214..215 "&" + LIFETIME@215..217 "\'b" + WHITESPACE@217..218 " " + PATH_TYPE@218..219 + PATH@218..219 + PATH_SEGMENT@218..219 + NAME_REF@218..219 + IDENT@218..219 "T" + COMMA@219..220 "," + WHITESPACE@220..221 " " + PARAM@221..226 + REFERENCE_TYPE@221..226 + AMP@221..222 "&" + LIFETIME@222..224 "\'c" + WHITESPACE@224..225 " " + PATH_TYPE@225..226 + PATH@225..226 + PATH_SEGMENT@225..226 + NAME_REF@225..226 + IDENT@225..226 "T" + R_PAREN@226..227 ")" + COLON@227..228 ":" + WHITESPACE@228..229 " " + TYPE_BOUND_LIST@229..233 + TYPE_BOUND@229..233 + PATH_TYPE@229..233 + PATH@229..233 + PATH_SEGMENT@229..233 + NAME_REF@229..233 + IDENT@229..233 "Copy" + COMMA@233..234 "," + WHITESPACE@234..235 "\n" + BLOCK_EXPR@235..238 + L_CURLY@235..236 "{" + WHITESPACE@236..237 "\n" + R_CURLY@237..238 "}" + WHITESPACE@238..239 "\n" +error 21..21: expected a function pointer or path +error 52..52: expected a function pointer or path +error 88..88: expected a function pointer or path +error 119..119: expected a function pointer or path +error 195..195: expected a function pointer or path diff --git a/crates/ra_syntax/test_data/parser/err/0044_unexpected_for_type.rs b/crates/ra_syntax/test_data/parser/err/0044_unexpected_for_type.rs index f34ac7fc578f..0e9f8ccb4f84 100644 --- a/crates/ra_syntax/test_data/parser/err/0044_unexpected_for_type.rs +++ b/crates/ra_syntax/test_data/parser/err/0044_unexpected_for_type.rs @@ -1,3 +1,9 @@ -type A = for<'a> &'a u32; -type B = for<'a> (&'a u32,); -type B = for<'a> [u32]; +type ForRef = for<'a> &'a u32; +type ForTup = for<'a> (&'a u32,); +type ForSlice = for<'a> [u32]; +type ForForFn = for<'a> for<'b> fn(&'a i32, &'b i32); +fn for_for_for() +where + for<'a> for<'b> for<'c> fn(&'a T, &'b T, &'c T): Copy, +{ +} diff --git a/crates/ra_syntax/test_data/parser/inline/ok/0003_where_pred_for.rast b/crates/ra_syntax/test_data/parser/inline/ok/0003_where_pred_for.rast index 4f88bfe434e8..cd0892451d9e 100644 --- a/crates/ra_syntax/test_data/parser/inline/ok/0003_where_pred_for.rast +++ b/crates/ra_syntax/test_data/parser/inline/ok/0003_where_pred_for.rast @@ -1,4 +1,4 @@ -SOURCE_FILE@0..292 +SOURCE_FILE@0..54 FN_DEF@0..53 FN_KW@0..2 "fn" WHITESPACE@2..3 " " @@ -58,253 +58,3 @@ SOURCE_FILE@0..292 WHITESPACE@51..52 " " R_CURLY@52..53 "}" WHITESPACE@53..54 "\n" - FN_DEF@54..103 - FN_KW@54..56 "fn" - WHITESPACE@56..57 " " - NAME@57..64 - IDENT@57..64 "for_ref" - TYPE_PARAM_LIST@64..67 - L_ANGLE@64..65 "<" - TYPE_PARAM@65..66 - NAME@65..66 - IDENT@65..66 "F" - R_ANGLE@66..67 ">" - PARAM_LIST@67..69 - L_PAREN@67..68 "(" - R_PAREN@68..69 ")" - WHITESPACE@69..70 "\n" - WHERE_CLAUSE@70..99 - WHERE_KW@70..75 "where" - WHITESPACE@75..79 "\n " - WHERE_PRED@79..99 - FOR_KW@79..82 "for" - TYPE_PARAM_LIST@82..86 - L_ANGLE@82..83 "<" - LIFETIME_PARAM@83..85 - LIFETIME@83..85 "\'a" - R_ANGLE@85..86 ">" - WHITESPACE@86..87 " " - REFERENCE_TYPE@87..92 - AMP@87..88 "&" - LIFETIME@88..90 "\'a" - WHITESPACE@90..91 " " - PATH_TYPE@91..92 - PATH@91..92 - PATH_SEGMENT@91..92 - NAME_REF@91..92 - IDENT@91..92 "F" - COLON@92..93 ":" - WHITESPACE@93..94 " " - TYPE_BOUND_LIST@94..99 - TYPE_BOUND@94..99 - PATH_TYPE@94..99 - PATH@94..99 - PATH_SEGMENT@94..99 - NAME_REF@94..99 - IDENT@94..99 "Debug" - WHITESPACE@99..100 "\n" - BLOCK_EXPR@100..103 - L_CURLY@100..101 "{" - WHITESPACE@101..102 " " - R_CURLY@102..103 "}" - WHITESPACE@103..104 "\n" - FN_DEF@104..164 - FN_KW@104..106 "fn" - WHITESPACE@106..107 " " - NAME@107..117 - IDENT@107..117 "for_parens" - TYPE_PARAM_LIST@117..120 - L_ANGLE@117..118 "<" - TYPE_PARAM@118..119 - NAME@118..119 - IDENT@118..119 "F" - R_ANGLE@119..120 ">" - PARAM_LIST@120..122 - L_PAREN@120..121 "(" - R_PAREN@121..122 ")" - WHITESPACE@122..123 "\n" - WHERE_CLAUSE@123..160 - WHERE_KW@123..128 "where" - WHITESPACE@128..132 "\n " - WHERE_PRED@132..160 - FOR_KW@132..135 "for" - TYPE_PARAM_LIST@135..139 - L_ANGLE@135..136 "<" - LIFETIME_PARAM@136..138 - LIFETIME@136..138 "\'a" - R_ANGLE@138..139 ">" - WHITESPACE@139..140 " " - PAREN_TYPE@140..147 - L_PAREN@140..141 "(" - REFERENCE_TYPE@141..146 - AMP@141..142 "&" - LIFETIME@142..144 "\'a" - WHITESPACE@144..145 " " - PATH_TYPE@145..146 - PATH@145..146 - PATH_SEGMENT@145..146 - NAME_REF@145..146 - IDENT@145..146 "F" - R_PAREN@146..147 ")" - COLON@147..148 ":" - WHITESPACE@148..149 " " - TYPE_BOUND_LIST@149..160 - TYPE_BOUND@149..160 - PATH_TYPE@149..160 - PATH@149..160 - PATH_SEGMENT@149..160 - NAME_REF@149..151 - IDENT@149..151 "Fn" - PARAM_LIST@151..160 - L_PAREN@151..152 "(" - PARAM@152..159 - REFERENCE_TYPE@152..159 - AMP@152..153 "&" - LIFETIME@153..155 "\'a" - WHITESPACE@155..156 " " - PATH_TYPE@156..159 - PATH@156..159 - PATH_SEGMENT@156..159 - NAME_REF@156..159 - IDENT@156..159 "str" - R_PAREN@159..160 ")" - WHITESPACE@160..161 "\n" - BLOCK_EXPR@161..164 - L_CURLY@161..162 "{" - WHITESPACE@162..163 " " - R_CURLY@163..164 "}" - WHITESPACE@164..165 "\n" - FN_DEF@165..215 - FN_KW@165..167 "fn" - WHITESPACE@167..168 " " - NAME@168..177 - IDENT@168..177 "for_slice" - TYPE_PARAM_LIST@177..180 - L_ANGLE@177..178 "<" - TYPE_PARAM@178..179 - NAME@178..179 - IDENT@178..179 "F" - R_ANGLE@179..180 ">" - PARAM_LIST@180..182 - L_PAREN@180..181 "(" - R_PAREN@181..182 ")" - WHITESPACE@182..183 "\n" - WHERE_CLAUSE@183..211 - WHERE_KW@183..188 "where" - WHITESPACE@188..192 "\n " - WHERE_PRED@192..211 - FOR_KW@192..195 "for" - TYPE_PARAM_LIST@195..199 - L_ANGLE@195..196 "<" - LIFETIME_PARAM@196..198 - LIFETIME@196..198 "\'a" - R_ANGLE@198..199 ">" - WHITESPACE@199..200 " " - SLICE_TYPE@200..207 - L_BRACK@200..201 "[" - REFERENCE_TYPE@201..206 - AMP@201..202 "&" - LIFETIME@202..204 "\'a" - WHITESPACE@204..205 " " - PATH_TYPE@205..206 - PATH@205..206 - PATH_SEGMENT@205..206 - NAME_REF@205..206 - IDENT@205..206 "F" - R_BRACK@206..207 "]" - COLON@207..208 ":" - WHITESPACE@208..209 " " - TYPE_BOUND_LIST@209..211 - TYPE_BOUND@209..211 - PATH_TYPE@209..211 - PATH@209..211 - PATH_SEGMENT@209..211 - NAME_REF@209..211 - IDENT@209..211 "Eq" - WHITESPACE@211..212 "\n" - BLOCK_EXPR@212..215 - L_CURLY@212..213 "{" - WHITESPACE@213..214 " " - R_CURLY@214..215 "}" - WHITESPACE@215..216 "\n" - FN_DEF@216..291 - FN_KW@216..218 "fn" - WHITESPACE@218..219 " " - NAME@219..228 - IDENT@219..228 "for_qpath" - TYPE_PARAM_LIST@228..231 - L_ANGLE@228..229 "<" - TYPE_PARAM@229..230 - NAME@229..230 - IDENT@229..230 "T" - R_ANGLE@230..231 ">" - PARAM_LIST@231..239 - L_PAREN@231..232 "(" - PARAM@232..238 - BIND_PAT@232..234 - NAME@232..234 - IDENT@232..234 "_t" - COLON@234..235 ":" - WHITESPACE@235..236 " " - REFERENCE_TYPE@236..238 - AMP@236..237 "&" - PATH_TYPE@237..238 - PATH@237..238 - PATH_SEGMENT@237..238 - NAME_REF@237..238 - IDENT@237..238 "T" - R_PAREN@238..239 ")" - WHITESPACE@239..240 "\n" - WHERE_CLAUSE@240..287 - WHERE_KW@240..245 "where" - WHITESPACE@245..250 "\n " - WHERE_PRED@250..287 - FOR_KW@250..253 "for" - TYPE_PARAM_LIST@253..257 - L_ANGLE@253..254 "<" - LIFETIME_PARAM@254..256 - LIFETIME@254..256 "\'a" - R_ANGLE@256..257 ">" - WHITESPACE@257..258 " " - PATH_TYPE@258..277 - PATH@258..277 - PATH@258..272 - PATH_SEGMENT@258..272 - L_ANGLE@258..259 "<" - REFERENCE_TYPE@259..264 - AMP@259..260 "&" - LIFETIME@260..262 "\'a" - WHITESPACE@262..263 " " - PATH_TYPE@263..264 - PATH@263..264 - PATH_SEGMENT@263..264 - NAME_REF@263..264 - IDENT@263..264 "T" - WHITESPACE@264..265 " " - AS_KW@265..267 "as" - WHITESPACE@267..268 " " - PATH_TYPE@268..271 - PATH@268..271 - PATH_SEGMENT@268..271 - NAME_REF@268..271 - IDENT@268..271 "Baz" - R_ANGLE@271..272 ">" - COLON2@272..274 "::" - PATH_SEGMENT@274..277 - NAME_REF@274..277 - IDENT@274..277 "Foo" - COLON@277..278 ":" - WHITESPACE@278..279 " " - TYPE_BOUND_LIST@279..287 - TYPE_BOUND@279..287 - PATH_TYPE@279..287 - PATH@279..287 - PATH_SEGMENT@279..287 - NAME_REF@279..287 - IDENT@279..287 "Iterator" - WHITESPACE@287..288 "\n" - BLOCK_EXPR@288..291 - L_CURLY@288..289 "{" - WHITESPACE@289..290 " " - R_CURLY@290..291 "}" - WHITESPACE@291..292 "\n" diff --git a/crates/ra_syntax/test_data/parser/inline/ok/0003_where_pred_for.rs b/crates/ra_syntax/test_data/parser/inline/ok/0003_where_pred_for.rs index 2d47596be39b..423bc105bd7b 100644 --- a/crates/ra_syntax/test_data/parser/inline/ok/0003_where_pred_for.rs +++ b/crates/ra_syntax/test_data/parser/inline/ok/0003_where_pred_for.rs @@ -2,19 +2,3 @@ fn for_trait() where for<'a> F: Fn(&'a str) { } -fn for_ref() -where - for<'a> &'a F: Debug -{ } -fn for_parens() -where - for<'a> (&'a F): Fn(&'a str) -{ } -fn for_slice() -where - for<'a> [&'a F]: Eq -{ } -fn for_qpath(_t: &T) -where - for<'a> <&'a T as Baz>::Foo: Iterator -{ } diff --git a/crates/ra_syntax/test_data/parser/ok/0067_where_for_pred.rast b/crates/ra_syntax/test_data/parser/ok/0067_where_for_pred.rast new file mode 100644 index 000000000000..5035851031f6 --- /dev/null +++ b/crates/ra_syntax/test_data/parser/ok/0067_where_for_pred.rast @@ -0,0 +1,392 @@ +SOURCE_FILE@0..374 + FN_DEF@0..55 + FN_KW@0..2 "fn" + WHITESPACE@2..3 " " + NAME@3..12 + IDENT@3..12 "for_trait" + TYPE_PARAM_LIST@12..15 + L_ANGLE@12..13 "<" + TYPE_PARAM@13..14 + NAME@13..14 + IDENT@13..14 "F" + R_ANGLE@14..15 ">" + PARAM_LIST@15..17 + L_PAREN@15..16 "(" + R_PAREN@16..17 ")" + WHITESPACE@17..18 "\n" + WHERE_CLAUSE@18..51 + WHERE_KW@18..23 "where" + WHITESPACE@23..28 "\n " + WHERE_PRED@28..50 + FOR_KW@28..31 "for" + TYPE_PARAM_LIST@31..35 + L_ANGLE@31..32 "<" + LIFETIME_PARAM@32..34 + LIFETIME@32..34 "\'a" + R_ANGLE@34..35 ">" + WHITESPACE@35..36 " " + PATH_TYPE@36..37 + PATH@36..37 + PATH_SEGMENT@36..37 + NAME_REF@36..37 + IDENT@36..37 "F" + COLON@37..38 ":" + WHITESPACE@38..39 " " + TYPE_BOUND_LIST@39..50 + TYPE_BOUND@39..50 + PATH_TYPE@39..50 + PATH@39..50 + PATH_SEGMENT@39..50 + NAME_REF@39..41 + IDENT@39..41 "Fn" + PARAM_LIST@41..50 + L_PAREN@41..42 "(" + PARAM@42..49 + REFERENCE_TYPE@42..49 + AMP@42..43 "&" + LIFETIME@43..45 "\'a" + WHITESPACE@45..46 " " + PATH_TYPE@46..49 + PATH@46..49 + PATH_SEGMENT@46..49 + NAME_REF@46..49 + IDENT@46..49 "str" + R_PAREN@49..50 ")" + COMMA@50..51 "," + WHITESPACE@51..52 "\n" + BLOCK_EXPR@52..55 + L_CURLY@52..53 "{" + WHITESPACE@53..54 "\n" + R_CURLY@54..55 "}" + WHITESPACE@55..56 "\n" + FN_DEF@56..107 + FN_KW@56..58 "fn" + WHITESPACE@58..59 " " + NAME@59..66 + IDENT@59..66 "for_ref" + TYPE_PARAM_LIST@66..69 + L_ANGLE@66..67 "<" + TYPE_PARAM@67..68 + NAME@67..68 + IDENT@67..68 "F" + R_ANGLE@68..69 ">" + PARAM_LIST@69..71 + L_PAREN@69..70 "(" + R_PAREN@70..71 ")" + WHITESPACE@71..72 "\n" + WHERE_CLAUSE@72..103 + WHERE_KW@72..77 "where" + WHITESPACE@77..82 "\n " + WHERE_PRED@82..102 + FOR_KW@82..85 "for" + TYPE_PARAM_LIST@85..89 + L_ANGLE@85..86 "<" + LIFETIME_PARAM@86..88 + LIFETIME@86..88 "\'a" + R_ANGLE@88..89 ">" + WHITESPACE@89..90 " " + REFERENCE_TYPE@90..95 + AMP@90..91 "&" + LIFETIME@91..93 "\'a" + WHITESPACE@93..94 " " + PATH_TYPE@94..95 + PATH@94..95 + PATH_SEGMENT@94..95 + NAME_REF@94..95 + IDENT@94..95 "F" + COLON@95..96 ":" + WHITESPACE@96..97 " " + TYPE_BOUND_LIST@97..102 + TYPE_BOUND@97..102 + PATH_TYPE@97..102 + PATH@97..102 + PATH_SEGMENT@97..102 + NAME_REF@97..102 + IDENT@97..102 "Debug" + COMMA@102..103 "," + WHITESPACE@103..104 "\n" + BLOCK_EXPR@104..107 + L_CURLY@104..105 "{" + WHITESPACE@105..106 "\n" + R_CURLY@106..107 "}" + WHITESPACE@107..108 "\n" + FN_DEF@108..170 + FN_KW@108..110 "fn" + WHITESPACE@110..111 " " + NAME@111..121 + IDENT@111..121 "for_parens" + TYPE_PARAM_LIST@121..124 + L_ANGLE@121..122 "<" + TYPE_PARAM@122..123 + NAME@122..123 + IDENT@122..123 "F" + R_ANGLE@123..124 ">" + PARAM_LIST@124..126 + L_PAREN@124..125 "(" + R_PAREN@125..126 ")" + WHITESPACE@126..127 "\n" + WHERE_CLAUSE@127..166 + WHERE_KW@127..132 "where" + WHITESPACE@132..137 "\n " + WHERE_PRED@137..165 + FOR_KW@137..140 "for" + TYPE_PARAM_LIST@140..144 + L_ANGLE@140..141 "<" + LIFETIME_PARAM@141..143 + LIFETIME@141..143 "\'a" + R_ANGLE@143..144 ">" + WHITESPACE@144..145 " " + PAREN_TYPE@145..152 + L_PAREN@145..146 "(" + REFERENCE_TYPE@146..151 + AMP@146..147 "&" + LIFETIME@147..149 "\'a" + WHITESPACE@149..150 " " + PATH_TYPE@150..151 + PATH@150..151 + PATH_SEGMENT@150..151 + NAME_REF@150..151 + IDENT@150..151 "F" + R_PAREN@151..152 ")" + COLON@152..153 ":" + WHITESPACE@153..154 " " + TYPE_BOUND_LIST@154..165 + TYPE_BOUND@154..165 + PATH_TYPE@154..165 + PATH@154..165 + PATH_SEGMENT@154..165 + NAME_REF@154..156 + IDENT@154..156 "Fn" + PARAM_LIST@156..165 + L_PAREN@156..157 "(" + PARAM@157..164 + REFERENCE_TYPE@157..164 + AMP@157..158 "&" + LIFETIME@158..160 "\'a" + WHITESPACE@160..161 " " + PATH_TYPE@161..164 + PATH@161..164 + PATH_SEGMENT@161..164 + NAME_REF@161..164 + IDENT@161..164 "str" + R_PAREN@164..165 ")" + COMMA@165..166 "," + WHITESPACE@166..167 "\n" + BLOCK_EXPR@167..170 + L_CURLY@167..168 "{" + WHITESPACE@168..169 "\n" + R_CURLY@169..170 "}" + WHITESPACE@170..171 "\n" + FN_DEF@171..223 + FN_KW@171..173 "fn" + WHITESPACE@173..174 " " + NAME@174..183 + IDENT@174..183 "for_slice" + TYPE_PARAM_LIST@183..186 + L_ANGLE@183..184 "<" + TYPE_PARAM@184..185 + NAME@184..185 + IDENT@184..185 "F" + R_ANGLE@185..186 ">" + PARAM_LIST@186..188 + L_PAREN@186..187 "(" + R_PAREN@187..188 ")" + WHITESPACE@188..189 "\n" + WHERE_CLAUSE@189..219 + WHERE_KW@189..194 "where" + WHITESPACE@194..199 "\n " + WHERE_PRED@199..218 + FOR_KW@199..202 "for" + TYPE_PARAM_LIST@202..206 + L_ANGLE@202..203 "<" + LIFETIME_PARAM@203..205 + LIFETIME@203..205 "\'a" + R_ANGLE@205..206 ">" + WHITESPACE@206..207 " " + SLICE_TYPE@207..214 + L_BRACK@207..208 "[" + REFERENCE_TYPE@208..213 + AMP@208..209 "&" + LIFETIME@209..211 "\'a" + WHITESPACE@211..212 " " + PATH_TYPE@212..213 + PATH@212..213 + PATH_SEGMENT@212..213 + NAME_REF@212..213 + IDENT@212..213 "F" + R_BRACK@213..214 "]" + COLON@214..215 ":" + WHITESPACE@215..216 " " + TYPE_BOUND_LIST@216..218 + TYPE_BOUND@216..218 + PATH_TYPE@216..218 + PATH@216..218 + PATH_SEGMENT@216..218 + NAME_REF@216..218 + IDENT@216..218 "Eq" + COMMA@218..219 "," + WHITESPACE@219..220 "\n" + BLOCK_EXPR@220..223 + L_CURLY@220..221 "{" + WHITESPACE@221..222 "\n" + R_CURLY@222..223 "}" + WHITESPACE@223..224 "\n" + FN_DEF@224..300 + FN_KW@224..226 "fn" + WHITESPACE@226..227 " " + NAME@227..236 + IDENT@227..236 "for_qpath" + TYPE_PARAM_LIST@236..239 + L_ANGLE@236..237 "<" + TYPE_PARAM@237..238 + NAME@237..238 + IDENT@237..238 "T" + R_ANGLE@238..239 ">" + PARAM_LIST@239..247 + L_PAREN@239..240 "(" + PARAM@240..246 + BIND_PAT@240..242 + NAME@240..242 + IDENT@240..242 "_t" + COLON@242..243 ":" + WHITESPACE@243..244 " " + REFERENCE_TYPE@244..246 + AMP@244..245 "&" + PATH_TYPE@245..246 + PATH@245..246 + PATH_SEGMENT@245..246 + NAME_REF@245..246 + IDENT@245..246 "T" + R_PAREN@246..247 ")" + WHITESPACE@247..248 "\n" + WHERE_CLAUSE@248..296 + WHERE_KW@248..253 "where" + WHITESPACE@253..258 "\n " + WHERE_PRED@258..295 + FOR_KW@258..261 "for" + TYPE_PARAM_LIST@261..265 + L_ANGLE@261..262 "<" + LIFETIME_PARAM@262..264 + LIFETIME@262..264 "\'a" + R_ANGLE@264..265 ">" + WHITESPACE@265..266 " " + PATH_TYPE@266..285 + PATH@266..285 + PATH@266..280 + PATH_SEGMENT@266..280 + L_ANGLE@266..267 "<" + REFERENCE_TYPE@267..272 + AMP@267..268 "&" + LIFETIME@268..270 "\'a" + WHITESPACE@270..271 " " + PATH_TYPE@271..272 + PATH@271..272 + PATH_SEGMENT@271..272 + NAME_REF@271..272 + IDENT@271..272 "T" + WHITESPACE@272..273 " " + AS_KW@273..275 "as" + WHITESPACE@275..276 " " + PATH_TYPE@276..279 + PATH@276..279 + PATH_SEGMENT@276..279 + NAME_REF@276..279 + IDENT@276..279 "Baz" + R_ANGLE@279..280 ">" + COLON2@280..282 "::" + PATH_SEGMENT@282..285 + NAME_REF@282..285 + IDENT@282..285 "Foo" + COLON@285..286 ":" + WHITESPACE@286..287 " " + TYPE_BOUND_LIST@287..295 + TYPE_BOUND@287..295 + PATH_TYPE@287..295 + PATH@287..295 + PATH_SEGMENT@287..295 + NAME_REF@287..295 + IDENT@287..295 "Iterator" + COMMA@295..296 "," + WHITESPACE@296..297 "\n" + BLOCK_EXPR@297..300 + L_CURLY@297..298 "{" + WHITESPACE@298..299 "\n" + R_CURLY@299..300 "}" + WHITESPACE@300..301 "\n" + FN_DEF@301..373 + FN_KW@301..303 "fn" + WHITESPACE@303..304 " " + NAME@304..314 + IDENT@304..314 "for_for_fn" + TYPE_PARAM_LIST@314..317 + L_ANGLE@314..315 "<" + TYPE_PARAM@315..316 + NAME@315..316 + IDENT@315..316 "T" + R_ANGLE@316..317 ">" + PARAM_LIST@317..319 + L_PAREN@317..318 "(" + R_PAREN@318..319 ")" + WHITESPACE@319..320 "\n" + WHERE_CLAUSE@320..369 + WHERE_KW@320..325 "where" + WHITESPACE@325..330 "\n " + WHERE_PRED@330..368 + FOR_KW@330..333 "for" + TYPE_PARAM_LIST@333..337 + L_ANGLE@333..334 "<" + LIFETIME_PARAM@334..336 + LIFETIME@334..336 "\'a" + R_ANGLE@336..337 ">" + WHITESPACE@337..338 " " + FOR_TYPE@338..362 + FOR_KW@338..341 "for" + TYPE_PARAM_LIST@341..345 + L_ANGLE@341..342 "<" + LIFETIME_PARAM@342..344 + LIFETIME@342..344 "\'b" + R_ANGLE@344..345 ">" + WHITESPACE@345..346 " " + FN_POINTER_TYPE@346..362 + FN_KW@346..348 "fn" + PARAM_LIST@348..362 + L_PAREN@348..349 "(" + PARAM@349..354 + REFERENCE_TYPE@349..354 + AMP@349..350 "&" + LIFETIME@350..352 "\'a" + WHITESPACE@352..353 " " + PATH_TYPE@353..354 + PATH@353..354 + PATH_SEGMENT@353..354 + NAME_REF@353..354 + IDENT@353..354 "T" + COMMA@354..355 "," + WHITESPACE@355..356 " " + PARAM@356..361 + REFERENCE_TYPE@356..361 + AMP@356..357 "&" + LIFETIME@357..359 "\'b" + WHITESPACE@359..360 " " + PATH_TYPE@360..361 + PATH@360..361 + PATH_SEGMENT@360..361 + NAME_REF@360..361 + IDENT@360..361 "T" + R_PAREN@361..362 ")" + COLON@362..363 ":" + WHITESPACE@363..364 " " + TYPE_BOUND_LIST@364..368 + TYPE_BOUND@364..368 + PATH_TYPE@364..368 + PATH@364..368 + PATH_SEGMENT@364..368 + NAME_REF@364..368 + IDENT@364..368 "Copy" + COMMA@368..369 "," + WHITESPACE@369..370 "\n" + BLOCK_EXPR@370..373 + L_CURLY@370..371 "{" + WHITESPACE@371..372 "\n" + R_CURLY@372..373 "}" + WHITESPACE@373..374 "\n" diff --git a/crates/ra_syntax/test_data/parser/ok/0067_where_for_pred.rs b/crates/ra_syntax/test_data/parser/ok/0067_where_for_pred.rs new file mode 100644 index 000000000000..9058c46190c4 --- /dev/null +++ b/crates/ra_syntax/test_data/parser/ok/0067_where_for_pred.rs @@ -0,0 +1,30 @@ +fn for_trait() +where + for<'a> F: Fn(&'a str), +{ +} +fn for_ref() +where + for<'a> &'a F: Debug, +{ +} +fn for_parens() +where + for<'a> (&'a F): Fn(&'a str), +{ +} +fn for_slice() +where + for<'a> [&'a F]: Eq, +{ +} +fn for_qpath(_t: &T) +where + for<'a> <&'a T as Baz>::Foo: Iterator, +{ +} +fn for_for_fn() +where + for<'a> for<'b> fn(&'a T, &'b T): Copy, +{ +} From 8622e4cc1b79f5d23b8a2c6610d749f5b987ea7e Mon Sep 17 00:00:00 2001 From: Matthew Jasper Date: Thu, 11 Jun 2020 18:15:03 +0100 Subject: [PATCH 166/172] Add example of old trait object syntax --- crates/ra_parser/src/grammar/types.rs | 1 + .../parser/inline/ok/0081_for_type.rast | 38 ++++++++++++++++++- .../parser/inline/ok/0081_for_type.rs | 1 + 3 files changed, 39 insertions(+), 1 deletion(-) diff --git a/crates/ra_parser/src/grammar/types.rs b/crates/ra_parser/src/grammar/types.rs index 63dd3774f11f..9e8e3bd97f82 100644 --- a/crates/ra_parser/src/grammar/types.rs +++ b/crates/ra_parser/src/grammar/types.rs @@ -217,6 +217,7 @@ pub(super) fn for_binder(p: &mut Parser) { // test for_type // type A = for<'a> fn() -> (); // type B = for<'a> unsafe extern "C" fn(&'a ()) -> (); +// type Obj = for<'a> PartialEq<&'a i32>; pub(super) fn for_type(p: &mut Parser) { assert!(p.at(T![for])); let m = p.start(); diff --git a/crates/ra_syntax/test_data/parser/inline/ok/0081_for_type.rast b/crates/ra_syntax/test_data/parser/inline/ok/0081_for_type.rast index 26a80017aea7..b26ac2d365b9 100644 --- a/crates/ra_syntax/test_data/parser/inline/ok/0081_for_type.rast +++ b/crates/ra_syntax/test_data/parser/inline/ok/0081_for_type.rast @@ -1,4 +1,4 @@ -SOURCE_FILE@0..82 +SOURCE_FILE@0..121 TYPE_ALIAS_DEF@0..28 TYPE_KW@0..4 "type" WHITESPACE@4..5 " " @@ -74,3 +74,39 @@ SOURCE_FILE@0..82 R_PAREN@79..80 ")" SEMICOLON@80..81 ";" WHITESPACE@81..82 "\n" + TYPE_ALIAS_DEF@82..120 + TYPE_KW@82..86 "type" + WHITESPACE@86..87 " " + NAME@87..90 + IDENT@87..90 "Obj" + WHITESPACE@90..91 " " + EQ@91..92 "=" + WHITESPACE@92..93 " " + FOR_TYPE@93..119 + FOR_KW@93..96 "for" + TYPE_PARAM_LIST@96..100 + L_ANGLE@96..97 "<" + LIFETIME_PARAM@97..99 + LIFETIME@97..99 "\'a" + R_ANGLE@99..100 ">" + WHITESPACE@100..101 " " + PATH_TYPE@101..119 + PATH@101..119 + PATH_SEGMENT@101..119 + NAME_REF@101..110 + IDENT@101..110 "PartialEq" + TYPE_ARG_LIST@110..119 + L_ANGLE@110..111 "<" + TYPE_ARG@111..118 + REFERENCE_TYPE@111..118 + AMP@111..112 "&" + LIFETIME@112..114 "\'a" + WHITESPACE@114..115 " " + PATH_TYPE@115..118 + PATH@115..118 + PATH_SEGMENT@115..118 + NAME_REF@115..118 + IDENT@115..118 "i32" + R_ANGLE@118..119 ">" + SEMICOLON@119..120 ";" + WHITESPACE@120..121 "\n" diff --git a/crates/ra_syntax/test_data/parser/inline/ok/0081_for_type.rs b/crates/ra_syntax/test_data/parser/inline/ok/0081_for_type.rs index 457e8744fe10..8ac7b9e10380 100644 --- a/crates/ra_syntax/test_data/parser/inline/ok/0081_for_type.rs +++ b/crates/ra_syntax/test_data/parser/inline/ok/0081_for_type.rs @@ -1,2 +1,3 @@ type A = for<'a> fn() -> (); type B = for<'a> unsafe extern "C" fn(&'a ()) -> (); +type Obj = for<'a> PartialEq<&'a i32>; From 33e009ec4258e058b966134cd251fd17b390d7f5 Mon Sep 17 00:00:00 2001 From: Veetaha Date: Fri, 12 Jun 2020 01:23:04 +0300 Subject: [PATCH 167/172] Use more idiomatic style for lifetimes in generated code --- xtask/src/codegen/gen_syntax.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/xtask/src/codegen/gen_syntax.rs b/xtask/src/codegen/gen_syntax.rs index 19d5594f5b06..e66304c01e65 100644 --- a/xtask/src/codegen/gen_syntax.rs +++ b/xtask/src/codegen/gen_syntax.rs @@ -40,7 +40,7 @@ fn generate_tokens(grammar: AstSrc<'_>) -> Result { pub(crate) syntax: SyntaxToken, } impl std::fmt::Display for #name { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::Display::fmt(&self.syntax, f) } } @@ -199,7 +199,7 @@ fn generate_nodes(kinds: KindsSrc<'_>, grammar: AstSrc<'_>) -> Result { enum_names.chain(node_names.clone()).map(|it| format_ident!("{}", it)).map(|name| { quote! { impl std::fmt::Display for #name { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::Display::fmt(self.syntax(), f) } } From c514060600ac6787da4bcfa0bbd8b74bb4cf7d18 Mon Sep 17 00:00:00 2001 From: Veetaha Date: Fri, 12 Jun 2020 01:26:58 +0300 Subject: [PATCH 168/172] Apply codegen with idiomatic lifetimes --- crates/ra_syntax/src/ast/generated/nodes.rs | 274 +++++++++---------- crates/ra_syntax/src/ast/generated/tokens.rs | 8 +- 2 files changed, 141 insertions(+), 141 deletions(-) diff --git a/crates/ra_syntax/src/ast/generated/nodes.rs b/crates/ra_syntax/src/ast/generated/nodes.rs index 40081ebb16ea..58141da11426 100644 --- a/crates/ra_syntax/src/ast/generated/nodes.rs +++ b/crates/ra_syntax/src/ast/generated/nodes.rs @@ -4851,687 +4851,687 @@ impl AstNode for FieldDefList { } } impl std::fmt::Display for NominalDef { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::Display::fmt(self.syntax(), f) } } impl std::fmt::Display for GenericParam { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::Display::fmt(self.syntax(), f) } } impl std::fmt::Display for GenericArg { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::Display::fmt(self.syntax(), f) } } impl std::fmt::Display for TypeRef { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::Display::fmt(self.syntax(), f) } } impl std::fmt::Display for ModuleItem { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::Display::fmt(self.syntax(), f) } } impl std::fmt::Display for AssocItem { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::Display::fmt(self.syntax(), f) } } impl std::fmt::Display for ExternItem { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::Display::fmt(self.syntax(), f) } } impl std::fmt::Display for Expr { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::Display::fmt(self.syntax(), f) } } impl std::fmt::Display for Pat { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::Display::fmt(self.syntax(), f) } } impl std::fmt::Display for RecordInnerPat { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::Display::fmt(self.syntax(), f) } } impl std::fmt::Display for AttrInput { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::Display::fmt(self.syntax(), f) } } impl std::fmt::Display for Stmt { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::Display::fmt(self.syntax(), f) } } impl std::fmt::Display for FieldDefList { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::Display::fmt(self.syntax(), f) } } impl std::fmt::Display for SourceFile { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::Display::fmt(self.syntax(), f) } } impl std::fmt::Display for FnDef { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::Display::fmt(self.syntax(), f) } } impl std::fmt::Display for RetType { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::Display::fmt(self.syntax(), f) } } impl std::fmt::Display for StructDef { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::Display::fmt(self.syntax(), f) } } impl std::fmt::Display for UnionDef { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::Display::fmt(self.syntax(), f) } } impl std::fmt::Display for RecordFieldDefList { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::Display::fmt(self.syntax(), f) } } impl std::fmt::Display for RecordFieldDef { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::Display::fmt(self.syntax(), f) } } impl std::fmt::Display for TupleFieldDefList { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::Display::fmt(self.syntax(), f) } } impl std::fmt::Display for TupleFieldDef { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::Display::fmt(self.syntax(), f) } } impl std::fmt::Display for EnumDef { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::Display::fmt(self.syntax(), f) } } impl std::fmt::Display for EnumVariantList { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::Display::fmt(self.syntax(), f) } } impl std::fmt::Display for EnumVariant { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::Display::fmt(self.syntax(), f) } } impl std::fmt::Display for TraitDef { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::Display::fmt(self.syntax(), f) } } impl std::fmt::Display for Module { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::Display::fmt(self.syntax(), f) } } impl std::fmt::Display for ItemList { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::Display::fmt(self.syntax(), f) } } impl std::fmt::Display for ConstDef { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::Display::fmt(self.syntax(), f) } } impl std::fmt::Display for StaticDef { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::Display::fmt(self.syntax(), f) } } impl std::fmt::Display for TypeAliasDef { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::Display::fmt(self.syntax(), f) } } impl std::fmt::Display for ImplDef { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::Display::fmt(self.syntax(), f) } } impl std::fmt::Display for ParenType { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::Display::fmt(self.syntax(), f) } } impl std::fmt::Display for TupleType { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::Display::fmt(self.syntax(), f) } } impl std::fmt::Display for NeverType { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::Display::fmt(self.syntax(), f) } } impl std::fmt::Display for PathType { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::Display::fmt(self.syntax(), f) } } impl std::fmt::Display for PointerType { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::Display::fmt(self.syntax(), f) } } impl std::fmt::Display for ArrayType { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::Display::fmt(self.syntax(), f) } } impl std::fmt::Display for SliceType { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::Display::fmt(self.syntax(), f) } } impl std::fmt::Display for ReferenceType { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::Display::fmt(self.syntax(), f) } } impl std::fmt::Display for PlaceholderType { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::Display::fmt(self.syntax(), f) } } impl std::fmt::Display for FnPointerType { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::Display::fmt(self.syntax(), f) } } impl std::fmt::Display for ForType { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::Display::fmt(self.syntax(), f) } } impl std::fmt::Display for ImplTraitType { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::Display::fmt(self.syntax(), f) } } impl std::fmt::Display for DynTraitType { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::Display::fmt(self.syntax(), f) } } impl std::fmt::Display for TupleExpr { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::Display::fmt(self.syntax(), f) } } impl std::fmt::Display for ArrayExpr { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::Display::fmt(self.syntax(), f) } } impl std::fmt::Display for ParenExpr { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::Display::fmt(self.syntax(), f) } } impl std::fmt::Display for PathExpr { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::Display::fmt(self.syntax(), f) } } impl std::fmt::Display for LambdaExpr { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::Display::fmt(self.syntax(), f) } } impl std::fmt::Display for IfExpr { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::Display::fmt(self.syntax(), f) } } impl std::fmt::Display for LoopExpr { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::Display::fmt(self.syntax(), f) } } impl std::fmt::Display for EffectExpr { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::Display::fmt(self.syntax(), f) } } impl std::fmt::Display for ForExpr { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::Display::fmt(self.syntax(), f) } } impl std::fmt::Display for WhileExpr { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::Display::fmt(self.syntax(), f) } } impl std::fmt::Display for ContinueExpr { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::Display::fmt(self.syntax(), f) } } impl std::fmt::Display for BreakExpr { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::Display::fmt(self.syntax(), f) } } impl std::fmt::Display for Label { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::Display::fmt(self.syntax(), f) } } impl std::fmt::Display for BlockExpr { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::Display::fmt(self.syntax(), f) } } impl std::fmt::Display for ReturnExpr { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::Display::fmt(self.syntax(), f) } } impl std::fmt::Display for CallExpr { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::Display::fmt(self.syntax(), f) } } impl std::fmt::Display for MethodCallExpr { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::Display::fmt(self.syntax(), f) } } impl std::fmt::Display for IndexExpr { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::Display::fmt(self.syntax(), f) } } impl std::fmt::Display for FieldExpr { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::Display::fmt(self.syntax(), f) } } impl std::fmt::Display for AwaitExpr { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::Display::fmt(self.syntax(), f) } } impl std::fmt::Display for TryExpr { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::Display::fmt(self.syntax(), f) } } impl std::fmt::Display for CastExpr { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::Display::fmt(self.syntax(), f) } } impl std::fmt::Display for RefExpr { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::Display::fmt(self.syntax(), f) } } impl std::fmt::Display for PrefixExpr { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::Display::fmt(self.syntax(), f) } } impl std::fmt::Display for BoxExpr { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::Display::fmt(self.syntax(), f) } } impl std::fmt::Display for RangeExpr { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::Display::fmt(self.syntax(), f) } } impl std::fmt::Display for BinExpr { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::Display::fmt(self.syntax(), f) } } impl std::fmt::Display for Literal { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::Display::fmt(self.syntax(), f) } } impl std::fmt::Display for MatchExpr { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::Display::fmt(self.syntax(), f) } } impl std::fmt::Display for MatchArmList { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::Display::fmt(self.syntax(), f) } } impl std::fmt::Display for MatchArm { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::Display::fmt(self.syntax(), f) } } impl std::fmt::Display for MatchGuard { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::Display::fmt(self.syntax(), f) } } impl std::fmt::Display for RecordLit { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::Display::fmt(self.syntax(), f) } } impl std::fmt::Display for RecordFieldList { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::Display::fmt(self.syntax(), f) } } impl std::fmt::Display for RecordField { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::Display::fmt(self.syntax(), f) } } impl std::fmt::Display for OrPat { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::Display::fmt(self.syntax(), f) } } impl std::fmt::Display for ParenPat { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::Display::fmt(self.syntax(), f) } } impl std::fmt::Display for RefPat { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::Display::fmt(self.syntax(), f) } } impl std::fmt::Display for BoxPat { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::Display::fmt(self.syntax(), f) } } impl std::fmt::Display for BindPat { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::Display::fmt(self.syntax(), f) } } impl std::fmt::Display for PlaceholderPat { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::Display::fmt(self.syntax(), f) } } impl std::fmt::Display for DotDotPat { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::Display::fmt(self.syntax(), f) } } impl std::fmt::Display for PathPat { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::Display::fmt(self.syntax(), f) } } impl std::fmt::Display for SlicePat { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::Display::fmt(self.syntax(), f) } } impl std::fmt::Display for RangePat { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::Display::fmt(self.syntax(), f) } } impl std::fmt::Display for LiteralPat { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::Display::fmt(self.syntax(), f) } } impl std::fmt::Display for MacroPat { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::Display::fmt(self.syntax(), f) } } impl std::fmt::Display for RecordPat { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::Display::fmt(self.syntax(), f) } } impl std::fmt::Display for RecordFieldPatList { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::Display::fmt(self.syntax(), f) } } impl std::fmt::Display for RecordFieldPat { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::Display::fmt(self.syntax(), f) } } impl std::fmt::Display for TupleStructPat { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::Display::fmt(self.syntax(), f) } } impl std::fmt::Display for TuplePat { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::Display::fmt(self.syntax(), f) } } impl std::fmt::Display for Visibility { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::Display::fmt(self.syntax(), f) } } impl std::fmt::Display for Name { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::Display::fmt(self.syntax(), f) } } impl std::fmt::Display for NameRef { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::Display::fmt(self.syntax(), f) } } impl std::fmt::Display for MacroCall { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::Display::fmt(self.syntax(), f) } } impl std::fmt::Display for Attr { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::Display::fmt(self.syntax(), f) } } impl std::fmt::Display for TokenTree { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::Display::fmt(self.syntax(), f) } } impl std::fmt::Display for TypeParamList { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::Display::fmt(self.syntax(), f) } } impl std::fmt::Display for TypeParam { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::Display::fmt(self.syntax(), f) } } impl std::fmt::Display for ConstParam { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::Display::fmt(self.syntax(), f) } } impl std::fmt::Display for LifetimeParam { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::Display::fmt(self.syntax(), f) } } impl std::fmt::Display for TypeBound { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::Display::fmt(self.syntax(), f) } } impl std::fmt::Display for TypeBoundList { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::Display::fmt(self.syntax(), f) } } impl std::fmt::Display for WherePred { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::Display::fmt(self.syntax(), f) } } impl std::fmt::Display for WhereClause { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::Display::fmt(self.syntax(), f) } } impl std::fmt::Display for Abi { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::Display::fmt(self.syntax(), f) } } impl std::fmt::Display for ExprStmt { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::Display::fmt(self.syntax(), f) } } impl std::fmt::Display for LetStmt { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::Display::fmt(self.syntax(), f) } } impl std::fmt::Display for Condition { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::Display::fmt(self.syntax(), f) } } impl std::fmt::Display for ParamList { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::Display::fmt(self.syntax(), f) } } impl std::fmt::Display for SelfParam { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::Display::fmt(self.syntax(), f) } } impl std::fmt::Display for Param { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::Display::fmt(self.syntax(), f) } } impl std::fmt::Display for UseItem { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::Display::fmt(self.syntax(), f) } } impl std::fmt::Display for UseTree { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::Display::fmt(self.syntax(), f) } } impl std::fmt::Display for Alias { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::Display::fmt(self.syntax(), f) } } impl std::fmt::Display for UseTreeList { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::Display::fmt(self.syntax(), f) } } impl std::fmt::Display for ExternCrateItem { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::Display::fmt(self.syntax(), f) } } impl std::fmt::Display for ArgList { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::Display::fmt(self.syntax(), f) } } impl std::fmt::Display for Path { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::Display::fmt(self.syntax(), f) } } impl std::fmt::Display for PathSegment { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::Display::fmt(self.syntax(), f) } } impl std::fmt::Display for TypeArgList { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::Display::fmt(self.syntax(), f) } } impl std::fmt::Display for TypeArg { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::Display::fmt(self.syntax(), f) } } impl std::fmt::Display for AssocTypeArg { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::Display::fmt(self.syntax(), f) } } impl std::fmt::Display for LifetimeArg { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::Display::fmt(self.syntax(), f) } } impl std::fmt::Display for ConstArg { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::Display::fmt(self.syntax(), f) } } impl std::fmt::Display for MacroItems { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::Display::fmt(self.syntax(), f) } } impl std::fmt::Display for MacroStmts { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::Display::fmt(self.syntax(), f) } } impl std::fmt::Display for ExternItemList { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::Display::fmt(self.syntax(), f) } } impl std::fmt::Display for ExternBlock { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::Display::fmt(self.syntax(), f) } } impl std::fmt::Display for MetaItem { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::Display::fmt(self.syntax(), f) } } impl std::fmt::Display for MacroDef { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::Display::fmt(self.syntax(), f) } } diff --git a/crates/ra_syntax/src/ast/generated/tokens.rs b/crates/ra_syntax/src/ast/generated/tokens.rs index f91befaac78a..abadd0b61c6f 100644 --- a/crates/ra_syntax/src/ast/generated/tokens.rs +++ b/crates/ra_syntax/src/ast/generated/tokens.rs @@ -11,7 +11,7 @@ pub struct Whitespace { pub(crate) syntax: SyntaxToken, } impl std::fmt::Display for Whitespace { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::Display::fmt(&self.syntax, f) } } @@ -32,7 +32,7 @@ pub struct Comment { pub(crate) syntax: SyntaxToken, } impl std::fmt::Display for Comment { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::Display::fmt(&self.syntax, f) } } @@ -53,7 +53,7 @@ pub struct String { pub(crate) syntax: SyntaxToken, } impl std::fmt::Display for String { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::Display::fmt(&self.syntax, f) } } @@ -74,7 +74,7 @@ pub struct RawString { pub(crate) syntax: SyntaxToken, } impl std::fmt::Display for RawString { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::Display::fmt(&self.syntax, f) } } From 4fefc7d06c8da093e783852c5f0d3aea31e2c939 Mon Sep 17 00:00:00 2001 From: Veetaha Date: Fri, 12 Jun 2020 02:06:28 +0300 Subject: [PATCH 169/172] Simplify --- crates/ra_syntax/src/ast/tokens.rs | 10 +++++----- xtask/src/codegen/gen_syntax.rs | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/crates/ra_syntax/src/ast/tokens.rs b/crates/ra_syntax/src/ast/tokens.rs index 56378385a1a9..2e72d4927589 100644 --- a/crates/ra_syntax/src/ast/tokens.rs +++ b/crates/ra_syntax/src/ast/tokens.rs @@ -84,7 +84,7 @@ impl Whitespace { } pub struct QuoteOffsets { - pub quotes: [TextRange; 2], + pub quotes: (TextRange, TextRange), pub contents: TextRange, } @@ -103,7 +103,7 @@ impl QuoteOffsets { let end = TextSize::of(literal); let res = QuoteOffsets { - quotes: [TextRange::new(start, left_quote), TextRange::new(right_quote, end)], + quotes: (TextRange::new(start, left_quote), TextRange::new(right_quote, end)), contents: TextRange::new(left_quote, right_quote), }; Some(res) @@ -116,17 +116,17 @@ pub trait HasQuotes: AstToken { let offsets = QuoteOffsets::new(text)?; let o = self.syntax().text_range().start(); let offsets = QuoteOffsets { - quotes: [offsets.quotes[0] + o, offsets.quotes[1] + o], + quotes: (offsets.quotes.0 + o, offsets.quotes.1 + o), contents: offsets.contents + o, }; Some(offsets) } fn open_quote_text_range(&self) -> Option { - self.quote_offsets().map(|it| it.quotes[0]) + self.quote_offsets().map(|it| it.quotes.0) } fn close_quote_text_range(&self) -> Option { - self.quote_offsets().map(|it| it.quotes[1]) + self.quote_offsets().map(|it| it.quotes.1) } fn text_range_between_quotes(&self) -> Option { diff --git a/xtask/src/codegen/gen_syntax.rs b/xtask/src/codegen/gen_syntax.rs index 19d5594f5b06..f8b34638b01d 100644 --- a/xtask/src/codegen/gen_syntax.rs +++ b/xtask/src/codegen/gen_syntax.rs @@ -68,7 +68,7 @@ fn generate_nodes(kinds: KindsSrc<'_>, grammar: AstSrc<'_>) -> Result { .iter() .map(|node| { let name = format_ident!("{}", node.name); - let kind = format_ident!("{}", to_upper_snake_case(&name.to_string())); + let kind = format_ident!("{}", to_upper_snake_case(node.name)); let traits = node.traits.iter().map(|trait_name| { let trait_name = format_ident!("{}", trait_name); quote!(impl ast::#trait_name for #name {}) From 59f195a3231d9cbc3baac2c6ca8e6400311deeb7 Mon Sep 17 00:00:00 2001 From: OptimalStrategy Date: Fri, 12 Jun 2020 01:11:54 -0400 Subject: [PATCH 170/172] Fix invalid shorthand initialization diagnostic for tuple structs --- crates/ra_ide/src/diagnostics.rs | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/crates/ra_ide/src/diagnostics.rs b/crates/ra_ide/src/diagnostics.rs index f44feaf697b7..9cd36ad35641 100644 --- a/crates/ra_ide/src/diagnostics.rs +++ b/crates/ra_ide/src/diagnostics.rs @@ -187,7 +187,12 @@ fn check_struct_shorthand_initialization( if let (Some(name_ref), Some(expr)) = (record_field.name_ref(), record_field.expr()) { let field_name = name_ref.syntax().text().to_string(); let field_expr = expr.syntax().text().to_string(); - if field_name == field_expr { + let field_name_is_tup_index = name_ref + .syntax() + .first_token() + .map(|token| token.kind().is_literal()) + .unwrap_or(false); + if field_name == field_expr && !field_name_is_tup_index { let mut edit_builder = TextEditBuilder::default(); edit_builder.delete(record_field.syntax().text_range()); edit_builder.insert(record_field.syntax().text_range().start(), field_name); @@ -719,6 +724,18 @@ mod tests { "#, check_struct_shorthand_initialization, ); + check_not_applicable( + r#" + struct A(usize); + + fn main() { + A { + 0: 0 + } + } + "#, + check_struct_shorthand_initialization, + ); check_apply( r#" From 0231e4ac77dacf6ca30f6b68c6081415f2da54ba Mon Sep 17 00:00:00 2001 From: Jonas Schievink Date: Fri, 12 Jun 2020 13:01:20 +0200 Subject: [PATCH 171/172] find_path: return shorter paths for external items If a containing module is already in scope, there's no need to use the full path to the item. --- crates/ra_hir_def/src/find_path.rs | 59 +++++++++++++++++++++++++---- crates/ra_hir_def/src/import_map.rs | 39 ++++++++++++------- 2 files changed, 77 insertions(+), 21 deletions(-) diff --git a/crates/ra_hir_def/src/find_path.rs b/crates/ra_hir_def/src/find_path.rs index a7f59e0287ff..06701a8309e7 100644 --- a/crates/ra_hir_def/src/find_path.rs +++ b/crates/ra_hir_def/src/find_path.rs @@ -159,10 +159,16 @@ fn find_path_inner( let crate_graph = db.crate_graph(); let extern_paths = crate_graph[from.krate].dependencies.iter().filter_map(|dep| { let import_map = db.import_map(dep.crate_id); - import_map.path_of(item).map(|modpath| { - let mut modpath = modpath.clone(); - modpath.segments.insert(0, dep.as_name()); - modpath + import_map.import_info_for(item).and_then(|info| { + // Determine best path for containing module and append last segment from `info`. + let mut path = find_path_inner( + db, + ItemInNs::Types(ModuleDefId::ModuleId(info.container)), + from, + best_path_len - 1, + )?; + path.segments.push(info.path.segments.last().unwrap().clone()); + Some(path) }) }); @@ -299,8 +305,8 @@ mod tests { /// `code` needs to contain a cursor marker; checks that `find_path` for the /// item the `path` refers to returns that same path when called from the /// module the cursor is in. - fn check_found_path(code: &str, path: &str) { - let (db, pos) = TestDB::with_position(code); + fn check_found_path(ra_fixture: &str, path: &str) { + let (db, pos) = TestDB::with_position(ra_fixture); let module = db.module_for_file(pos.file_id); let parsed_path_file = ra_syntax::SourceFile::parse(&format!("use {};", path)); let ast_path = parsed_path_file @@ -420,7 +426,6 @@ mod tests { #[test] fn different_crate_renamed() { - // Even if a local path exists, if the item is defined externally, prefer an external path. let code = r#" //- /main.rs crate:main deps:std extern crate std as std_renamed; @@ -428,7 +433,45 @@ mod tests { //- /std.rs crate:std pub struct S; "#; - check_found_path(code, "std::S"); + check_found_path(code, "std_renamed::S"); + } + + #[test] + fn partially_imported() { + // Tests that short paths are used even for external items, when parts of the path are + // already in scope. + check_found_path( + r#" + //- /main.rs crate:main deps:ra_syntax + + use ra_syntax::ast; + <|> + + //- /lib.rs crate:ra_syntax + pub mod ast { + pub enum ModuleItem { + A, B, C, + } + } + "#, + "ast::ModuleItem", + ); + + check_found_path( + r#" + //- /main.rs crate:main deps:ra_syntax + + <|> + + //- /lib.rs crate:ra_syntax + pub mod ast { + pub enum ModuleItem { + A, B, C, + } + } + "#, + "ra_syntax::ast::ModuleItem", + ); } #[test] diff --git a/crates/ra_hir_def/src/import_map.rs b/crates/ra_hir_def/src/import_map.rs index 36b4fdd81606..68e20d06b8b1 100644 --- a/crates/ra_hir_def/src/import_map.rs +++ b/crates/ra_hir_def/src/import_map.rs @@ -17,6 +17,15 @@ use crate::{ type FxIndexMap = IndexMap>; +/// Item import details stored in the `ImportMap`. +#[derive(Debug, Clone, Eq, PartialEq)] +pub struct ImportInfo { + /// A path that can be used to import the item, relative to the crate's root. + pub path: ModPath, + /// The module containing this item. + pub container: ModuleId, +} + /// A map from publicly exported items to the path needed to import/name them from a downstream /// crate. /// @@ -26,7 +35,7 @@ type FxIndexMap = IndexMap>; /// Note that all paths are relative to the containing crate's root, so the crate name still needs /// to be prepended to the `ModPath` before the path is valid. pub struct ImportMap { - map: FxIndexMap, + map: FxIndexMap, /// List of keys stored in `map`, sorted lexicographically by their `ModPath`. Indexed by the /// values returned by running `fst`. @@ -78,12 +87,12 @@ impl ImportMap { let path = mk_path(); match import_map.entry(item) { Entry::Vacant(entry) => { - entry.insert(path); + entry.insert(ImportInfo { path, container: module }); } Entry::Occupied(mut entry) => { // If the new path is shorter, prefer that one. - if path.len() < entry.get().len() { - *entry.get_mut() = path; + if path.len() < entry.get().path.len() { + *entry.get_mut() = ImportInfo { path, container: module }; } else { continue; } @@ -119,7 +128,7 @@ impl ImportMap { let start = last_batch_start; last_batch_start = idx + 1; - let key = fst_path(&importables[start].1); + let key = fst_path(&importables[start].1.path); builder.insert(key, start as u64).unwrap(); } @@ -132,6 +141,10 @@ impl ImportMap { /// Returns the `ModPath` needed to import/mention `item`, relative to this crate's root. pub fn path_of(&self, item: ItemInNs) -> Option<&ModPath> { + Some(&self.map.get(&item)?.path) + } + + pub fn import_info_for(&self, item: ItemInNs) -> Option<&ImportInfo> { self.map.get(&item) } } @@ -150,13 +163,13 @@ impl fmt::Debug for ImportMap { let mut importable_paths: Vec<_> = self .map .iter() - .map(|(item, modpath)| { + .map(|(item, info)| { let ns = match item { ItemInNs::Types(_) => "t", ItemInNs::Values(_) => "v", ItemInNs::Macros(_) => "m", }; - format!("- {} ({})", modpath, ns) + format!("- {} ({})", info.path, ns) }) .collect(); @@ -171,9 +184,9 @@ fn fst_path(path: &ModPath) -> String { s } -fn cmp((_, lhs): &(&ItemInNs, &ModPath), (_, rhs): &(&ItemInNs, &ModPath)) -> Ordering { - let lhs_str = fst_path(lhs); - let rhs_str = fst_path(rhs); +fn cmp((_, lhs): &(&ItemInNs, &ImportInfo), (_, rhs): &(&ItemInNs, &ImportInfo)) -> Ordering { + let lhs_str = fst_path(&lhs.path); + let rhs_str = fst_path(&rhs.path); lhs_str.cmp(&rhs_str) } @@ -243,7 +256,7 @@ pub fn search_dependencies<'a>( let importables = &import_map.importables[indexed_value.value as usize..]; // Path shared by the importable items in this group. - let path = &import_map.map[&importables[0]]; + let path = &import_map.map[&importables[0]].path; if query.anchor_end { // Last segment must match query. @@ -256,14 +269,14 @@ pub fn search_dependencies<'a>( // Add the items from this `ModPath` group. Those are all subsequent items in // `importables` whose paths match `path`. let iter = importables.iter().copied().take_while(|item| { - let item_path = &import_map.map[item]; + let item_path = &import_map.map[item].path; fst_path(item_path) == fst_path(path) }); if query.case_sensitive { // FIXME: This does not do a subsequence match. res.extend(iter.filter(|item| { - let item_path = &import_map.map[item]; + let item_path = &import_map.map[item].path; item_path.to_string().contains(&query.query) })); } else { From 591b5ec2c15a83fd10da7049b5f3ea1a783d52ed Mon Sep 17 00:00:00 2001 From: OptimalStrategy <17456182+OptimalStrategy@users.noreply.github.com> Date: Fri, 12 Jun 2020 10:16:19 -0400 Subject: [PATCH 172/172] simplify determining whether the field is a tuple field --- crates/ra_ide/src/diagnostics.rs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/crates/ra_ide/src/diagnostics.rs b/crates/ra_ide/src/diagnostics.rs index 9cd36ad35641..e1bfd72f96af 100644 --- a/crates/ra_ide/src/diagnostics.rs +++ b/crates/ra_ide/src/diagnostics.rs @@ -187,11 +187,7 @@ fn check_struct_shorthand_initialization( if let (Some(name_ref), Some(expr)) = (record_field.name_ref(), record_field.expr()) { let field_name = name_ref.syntax().text().to_string(); let field_expr = expr.syntax().text().to_string(); - let field_name_is_tup_index = name_ref - .syntax() - .first_token() - .map(|token| token.kind().is_literal()) - .unwrap_or(false); + let field_name_is_tup_index = name_ref.as_tuple_field().is_some(); if field_name == field_expr && !field_name_is_tup_index { let mut edit_builder = TextEditBuilder::default(); edit_builder.delete(record_field.syntax().text_range());