diff options
Diffstat (limited to 'crates')
34 files changed, 1316 insertions, 204 deletions
diff --git a/crates/ra_assists/src/handlers/add_missing_impl_members.rs b/crates/ra_assists/src/handlers/add_missing_impl_members.rs index 448697d31..ab21388c8 100644 --- a/crates/ra_assists/src/handlers/add_missing_impl_members.rs +++ b/crates/ra_assists/src/handlers/add_missing_impl_members.rs | |||
@@ -1,4 +1,4 @@ | |||
1 | use hir::{db::HirDatabase, HasSource, InFile}; | 1 | use hir::{HasSource, InFile}; |
2 | use ra_syntax::{ | 2 | use ra_syntax::{ |
3 | ast::{self, edit, make, AstNode, NameOwner}, | 3 | ast::{self, edit, make, AstNode, NameOwner}, |
4 | SmolStr, | 4 | SmolStr, |
@@ -6,6 +6,7 @@ use ra_syntax::{ | |||
6 | 6 | ||
7 | use crate::{ | 7 | use crate::{ |
8 | ast_transform::{self, AstTransform, QualifyPaths, SubstituteTypeParams}, | 8 | ast_transform::{self, AstTransform, QualifyPaths, SubstituteTypeParams}, |
9 | utils::{get_missing_impl_items, resolve_target_trait}, | ||
9 | Assist, AssistCtx, AssistId, | 10 | Assist, AssistCtx, AssistId, |
10 | }; | 11 | }; |
11 | 12 | ||
@@ -103,11 +104,9 @@ fn add_missing_impl_members_inner( | |||
103 | let impl_node = ctx.find_node_at_offset::<ast::ImplBlock>()?; | 104 | let impl_node = ctx.find_node_at_offset::<ast::ImplBlock>()?; |
104 | let impl_item_list = impl_node.item_list()?; | 105 | let impl_item_list = impl_node.item_list()?; |
105 | 106 | ||
106 | let (trait_, trait_def) = { | 107 | let analyzer = ctx.source_analyzer(impl_node.syntax(), None); |
107 | let analyzer = ctx.source_analyzer(impl_node.syntax(), None); | ||
108 | 108 | ||
109 | resolve_target_trait_def(ctx.db, &analyzer, &impl_node)? | 109 | let trait_ = resolve_target_trait(ctx.db, &analyzer, &impl_node)?; |
110 | }; | ||
111 | 110 | ||
112 | let def_name = |item: &ast::ImplItem| -> Option<SmolStr> { | 111 | let def_name = |item: &ast::ImplItem| -> Option<SmolStr> { |
113 | match item { | 112 | match item { |
@@ -118,11 +117,14 @@ fn add_missing_impl_members_inner( | |||
118 | .map(|it| it.text().clone()) | 117 | .map(|it| it.text().clone()) |
119 | }; | 118 | }; |
120 | 119 | ||
121 | let trait_items = trait_def.item_list()?.impl_items(); | 120 | let missing_items = get_missing_impl_items(ctx.db, &analyzer, &impl_node) |
122 | let impl_items = impl_item_list.impl_items().collect::<Vec<_>>(); | 121 | .iter() |
123 | 122 | .map(|i| match i { | |
124 | let missing_items: Vec<_> = trait_items | 123 | hir::AssocItem::Function(i) => ast::ImplItem::FnDef(i.source(ctx.db).value), |
125 | .filter(|t| def_name(t).is_some()) | 124 | hir::AssocItem::TypeAlias(i) => ast::ImplItem::TypeAliasDef(i.source(ctx.db).value), |
125 | hir::AssocItem::Const(i) => ast::ImplItem::ConstDef(i.source(ctx.db).value), | ||
126 | }) | ||
127 | .filter(|t| def_name(&t).is_some()) | ||
126 | .filter(|t| match t { | 128 | .filter(|t| match t { |
127 | ast::ImplItem::FnDef(def) => match mode { | 129 | ast::ImplItem::FnDef(def) => match mode { |
128 | AddMissingImplMembersMode::DefaultMethodsOnly => def.body().is_some(), | 130 | AddMissingImplMembersMode::DefaultMethodsOnly => def.body().is_some(), |
@@ -130,8 +132,8 @@ fn add_missing_impl_members_inner( | |||
130 | }, | 132 | }, |
131 | _ => mode == AddMissingImplMembersMode::NoDefaultMethods, | 133 | _ => mode == AddMissingImplMembersMode::NoDefaultMethods, |
132 | }) | 134 | }) |
133 | .filter(|t| impl_items.iter().all(|i| def_name(i) != def_name(t))) | 135 | .collect::<Vec<_>>(); |
134 | .collect(); | 136 | |
135 | if missing_items.is_empty() { | 137 | if missing_items.is_empty() { |
136 | return None; | 138 | return None; |
137 | } | 139 | } |
@@ -177,27 +179,6 @@ fn add_body(fn_def: ast::FnDef) -> ast::FnDef { | |||
177 | } | 179 | } |
178 | } | 180 | } |
179 | 181 | ||
180 | /// Given an `ast::ImplBlock`, resolves the target trait (the one being | ||
181 | /// implemented) to a `ast::TraitDef`. | ||
182 | fn resolve_target_trait_def( | ||
183 | db: &impl HirDatabase, | ||
184 | analyzer: &hir::SourceAnalyzer, | ||
185 | impl_block: &ast::ImplBlock, | ||
186 | ) -> Option<(hir::Trait, ast::TraitDef)> { | ||
187 | let ast_path = impl_block | ||
188 | .target_trait() | ||
189 | .map(|it| it.syntax().clone()) | ||
190 | .and_then(ast::PathType::cast)? | ||
191 | .path()?; | ||
192 | |||
193 | match analyzer.resolve_path(db, &ast_path) { | ||
194 | Some(hir::PathResolution::Def(hir::ModuleDef::Trait(def))) => { | ||
195 | Some((def, def.source(db).value)) | ||
196 | } | ||
197 | _ => None, | ||
198 | } | ||
199 | } | ||
200 | |||
201 | #[cfg(test)] | 182 | #[cfg(test)] |
202 | mod tests { | 183 | mod tests { |
203 | use super::*; | 184 | use super::*; |
diff --git a/crates/ra_assists/src/lib.rs b/crates/ra_assists/src/lib.rs index 828a8e9e8..cb124eaf0 100644 --- a/crates/ra_assists/src/lib.rs +++ b/crates/ra_assists/src/lib.rs | |||
@@ -9,7 +9,7 @@ mod assist_ctx; | |||
9 | mod marks; | 9 | mod marks; |
10 | #[cfg(test)] | 10 | #[cfg(test)] |
11 | mod doc_tests; | 11 | mod doc_tests; |
12 | mod utils; | 12 | pub mod utils; |
13 | pub mod ast_transform; | 13 | pub mod ast_transform; |
14 | 14 | ||
15 | use ra_db::FileRange; | 15 | use ra_db::FileRange; |
diff --git a/crates/ra_assists/src/utils.rs b/crates/ra_assists/src/utils.rs index 0d5722295..6ff44c95c 100644 --- a/crates/ra_assists/src/utils.rs +++ b/crates/ra_assists/src/utils.rs | |||
@@ -1,10 +1,81 @@ | |||
1 | //! Assorted functions shared by several assists. | 1 | //! Assorted functions shared by several assists. |
2 | 2 | ||
3 | use ra_syntax::{ | 3 | use ra_syntax::{ |
4 | ast::{self, make}, | 4 | ast::{self, make, NameOwner}, |
5 | T, | 5 | AstNode, T, |
6 | }; | 6 | }; |
7 | 7 | ||
8 | use hir::db::HirDatabase; | ||
9 | use rustc_hash::FxHashSet; | ||
10 | |||
11 | pub fn get_missing_impl_items( | ||
12 | db: &impl HirDatabase, | ||
13 | analyzer: &hir::SourceAnalyzer, | ||
14 | impl_block: &ast::ImplBlock, | ||
15 | ) -> Vec<hir::AssocItem> { | ||
16 | // Names must be unique between constants and functions. However, type aliases | ||
17 | // may share the same name as a function or constant. | ||
18 | let mut impl_fns_consts = FxHashSet::default(); | ||
19 | let mut impl_type = FxHashSet::default(); | ||
20 | |||
21 | if let Some(item_list) = impl_block.item_list() { | ||
22 | for item in item_list.impl_items() { | ||
23 | match item { | ||
24 | ast::ImplItem::FnDef(f) => { | ||
25 | if let Some(n) = f.name() { | ||
26 | impl_fns_consts.insert(n.syntax().to_string()); | ||
27 | } | ||
28 | } | ||
29 | |||
30 | ast::ImplItem::TypeAliasDef(t) => { | ||
31 | if let Some(n) = t.name() { | ||
32 | impl_type.insert(n.syntax().to_string()); | ||
33 | } | ||
34 | } | ||
35 | |||
36 | ast::ImplItem::ConstDef(c) => { | ||
37 | if let Some(n) = c.name() { | ||
38 | impl_fns_consts.insert(n.syntax().to_string()); | ||
39 | } | ||
40 | } | ||
41 | } | ||
42 | } | ||
43 | } | ||
44 | |||
45 | resolve_target_trait(db, analyzer, impl_block).map_or(vec![], |target_trait| { | ||
46 | target_trait | ||
47 | .items(db) | ||
48 | .iter() | ||
49 | .filter(|i| match i { | ||
50 | hir::AssocItem::Function(f) => !impl_fns_consts.contains(&f.name(db).to_string()), | ||
51 | hir::AssocItem::TypeAlias(t) => !impl_type.contains(&t.name(db).to_string()), | ||
52 | hir::AssocItem::Const(c) => c | ||
53 | .name(db) | ||
54 | .map(|n| !impl_fns_consts.contains(&n.to_string())) | ||
55 | .unwrap_or_default(), | ||
56 | }) | ||
57 | .cloned() | ||
58 | .collect() | ||
59 | }) | ||
60 | } | ||
61 | |||
62 | pub(crate) fn resolve_target_trait( | ||
63 | db: &impl HirDatabase, | ||
64 | analyzer: &hir::SourceAnalyzer, | ||
65 | impl_block: &ast::ImplBlock, | ||
66 | ) -> Option<hir::Trait> { | ||
67 | let ast_path = impl_block | ||
68 | .target_trait() | ||
69 | .map(|it| it.syntax().clone()) | ||
70 | .and_then(ast::PathType::cast)? | ||
71 | .path()?; | ||
72 | |||
73 | match analyzer.resolve_path(db, &ast_path) { | ||
74 | Some(hir::PathResolution::Def(hir::ModuleDef::Trait(def))) => Some(def), | ||
75 | _ => None, | ||
76 | } | ||
77 | } | ||
78 | |||
8 | pub(crate) fn invert_boolean_expression(expr: ast::Expr) -> ast::Expr { | 79 | pub(crate) fn invert_boolean_expression(expr: ast::Expr) -> ast::Expr { |
9 | if let Some(expr) = invert_special_case(&expr) { | 80 | if let Some(expr) = invert_special_case(&expr) { |
10 | return expr; | 81 | return expr; |
diff --git a/crates/ra_cli/Cargo.toml b/crates/ra_cli/Cargo.toml index bcd408421..53d4876f6 100644 --- a/crates/ra_cli/Cargo.toml +++ b/crates/ra_cli/Cargo.toml | |||
@@ -6,8 +6,10 @@ authors = ["rust-analyzer developers"] | |||
6 | publish = false | 6 | publish = false |
7 | 7 | ||
8 | [dependencies] | 8 | [dependencies] |
9 | itertools = "0.8.0" | ||
9 | pico-args = "0.3.0" | 10 | pico-args = "0.3.0" |
10 | env_logger = { version = "0.7.1", default-features = false } | 11 | env_logger = { version = "0.7.1", default-features = false } |
12 | rand = { version = "0.7.0", features = ["small_rng"] } | ||
11 | 13 | ||
12 | ra_syntax = { path = "../ra_syntax" } | 14 | ra_syntax = { path = "../ra_syntax" } |
13 | ra_ide = { path = "../ra_ide" } | 15 | ra_ide = { path = "../ra_ide" } |
diff --git a/crates/ra_cli/src/analysis_bench.rs b/crates/ra_cli/src/analysis_bench.rs index 5485a38ff..4835a68ce 100644 --- a/crates/ra_cli/src/analysis_bench.rs +++ b/crates/ra_cli/src/analysis_bench.rs | |||
@@ -2,6 +2,7 @@ | |||
2 | 2 | ||
3 | use std::{ | 3 | use std::{ |
4 | path::{Path, PathBuf}, | 4 | path::{Path, PathBuf}, |
5 | str::FromStr, | ||
5 | sync::Arc, | 6 | sync::Arc, |
6 | time::Instant, | 7 | time::Instant, |
7 | }; | 8 | }; |
@@ -14,12 +15,35 @@ use ra_ide::{Analysis, AnalysisChange, AnalysisHost, FilePosition, LineCol}; | |||
14 | 15 | ||
15 | use crate::Result; | 16 | use crate::Result; |
16 | 17 | ||
18 | pub(crate) struct Position { | ||
19 | path: PathBuf, | ||
20 | line: u32, | ||
21 | column: u32, | ||
22 | } | ||
23 | |||
24 | impl FromStr for Position { | ||
25 | type Err = Box<dyn std::error::Error + Send + Sync>; | ||
26 | fn from_str(s: &str) -> Result<Self> { | ||
27 | let (path_line, column) = rsplit_at_char(s, ':')?; | ||
28 | let (path, line) = rsplit_at_char(path_line, ':')?; | ||
29 | Ok(Position { path: path.into(), line: line.parse()?, column: column.parse()? }) | ||
30 | } | ||
31 | } | ||
32 | |||
33 | fn rsplit_at_char(s: &str, c: char) -> Result<(&str, &str)> { | ||
34 | let idx = s.rfind(':').ok_or_else(|| format!("no `{}` in {}", c, s))?; | ||
35 | Ok((&s[..idx], &s[idx + 1..])) | ||
36 | } | ||
37 | |||
17 | pub(crate) enum Op { | 38 | pub(crate) enum Op { |
18 | Highlight { path: PathBuf }, | 39 | Highlight { path: PathBuf }, |
19 | Complete { path: PathBuf, line: u32, column: u32 }, | 40 | Complete(Position), |
41 | GotoDef(Position), | ||
20 | } | 42 | } |
21 | 43 | ||
22 | pub(crate) fn run(verbose: bool, path: &Path, op: Op) -> Result<()> { | 44 | pub(crate) fn run(verbose: bool, path: &Path, op: Op) -> Result<()> { |
45 | ra_prof::init(); | ||
46 | |||
23 | let start = Instant::now(); | 47 | let start = Instant::now(); |
24 | eprint!("loading: "); | 48 | eprint!("loading: "); |
25 | let (mut host, roots) = ra_batch::load_cargo(path)?; | 49 | let (mut host, roots) = ra_batch::load_cargo(path)?; |
@@ -29,7 +53,7 @@ pub(crate) fn run(verbose: bool, path: &Path, op: Op) -> Result<()> { | |||
29 | let file_id = { | 53 | let file_id = { |
30 | let path = match &op { | 54 | let path = match &op { |
31 | Op::Highlight { path } => path, | 55 | Op::Highlight { path } => path, |
32 | Op::Complete { path, .. } => path, | 56 | Op::Complete(pos) | Op::GotoDef(pos) => &pos.path, |
33 | }; | 57 | }; |
34 | let path = std::env::current_dir()?.join(path).canonicalize()?; | 58 | let path = std::env::current_dir()?.join(path).canonicalize()?; |
35 | roots | 59 | roots |
@@ -49,7 +73,7 @@ pub(crate) fn run(verbose: bool, path: &Path, op: Op) -> Result<()> { | |||
49 | .ok_or_else(|| format!("Can't find {:?}", path))? | 73 | .ok_or_else(|| format!("Can't find {:?}", path))? |
50 | }; | 74 | }; |
51 | 75 | ||
52 | match op { | 76 | match &op { |
53 | Op::Highlight { .. } => { | 77 | Op::Highlight { .. } => { |
54 | let res = do_work(&mut host, file_id, |analysis| { | 78 | let res = do_work(&mut host, file_id, |analysis| { |
55 | analysis.diagnostics(file_id).unwrap(); | 79 | analysis.diagnostics(file_id).unwrap(); |
@@ -59,16 +83,30 @@ pub(crate) fn run(verbose: bool, path: &Path, op: Op) -> Result<()> { | |||
59 | println!("\n{}", res); | 83 | println!("\n{}", res); |
60 | } | 84 | } |
61 | } | 85 | } |
62 | Op::Complete { line, column, .. } => { | 86 | Op::Complete(pos) | Op::GotoDef(pos) => { |
87 | let is_completion = match op { | ||
88 | Op::Complete(..) => true, | ||
89 | _ => false, | ||
90 | }; | ||
91 | |||
63 | let offset = host | 92 | let offset = host |
64 | .analysis() | 93 | .analysis() |
65 | .file_line_index(file_id)? | 94 | .file_line_index(file_id)? |
66 | .offset(LineCol { line, col_utf16: column }); | 95 | .offset(LineCol { line: pos.line - 1, col_utf16: pos.column }); |
67 | let file_postion = FilePosition { file_id, offset }; | 96 | let file_postion = FilePosition { file_id, offset }; |
68 | 97 | ||
69 | let res = do_work(&mut host, file_id, |analysis| analysis.completions(file_postion)); | 98 | if is_completion { |
70 | if verbose { | 99 | let res = |
71 | println!("\n{:#?}", res); | 100 | do_work(&mut host, file_id, |analysis| analysis.completions(file_postion)); |
101 | if verbose { | ||
102 | println!("\n{:#?}", res); | ||
103 | } | ||
104 | } else { | ||
105 | let res = | ||
106 | do_work(&mut host, file_id, |analysis| analysis.goto_definition(file_postion)); | ||
107 | if verbose { | ||
108 | println!("\n{:#?}", res); | ||
109 | } | ||
72 | } | 110 | } |
73 | } | 111 | } |
74 | } | 112 | } |
diff --git a/crates/ra_cli/src/analysis_stats.rs b/crates/ra_cli/src/analysis_stats.rs index 833235bff..6d2dd34c6 100644 --- a/crates/ra_cli/src/analysis_stats.rs +++ b/crates/ra_cli/src/analysis_stats.rs | |||
@@ -2,6 +2,9 @@ | |||
2 | 2 | ||
3 | use std::{collections::HashSet, fmt::Write, path::Path, time::Instant}; | 3 | use std::{collections::HashSet, fmt::Write, path::Path, time::Instant}; |
4 | 4 | ||
5 | use itertools::Itertools; | ||
6 | use rand::{seq::SliceRandom, thread_rng}; | ||
7 | |||
5 | use hir::{ | 8 | use hir::{ |
6 | db::{DefDatabase, HirDatabase}, | 9 | db::{DefDatabase, HirDatabase}, |
7 | AssocItem, Crate, HasSource, HirDisplay, ModuleDef, | 10 | AssocItem, Crate, HasSource, HirDisplay, ModuleDef, |
@@ -19,6 +22,7 @@ pub fn run( | |||
19 | path: &Path, | 22 | path: &Path, |
20 | only: Option<&str>, | 23 | only: Option<&str>, |
21 | with_deps: bool, | 24 | with_deps: bool, |
25 | randomize: bool, | ||
22 | ) -> Result<()> { | 26 | ) -> Result<()> { |
23 | let db_load_time = Instant::now(); | 27 | let db_load_time = Instant::now(); |
24 | let (mut host, roots) = ra_batch::load_cargo(path)?; | 28 | let (mut host, roots) = ra_batch::load_cargo(path)?; |
@@ -41,7 +45,11 @@ pub fn run( | |||
41 | }) | 45 | }) |
42 | .collect::<HashSet<_>>(); | 46 | .collect::<HashSet<_>>(); |
43 | 47 | ||
44 | for krate in Crate::all(db) { | 48 | let mut krates = Crate::all(db); |
49 | if randomize { | ||
50 | krates.shuffle(&mut thread_rng()); | ||
51 | } | ||
52 | for krate in krates { | ||
45 | let module = krate.root_module(db).expect("crate without root module"); | 53 | let module = krate.root_module(db).expect("crate without root module"); |
46 | let file_id = module.definition_source(db).file_id; | 54 | let file_id = module.definition_source(db).file_id; |
47 | if members.contains(&db.file_source_root(file_id.original_file(db))) { | 55 | if members.contains(&db.file_source_root(file_id.original_file(db))) { |
@@ -50,6 +58,10 @@ pub fn run( | |||
50 | } | 58 | } |
51 | } | 59 | } |
52 | 60 | ||
61 | if randomize { | ||
62 | visit_queue.shuffle(&mut thread_rng()); | ||
63 | } | ||
64 | |||
53 | println!("Crates in this dir: {}", num_crates); | 65 | println!("Crates in this dir: {}", num_crates); |
54 | let mut num_decls = 0; | 66 | let mut num_decls = 0; |
55 | let mut funcs = Vec::new(); | 67 | let mut funcs = Vec::new(); |
@@ -79,10 +91,14 @@ pub fn run( | |||
79 | println!("Total functions: {}", funcs.len()); | 91 | println!("Total functions: {}", funcs.len()); |
80 | println!("Item Collection: {:?}, {}", analysis_time.elapsed(), ra_prof::memory_usage()); | 92 | println!("Item Collection: {:?}, {}", analysis_time.elapsed(), ra_prof::memory_usage()); |
81 | 93 | ||
94 | if randomize { | ||
95 | funcs.shuffle(&mut thread_rng()); | ||
96 | } | ||
97 | |||
82 | let inference_time = Instant::now(); | 98 | let inference_time = Instant::now(); |
83 | let mut bar = match verbosity { | 99 | let mut bar = match verbosity { |
84 | Verbosity::Verbose | Verbosity::Normal => ProgressReport::new(funcs.len() as u64), | 100 | Verbosity::Quiet | Verbosity::Spammy => ProgressReport::hidden(), |
85 | Verbosity::Quiet => ProgressReport::hidden(), | 101 | _ => ProgressReport::new(funcs.len() as u64), |
86 | }; | 102 | }; |
87 | 103 | ||
88 | bar.tick(); | 104 | bar.tick(); |
@@ -92,7 +108,20 @@ pub fn run( | |||
92 | let mut num_type_mismatches = 0; | 108 | let mut num_type_mismatches = 0; |
93 | for f in funcs { | 109 | for f in funcs { |
94 | let name = f.name(db); | 110 | let name = f.name(db); |
95 | let mut msg = format!("processing: {}", name); | 111 | let full_name = f |
112 | .module(db) | ||
113 | .path_to_root(db) | ||
114 | .into_iter() | ||
115 | .rev() | ||
116 | .filter_map(|it| it.name(db)) | ||
117 | .chain(Some(f.name(db))) | ||
118 | .join("::"); | ||
119 | if let Some(only_name) = only { | ||
120 | if name.to_string() != only_name && full_name != only_name { | ||
121 | continue; | ||
122 | } | ||
123 | } | ||
124 | let mut msg = format!("processing: {}", full_name); | ||
96 | if verbosity.is_verbose() { | 125 | if verbosity.is_verbose() { |
97 | let src = f.source(db); | 126 | let src = f.source(db); |
98 | let original_file = src.file_id.original_file(db); | 127 | let original_file = src.file_id.original_file(db); |
@@ -100,15 +129,15 @@ pub fn run( | |||
100 | let syntax_range = src.value.syntax().text_range(); | 129 | let syntax_range = src.value.syntax().text_range(); |
101 | write!(msg, " ({:?} {})", path, syntax_range).unwrap(); | 130 | write!(msg, " ({:?} {})", path, syntax_range).unwrap(); |
102 | } | 131 | } |
103 | bar.set_message(&msg); | 132 | if verbosity.is_spammy() { |
104 | if let Some(only_name) = only { | 133 | bar.println(format!("{}", msg)); |
105 | if name.to_string() != only_name { | ||
106 | continue; | ||
107 | } | ||
108 | } | 134 | } |
135 | bar.set_message(&msg); | ||
109 | let f_id = FunctionId::from(f); | 136 | let f_id = FunctionId::from(f); |
110 | let body = db.body(f_id.into()); | 137 | let body = db.body(f_id.into()); |
111 | let inference_result = db.infer(f_id.into()); | 138 | let inference_result = db.infer(f_id.into()); |
139 | let (previous_exprs, previous_unknown, previous_partially_unknown) = | ||
140 | (num_exprs, num_exprs_unknown, num_exprs_partially_unknown); | ||
112 | for (expr_id, _) in body.exprs.iter() { | 141 | for (expr_id, _) in body.exprs.iter() { |
113 | let ty = &inference_result[expr_id]; | 142 | let ty = &inference_result[expr_id]; |
114 | num_exprs += 1; | 143 | num_exprs += 1; |
@@ -125,6 +154,33 @@ pub fn run( | |||
125 | num_exprs_partially_unknown += 1; | 154 | num_exprs_partially_unknown += 1; |
126 | } | 155 | } |
127 | } | 156 | } |
157 | if only.is_some() && verbosity.is_spammy() { | ||
158 | // in super-verbose mode for just one function, we print every single expression | ||
159 | let (_, sm) = db.body_with_source_map(f_id.into()); | ||
160 | let src = sm.expr_syntax(expr_id); | ||
161 | if let Some(src) = src { | ||
162 | let original_file = src.file_id.original_file(db); | ||
163 | let line_index = host.analysis().file_line_index(original_file).unwrap(); | ||
164 | let text_range = src.value.either( | ||
165 | |it| it.syntax_node_ptr().range(), | ||
166 | |it| it.syntax_node_ptr().range(), | ||
167 | ); | ||
168 | let (start, end) = ( | ||
169 | line_index.line_col(text_range.start()), | ||
170 | line_index.line_col(text_range.end()), | ||
171 | ); | ||
172 | bar.println(format!( | ||
173 | "{}:{}-{}:{}: {}", | ||
174 | start.line + 1, | ||
175 | start.col_utf16, | ||
176 | end.line + 1, | ||
177 | end.col_utf16, | ||
178 | ty.display(db) | ||
179 | )); | ||
180 | } else { | ||
181 | bar.println(format!("unknown location: {}", ty.display(db))); | ||
182 | } | ||
183 | } | ||
128 | if let Some(mismatch) = inference_result.type_mismatch_for_expr(expr_id) { | 184 | if let Some(mismatch) = inference_result.type_mismatch_for_expr(expr_id) { |
129 | num_type_mismatches += 1; | 185 | num_type_mismatches += 1; |
130 | if verbosity.is_verbose() { | 186 | if verbosity.is_verbose() { |
@@ -164,6 +220,15 @@ pub fn run( | |||
164 | } | 220 | } |
165 | } | 221 | } |
166 | } | 222 | } |
223 | if verbosity.is_spammy() { | ||
224 | bar.println(format!( | ||
225 | "In {}: {} exprs, {} unknown, {} partial", | ||
226 | full_name, | ||
227 | num_exprs - previous_exprs, | ||
228 | num_exprs_unknown - previous_unknown, | ||
229 | num_exprs_partially_unknown - previous_partially_unknown | ||
230 | )); | ||
231 | } | ||
167 | bar.inc(1); | 232 | bar.inc(1); |
168 | } | 233 | } |
169 | bar.finish_and_clear(); | 234 | bar.finish_and_clear(); |
diff --git a/crates/ra_cli/src/main.rs b/crates/ra_cli/src/main.rs index 806612c2c..750cbab86 100644 --- a/crates/ra_cli/src/main.rs +++ b/crates/ra_cli/src/main.rs | |||
@@ -16,6 +16,7 @@ type Result<T> = std::result::Result<T, Box<dyn Error + Send + Sync>>; | |||
16 | 16 | ||
17 | #[derive(Clone, Copy)] | 17 | #[derive(Clone, Copy)] |
18 | pub enum Verbosity { | 18 | pub enum Verbosity { |
19 | Spammy, | ||
19 | Verbose, | 20 | Verbose, |
20 | Normal, | 21 | Normal, |
21 | Quiet, | 22 | Quiet, |
@@ -24,7 +25,13 @@ pub enum Verbosity { | |||
24 | impl Verbosity { | 25 | impl Verbosity { |
25 | fn is_verbose(self) -> bool { | 26 | fn is_verbose(self) -> bool { |
26 | match self { | 27 | match self { |
27 | Verbosity::Verbose => true, | 28 | Verbosity::Verbose | Verbosity::Spammy => true, |
29 | _ => false, | ||
30 | } | ||
31 | } | ||
32 | fn is_spammy(self) -> bool { | ||
33 | match self { | ||
34 | Verbosity::Spammy => true, | ||
28 | _ => false, | 35 | _ => false, |
29 | } | 36 | } |
30 | } | 37 | } |
@@ -86,14 +93,18 @@ fn main() -> Result<()> { | |||
86 | return Ok(()); | 93 | return Ok(()); |
87 | } | 94 | } |
88 | let verbosity = match ( | 95 | let verbosity = match ( |
96 | matches.contains(["-vv", "--spammy"]), | ||
89 | matches.contains(["-v", "--verbose"]), | 97 | matches.contains(["-v", "--verbose"]), |
90 | matches.contains(["-q", "--quiet"]), | 98 | matches.contains(["-q", "--quiet"]), |
91 | ) { | 99 | ) { |
92 | (false, false) => Verbosity::Normal, | 100 | (true, _, true) => Err("Invalid flags: -q conflicts with -vv")?, |
93 | (false, true) => Verbosity::Quiet, | 101 | (true, _, false) => Verbosity::Spammy, |
94 | (true, false) => Verbosity::Verbose, | 102 | (false, false, false) => Verbosity::Normal, |
95 | (true, true) => Err("Invalid flags: -q conflicts with -v")?, | 103 | (false, false, true) => Verbosity::Quiet, |
104 | (false, true, false) => Verbosity::Verbose, | ||
105 | (false, true, true) => Err("Invalid flags: -q conflicts with -v")?, | ||
96 | }; | 106 | }; |
107 | let randomize = matches.contains("--randomize"); | ||
97 | let memory_usage = matches.contains("--memory-usage"); | 108 | let memory_usage = matches.contains("--memory-usage"); |
98 | let only: Option<String> = matches.opt_value_from_str(["-o", "--only"])?; | 109 | let only: Option<String> = matches.opt_value_from_str(["-o", "--only"])?; |
99 | let with_deps: bool = matches.contains("--with-deps"); | 110 | let with_deps: bool = matches.contains("--with-deps"); |
@@ -111,6 +122,7 @@ fn main() -> Result<()> { | |||
111 | path.as_ref(), | 122 | path.as_ref(), |
112 | only.as_ref().map(String::as_ref), | 123 | only.as_ref().map(String::as_ref), |
113 | with_deps, | 124 | with_deps, |
125 | randomize, | ||
114 | )?; | 126 | )?; |
115 | } | 127 | } |
116 | "analysis-bench" => { | 128 | "analysis-bench" => { |
@@ -120,25 +132,16 @@ fn main() -> Result<()> { | |||
120 | } | 132 | } |
121 | let verbose = matches.contains(["-v", "--verbose"]); | 133 | let verbose = matches.contains(["-v", "--verbose"]); |
122 | let path: String = matches.opt_value_from_str("--path")?.unwrap_or_default(); | 134 | let path: String = matches.opt_value_from_str("--path")?.unwrap_or_default(); |
123 | let highlight_path = matches.opt_value_from_str("--highlight")?; | 135 | let highlight_path: Option<String> = matches.opt_value_from_str("--highlight")?; |
124 | let complete_path = matches.opt_value_from_str("--complete")?; | 136 | let complete_path: Option<String> = matches.opt_value_from_str("--complete")?; |
125 | if highlight_path.is_some() && complete_path.is_some() { | 137 | let goto_def_path: Option<String> = matches.opt_value_from_str("--goto-def")?; |
126 | panic!("either --highlight or --complete must be set, not both") | 138 | let op = match (highlight_path, complete_path, goto_def_path) { |
127 | } | 139 | (Some(path), None, None) => analysis_bench::Op::Highlight { path: path.into() }, |
128 | let op = if let Some(path) = highlight_path { | 140 | (None, Some(position), None) => analysis_bench::Op::Complete(position.parse()?), |
129 | let path: String = path; | 141 | (None, None, Some(position)) => analysis_bench::Op::GotoDef(position.parse()?), |
130 | analysis_bench::Op::Highlight { path: path.into() } | 142 | _ => panic!( |
131 | } else if let Some(path_line_col) = complete_path { | 143 | "exactly one of `--highlight`, `--complete` or `--goto-def` must be set" |
132 | let path_line_col: String = path_line_col; | 144 | ), |
133 | let (path_line, column) = rsplit_at_char(path_line_col.as_str(), ':')?; | ||
134 | let (path, line) = rsplit_at_char(path_line, ':')?; | ||
135 | analysis_bench::Op::Complete { | ||
136 | path: path.into(), | ||
137 | line: line.parse()?, | ||
138 | column: column.parse()?, | ||
139 | } | ||
140 | } else { | ||
141 | panic!("either --highlight or --complete must be set") | ||
142 | }; | 145 | }; |
143 | matches.finish().or_else(handle_extra_flags)?; | 146 | matches.finish().or_else(handle_extra_flags)?; |
144 | analysis_bench::run(verbose, path.as_ref(), op)?; | 147 | analysis_bench::run(verbose, path.as_ref(), op)?; |
@@ -171,8 +174,3 @@ fn read_stdin() -> Result<String> { | |||
171 | std::io::stdin().read_to_string(&mut buff)?; | 174 | std::io::stdin().read_to_string(&mut buff)?; |
172 | Ok(buff) | 175 | Ok(buff) |
173 | } | 176 | } |
174 | |||
175 | fn rsplit_at_char(s: &str, c: char) -> Result<(&str, &str)> { | ||
176 | let idx = s.rfind(':').ok_or_else(|| format!("no `{}` in {}", c, s))?; | ||
177 | Ok((&s[..idx], &s[idx + 1..])) | ||
178 | } | ||
diff --git a/crates/ra_hir/src/source_analyzer.rs b/crates/ra_hir/src/source_analyzer.rs index bb9a35c5d..94d5b4cfd 100644 --- a/crates/ra_hir/src/source_analyzer.rs +++ b/crates/ra_hir/src/source_analyzer.rs | |||
@@ -15,11 +15,9 @@ use hir_def::{ | |||
15 | }, | 15 | }, |
16 | expr::{ExprId, PatId}, | 16 | expr::{ExprId, PatId}, |
17 | resolver::{self, resolver_for_scope, Resolver, TypeNs, ValueNs}, | 17 | resolver::{self, resolver_for_scope, Resolver, TypeNs, ValueNs}, |
18 | DefWithBodyId, TraitId, | 18 | AsMacroCall, DefWithBodyId, TraitId, |
19 | }; | ||
20 | use hir_expand::{ | ||
21 | hygiene::Hygiene, name::AsName, AstId, HirFileId, InFile, MacroCallId, MacroCallKind, | ||
22 | }; | 19 | }; |
20 | use hir_expand::{hygiene::Hygiene, name::AsName, HirFileId, InFile, MacroCallId}; | ||
23 | use hir_ty::{InEnvironment, InferenceResult, TraitEnvironment}; | 21 | use hir_ty::{InEnvironment, InferenceResult, TraitEnvironment}; |
24 | use ra_syntax::{ | 22 | use ra_syntax::{ |
25 | ast::{self, AstNode}, | 23 | ast::{self, AstNode}, |
@@ -363,12 +361,10 @@ impl SourceAnalyzer { | |||
363 | db: &impl HirDatabase, | 361 | db: &impl HirDatabase, |
364 | macro_call: InFile<&ast::MacroCall>, | 362 | macro_call: InFile<&ast::MacroCall>, |
365 | ) -> Option<Expansion> { | 363 | ) -> Option<Expansion> { |
366 | let def = self.resolve_macro_call(db, macro_call)?.id; | 364 | let macro_call_id = macro_call.as_call_id(db, |path| { |
367 | let ast_id = AstId::new( | 365 | self.resolver.resolve_path_as_macro(db, &path).map(|it| it.into()) |
368 | macro_call.file_id, | 366 | })?; |
369 | db.ast_id_map(macro_call.file_id).ast_id(macro_call.value), | 367 | Some(Expansion { macro_call_id }) |
370 | ); | ||
371 | Some(Expansion { macro_call_id: def.as_call_id(db, MacroCallKind::FnLike(ast_id)) }) | ||
372 | } | 368 | } |
373 | } | 369 | } |
374 | 370 | ||
diff --git a/crates/ra_hir_def/src/body.rs b/crates/ra_hir_def/src/body.rs index 142c52d35..010d35e55 100644 --- a/crates/ra_hir_def/src/body.rs +++ b/crates/ra_hir_def/src/body.rs | |||
@@ -7,9 +7,7 @@ use std::{mem, ops::Index, sync::Arc}; | |||
7 | 7 | ||
8 | use drop_bomb::DropBomb; | 8 | use drop_bomb::DropBomb; |
9 | use either::Either; | 9 | use either::Either; |
10 | use hir_expand::{ | 10 | use hir_expand::{ast_id_map::AstIdMap, hygiene::Hygiene, AstId, HirFileId, InFile, MacroDefId}; |
11 | ast_id_map::AstIdMap, hygiene::Hygiene, AstId, HirFileId, InFile, MacroCallKind, MacroDefId, | ||
12 | }; | ||
13 | use ra_arena::{map::ArenaMap, Arena}; | 11 | use ra_arena::{map::ArenaMap, Arena}; |
14 | use ra_prof::profile; | 12 | use ra_prof::profile; |
15 | use ra_syntax::{ast, AstNode, AstPtr}; | 13 | use ra_syntax::{ast, AstNode, AstPtr}; |
@@ -23,7 +21,7 @@ use crate::{ | |||
23 | nameres::CrateDefMap, | 21 | nameres::CrateDefMap, |
24 | path::{ModPath, Path}, | 22 | path::{ModPath, Path}, |
25 | src::HasSource, | 23 | src::HasSource, |
26 | DefWithBodyId, HasModule, Lookup, ModuleId, | 24 | AsMacroCall, DefWithBodyId, HasModule, Lookup, ModuleId, |
27 | }; | 25 | }; |
28 | 26 | ||
29 | pub(crate) struct Expander { | 27 | pub(crate) struct Expander { |
@@ -51,30 +49,26 @@ impl Expander { | |||
51 | db: &DB, | 49 | db: &DB, |
52 | macro_call: ast::MacroCall, | 50 | macro_call: ast::MacroCall, |
53 | ) -> Option<(Mark, T)> { | 51 | ) -> Option<(Mark, T)> { |
54 | let ast_id = AstId::new( | 52 | let macro_call = InFile::new(self.current_file_id, ¯o_call); |
55 | self.current_file_id, | 53 | |
56 | db.ast_id_map(self.current_file_id).ast_id(¯o_call), | 54 | if let Some(call_id) = |
57 | ); | 55 | macro_call.as_call_id(db, |path| self.resolve_path_as_macro(db, &path)) |
58 | 56 | { | |
59 | if let Some(path) = macro_call.path().and_then(|path| self.parse_mod_path(path)) { | 57 | let file_id = call_id.as_file(); |
60 | if let Some(def) = self.resolve_path_as_macro(db, &path) { | 58 | if let Some(node) = db.parse_or_expand(file_id) { |
61 | let call_id = def.as_call_id(db, MacroCallKind::FnLike(ast_id)); | 59 | if let Some(expr) = T::cast(node) { |
62 | let file_id = call_id.as_file(); | 60 | log::debug!("macro expansion {:#?}", expr.syntax()); |
63 | if let Some(node) = db.parse_or_expand(file_id) { | 61 | |
64 | if let Some(expr) = T::cast(node) { | 62 | let mark = Mark { |
65 | log::debug!("macro expansion {:#?}", expr.syntax()); | 63 | file_id: self.current_file_id, |
66 | 64 | ast_id_map: mem::take(&mut self.ast_id_map), | |
67 | let mark = Mark { | 65 | bomb: DropBomb::new("expansion mark dropped"), |
68 | file_id: self.current_file_id, | 66 | }; |
69 | ast_id_map: mem::take(&mut self.ast_id_map), | 67 | self.hygiene = Hygiene::new(db, file_id); |
70 | bomb: DropBomb::new("expansion mark dropped"), | 68 | self.current_file_id = file_id; |
71 | }; | 69 | self.ast_id_map = db.ast_id_map(file_id); |
72 | self.hygiene = Hygiene::new(db, file_id); | 70 | |
73 | self.current_file_id = file_id; | 71 | return Some((mark, expr)); |
74 | self.ast_id_map = db.ast_id_map(file_id); | ||
75 | |||
76 | return Some((mark, expr)); | ||
77 | } | ||
78 | } | 72 | } |
79 | } | 73 | } |
80 | } | 74 | } |
@@ -99,10 +93,6 @@ impl Expander { | |||
99 | Path::from_src(path, &self.hygiene) | 93 | Path::from_src(path, &self.hygiene) |
100 | } | 94 | } |
101 | 95 | ||
102 | fn parse_mod_path(&mut self, path: ast::Path) -> Option<ModPath> { | ||
103 | ModPath::from_src(path, &self.hygiene) | ||
104 | } | ||
105 | |||
106 | fn resolve_path_as_macro(&self, db: &impl DefDatabase, path: &ModPath) -> Option<MacroDefId> { | 96 | fn resolve_path_as_macro(&self, db: &impl DefDatabase, path: &ModPath) -> Option<MacroDefId> { |
107 | self.crate_def_map | 97 | self.crate_def_map |
108 | .resolve_path(db, self.module.local_id, path, BuiltinShadowMode::Other) | 98 | .resolve_path(db, self.module.local_id, path, BuiltinShadowMode::Other) |
diff --git a/crates/ra_hir_def/src/lib.rs b/crates/ra_hir_def/src/lib.rs index feb3a300d..aa0b558b8 100644 --- a/crates/ra_hir_def/src/lib.rs +++ b/crates/ra_hir_def/src/lib.rs | |||
@@ -46,7 +46,10 @@ mod marks; | |||
46 | 46 | ||
47 | use std::hash::Hash; | 47 | use std::hash::Hash; |
48 | 48 | ||
49 | use hir_expand::{ast_id_map::FileAstId, AstId, HirFileId, InFile, MacroDefId}; | 49 | use hir_expand::{ |
50 | ast_id_map::FileAstId, db::AstDatabase, hygiene::Hygiene, AstId, HirFileId, InFile, | ||
51 | MacroCallId, MacroCallKind, MacroDefId, | ||
52 | }; | ||
50 | use ra_arena::{impl_arena_id, RawId}; | 53 | use ra_arena::{impl_arena_id, RawId}; |
51 | use ra_db::{impl_intern_key, salsa, CrateId}; | 54 | use ra_db::{impl_intern_key, salsa, CrateId}; |
52 | use ra_syntax::{ast, AstNode}; | 55 | use ra_syntax::{ast, AstNode}; |
@@ -413,3 +416,61 @@ impl HasModule for StaticLoc { | |||
413 | self.container.module(db) | 416 | self.container.module(db) |
414 | } | 417 | } |
415 | } | 418 | } |
419 | |||
420 | /// A helper trait for converting to MacroCallId | ||
421 | pub trait AsMacroCall { | ||
422 | fn as_call_id( | ||
423 | &self, | ||
424 | db: &(impl db::DefDatabase + AstDatabase), | ||
425 | resolver: impl Fn(path::ModPath) -> Option<MacroDefId>, | ||
426 | ) -> Option<MacroCallId>; | ||
427 | } | ||
428 | |||
429 | impl AsMacroCall for InFile<&ast::MacroCall> { | ||
430 | fn as_call_id( | ||
431 | &self, | ||
432 | db: &(impl db::DefDatabase + AstDatabase), | ||
433 | resolver: impl Fn(path::ModPath) -> Option<MacroDefId>, | ||
434 | ) -> Option<MacroCallId> { | ||
435 | let ast_id = AstId::new(self.file_id, db.ast_id_map(self.file_id).ast_id(self.value)); | ||
436 | let h = Hygiene::new(db, self.file_id); | ||
437 | let path = path::ModPath::from_src(self.value.path()?, &h)?; | ||
438 | |||
439 | AstIdWithPath::new(ast_id.file_id, ast_id.value, path).as_call_id(db, resolver) | ||
440 | } | ||
441 | } | ||
442 | |||
443 | /// Helper wrapper for `AstId` with `ModPath` | ||
444 | #[derive(Clone, Debug, Eq, PartialEq)] | ||
445 | struct AstIdWithPath<T: ast::AstNode> { | ||
446 | pub ast_id: AstId<T>, | ||
447 | pub path: path::ModPath, | ||
448 | } | ||
449 | |||
450 | impl<T: ast::AstNode> AstIdWithPath<T> { | ||
451 | pub fn new(file_id: HirFileId, ast_id: FileAstId<T>, path: path::ModPath) -> AstIdWithPath<T> { | ||
452 | AstIdWithPath { ast_id: AstId::new(file_id, ast_id), path } | ||
453 | } | ||
454 | } | ||
455 | |||
456 | impl AsMacroCall for AstIdWithPath<ast::MacroCall> { | ||
457 | fn as_call_id( | ||
458 | &self, | ||
459 | db: &impl AstDatabase, | ||
460 | resolver: impl Fn(path::ModPath) -> Option<MacroDefId>, | ||
461 | ) -> Option<MacroCallId> { | ||
462 | let def = resolver(self.path.clone())?; | ||
463 | Some(def.as_call_id(db, MacroCallKind::FnLike(self.ast_id.clone()))) | ||
464 | } | ||
465 | } | ||
466 | |||
467 | impl AsMacroCall for AstIdWithPath<ast::ModuleItem> { | ||
468 | fn as_call_id( | ||
469 | &self, | ||
470 | db: &impl AstDatabase, | ||
471 | resolver: impl Fn(path::ModPath) -> Option<MacroDefId>, | ||
472 | ) -> Option<MacroCallId> { | ||
473 | let def = resolver(self.path.clone())?; | ||
474 | Some(def.as_call_id(db, MacroCallKind::Attr(self.ast_id.clone()))) | ||
475 | } | ||
476 | } | ||
diff --git a/crates/ra_hir_def/src/nameres/collector.rs b/crates/ra_hir_def/src/nameres/collector.rs index 6352c71ef..51c65a5d7 100644 --- a/crates/ra_hir_def/src/nameres/collector.rs +++ b/crates/ra_hir_def/src/nameres/collector.rs | |||
@@ -7,7 +7,7 @@ use hir_expand::{ | |||
7 | builtin_derive::find_builtin_derive, | 7 | builtin_derive::find_builtin_derive, |
8 | builtin_macro::find_builtin_macro, | 8 | builtin_macro::find_builtin_macro, |
9 | name::{name, AsName, Name}, | 9 | name::{name, AsName, Name}, |
10 | HirFileId, MacroCallId, MacroCallKind, MacroDefId, MacroDefKind, | 10 | HirFileId, MacroCallId, MacroDefId, MacroDefKind, |
11 | }; | 11 | }; |
12 | use ra_cfg::CfgOptions; | 12 | use ra_cfg::CfgOptions; |
13 | use ra_db::{CrateId, FileId}; | 13 | use ra_db::{CrateId, FileId}; |
@@ -25,8 +25,9 @@ use crate::{ | |||
25 | path::{ImportAlias, ModPath, PathKind}, | 25 | path::{ImportAlias, ModPath, PathKind}, |
26 | per_ns::PerNs, | 26 | per_ns::PerNs, |
27 | visibility::Visibility, | 27 | visibility::Visibility, |
28 | AdtId, AstId, ConstLoc, ContainerId, EnumLoc, EnumVariantId, FunctionLoc, ImplLoc, Intern, | 28 | AdtId, AsMacroCall, AstId, AstIdWithPath, ConstLoc, ContainerId, EnumLoc, EnumVariantId, |
29 | LocalModuleId, ModuleDefId, ModuleId, StaticLoc, StructLoc, TraitLoc, TypeAliasLoc, UnionLoc, | 29 | FunctionLoc, ImplLoc, Intern, LocalModuleId, ModuleDefId, ModuleId, StaticLoc, StructLoc, |
30 | TraitLoc, TypeAliasLoc, UnionLoc, | ||
30 | }; | 31 | }; |
31 | 32 | ||
32 | pub(super) fn collect_defs(db: &impl DefDatabase, mut def_map: CrateDefMap) -> CrateDefMap { | 33 | pub(super) fn collect_defs(db: &impl DefDatabase, mut def_map: CrateDefMap) -> CrateDefMap { |
@@ -99,11 +100,16 @@ struct ImportDirective { | |||
99 | #[derive(Clone, Debug, Eq, PartialEq)] | 100 | #[derive(Clone, Debug, Eq, PartialEq)] |
100 | struct MacroDirective { | 101 | struct MacroDirective { |
101 | module_id: LocalModuleId, | 102 | module_id: LocalModuleId, |
102 | ast_id: AstId<ast::MacroCall>, | 103 | ast_id: AstIdWithPath<ast::MacroCall>, |
103 | path: ModPath, | ||
104 | legacy: Option<MacroCallId>, | 104 | legacy: Option<MacroCallId>, |
105 | } | 105 | } |
106 | 106 | ||
107 | #[derive(Clone, Debug, Eq, PartialEq)] | ||
108 | struct DeriveDirective { | ||
109 | module_id: LocalModuleId, | ||
110 | ast_id: AstIdWithPath<ast::ModuleItem>, | ||
111 | } | ||
112 | |||
107 | /// Walks the tree of module recursively | 113 | /// Walks the tree of module recursively |
108 | struct DefCollector<'a, DB> { | 114 | struct DefCollector<'a, DB> { |
109 | db: &'a DB, | 115 | db: &'a DB, |
@@ -112,7 +118,7 @@ struct DefCollector<'a, DB> { | |||
112 | unresolved_imports: Vec<ImportDirective>, | 118 | unresolved_imports: Vec<ImportDirective>, |
113 | resolved_imports: Vec<ImportDirective>, | 119 | resolved_imports: Vec<ImportDirective>, |
114 | unexpanded_macros: Vec<MacroDirective>, | 120 | unexpanded_macros: Vec<MacroDirective>, |
115 | unexpanded_attribute_macros: Vec<(LocalModuleId, AstId<ast::ModuleItem>, ModPath)>, | 121 | unexpanded_attribute_macros: Vec<DeriveDirective>, |
116 | mod_dirs: FxHashMap<LocalModuleId, ModDir>, | 122 | mod_dirs: FxHashMap<LocalModuleId, ModDir>, |
117 | cfg_options: &'a CfgOptions, | 123 | cfg_options: &'a CfgOptions, |
118 | } | 124 | } |
@@ -146,7 +152,7 @@ where | |||
146 | ReachedFixedPoint::Yes => break, | 152 | ReachedFixedPoint::Yes => break, |
147 | ReachedFixedPoint::No => i += 1, | 153 | ReachedFixedPoint::No => i += 1, |
148 | } | 154 | } |
149 | if i == 1000 { | 155 | if i == 10000 { |
150 | log::error!("name resolution is stuck"); | 156 | log::error!("name resolution is stuck"); |
151 | break; | 157 | break; |
152 | } | 158 | } |
@@ -515,16 +521,16 @@ where | |||
515 | return false; | 521 | return false; |
516 | } | 522 | } |
517 | 523 | ||
518 | let resolved_res = self.def_map.resolve_path_fp_with_macro( | 524 | if let Some(call_id) = directive.ast_id.as_call_id(self.db, |path| { |
519 | self.db, | 525 | let resolved_res = self.def_map.resolve_path_fp_with_macro( |
520 | ResolveMode::Other, | 526 | self.db, |
521 | directive.module_id, | 527 | ResolveMode::Other, |
522 | &directive.path, | 528 | directive.module_id, |
523 | BuiltinShadowMode::Module, | 529 | &path, |
524 | ); | 530 | BuiltinShadowMode::Module, |
525 | 531 | ); | |
526 | if let Some(def) = resolved_res.resolved_def.take_macros() { | 532 | resolved_res.resolved_def.take_macros() |
527 | let call_id = def.as_call_id(self.db, MacroCallKind::FnLike(directive.ast_id)); | 533 | }) { |
528 | resolved.push((directive.module_id, call_id)); | 534 | resolved.push((directive.module_id, call_id)); |
529 | res = ReachedFixedPoint::No; | 535 | res = ReachedFixedPoint::No; |
530 | return false; | 536 | return false; |
@@ -532,12 +538,11 @@ where | |||
532 | 538 | ||
533 | true | 539 | true |
534 | }); | 540 | }); |
535 | attribute_macros.retain(|(module_id, ast_id, path)| { | 541 | attribute_macros.retain(|directive| { |
536 | let resolved_res = self.resolve_attribute_macro(path); | 542 | if let Some(call_id) = |
537 | 543 | directive.ast_id.as_call_id(self.db, |path| self.resolve_attribute_macro(&path)) | |
538 | if let Some(def) = resolved_res { | 544 | { |
539 | let call_id = def.as_call_id(self.db, MacroCallKind::Attr(*ast_id)); | 545 | resolved.push((directive.module_id, call_id)); |
540 | resolved.push((*module_id, call_id)); | ||
541 | res = ReachedFixedPoint::No; | 546 | res = ReachedFixedPoint::No; |
542 | return false; | 547 | return false; |
543 | } | 548 | } |
@@ -833,20 +838,22 @@ where | |||
833 | }; | 838 | }; |
834 | let path = ModPath::from_tt_ident(ident); | 839 | let path = ModPath::from_tt_ident(ident); |
835 | 840 | ||
836 | let ast_id = AstId::new(self.file_id, def.kind.ast_id()); | 841 | let ast_id = AstIdWithPath::new(self.file_id, def.kind.ast_id(), path); |
837 | self.def_collector.unexpanded_attribute_macros.push((self.module_id, ast_id, path)); | 842 | self.def_collector |
843 | .unexpanded_attribute_macros | ||
844 | .push(DeriveDirective { module_id: self.module_id, ast_id }); | ||
838 | } | 845 | } |
839 | } | 846 | } |
840 | } | 847 | } |
841 | 848 | ||
842 | fn collect_macro(&mut self, mac: &raw::MacroData) { | 849 | fn collect_macro(&mut self, mac: &raw::MacroData) { |
843 | let ast_id = AstId::new(self.file_id, mac.ast_id); | 850 | let mut ast_id = AstIdWithPath::new(self.file_id, mac.ast_id, mac.path.clone()); |
844 | 851 | ||
845 | // Case 0: builtin macros | 852 | // Case 0: builtin macros |
846 | if mac.builtin { | 853 | if mac.builtin { |
847 | if let Some(name) = &mac.name { | 854 | if let Some(name) = &mac.name { |
848 | let krate = self.def_collector.def_map.krate; | 855 | let krate = self.def_collector.def_map.krate; |
849 | if let Some(macro_id) = find_builtin_macro(name, krate, ast_id) { | 856 | if let Some(macro_id) = find_builtin_macro(name, krate, ast_id.ast_id) { |
850 | self.def_collector.define_macro( | 857 | self.def_collector.define_macro( |
851 | self.module_id, | 858 | self.module_id, |
852 | name.clone(), | 859 | name.clone(), |
@@ -862,7 +869,7 @@ where | |||
862 | if is_macro_rules(&mac.path) { | 869 | if is_macro_rules(&mac.path) { |
863 | if let Some(name) = &mac.name { | 870 | if let Some(name) = &mac.name { |
864 | let macro_id = MacroDefId { | 871 | let macro_id = MacroDefId { |
865 | ast_id: Some(ast_id), | 872 | ast_id: Some(ast_id.ast_id), |
866 | krate: Some(self.def_collector.def_map.krate), | 873 | krate: Some(self.def_collector.def_map.krate), |
867 | kind: MacroDefKind::Declarative, | 874 | kind: MacroDefKind::Declarative, |
868 | }; | 875 | }; |
@@ -872,15 +879,13 @@ where | |||
872 | } | 879 | } |
873 | 880 | ||
874 | // Case 2: try to resolve in legacy scope and expand macro_rules | 881 | // Case 2: try to resolve in legacy scope and expand macro_rules |
875 | if let Some(macro_def) = mac.path.as_ident().and_then(|name| { | 882 | if let Some(macro_call_id) = ast_id.as_call_id(self.def_collector.db, |path| { |
876 | self.def_collector.def_map[self.module_id].scope.get_legacy_macro(&name) | 883 | path.as_ident().and_then(|name| { |
884 | self.def_collector.def_map[self.module_id].scope.get_legacy_macro(&name) | ||
885 | }) | ||
877 | }) { | 886 | }) { |
878 | let macro_call_id = | ||
879 | macro_def.as_call_id(self.def_collector.db, MacroCallKind::FnLike(ast_id)); | ||
880 | |||
881 | self.def_collector.unexpanded_macros.push(MacroDirective { | 887 | self.def_collector.unexpanded_macros.push(MacroDirective { |
882 | module_id: self.module_id, | 888 | module_id: self.module_id, |
883 | path: mac.path.clone(), | ||
884 | ast_id, | 889 | ast_id, |
885 | legacy: Some(macro_call_id), | 890 | legacy: Some(macro_call_id), |
886 | }); | 891 | }); |
@@ -890,14 +895,12 @@ where | |||
890 | 895 | ||
891 | // Case 3: resolve in module scope, expand during name resolution. | 896 | // Case 3: resolve in module scope, expand during name resolution. |
892 | // We rewrite simple path `macro_name` to `self::macro_name` to force resolve in module scope only. | 897 | // We rewrite simple path `macro_name` to `self::macro_name` to force resolve in module scope only. |
893 | let mut path = mac.path.clone(); | 898 | if ast_id.path.is_ident() { |
894 | if path.is_ident() { | 899 | ast_id.path.kind = PathKind::Super(0); |
895 | path.kind = PathKind::Super(0); | ||
896 | } | 900 | } |
897 | 901 | ||
898 | self.def_collector.unexpanded_macros.push(MacroDirective { | 902 | self.def_collector.unexpanded_macros.push(MacroDirective { |
899 | module_id: self.module_id, | 903 | module_id: self.module_id, |
900 | path, | ||
901 | ast_id, | 904 | ast_id, |
902 | legacy: None, | 905 | legacy: None, |
903 | }); | 906 | }); |
diff --git a/crates/ra_hir_ty/src/infer/unify.rs b/crates/ra_hir_ty/src/infer/unify.rs index fe05642ae..1dc842f40 100644 --- a/crates/ra_hir_ty/src/infer/unify.rs +++ b/crates/ra_hir_ty/src/infer/unify.rs | |||
@@ -249,6 +249,8 @@ impl InferenceTable { | |||
249 | match (ty1, ty2) { | 249 | match (ty1, ty2) { |
250 | (Ty::Unknown, _) | (_, Ty::Unknown) => true, | 250 | (Ty::Unknown, _) | (_, Ty::Unknown) => true, |
251 | 251 | ||
252 | (Ty::Placeholder(p1), Ty::Placeholder(p2)) if *p1 == *p2 => true, | ||
253 | |||
252 | (Ty::Infer(InferTy::TypeVar(tv1)), Ty::Infer(InferTy::TypeVar(tv2))) | 254 | (Ty::Infer(InferTy::TypeVar(tv1)), Ty::Infer(InferTy::TypeVar(tv2))) |
253 | | (Ty::Infer(InferTy::IntVar(tv1)), Ty::Infer(InferTy::IntVar(tv2))) | 255 | | (Ty::Infer(InferTy::IntVar(tv1)), Ty::Infer(InferTy::IntVar(tv2))) |
254 | | (Ty::Infer(InferTy::FloatVar(tv1)), Ty::Infer(InferTy::FloatVar(tv2))) | 256 | | (Ty::Infer(InferTy::FloatVar(tv1)), Ty::Infer(InferTy::FloatVar(tv2))) |
diff --git a/crates/ra_hir_ty/src/tests/coercion.rs b/crates/ra_hir_ty/src/tests/coercion.rs index fc5ef36a5..42330b269 100644 --- a/crates/ra_hir_ty/src/tests/coercion.rs +++ b/crates/ra_hir_ty/src/tests/coercion.rs | |||
@@ -526,3 +526,25 @@ fn test() { | |||
526 | "### | 526 | "### |
527 | ); | 527 | ); |
528 | } | 528 | } |
529 | |||
530 | #[test] | ||
531 | fn coerce_placeholder_ref() { | ||
532 | // placeholders should unify, even behind references | ||
533 | assert_snapshot!( | ||
534 | infer_with_mismatches(r#" | ||
535 | struct S<T> { t: T } | ||
536 | impl<TT> S<TT> { | ||
537 | fn get(&self) -> &TT { | ||
538 | &self.t | ||
539 | } | ||
540 | } | ||
541 | "#, true), | ||
542 | @r###" | ||
543 | [51; 55) 'self': &S<TT> | ||
544 | [64; 87) '{ ... }': &TT | ||
545 | [74; 81) '&self.t': &TT | ||
546 | [75; 79) 'self': &S<TT> | ||
547 | [75; 81) 'self.t': TT | ||
548 | "### | ||
549 | ); | ||
550 | } | ||
diff --git a/crates/ra_hir_ty/src/traits.rs b/crates/ra_hir_ty/src/traits.rs index 88af61e87..ff8e75b48 100644 --- a/crates/ra_hir_ty/src/traits.rs +++ b/crates/ra_hir_ty/src/traits.rs | |||
@@ -60,6 +60,9 @@ impl TraitSolver { | |||
60 | context.0.db.check_canceled(); | 60 | context.0.db.check_canceled(); |
61 | let remaining = fuel.get(); | 61 | let remaining = fuel.get(); |
62 | fuel.set(remaining - 1); | 62 | fuel.set(remaining - 1); |
63 | if remaining == 0 { | ||
64 | log::debug!("fuel exhausted"); | ||
65 | } | ||
63 | remaining > 0 | 66 | remaining > 0 |
64 | }) | 67 | }) |
65 | } | 68 | } |
diff --git a/crates/ra_ide/src/completion.rs b/crates/ra_ide/src/completion.rs index fedc02e14..4bdc6ba23 100644 --- a/crates/ra_ide/src/completion.rs +++ b/crates/ra_ide/src/completion.rs | |||
@@ -15,6 +15,7 @@ mod complete_path; | |||
15 | mod complete_scope; | 15 | mod complete_scope; |
16 | mod complete_postfix; | 16 | mod complete_postfix; |
17 | mod complete_macro_in_item_position; | 17 | mod complete_macro_in_item_position; |
18 | mod complete_trait_impl; | ||
18 | 19 | ||
19 | use ra_db::SourceDatabase; | 20 | use ra_db::SourceDatabase; |
20 | use ra_ide_db::RootDatabase; | 21 | use ra_ide_db::RootDatabase; |
@@ -74,5 +75,7 @@ pub(crate) fn completions(db: &RootDatabase, position: FilePosition) -> Option<C | |||
74 | complete_pattern::complete_pattern(&mut acc, &ctx); | 75 | complete_pattern::complete_pattern(&mut acc, &ctx); |
75 | complete_postfix::complete_postfix(&mut acc, &ctx); | 76 | complete_postfix::complete_postfix(&mut acc, &ctx); |
76 | complete_macro_in_item_position::complete_macro_in_item_position(&mut acc, &ctx); | 77 | complete_macro_in_item_position::complete_macro_in_item_position(&mut acc, &ctx); |
78 | complete_trait_impl::complete_trait_impl(&mut acc, &ctx); | ||
79 | |||
77 | Some(acc) | 80 | Some(acc) |
78 | } | 81 | } |
diff --git a/crates/ra_ide/src/completion/complete_trait_impl.rs b/crates/ra_ide/src/completion/complete_trait_impl.rs new file mode 100644 index 000000000..6ff10c017 --- /dev/null +++ b/crates/ra_ide/src/completion/complete_trait_impl.rs | |||
@@ -0,0 +1,436 @@ | |||
1 | //! Completion for associated items in a trait implementation. | ||
2 | //! | ||
3 | //! This module adds the completion items related to implementing associated | ||
4 | //! items within a `impl Trait for Struct` block. The current context node | ||
5 | //! must be within either a `FN_DEF`, `TYPE_ALIAS_DEF`, or `CONST_DEF` node | ||
6 | //! and an direct child of an `IMPL_BLOCK`. | ||
7 | //! | ||
8 | //! # Examples | ||
9 | //! | ||
10 | //! Considering the following trait `impl`: | ||
11 | //! | ||
12 | //! ```ignore | ||
13 | //! trait SomeTrait { | ||
14 | //! fn foo(); | ||
15 | //! } | ||
16 | //! | ||
17 | //! impl SomeTrait for () { | ||
18 | //! fn f<|> | ||
19 | //! } | ||
20 | //! ``` | ||
21 | //! | ||
22 | //! may result in the completion of the following method: | ||
23 | //! | ||
24 | //! ```ignore | ||
25 | //! # trait SomeTrait { | ||
26 | //! # fn foo(); | ||
27 | //! # } | ||
28 | //! | ||
29 | //! impl SomeTrait for () { | ||
30 | //! fn foo() {}<|> | ||
31 | //! } | ||
32 | //! ``` | ||
33 | |||
34 | use hir::{self, Docs, HasSource}; | ||
35 | use ra_assists::utils::get_missing_impl_items; | ||
36 | use ra_syntax::{ | ||
37 | ast::{self, edit}, | ||
38 | AstNode, SyntaxKind, SyntaxNode, TextRange, | ||
39 | }; | ||
40 | use ra_text_edit::TextEdit; | ||
41 | |||
42 | use crate::{ | ||
43 | completion::{ | ||
44 | CompletionContext, CompletionItem, CompletionItemKind, CompletionKind, Completions, | ||
45 | }, | ||
46 | display::FunctionSignature, | ||
47 | }; | ||
48 | |||
49 | pub(crate) fn complete_trait_impl(acc: &mut Completions, ctx: &CompletionContext) { | ||
50 | let trigger = ctx.token.ancestors().find(|p| match p.kind() { | ||
51 | SyntaxKind::FN_DEF | ||
52 | | SyntaxKind::TYPE_ALIAS_DEF | ||
53 | | SyntaxKind::CONST_DEF | ||
54 | | SyntaxKind::BLOCK_EXPR => true, | ||
55 | _ => false, | ||
56 | }); | ||
57 | |||
58 | let impl_block = trigger | ||
59 | .as_ref() | ||
60 | .and_then(|node| node.parent()) | ||
61 | .and_then(|node| node.parent()) | ||
62 | .and_then(|node| ast::ImplBlock::cast(node)); | ||
63 | |||
64 | if let (Some(trigger), Some(impl_block)) = (trigger, impl_block) { | ||
65 | match trigger.kind() { | ||
66 | SyntaxKind::FN_DEF => { | ||
67 | for missing_fn in get_missing_impl_items(ctx.db, &ctx.analyzer, &impl_block) | ||
68 | .iter() | ||
69 | .filter_map(|item| match item { | ||
70 | hir::AssocItem::Function(fn_item) => Some(fn_item), | ||
71 | _ => None, | ||
72 | }) | ||
73 | { | ||
74 | add_function_impl(&trigger, acc, ctx, &missing_fn); | ||
75 | } | ||
76 | } | ||
77 | |||
78 | SyntaxKind::TYPE_ALIAS_DEF => { | ||
79 | for missing_fn in get_missing_impl_items(ctx.db, &ctx.analyzer, &impl_block) | ||
80 | .iter() | ||
81 | .filter_map(|item| match item { | ||
82 | hir::AssocItem::TypeAlias(type_item) => Some(type_item), | ||
83 | _ => None, | ||
84 | }) | ||
85 | { | ||
86 | add_type_alias_impl(&trigger, acc, ctx, &missing_fn); | ||
87 | } | ||
88 | } | ||
89 | |||
90 | SyntaxKind::CONST_DEF => { | ||
91 | for missing_fn in get_missing_impl_items(ctx.db, &ctx.analyzer, &impl_block) | ||
92 | .iter() | ||
93 | .filter_map(|item| match item { | ||
94 | hir::AssocItem::Const(const_item) => Some(const_item), | ||
95 | _ => None, | ||
96 | }) | ||
97 | { | ||
98 | add_const_impl(&trigger, acc, ctx, &missing_fn); | ||
99 | } | ||
100 | } | ||
101 | |||
102 | _ => {} | ||
103 | } | ||
104 | } | ||
105 | } | ||
106 | |||
107 | fn add_function_impl( | ||
108 | fn_def_node: &SyntaxNode, | ||
109 | acc: &mut Completions, | ||
110 | ctx: &CompletionContext, | ||
111 | func: &hir::Function, | ||
112 | ) { | ||
113 | let display = FunctionSignature::from_hir(ctx.db, func.clone()); | ||
114 | |||
115 | let fn_name = func.name(ctx.db).to_string(); | ||
116 | |||
117 | let label = if func.params(ctx.db).len() > 0 { | ||
118 | format!("fn {}(..)", fn_name) | ||
119 | } else { | ||
120 | format!("fn {}()", fn_name) | ||
121 | }; | ||
122 | |||
123 | let builder = CompletionItem::new(CompletionKind::Magic, ctx.source_range(), label.clone()) | ||
124 | .lookup_by(fn_name) | ||
125 | .set_documentation(func.docs(ctx.db)); | ||
126 | |||
127 | let completion_kind = if func.has_self_param(ctx.db) { | ||
128 | CompletionItemKind::Method | ||
129 | } else { | ||
130 | CompletionItemKind::Function | ||
131 | }; | ||
132 | |||
133 | let snippet = format!("{} {{}}", display); | ||
134 | |||
135 | let range = TextRange::from_to(fn_def_node.text_range().start(), ctx.source_range().end()); | ||
136 | |||
137 | builder.text_edit(TextEdit::replace(range, snippet)).kind(completion_kind).add_to(acc); | ||
138 | } | ||
139 | |||
140 | fn add_type_alias_impl( | ||
141 | type_def_node: &SyntaxNode, | ||
142 | acc: &mut Completions, | ||
143 | ctx: &CompletionContext, | ||
144 | type_alias: &hir::TypeAlias, | ||
145 | ) { | ||
146 | let alias_name = type_alias.name(ctx.db).to_string(); | ||
147 | |||
148 | let snippet = format!("type {} = ", alias_name); | ||
149 | |||
150 | let range = TextRange::from_to(type_def_node.text_range().start(), ctx.source_range().end()); | ||
151 | |||
152 | CompletionItem::new(CompletionKind::Magic, ctx.source_range(), snippet.clone()) | ||
153 | .text_edit(TextEdit::replace(range, snippet)) | ||
154 | .lookup_by(alias_name) | ||
155 | .kind(CompletionItemKind::TypeAlias) | ||
156 | .set_documentation(type_alias.docs(ctx.db)) | ||
157 | .add_to(acc); | ||
158 | } | ||
159 | |||
160 | fn add_const_impl( | ||
161 | const_def_node: &SyntaxNode, | ||
162 | acc: &mut Completions, | ||
163 | ctx: &CompletionContext, | ||
164 | const_: &hir::Const, | ||
165 | ) { | ||
166 | let const_name = const_.name(ctx.db).map(|n| n.to_string()); | ||
167 | |||
168 | if let Some(const_name) = const_name { | ||
169 | let snippet = make_const_compl_syntax(&const_.source(ctx.db).value); | ||
170 | |||
171 | let range = | ||
172 | TextRange::from_to(const_def_node.text_range().start(), ctx.source_range().end()); | ||
173 | |||
174 | CompletionItem::new(CompletionKind::Magic, ctx.source_range(), snippet.clone()) | ||
175 | .text_edit(TextEdit::replace(range, snippet)) | ||
176 | .lookup_by(const_name) | ||
177 | .kind(CompletionItemKind::Const) | ||
178 | .set_documentation(const_.docs(ctx.db)) | ||
179 | .add_to(acc); | ||
180 | } | ||
181 | } | ||
182 | |||
183 | fn make_const_compl_syntax(const_: &ast::ConstDef) -> String { | ||
184 | let const_ = edit::strip_attrs_and_docs(const_); | ||
185 | |||
186 | let const_start = const_.syntax().text_range().start(); | ||
187 | let const_end = const_.syntax().text_range().end(); | ||
188 | |||
189 | let start = | ||
190 | const_.syntax().first_child_or_token().map_or(const_start, |f| f.text_range().start()); | ||
191 | |||
192 | let end = const_ | ||
193 | .syntax() | ||
194 | .children_with_tokens() | ||
195 | .find(|s| s.kind() == SyntaxKind::SEMI || s.kind() == SyntaxKind::EQ) | ||
196 | .map_or(const_end, |f| f.text_range().start()); | ||
197 | |||
198 | let len = end - start; | ||
199 | let range = TextRange::from_to(0.into(), len); | ||
200 | |||
201 | let syntax = const_.syntax().text().slice(range).to_string(); | ||
202 | |||
203 | format!("{} = ", syntax.trim_end()) | ||
204 | } | ||
205 | |||
206 | #[cfg(test)] | ||
207 | mod tests { | ||
208 | use crate::completion::{do_completion, CompletionItem, CompletionKind}; | ||
209 | use insta::assert_debug_snapshot; | ||
210 | |||
211 | fn complete(code: &str) -> Vec<CompletionItem> { | ||
212 | do_completion(code, CompletionKind::Magic) | ||
213 | } | ||
214 | |||
215 | #[test] | ||
216 | fn single_function() { | ||
217 | let completions = complete( | ||
218 | r" | ||
219 | trait Test { | ||
220 | fn foo(); | ||
221 | } | ||
222 | |||
223 | struct T1; | ||
224 | |||
225 | impl Test for T1 { | ||
226 | fn f<|> | ||
227 | } | ||
228 | ", | ||
229 | ); | ||
230 | assert_debug_snapshot!(completions, @r###" | ||
231 | [ | ||
232 | CompletionItem { | ||
233 | label: "fn foo()", | ||
234 | source_range: [141; 142), | ||
235 | delete: [138; 142), | ||
236 | insert: "fn foo() {}", | ||
237 | kind: Function, | ||
238 | lookup: "foo", | ||
239 | }, | ||
240 | ] | ||
241 | "###); | ||
242 | } | ||
243 | |||
244 | #[test] | ||
245 | fn hide_implemented_fn() { | ||
246 | let completions = complete( | ||
247 | r" | ||
248 | trait Test { | ||
249 | fn foo(); | ||
250 | fn foo_bar(); | ||
251 | } | ||
252 | |||
253 | struct T1; | ||
254 | |||
255 | impl Test for T1 { | ||
256 | fn foo() {} | ||
257 | |||
258 | fn f<|> | ||
259 | } | ||
260 | ", | ||
261 | ); | ||
262 | assert_debug_snapshot!(completions, @r###" | ||
263 | [ | ||
264 | CompletionItem { | ||
265 | label: "fn foo_bar()", | ||
266 | source_range: [200; 201), | ||
267 | delete: [197; 201), | ||
268 | insert: "fn foo_bar() {}", | ||
269 | kind: Function, | ||
270 | lookup: "foo_bar", | ||
271 | }, | ||
272 | ] | ||
273 | "###); | ||
274 | } | ||
275 | |||
276 | #[test] | ||
277 | fn completes_only_on_top_level() { | ||
278 | let completions = complete( | ||
279 | r" | ||
280 | trait Test { | ||
281 | fn foo(); | ||
282 | |||
283 | fn foo_bar(); | ||
284 | } | ||
285 | |||
286 | struct T1; | ||
287 | |||
288 | impl Test for T1 { | ||
289 | fn foo() { | ||
290 | <|> | ||
291 | } | ||
292 | } | ||
293 | ", | ||
294 | ); | ||
295 | assert_debug_snapshot!(completions, @r###"[]"###); | ||
296 | } | ||
297 | |||
298 | #[test] | ||
299 | fn generic_fn() { | ||
300 | let completions = complete( | ||
301 | r" | ||
302 | trait Test { | ||
303 | fn foo<T>(); | ||
304 | } | ||
305 | |||
306 | struct T1; | ||
307 | |||
308 | impl Test for T1 { | ||
309 | fn f<|> | ||
310 | } | ||
311 | ", | ||
312 | ); | ||
313 | assert_debug_snapshot!(completions, @r###" | ||
314 | [ | ||
315 | CompletionItem { | ||
316 | label: "fn foo()", | ||
317 | source_range: [144; 145), | ||
318 | delete: [141; 145), | ||
319 | insert: "fn foo<T>() {}", | ||
320 | kind: Function, | ||
321 | lookup: "foo", | ||
322 | }, | ||
323 | ] | ||
324 | "###); | ||
325 | } | ||
326 | |||
327 | #[test] | ||
328 | fn generic_constrait_fn() { | ||
329 | let completions = complete( | ||
330 | r" | ||
331 | trait Test { | ||
332 | fn foo<T>() where T: Into<String>; | ||
333 | } | ||
334 | |||
335 | struct T1; | ||
336 | |||
337 | impl Test for T1 { | ||
338 | fn f<|> | ||
339 | } | ||
340 | ", | ||
341 | ); | ||
342 | assert_debug_snapshot!(completions, @r###" | ||
343 | [ | ||
344 | CompletionItem { | ||
345 | label: "fn foo()", | ||
346 | source_range: [166; 167), | ||
347 | delete: [163; 167), | ||
348 | insert: "fn foo<T>()\nwhere T: Into<String> {}", | ||
349 | kind: Function, | ||
350 | lookup: "foo", | ||
351 | }, | ||
352 | ] | ||
353 | "###); | ||
354 | } | ||
355 | |||
356 | #[test] | ||
357 | fn associated_type() { | ||
358 | let completions = complete( | ||
359 | r" | ||
360 | trait Test { | ||
361 | type SomeType; | ||
362 | } | ||
363 | |||
364 | impl Test for () { | ||
365 | type S<|> | ||
366 | } | ||
367 | ", | ||
368 | ); | ||
369 | assert_debug_snapshot!(completions, @r###" | ||
370 | [ | ||
371 | CompletionItem { | ||
372 | label: "type SomeType = ", | ||
373 | source_range: [124; 125), | ||
374 | delete: [119; 125), | ||
375 | insert: "type SomeType = ", | ||
376 | kind: TypeAlias, | ||
377 | lookup: "SomeType", | ||
378 | }, | ||
379 | ] | ||
380 | "###); | ||
381 | } | ||
382 | |||
383 | #[test] | ||
384 | fn associated_const() { | ||
385 | let completions = complete( | ||
386 | r" | ||
387 | trait Test { | ||
388 | const SOME_CONST: u16; | ||
389 | } | ||
390 | |||
391 | impl Test for () { | ||
392 | const S<|> | ||
393 | } | ||
394 | ", | ||
395 | ); | ||
396 | assert_debug_snapshot!(completions, @r###" | ||
397 | [ | ||
398 | CompletionItem { | ||
399 | label: "const SOME_CONST: u16 = ", | ||
400 | source_range: [133; 134), | ||
401 | delete: [127; 134), | ||
402 | insert: "const SOME_CONST: u16 = ", | ||
403 | kind: Const, | ||
404 | lookup: "SOME_CONST", | ||
405 | }, | ||
406 | ] | ||
407 | "###); | ||
408 | } | ||
409 | |||
410 | #[test] | ||
411 | fn associated_const_with_default() { | ||
412 | let completions = complete( | ||
413 | r" | ||
414 | trait Test { | ||
415 | const SOME_CONST: u16 = 42; | ||
416 | } | ||
417 | |||
418 | impl Test for () { | ||
419 | const S<|> | ||
420 | } | ||
421 | ", | ||
422 | ); | ||
423 | assert_debug_snapshot!(completions, @r###" | ||
424 | [ | ||
425 | CompletionItem { | ||
426 | label: "const SOME_CONST: u16 = ", | ||
427 | source_range: [138; 139), | ||
428 | delete: [132; 139), | ||
429 | insert: "const SOME_CONST: u16 = ", | ||
430 | kind: Const, | ||
431 | lookup: "SOME_CONST", | ||
432 | }, | ||
433 | ] | ||
434 | "###); | ||
435 | } | ||
436 | } | ||
diff --git a/crates/ra_ide/src/completion/completion_context.rs b/crates/ra_ide/src/completion/completion_context.rs index 5a0407fd7..8678a3234 100644 --- a/crates/ra_ide/src/completion/completion_context.rs +++ b/crates/ra_ide/src/completion/completion_context.rs | |||
@@ -25,6 +25,7 @@ pub(crate) struct CompletionContext<'a> { | |||
25 | pub(super) use_item_syntax: Option<ast::UseItem>, | 25 | pub(super) use_item_syntax: Option<ast::UseItem>, |
26 | pub(super) record_lit_syntax: Option<ast::RecordLit>, | 26 | pub(super) record_lit_syntax: Option<ast::RecordLit>, |
27 | pub(super) record_lit_pat: Option<ast::RecordPat>, | 27 | pub(super) record_lit_pat: Option<ast::RecordPat>, |
28 | pub(super) impl_block: Option<ast::ImplBlock>, | ||
28 | pub(super) is_param: bool, | 29 | pub(super) is_param: bool, |
29 | /// If a name-binding or reference to a const in a pattern. | 30 | /// If a name-binding or reference to a const in a pattern. |
30 | /// Irrefutable patterns (like let) are excluded. | 31 | /// Irrefutable patterns (like let) are excluded. |
@@ -72,6 +73,7 @@ impl<'a> CompletionContext<'a> { | |||
72 | use_item_syntax: None, | 73 | use_item_syntax: None, |
73 | record_lit_syntax: None, | 74 | record_lit_syntax: None, |
74 | record_lit_pat: None, | 75 | record_lit_pat: None, |
76 | impl_block: None, | ||
75 | is_param: false, | 77 | is_param: false, |
76 | is_pat_binding: false, | 78 | is_pat_binding: false, |
77 | is_trivial_path: false, | 79 | is_trivial_path: false, |
@@ -148,6 +150,13 @@ impl<'a> CompletionContext<'a> { | |||
148 | self.record_lit_syntax = find_node_at_offset(original_file.syntax(), self.offset); | 150 | self.record_lit_syntax = find_node_at_offset(original_file.syntax(), self.offset); |
149 | } | 151 | } |
150 | 152 | ||
153 | self.impl_block = self | ||
154 | .token | ||
155 | .parent() | ||
156 | .ancestors() | ||
157 | .take_while(|it| it.kind() != SOURCE_FILE && it.kind() != MODULE) | ||
158 | .find_map(ast::ImplBlock::cast); | ||
159 | |||
151 | let top_node = name_ref | 160 | let top_node = name_ref |
152 | .syntax() | 161 | .syntax() |
153 | .ancestors() | 162 | .ancestors() |
diff --git a/crates/ra_ide/src/lib.rs b/crates/ra_ide/src/lib.rs index 689921f3f..f86f98be7 100644 --- a/crates/ra_ide/src/lib.rs +++ b/crates/ra_ide/src/lib.rs | |||
@@ -37,6 +37,7 @@ mod display; | |||
37 | mod inlay_hints; | 37 | mod inlay_hints; |
38 | mod expand; | 38 | mod expand; |
39 | mod expand_macro; | 39 | mod expand_macro; |
40 | mod ssr; | ||
40 | 41 | ||
41 | #[cfg(test)] | 42 | #[cfg(test)] |
42 | mod marks; | 43 | mod marks; |
@@ -71,8 +72,9 @@ pub use crate::{ | |||
71 | references::{ | 72 | references::{ |
72 | Declaration, Reference, ReferenceAccess, ReferenceKind, ReferenceSearchResult, SearchScope, | 73 | Declaration, Reference, ReferenceAccess, ReferenceKind, ReferenceSearchResult, SearchScope, |
73 | }, | 74 | }, |
74 | runnables::{Runnable, RunnableKind}, | 75 | runnables::{Runnable, RunnableKind, TestId}, |
75 | source_change::{FileSystemEdit, SourceChange, SourceFileEdit}, | 76 | source_change::{FileSystemEdit, SourceChange, SourceFileEdit}, |
77 | ssr::SsrError, | ||
76 | syntax_highlighting::HighlightedRange, | 78 | syntax_highlighting::HighlightedRange, |
77 | }; | 79 | }; |
78 | 80 | ||
@@ -464,6 +466,16 @@ impl Analysis { | |||
464 | self.with_db(|db| references::rename(db, position, new_name)) | 466 | self.with_db(|db| references::rename(db, position, new_name)) |
465 | } | 467 | } |
466 | 468 | ||
469 | pub fn structural_search_replace( | ||
470 | &self, | ||
471 | query: &str, | ||
472 | ) -> Cancelable<Result<SourceChange, SsrError>> { | ||
473 | self.with_db(|db| { | ||
474 | let edits = ssr::parse_search_replace(query, db)?; | ||
475 | Ok(SourceChange::source_file_edits("ssr", edits)) | ||
476 | }) | ||
477 | } | ||
478 | |||
467 | /// Performs an operation on that may be Canceled. | 479 | /// Performs an operation on that may be Canceled. |
468 | fn with_db<F: FnOnce(&RootDatabase) -> T + std::panic::UnwindSafe, T>( | 480 | fn with_db<F: FnOnce(&RootDatabase) -> T + std::panic::UnwindSafe, T>( |
469 | &self, | 481 | &self, |
diff --git a/crates/ra_ide/src/runnables.rs b/crates/ra_ide/src/runnables.rs index b6b0c70f9..be2a67d0a 100644 --- a/crates/ra_ide/src/runnables.rs +++ b/crates/ra_ide/src/runnables.rs | |||
@@ -1,6 +1,6 @@ | |||
1 | //! FIXME: write short doc here | 1 | //! FIXME: write short doc here |
2 | 2 | ||
3 | use hir::InFile; | 3 | use hir::{InFile, SourceBinder}; |
4 | use itertools::Itertools; | 4 | use itertools::Itertools; |
5 | use ra_db::SourceDatabase; | 5 | use ra_db::SourceDatabase; |
6 | use ra_ide_db::RootDatabase; | 6 | use ra_ide_db::RootDatabase; |
@@ -10,6 +10,7 @@ use ra_syntax::{ | |||
10 | }; | 10 | }; |
11 | 11 | ||
12 | use crate::FileId; | 12 | use crate::FileId; |
13 | use std::fmt::Display; | ||
13 | 14 | ||
14 | #[derive(Debug)] | 15 | #[derive(Debug)] |
15 | pub struct Runnable { | 16 | pub struct Runnable { |
@@ -18,38 +19,84 @@ pub struct Runnable { | |||
18 | } | 19 | } |
19 | 20 | ||
20 | #[derive(Debug)] | 21 | #[derive(Debug)] |
22 | pub enum TestId { | ||
23 | Name(String), | ||
24 | Path(String), | ||
25 | } | ||
26 | |||
27 | impl Display for TestId { | ||
28 | fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { | ||
29 | match self { | ||
30 | TestId::Name(name) => write!(f, "{}", name), | ||
31 | TestId::Path(path) => write!(f, "{}", path), | ||
32 | } | ||
33 | } | ||
34 | } | ||
35 | |||
36 | #[derive(Debug)] | ||
21 | pub enum RunnableKind { | 37 | pub enum RunnableKind { |
22 | Test { name: String }, | 38 | Test { test_id: TestId }, |
23 | TestMod { path: String }, | 39 | TestMod { path: String }, |
24 | Bench { name: String }, | 40 | Bench { test_id: TestId }, |
25 | Bin, | 41 | Bin, |
26 | } | 42 | } |
27 | 43 | ||
28 | pub(crate) fn runnables(db: &RootDatabase, file_id: FileId) -> Vec<Runnable> { | 44 | pub(crate) fn runnables(db: &RootDatabase, file_id: FileId) -> Vec<Runnable> { |
29 | let parse = db.parse(file_id); | 45 | let parse = db.parse(file_id); |
30 | parse.tree().syntax().descendants().filter_map(|i| runnable(db, file_id, i)).collect() | 46 | let mut sb = SourceBinder::new(db); |
47 | parse.tree().syntax().descendants().filter_map(|i| runnable(db, &mut sb, file_id, i)).collect() | ||
31 | } | 48 | } |
32 | 49 | ||
33 | fn runnable(db: &RootDatabase, file_id: FileId, item: SyntaxNode) -> Option<Runnable> { | 50 | fn runnable( |
51 | db: &RootDatabase, | ||
52 | source_binder: &mut SourceBinder<RootDatabase>, | ||
53 | file_id: FileId, | ||
54 | item: SyntaxNode, | ||
55 | ) -> Option<Runnable> { | ||
34 | match_ast! { | 56 | match_ast! { |
35 | match item { | 57 | match item { |
36 | ast::FnDef(it) => { runnable_fn(it) }, | 58 | ast::FnDef(it) => { runnable_fn(db, source_binder, file_id, it) }, |
37 | ast::Module(it) => { runnable_mod(db, file_id, it) }, | 59 | ast::Module(it) => { runnable_mod(db, source_binder, file_id, it) }, |
38 | _ => { None }, | 60 | _ => { None }, |
39 | } | 61 | } |
40 | } | 62 | } |
41 | } | 63 | } |
42 | 64 | ||
43 | fn runnable_fn(fn_def: ast::FnDef) -> Option<Runnable> { | 65 | fn runnable_fn( |
44 | let name = fn_def.name()?.text().clone(); | 66 | db: &RootDatabase, |
45 | let kind = if name == "main" { | 67 | source_binder: &mut SourceBinder<RootDatabase>, |
68 | file_id: FileId, | ||
69 | fn_def: ast::FnDef, | ||
70 | ) -> Option<Runnable> { | ||
71 | let name_string = fn_def.name()?.text().to_string(); | ||
72 | |||
73 | let kind = if name_string == "main" { | ||
46 | RunnableKind::Bin | 74 | RunnableKind::Bin |
47 | } else if has_test_related_attribute(&fn_def) { | ||
48 | RunnableKind::Test { name: name.to_string() } | ||
49 | } else if fn_def.has_atom_attr("bench") { | ||
50 | RunnableKind::Bench { name: name.to_string() } | ||
51 | } else { | 75 | } else { |
52 | return None; | 76 | let test_id = if let Some(module) = source_binder |
77 | .to_def(InFile::new(file_id.into(), fn_def.clone())) | ||
78 | .map(|def| def.module(db)) | ||
79 | { | ||
80 | let path = module | ||
81 | .path_to_root(db) | ||
82 | .into_iter() | ||
83 | .rev() | ||
84 | .filter_map(|it| it.name(db)) | ||
85 | .map(|name| name.to_string()) | ||
86 | .chain(std::iter::once(name_string)) | ||
87 | .join("::"); | ||
88 | TestId::Path(path) | ||
89 | } else { | ||
90 | TestId::Name(name_string) | ||
91 | }; | ||
92 | |||
93 | if has_test_related_attribute(&fn_def) { | ||
94 | RunnableKind::Test { test_id } | ||
95 | } else if fn_def.has_atom_attr("bench") { | ||
96 | RunnableKind::Bench { test_id } | ||
97 | } else { | ||
98 | return None; | ||
99 | } | ||
53 | }; | 100 | }; |
54 | Some(Runnable { range: fn_def.syntax().text_range(), kind }) | 101 | Some(Runnable { range: fn_def.syntax().text_range(), kind }) |
55 | } | 102 | } |
@@ -68,7 +115,12 @@ fn has_test_related_attribute(fn_def: &ast::FnDef) -> bool { | |||
68 | .any(|attribute_text| attribute_text.contains("test")) | 115 | .any(|attribute_text| attribute_text.contains("test")) |
69 | } | 116 | } |
70 | 117 | ||
71 | fn runnable_mod(db: &RootDatabase, file_id: FileId, module: ast::Module) -> Option<Runnable> { | 118 | fn runnable_mod( |
119 | db: &RootDatabase, | ||
120 | source_binder: &mut SourceBinder<RootDatabase>, | ||
121 | file_id: FileId, | ||
122 | module: ast::Module, | ||
123 | ) -> Option<Runnable> { | ||
72 | let has_test_function = module | 124 | let has_test_function = module |
73 | .item_list()? | 125 | .item_list()? |
74 | .items() | 126 | .items() |
@@ -76,13 +128,12 @@ fn runnable_mod(db: &RootDatabase, file_id: FileId, module: ast::Module) -> Opti | |||
76 | ast::ModuleItem::FnDef(it) => Some(it), | 128 | ast::ModuleItem::FnDef(it) => Some(it), |
77 | _ => None, | 129 | _ => None, |
78 | }) | 130 | }) |
79 | .any(|f| f.has_atom_attr("test")); | 131 | .any(|f| has_test_related_attribute(&f)); |
80 | if !has_test_function { | 132 | if !has_test_function { |
81 | return None; | 133 | return None; |
82 | } | 134 | } |
83 | let range = module.syntax().text_range(); | 135 | let range = module.syntax().text_range(); |
84 | let mut sb = hir::SourceBinder::new(db); | 136 | let module = source_binder.to_def(InFile::new(file_id.into(), module))?; |
85 | let module = sb.to_def(InFile::new(file_id.into(), module))?; | ||
86 | 137 | ||
87 | let path = module.path_to_root(db).into_iter().rev().filter_map(|it| it.name(db)).join("::"); | 138 | let path = module.path_to_root(db).into_iter().rev().filter_map(|it| it.name(db)).join("::"); |
88 | Some(Runnable { range, kind: RunnableKind::TestMod { path } }) | 139 | Some(Runnable { range, kind: RunnableKind::TestMod { path } }) |
@@ -121,13 +172,17 @@ mod tests { | |||
121 | Runnable { | 172 | Runnable { |
122 | range: [22; 46), | 173 | range: [22; 46), |
123 | kind: Test { | 174 | kind: Test { |
124 | name: "test_foo", | 175 | test_id: Path( |
176 | "test_foo", | ||
177 | ), | ||
125 | }, | 178 | }, |
126 | }, | 179 | }, |
127 | Runnable { | 180 | Runnable { |
128 | range: [47; 81), | 181 | range: [47; 81), |
129 | kind: Test { | 182 | kind: Test { |
130 | name: "test_foo", | 183 | test_id: Path( |
184 | "test_foo", | ||
185 | ), | ||
131 | }, | 186 | }, |
132 | }, | 187 | }, |
133 | ] | 188 | ] |
@@ -160,7 +215,9 @@ mod tests { | |||
160 | Runnable { | 215 | Runnable { |
161 | range: [28; 57), | 216 | range: [28; 57), |
162 | kind: Test { | 217 | kind: Test { |
163 | name: "test_foo1", | 218 | test_id: Path( |
219 | "test_mod::test_foo1", | ||
220 | ), | ||
164 | }, | 221 | }, |
165 | }, | 222 | }, |
166 | ] | 223 | ] |
@@ -195,7 +252,9 @@ mod tests { | |||
195 | Runnable { | 252 | Runnable { |
196 | range: [46; 79), | 253 | range: [46; 79), |
197 | kind: Test { | 254 | kind: Test { |
198 | name: "test_foo1", | 255 | test_id: Path( |
256 | "foo::test_mod::test_foo1", | ||
257 | ), | ||
199 | }, | 258 | }, |
200 | }, | 259 | }, |
201 | ] | 260 | ] |
@@ -232,7 +291,9 @@ mod tests { | |||
232 | Runnable { | 291 | Runnable { |
233 | range: [68; 105), | 292 | range: [68; 105), |
234 | kind: Test { | 293 | kind: Test { |
235 | name: "test_foo1", | 294 | test_id: Path( |
295 | "foo::bar::test_mod::test_foo1", | ||
296 | ), | ||
236 | }, | 297 | }, |
237 | }, | 298 | }, |
238 | ] | 299 | ] |
diff --git a/crates/ra_ide/src/snapshots/highlighting.html b/crates/ra_ide/src/snapshots/highlighting.html index 1cc55e78b..a02dbaf2f 100644 --- a/crates/ra_ide/src/snapshots/highlighting.html +++ b/crates/ra_ide/src/snapshots/highlighting.html | |||
@@ -16,6 +16,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd | |||
16 | .literal { color: #BFEBBF; } | 16 | .literal { color: #BFEBBF; } |
17 | .literal\.numeric { color: #6A8759; } | 17 | .literal\.numeric { color: #6A8759; } |
18 | .macro { color: #94BFF3; } | 18 | .macro { color: #94BFF3; } |
19 | .module { color: #AFD8AF; } | ||
19 | .variable { color: #DCDCCC; } | 20 | .variable { color: #DCDCCC; } |
20 | .variable\.mut { color: #DCDCCC; text-decoration: underline; } | 21 | .variable\.mut { color: #DCDCCC; text-decoration: underline; } |
21 | 22 | ||
diff --git a/crates/ra_ide/src/snapshots/rainbow_highlighting.html b/crates/ra_ide/src/snapshots/rainbow_highlighting.html index 918fd4b97..95f038f00 100644 --- a/crates/ra_ide/src/snapshots/rainbow_highlighting.html +++ b/crates/ra_ide/src/snapshots/rainbow_highlighting.html | |||
@@ -16,6 +16,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd | |||
16 | .literal { color: #BFEBBF; } | 16 | .literal { color: #BFEBBF; } |
17 | .literal\.numeric { color: #6A8759; } | 17 | .literal\.numeric { color: #6A8759; } |
18 | .macro { color: #94BFF3; } | 18 | .macro { color: #94BFF3; } |
19 | .module { color: #AFD8AF; } | ||
19 | .variable { color: #DCDCCC; } | 20 | .variable { color: #DCDCCC; } |
20 | .variable\.mut { color: #DCDCCC; text-decoration: underline; } | 21 | .variable\.mut { color: #DCDCCC; text-decoration: underline; } |
21 | 22 | ||
diff --git a/crates/ra_ide/src/ssr.rs b/crates/ra_ide/src/ssr.rs new file mode 100644 index 000000000..14eb0b8b2 --- /dev/null +++ b/crates/ra_ide/src/ssr.rs | |||
@@ -0,0 +1,324 @@ | |||
1 | //! structural search replace | ||
2 | |||
3 | use crate::source_change::SourceFileEdit; | ||
4 | use ra_ide_db::RootDatabase; | ||
5 | use ra_syntax::ast::make::expr_from_text; | ||
6 | use ra_syntax::AstNode; | ||
7 | use ra_syntax::SyntaxElement; | ||
8 | use ra_syntax::SyntaxNode; | ||
9 | use ra_text_edit::{TextEdit, TextEditBuilder}; | ||
10 | use rustc_hash::FxHashMap; | ||
11 | use std::collections::HashMap; | ||
12 | use std::str::FromStr; | ||
13 | |||
14 | pub use ra_db::{SourceDatabase, SourceDatabaseExt}; | ||
15 | use ra_ide_db::symbol_index::SymbolsDatabase; | ||
16 | |||
17 | #[derive(Debug, PartialEq)] | ||
18 | pub struct SsrError(String); | ||
19 | |||
20 | impl std::fmt::Display for SsrError { | ||
21 | fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { | ||
22 | write!(f, "Parse error: {}", self.0) | ||
23 | } | ||
24 | } | ||
25 | |||
26 | impl std::error::Error for SsrError {} | ||
27 | |||
28 | pub fn parse_search_replace( | ||
29 | query: &str, | ||
30 | db: &RootDatabase, | ||
31 | ) -> Result<Vec<SourceFileEdit>, SsrError> { | ||
32 | let mut edits = vec![]; | ||
33 | let query: SsrQuery = query.parse()?; | ||
34 | for &root in db.local_roots().iter() { | ||
35 | let sr = db.source_root(root); | ||
36 | for file_id in sr.walk() { | ||
37 | dbg!(db.file_relative_path(file_id)); | ||
38 | let matches = find(&query.pattern, db.parse(file_id).tree().syntax()); | ||
39 | if !matches.matches.is_empty() { | ||
40 | edits.push(SourceFileEdit { file_id, edit: replace(&matches, &query.template) }); | ||
41 | } | ||
42 | } | ||
43 | } | ||
44 | Ok(edits) | ||
45 | } | ||
46 | |||
47 | #[derive(Debug)] | ||
48 | struct SsrQuery { | ||
49 | pattern: SsrPattern, | ||
50 | template: SsrTemplate, | ||
51 | } | ||
52 | |||
53 | #[derive(Debug)] | ||
54 | struct SsrPattern { | ||
55 | pattern: SyntaxNode, | ||
56 | vars: Vec<Var>, | ||
57 | } | ||
58 | |||
59 | /// represents an `$var` in an SSR query | ||
60 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] | ||
61 | struct Var(String); | ||
62 | |||
63 | #[derive(Debug)] | ||
64 | struct SsrTemplate { | ||
65 | template: SyntaxNode, | ||
66 | placeholders: FxHashMap<SyntaxNode, Var>, | ||
67 | } | ||
68 | |||
69 | type Binding = HashMap<Var, SyntaxNode>; | ||
70 | |||
71 | #[derive(Debug)] | ||
72 | struct Match { | ||
73 | place: SyntaxNode, | ||
74 | binding: Binding, | ||
75 | } | ||
76 | |||
77 | #[derive(Debug)] | ||
78 | struct SsrMatches { | ||
79 | matches: Vec<Match>, | ||
80 | } | ||
81 | |||
82 | impl FromStr for SsrQuery { | ||
83 | type Err = SsrError; | ||
84 | |||
85 | fn from_str(query: &str) -> Result<SsrQuery, SsrError> { | ||
86 | let mut it = query.split("==>>"); | ||
87 | let pattern = it.next().expect("at least empty string").trim(); | ||
88 | let mut template = | ||
89 | it.next().ok_or(SsrError("Cannot find delemiter `==>>`".into()))?.trim().to_string(); | ||
90 | if it.next().is_some() { | ||
91 | return Err(SsrError("More than one delimiter found".into())); | ||
92 | } | ||
93 | let mut vars = vec![]; | ||
94 | let mut it = pattern.split('$'); | ||
95 | let mut pattern = it.next().expect("something").to_string(); | ||
96 | |||
97 | for part in it.map(split_by_var) { | ||
98 | let (var, var_type, remainder) = part?; | ||
99 | is_expr(var_type)?; | ||
100 | let new_var = create_name(var, &mut vars)?; | ||
101 | pattern.push_str(new_var); | ||
102 | pattern.push_str(remainder); | ||
103 | template = replace_in_template(template, var, new_var); | ||
104 | } | ||
105 | |||
106 | let template = expr_from_text(&template).syntax().clone(); | ||
107 | let mut placeholders = FxHashMap::default(); | ||
108 | |||
109 | traverse(&template, &mut |n| { | ||
110 | if let Some(v) = vars.iter().find(|v| v.0.as_str() == n.text()) { | ||
111 | placeholders.insert(n.clone(), v.clone()); | ||
112 | false | ||
113 | } else { | ||
114 | true | ||
115 | } | ||
116 | }); | ||
117 | |||
118 | let pattern = SsrPattern { pattern: expr_from_text(&pattern).syntax().clone(), vars }; | ||
119 | let template = SsrTemplate { template, placeholders }; | ||
120 | Ok(SsrQuery { pattern, template }) | ||
121 | } | ||
122 | } | ||
123 | |||
124 | fn traverse(node: &SyntaxNode, go: &mut impl FnMut(&SyntaxNode) -> bool) { | ||
125 | if !go(node) { | ||
126 | return; | ||
127 | } | ||
128 | for ref child in node.children() { | ||
129 | traverse(child, go); | ||
130 | } | ||
131 | } | ||
132 | |||
133 | fn split_by_var(s: &str) -> Result<(&str, &str, &str), SsrError> { | ||
134 | let end_of_name = s.find(":").ok_or(SsrError("Use $<name>:expr".into()))?; | ||
135 | let name = &s[0..end_of_name]; | ||
136 | is_name(name)?; | ||
137 | let type_begin = end_of_name + 1; | ||
138 | let type_length = s[type_begin..].find(|c| !char::is_ascii_alphanumeric(&c)).unwrap_or(s.len()); | ||
139 | let type_name = &s[type_begin..type_begin + type_length]; | ||
140 | Ok((name, type_name, &s[type_begin + type_length..])) | ||
141 | } | ||
142 | |||
143 | fn is_name(s: &str) -> Result<(), SsrError> { | ||
144 | if s.chars().all(|c| c.is_ascii_alphanumeric() || c == '_') { | ||
145 | Ok(()) | ||
146 | } else { | ||
147 | Err(SsrError("Name can contain only alphanumerics and _".into())) | ||
148 | } | ||
149 | } | ||
150 | |||
151 | fn is_expr(s: &str) -> Result<(), SsrError> { | ||
152 | if s == "expr" { | ||
153 | Ok(()) | ||
154 | } else { | ||
155 | Err(SsrError("Only $<name>:expr is supported".into())) | ||
156 | } | ||
157 | } | ||
158 | |||
159 | fn replace_in_template(template: String, var: &str, new_var: &str) -> String { | ||
160 | let name = format!("${}", var); | ||
161 | template.replace(&name, new_var) | ||
162 | } | ||
163 | |||
164 | fn create_name<'a>(name: &str, vars: &'a mut Vec<Var>) -> Result<&'a str, SsrError> { | ||
165 | let sanitized_name = format!("__search_pattern_{}", name); | ||
166 | if vars.iter().any(|a| a.0 == sanitized_name) { | ||
167 | return Err(SsrError(format!("Name `{}` repeats more than once", name))); | ||
168 | } | ||
169 | vars.push(Var(sanitized_name)); | ||
170 | Ok(&vars.last().unwrap().0) | ||
171 | } | ||
172 | |||
173 | fn find(pattern: &SsrPattern, code: &SyntaxNode) -> SsrMatches { | ||
174 | fn check( | ||
175 | pattern: &SyntaxElement, | ||
176 | code: &SyntaxElement, | ||
177 | placeholders: &[Var], | ||
178 | match_: &mut Match, | ||
179 | ) -> bool { | ||
180 | match (pattern, code) { | ||
181 | (SyntaxElement::Token(ref pattern), SyntaxElement::Token(ref code)) => { | ||
182 | pattern.text() == code.text() | ||
183 | } | ||
184 | (SyntaxElement::Node(ref pattern), SyntaxElement::Node(ref code)) => { | ||
185 | if placeholders.iter().find(|&n| n.0.as_str() == pattern.text()).is_some() { | ||
186 | match_.binding.insert(Var(pattern.text().to_string()), code.clone()); | ||
187 | true | ||
188 | } else { | ||
189 | pattern.green().children().count() == code.green().children().count() | ||
190 | && pattern | ||
191 | .children_with_tokens() | ||
192 | .zip(code.children_with_tokens()) | ||
193 | .all(|(a, b)| check(&a, &b, placeholders, match_)) | ||
194 | } | ||
195 | } | ||
196 | _ => false, | ||
197 | } | ||
198 | } | ||
199 | let kind = pattern.pattern.kind(); | ||
200 | let matches = code | ||
201 | .descendants_with_tokens() | ||
202 | .filter(|n| n.kind() == kind) | ||
203 | .filter_map(|code| { | ||
204 | let mut match_ = | ||
205 | Match { place: code.as_node().unwrap().clone(), binding: HashMap::new() }; | ||
206 | if check( | ||
207 | &SyntaxElement::from(pattern.pattern.clone()), | ||
208 | &code, | ||
209 | &pattern.vars, | ||
210 | &mut match_, | ||
211 | ) { | ||
212 | Some(match_) | ||
213 | } else { | ||
214 | None | ||
215 | } | ||
216 | }) | ||
217 | .collect(); | ||
218 | SsrMatches { matches } | ||
219 | } | ||
220 | |||
221 | fn replace(matches: &SsrMatches, template: &SsrTemplate) -> TextEdit { | ||
222 | let mut builder = TextEditBuilder::default(); | ||
223 | for match_ in &matches.matches { | ||
224 | builder.replace(match_.place.text_range(), render_replace(&match_.binding, template)); | ||
225 | } | ||
226 | builder.finish() | ||
227 | } | ||
228 | |||
229 | fn render_replace(binding: &Binding, template: &SsrTemplate) -> String { | ||
230 | let mut builder = TextEditBuilder::default(); | ||
231 | for element in template.template.descendants() { | ||
232 | if let Some(var) = template.placeholders.get(&element) { | ||
233 | builder.replace(element.text_range(), binding[var].to_string()) | ||
234 | } | ||
235 | } | ||
236 | builder.finish().apply(&template.template.text().to_string()) | ||
237 | } | ||
238 | |||
239 | #[cfg(test)] | ||
240 | mod tests { | ||
241 | use super::*; | ||
242 | use ra_syntax::SourceFile; | ||
243 | |||
244 | fn parse_error_text(query: &str) -> String { | ||
245 | format!("{}", query.parse::<SsrQuery>().unwrap_err()) | ||
246 | } | ||
247 | |||
248 | #[test] | ||
249 | fn parser_happy_case() { | ||
250 | let result: SsrQuery = "foo($a:expr, $b:expr) ==>> bar($b, $a)".parse().unwrap(); | ||
251 | assert_eq!(&result.pattern.pattern.text(), "foo(__search_pattern_a, __search_pattern_b)"); | ||
252 | assert_eq!(result.pattern.vars.len(), 2); | ||
253 | assert_eq!(result.pattern.vars[0].0, "__search_pattern_a"); | ||
254 | assert_eq!(result.pattern.vars[1].0, "__search_pattern_b"); | ||
255 | assert_eq!(&result.template.template.text(), "bar(__search_pattern_b, __search_pattern_a)"); | ||
256 | dbg!(result.template.placeholders); | ||
257 | } | ||
258 | |||
259 | #[test] | ||
260 | fn parser_empty_query() { | ||
261 | assert_eq!(parse_error_text(""), "Parse error: Cannot find delemiter `==>>`"); | ||
262 | } | ||
263 | |||
264 | #[test] | ||
265 | fn parser_no_delimiter() { | ||
266 | assert_eq!(parse_error_text("foo()"), "Parse error: Cannot find delemiter `==>>`"); | ||
267 | } | ||
268 | |||
269 | #[test] | ||
270 | fn parser_two_delimiters() { | ||
271 | assert_eq!( | ||
272 | parse_error_text("foo() ==>> a ==>> b "), | ||
273 | "Parse error: More than one delimiter found" | ||
274 | ); | ||
275 | } | ||
276 | |||
277 | #[test] | ||
278 | fn parser_no_pattern_type() { | ||
279 | assert_eq!(parse_error_text("foo($a) ==>>"), "Parse error: Use $<name>:expr"); | ||
280 | } | ||
281 | |||
282 | #[test] | ||
283 | fn parser_invalid_name() { | ||
284 | assert_eq!( | ||
285 | parse_error_text("foo($a+:expr) ==>>"), | ||
286 | "Parse error: Name can contain only alphanumerics and _" | ||
287 | ); | ||
288 | } | ||
289 | |||
290 | #[test] | ||
291 | fn parser_invalid_type() { | ||
292 | assert_eq!( | ||
293 | parse_error_text("foo($a:ident) ==>>"), | ||
294 | "Parse error: Only $<name>:expr is supported" | ||
295 | ); | ||
296 | } | ||
297 | |||
298 | #[test] | ||
299 | fn parser_repeated_name() { | ||
300 | assert_eq!( | ||
301 | parse_error_text("foo($a:expr, $a:expr) ==>>"), | ||
302 | "Parse error: Name `a` repeats more than once" | ||
303 | ); | ||
304 | } | ||
305 | |||
306 | #[test] | ||
307 | fn parse_match_replace() { | ||
308 | let query: SsrQuery = "foo($x:expr) ==>> bar($x)".parse().unwrap(); | ||
309 | let input = "fn main() { foo(1+2); }"; | ||
310 | |||
311 | let code = SourceFile::parse(input).tree(); | ||
312 | let matches = find(&query.pattern, code.syntax()); | ||
313 | assert_eq!(matches.matches.len(), 1); | ||
314 | assert_eq!(matches.matches[0].place.text(), "foo(1+2)"); | ||
315 | assert_eq!(matches.matches[0].binding.len(), 1); | ||
316 | assert_eq!( | ||
317 | matches.matches[0].binding[&Var("__search_pattern_x".to_string())].text(), | ||
318 | "1+2" | ||
319 | ); | ||
320 | |||
321 | let edit = replace(&matches, &query.template); | ||
322 | assert_eq!(edit.apply(input), "fn main() { bar(1+2); }"); | ||
323 | } | ||
324 | } | ||
diff --git a/crates/ra_ide/src/syntax_highlighting.rs b/crates/ra_ide/src/syntax_highlighting.rs index 174e13595..20c414ca1 100644 --- a/crates/ra_ide/src/syntax_highlighting.rs +++ b/crates/ra_ide/src/syntax_highlighting.rs | |||
@@ -365,6 +365,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd | |||
365 | .literal { color: #BFEBBF; } | 365 | .literal { color: #BFEBBF; } |
366 | .literal\\.numeric { color: #6A8759; } | 366 | .literal\\.numeric { color: #6A8759; } |
367 | .macro { color: #94BFF3; } | 367 | .macro { color: #94BFF3; } |
368 | .module { color: #AFD8AF; } | ||
368 | .variable { color: #DCDCCC; } | 369 | .variable { color: #DCDCCC; } |
369 | .variable\\.mut { color: #DCDCCC; text-decoration: underline; } | 370 | .variable\\.mut { color: #DCDCCC; text-decoration: underline; } |
370 | 371 | ||
diff --git a/crates/ra_ide_db/Cargo.toml b/crates/ra_ide_db/Cargo.toml index 716e88bc1..495fffb5a 100644 --- a/crates/ra_ide_db/Cargo.toml +++ b/crates/ra_ide_db/Cargo.toml | |||
@@ -22,7 +22,6 @@ fst = { version = "0.3.1", default-features = false } | |||
22 | rustc-hash = "1.0" | 22 | rustc-hash = "1.0" |
23 | unicase = "2.2.0" | 23 | unicase = "2.2.0" |
24 | superslice = "1.0.0" | 24 | superslice = "1.0.0" |
25 | rand = { version = "0.7.0", features = ["small_rng"] } | ||
26 | once_cell = "1.2.0" | 25 | once_cell = "1.2.0" |
27 | 26 | ||
28 | ra_syntax = { path = "../ra_syntax" } | 27 | ra_syntax = { path = "../ra_syntax" } |
diff --git a/crates/ra_lsp_server/src/cargo_target_spec.rs b/crates/ra_lsp_server/src/cargo_target_spec.rs index 594caffe2..5fd1e7b6b 100644 --- a/crates/ra_lsp_server/src/cargo_target_spec.rs +++ b/crates/ra_lsp_server/src/cargo_target_spec.rs | |||
@@ -1,6 +1,6 @@ | |||
1 | //! FIXME: write short doc here | 1 | //! FIXME: write short doc here |
2 | 2 | ||
3 | use ra_ide::{FileId, RunnableKind}; | 3 | use ra_ide::{FileId, RunnableKind, TestId}; |
4 | use ra_project_model::{self, ProjectWorkspace, TargetKind}; | 4 | use ra_project_model::{self, ProjectWorkspace, TargetKind}; |
5 | 5 | ||
6 | use crate::{world::WorldSnapshot, Result}; | 6 | use crate::{world::WorldSnapshot, Result}; |
@@ -13,13 +13,16 @@ pub(crate) fn runnable_args( | |||
13 | let spec = CargoTargetSpec::for_file(world, file_id)?; | 13 | let spec = CargoTargetSpec::for_file(world, file_id)?; |
14 | let mut res = Vec::new(); | 14 | let mut res = Vec::new(); |
15 | match kind { | 15 | match kind { |
16 | RunnableKind::Test { name } => { | 16 | RunnableKind::Test { test_id } => { |
17 | res.push("test".to_string()); | 17 | res.push("test".to_string()); |
18 | if let Some(spec) = spec { | 18 | if let Some(spec) = spec { |
19 | spec.push_to(&mut res); | 19 | spec.push_to(&mut res); |
20 | } | 20 | } |
21 | res.push("--".to_string()); | 21 | res.push("--".to_string()); |
22 | res.push(name.to_string()); | 22 | res.push(test_id.to_string()); |
23 | if let TestId::Path(_) = test_id { | ||
24 | res.push("--exact".to_string()); | ||
25 | } | ||
23 | res.push("--nocapture".to_string()); | 26 | res.push("--nocapture".to_string()); |
24 | } | 27 | } |
25 | RunnableKind::TestMod { path } => { | 28 | RunnableKind::TestMod { path } => { |
@@ -31,13 +34,16 @@ pub(crate) fn runnable_args( | |||
31 | res.push(path.to_string()); | 34 | res.push(path.to_string()); |
32 | res.push("--nocapture".to_string()); | 35 | res.push("--nocapture".to_string()); |
33 | } | 36 | } |
34 | RunnableKind::Bench { name } => { | 37 | RunnableKind::Bench { test_id } => { |
35 | res.push("bench".to_string()); | 38 | res.push("bench".to_string()); |
36 | if let Some(spec) = spec { | 39 | if let Some(spec) = spec { |
37 | spec.push_to(&mut res); | 40 | spec.push_to(&mut res); |
38 | } | 41 | } |
39 | res.push("--".to_string()); | 42 | res.push("--".to_string()); |
40 | res.push(name.to_string()); | 43 | res.push(test_id.to_string()); |
44 | if let TestId::Path(_) = test_id { | ||
45 | res.push("--exact".to_string()); | ||
46 | } | ||
41 | res.push("--nocapture".to_string()); | 47 | res.push("--nocapture".to_string()); |
42 | } | 48 | } |
43 | RunnableKind::Bin => { | 49 | RunnableKind::Bin => { |
diff --git a/crates/ra_lsp_server/src/config.rs b/crates/ra_lsp_server/src/config.rs index 2d7948d74..3314269ec 100644 --- a/crates/ra_lsp_server/src/config.rs +++ b/crates/ra_lsp_server/src/config.rs | |||
@@ -44,6 +44,8 @@ pub struct ServerConfig { | |||
44 | /// Fine grained feature flags to disable specific features. | 44 | /// Fine grained feature flags to disable specific features. |
45 | pub feature_flags: FxHashMap<String, bool>, | 45 | pub feature_flags: FxHashMap<String, bool>, |
46 | 46 | ||
47 | pub rustfmt_args: Vec<String>, | ||
48 | |||
47 | /// Cargo feature configurations. | 49 | /// Cargo feature configurations. |
48 | pub cargo_features: CargoFeatures, | 50 | pub cargo_features: CargoFeatures, |
49 | } | 51 | } |
@@ -63,6 +65,7 @@ impl Default for ServerConfig { | |||
63 | with_sysroot: true, | 65 | with_sysroot: true, |
64 | feature_flags: FxHashMap::default(), | 66 | feature_flags: FxHashMap::default(), |
65 | cargo_features: Default::default(), | 67 | cargo_features: Default::default(), |
68 | rustfmt_args: Vec::new(), | ||
66 | } | 69 | } |
67 | } | 70 | } |
68 | } | 71 | } |
diff --git a/crates/ra_lsp_server/src/main.rs b/crates/ra_lsp_server/src/main.rs index c8a017c5c..ed2eaabd4 100644 --- a/crates/ra_lsp_server/src/main.rs +++ b/crates/ra_lsp_server/src/main.rs | |||
@@ -15,13 +15,8 @@ fn main() -> Result<()> { | |||
15 | 15 | ||
16 | fn setup_logging() -> Result<()> { | 16 | fn setup_logging() -> Result<()> { |
17 | std::env::set_var("RUST_BACKTRACE", "short"); | 17 | std::env::set_var("RUST_BACKTRACE", "short"); |
18 | |||
19 | env_logger::try_init()?; | 18 | env_logger::try_init()?; |
20 | 19 | ra_prof::init(); | |
21 | ra_prof::set_filter(match std::env::var("RA_PROFILE") { | ||
22 | Ok(spec) => ra_prof::Filter::from_spec(&spec), | ||
23 | Err(_) => ra_prof::Filter::disabled(), | ||
24 | }); | ||
25 | Ok(()) | 20 | Ok(()) |
26 | } | 21 | } |
27 | 22 | ||
diff --git a/crates/ra_lsp_server/src/main_loop.rs b/crates/ra_lsp_server/src/main_loop.rs index ceff82fda..7ae2e1e6f 100644 --- a/crates/ra_lsp_server/src/main_loop.rs +++ b/crates/ra_lsp_server/src/main_loop.rs | |||
@@ -178,6 +178,7 @@ pub fn main_loop( | |||
178 | command: config.cargo_watch_command, | 178 | command: config.cargo_watch_command, |
179 | all_targets: config.cargo_watch_all_targets, | 179 | all_targets: config.cargo_watch_all_targets, |
180 | }, | 180 | }, |
181 | rustfmt_args: config.rustfmt_args, | ||
181 | } | 182 | } |
182 | }; | 183 | }; |
183 | 184 | ||
@@ -526,6 +527,7 @@ fn on_request( | |||
526 | .on::<req::CallHierarchyPrepare>(handlers::handle_call_hierarchy_prepare)? | 527 | .on::<req::CallHierarchyPrepare>(handlers::handle_call_hierarchy_prepare)? |
527 | .on::<req::CallHierarchyIncomingCalls>(handlers::handle_call_hierarchy_incoming)? | 528 | .on::<req::CallHierarchyIncomingCalls>(handlers::handle_call_hierarchy_incoming)? |
528 | .on::<req::CallHierarchyOutgoingCalls>(handlers::handle_call_hierarchy_outgoing)? | 529 | .on::<req::CallHierarchyOutgoingCalls>(handlers::handle_call_hierarchy_outgoing)? |
530 | .on::<req::Ssr>(handlers::handle_ssr)? | ||
529 | .finish(); | 531 | .finish(); |
530 | Ok(()) | 532 | Ok(()) |
531 | } | 533 | } |
diff --git a/crates/ra_lsp_server/src/main_loop/handlers.rs b/crates/ra_lsp_server/src/main_loop/handlers.rs index 2e598fdcd..ae51141cb 100644 --- a/crates/ra_lsp_server/src/main_loop/handlers.rs +++ b/crates/ra_lsp_server/src/main_loop/handlers.rs | |||
@@ -590,6 +590,7 @@ pub fn handle_formatting( | |||
590 | let end_position = TextUnit::of_str(&file).conv_with(&file_line_index); | 590 | let end_position = TextUnit::of_str(&file).conv_with(&file_line_index); |
591 | 591 | ||
592 | let mut rustfmt = process::Command::new("rustfmt"); | 592 | let mut rustfmt = process::Command::new("rustfmt"); |
593 | rustfmt.args(&world.options.rustfmt_args); | ||
593 | if let Some(&crate_id) = crate_ids.first() { | 594 | if let Some(&crate_id) = crate_ids.first() { |
594 | // Assume all crates are in the same edition | 595 | // Assume all crates are in the same edition |
595 | let edition = world.analysis().crate_edition(crate_id)?; | 596 | let edition = world.analysis().crate_edition(crate_id)?; |
@@ -881,6 +882,11 @@ pub fn handle_document_highlight( | |||
881 | )) | 882 | )) |
882 | } | 883 | } |
883 | 884 | ||
885 | pub fn handle_ssr(world: WorldSnapshot, params: req::SsrParams) -> Result<req::SourceChange> { | ||
886 | let _p = profile("handle_ssr"); | ||
887 | world.analysis().structural_search_replace(¶ms.arg)??.try_conv_with(&world) | ||
888 | } | ||
889 | |||
884 | pub fn publish_diagnostics(world: &WorldSnapshot, file_id: FileId) -> Result<DiagnosticTask> { | 890 | pub fn publish_diagnostics(world: &WorldSnapshot, file_id: FileId) -> Result<DiagnosticTask> { |
885 | let _p = profile("publish_diagnostics"); | 891 | let _p = profile("publish_diagnostics"); |
886 | let line_index = world.analysis().file_line_index(file_id)?; | 892 | let line_index = world.analysis().file_line_index(file_id)?; |
@@ -918,9 +924,9 @@ fn to_lsp_runnable( | |||
918 | let args = runnable_args(world, file_id, &runnable.kind)?; | 924 | let args = runnable_args(world, file_id, &runnable.kind)?; |
919 | let line_index = world.analysis().file_line_index(file_id)?; | 925 | let line_index = world.analysis().file_line_index(file_id)?; |
920 | let label = match &runnable.kind { | 926 | let label = match &runnable.kind { |
921 | RunnableKind::Test { name } => format!("test {}", name), | 927 | RunnableKind::Test { test_id } => format!("test {}", test_id), |
922 | RunnableKind::TestMod { path } => format!("test-mod {}", path), | 928 | RunnableKind::TestMod { path } => format!("test-mod {}", path), |
923 | RunnableKind::Bench { name } => format!("bench {}", name), | 929 | RunnableKind::Bench { test_id } => format!("bench {}", test_id), |
924 | RunnableKind::Bin => "run binary".to_string(), | 930 | RunnableKind::Bin => "run binary".to_string(), |
925 | }; | 931 | }; |
926 | Ok(req::Runnable { | 932 | Ok(req::Runnable { |
diff --git a/crates/ra_lsp_server/src/req.rs b/crates/ra_lsp_server/src/req.rs index dc327f53d..7ff7f60b3 100644 --- a/crates/ra_lsp_server/src/req.rs +++ b/crates/ra_lsp_server/src/req.rs | |||
@@ -206,3 +206,16 @@ pub struct InlayHint { | |||
206 | pub kind: InlayKind, | 206 | pub kind: InlayKind, |
207 | pub label: String, | 207 | pub label: String, |
208 | } | 208 | } |
209 | |||
210 | pub enum Ssr {} | ||
211 | |||
212 | impl Request for Ssr { | ||
213 | type Params = SsrParams; | ||
214 | type Result = SourceChange; | ||
215 | const METHOD: &'static str = "rust-analyzer/ssr"; | ||
216 | } | ||
217 | |||
218 | #[derive(Debug, Deserialize, Serialize)] | ||
219 | pub struct SsrParams { | ||
220 | pub arg: String, | ||
221 | } | ||
diff --git a/crates/ra_lsp_server/src/world.rs b/crates/ra_lsp_server/src/world.rs index 1ee02b47c..d993c5fc4 100644 --- a/crates/ra_lsp_server/src/world.rs +++ b/crates/ra_lsp_server/src/world.rs | |||
@@ -34,6 +34,7 @@ pub struct Options { | |||
34 | pub supports_location_link: bool, | 34 | pub supports_location_link: bool, |
35 | pub line_folding_only: bool, | 35 | pub line_folding_only: bool, |
36 | pub max_inlay_hint_length: Option<usize>, | 36 | pub max_inlay_hint_length: Option<usize>, |
37 | pub rustfmt_args: Vec<String>, | ||
37 | pub cargo_watch: CheckOptions, | 38 | pub cargo_watch: CheckOptions, |
38 | } | 39 | } |
39 | 40 | ||
diff --git a/crates/ra_lsp_server/tests/heavy_tests/main.rs b/crates/ra_lsp_server/tests/heavy_tests/main.rs index dff63a12d..9ca31cbcc 100644 --- a/crates/ra_lsp_server/tests/heavy_tests/main.rs +++ b/crates/ra_lsp_server/tests/heavy_tests/main.rs | |||
@@ -147,7 +147,7 @@ fn main() {} | |||
147 | }, | 147 | }, |
148 | json!([ | 148 | json!([ |
149 | { | 149 | { |
150 | "args": [ "test", "--package", "foo", "--test", "spam", "--", "test_eggs", "--nocapture" ], | 150 | "args": [ "test", "--package", "foo", "--test", "spam", "--", "test_eggs", "--exact", "--nocapture" ], |
151 | "bin": "cargo", | 151 | "bin": "cargo", |
152 | "env": { "RUST_BACKTRACE": "short" }, | 152 | "env": { "RUST_BACKTRACE": "short" }, |
153 | "label": "test test_eggs", | 153 | "label": "test test_eggs", |
diff --git a/crates/ra_prof/src/lib.rs b/crates/ra_prof/src/lib.rs index d38ff397e..c0bfbc2ee 100644 --- a/crates/ra_prof/src/lib.rs +++ b/crates/ra_prof/src/lib.rs | |||
@@ -26,6 +26,13 @@ pub use crate::memory_usage::{Bytes, MemoryUsage}; | |||
26 | #[global_allocator] | 26 | #[global_allocator] |
27 | static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc; | 27 | static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc; |
28 | 28 | ||
29 | pub fn init() { | ||
30 | set_filter(match std::env::var("RA_PROFILE") { | ||
31 | Ok(spec) => Filter::from_spec(&spec), | ||
32 | Err(_) => Filter::disabled(), | ||
33 | }); | ||
34 | } | ||
35 | |||
29 | /// Set profiling filter. It specifies descriptions allowed to profile. | 36 | /// Set profiling filter. It specifies descriptions allowed to profile. |
30 | /// This is helpful when call stack has too many nested profiling scopes. | 37 | /// This is helpful when call stack has too many nested profiling scopes. |
31 | /// Additionally filter can specify maximum depth of profiling scopes nesting. | 38 | /// Additionally filter can specify maximum depth of profiling scopes nesting. |
diff --git a/crates/ra_syntax/src/ast/make.rs b/crates/ra_syntax/src/ast/make.rs index 862eb1172..89d1403e7 100644 --- a/crates/ra_syntax/src/ast/make.rs +++ b/crates/ra_syntax/src/ast/make.rs | |||
@@ -84,7 +84,7 @@ pub fn expr_prefix(op: SyntaxKind, expr: ast::Expr) -> ast::Expr { | |||
84 | let token = token(op); | 84 | let token = token(op); |
85 | expr_from_text(&format!("{}{}", token, expr.syntax())) | 85 | expr_from_text(&format!("{}{}", token, expr.syntax())) |
86 | } | 86 | } |
87 | fn expr_from_text(text: &str) -> ast::Expr { | 87 | pub fn expr_from_text(text: &str) -> ast::Expr { |
88 | ast_from_text(&format!("const C: () = {};", text)) | 88 | ast_from_text(&format!("const C: () = {};", text)) |
89 | } | 89 | } |
90 | 90 | ||