aboutsummaryrefslogtreecommitdiff
path: root/crates
diff options
context:
space:
mode:
Diffstat (limited to 'crates')
-rw-r--r--crates/ra_db/src/fixture.rs6
-rw-r--r--crates/ra_db/src/input.rs8
-rw-r--r--crates/ra_hir_expand/src/builtin_macro.rs12
-rw-r--r--crates/ra_hir_expand/src/db.rs24
-rw-r--r--crates/ra_hir_ty/src/tests/macros.rs81
-rw-r--r--crates/ra_hir_ty/src/tests/regression.rs17
-rw-r--r--crates/ra_ide/Cargo.toml1
-rw-r--r--crates/ra_ide/src/call_hierarchy.rs337
-rw-r--r--crates/ra_ide/src/call_info.rs15
-rw-r--r--crates/ra_ide/src/change.rs3
-rw-r--r--crates/ra_ide/src/display/navigation_target.rs2
-rw-r--r--crates/ra_ide/src/feature_flags.rs1
-rw-r--r--crates/ra_ide/src/hover.rs20
-rw-r--r--crates/ra_ide/src/lib.rs24
-rw-r--r--crates/ra_ide/src/references.rs352
-rw-r--r--crates/ra_ide/src/references/rename.rs8
-rw-r--r--crates/ra_lsp_server/src/caps.rs6
-rw-r--r--crates/ra_lsp_server/src/conv.rs32
-rw-r--r--crates/ra_lsp_server/src/main_loop.rs42
-rw-r--r--crates/ra_lsp_server/src/main_loop/handlers.rs131
-rw-r--r--crates/ra_lsp_server/src/req.rs16
-rw-r--r--crates/ra_parser/src/grammar/items/traits.rs10
-rw-r--r--crates/ra_project_model/src/lib.rs14
-rw-r--r--crates/ra_project_model/src/sysroot.rs5
-rw-r--r--crates/ra_syntax/Cargo.toml2
-rw-r--r--crates/ra_syntax/src/algo.rs3
-rw-r--r--crates/ra_syntax/src/ast/expr_extensions.rs1
-rw-r--r--crates/ra_syntax/src/parsing/reparsing.rs3
-rw-r--r--crates/ra_syntax/src/syntax_node.rs8
-rw-r--r--crates/ra_syntax/test_data/parser/inline/ok/0151_trait_alias.rs3
-rw-r--r--crates/ra_syntax/test_data/parser/inline/ok/0151_trait_alias.txt128
31 files changed, 1221 insertions, 94 deletions
diff --git a/crates/ra_db/src/fixture.rs b/crates/ra_db/src/fixture.rs
index e8f335e33..30b598e9a 100644
--- a/crates/ra_db/src/fixture.rs
+++ b/crates/ra_db/src/fixture.rs
@@ -49,7 +49,7 @@ fn with_single_file(db: &mut dyn SourceDatabaseExt, text: &str) -> FileId {
49 let file_id = FileId(0); 49 let file_id = FileId(0);
50 let rel_path: RelativePathBuf = "/main.rs".into(); 50 let rel_path: RelativePathBuf = "/main.rs".into();
51 51
52 let mut source_root = SourceRoot::default(); 52 let mut source_root = SourceRoot::new_local();
53 source_root.insert_file(rel_path.clone(), file_id); 53 source_root.insert_file(rel_path.clone(), file_id);
54 54
55 let mut crate_graph = CrateGraph::default(); 55 let mut crate_graph = CrateGraph::default();
@@ -77,7 +77,7 @@ fn with_files(db: &mut dyn SourceDatabaseExt, fixture: &str) -> Option<FilePosit
77 let mut crate_deps = Vec::new(); 77 let mut crate_deps = Vec::new();
78 let mut default_crate_root: Option<FileId> = None; 78 let mut default_crate_root: Option<FileId> = None;
79 79
80 let mut source_root = SourceRoot::default(); 80 let mut source_root = SourceRoot::new_local();
81 let mut source_root_id = WORKSPACE; 81 let mut source_root_id = WORKSPACE;
82 let mut source_root_prefix: RelativePathBuf = "/".into(); 82 let mut source_root_prefix: RelativePathBuf = "/".into();
83 let mut file_id = FileId(0); 83 let mut file_id = FileId(0);
@@ -87,7 +87,7 @@ fn with_files(db: &mut dyn SourceDatabaseExt, fixture: &str) -> Option<FilePosit
87 for entry in fixture.iter() { 87 for entry in fixture.iter() {
88 let meta = match parse_meta(&entry.meta) { 88 let meta = match parse_meta(&entry.meta) {
89 ParsedMeta::Root { path } => { 89 ParsedMeta::Root { path } => {
90 let source_root = std::mem::replace(&mut source_root, SourceRoot::default()); 90 let source_root = std::mem::replace(&mut source_root, SourceRoot::new_local());
91 db.set_source_root(source_root_id, Arc::new(source_root)); 91 db.set_source_root(source_root_id, Arc::new(source_root));
92 source_root_id.0 += 1; 92 source_root_id.0 += 1;
93 source_root_prefix = path; 93 source_root_prefix = path;
diff --git a/crates/ra_db/src/input.rs b/crates/ra_db/src/input.rs
index 2a7ed20d1..07269237a 100644
--- a/crates/ra_db/src/input.rs
+++ b/crates/ra_db/src/input.rs
@@ -33,7 +33,7 @@ pub struct FileId(pub u32);
33#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)] 33#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
34pub struct SourceRootId(pub u32); 34pub struct SourceRootId(pub u32);
35 35
36#[derive(Default, Clone, Debug, PartialEq, Eq)] 36#[derive(Clone, Debug, PartialEq, Eq)]
37pub struct SourceRoot { 37pub struct SourceRoot {
38 /// Sysroot or crates.io library. 38 /// Sysroot or crates.io library.
39 /// 39 ///
@@ -44,11 +44,11 @@ pub struct SourceRoot {
44} 44}
45 45
46impl SourceRoot { 46impl SourceRoot {
47 pub fn new() -> SourceRoot { 47 pub fn new_local() -> SourceRoot {
48 Default::default() 48 SourceRoot { is_library: false, files: Default::default() }
49 } 49 }
50 pub fn new_library() -> SourceRoot { 50 pub fn new_library() -> SourceRoot {
51 SourceRoot { is_library: true, ..SourceRoot::new() } 51 SourceRoot { is_library: true, files: Default::default() }
52 } 52 }
53 pub fn insert_file(&mut self, path: RelativePathBuf, file_id: FileId) { 53 pub fn insert_file(&mut self, path: RelativePathBuf, file_id: FileId) {
54 self.files.insert(path, file_id); 54 self.files.insert(path, file_id);
diff --git a/crates/ra_hir_expand/src/builtin_macro.rs b/crates/ra_hir_expand/src/builtin_macro.rs
index 2c119269c..e9e275670 100644
--- a/crates/ra_hir_expand/src/builtin_macro.rs
+++ b/crates/ra_hir_expand/src/builtin_macro.rs
@@ -62,8 +62,13 @@ register_builtin! {
62} 62}
63 63
64fn to_line_number(db: &dyn AstDatabase, file: HirFileId, pos: TextUnit) -> usize { 64fn to_line_number(db: &dyn AstDatabase, file: HirFileId, pos: TextUnit) -> usize {
65 // FIXME: Use expansion info
66 let file_id = file.original_file(db); 65 let file_id = file.original_file(db);
66
67 // FIXME: if the file is coming from macro, we return a dummy value for now.
68 if file.call_node(db).map(|it| it.file_id != file_id.into()).unwrap_or(true) {
69 return 0;
70 }
71
67 let text = db.file_text(file_id); 72 let text = db.file_text(file_id);
68 let mut line_num = 1; 73 let mut line_num = 1;
69 74
@@ -150,8 +155,11 @@ fn option_env_expand(
150} 155}
151 156
152fn to_col_number(db: &dyn AstDatabase, file: HirFileId, pos: TextUnit) -> usize { 157fn to_col_number(db: &dyn AstDatabase, file: HirFileId, pos: TextUnit) -> usize {
153 // FIXME: Use expansion info
154 let file_id = file.original_file(db); 158 let file_id = file.original_file(db);
159 // FIXME: if the file is coming from macro, we return a dummy value for now.
160 if file.call_node(db).map(|it| it.file_id != file_id.into()).unwrap_or(true) {
161 return 0;
162 }
155 let text = db.file_text(file_id); 163 let text = db.file_text(file_id);
156 164
157 let pos = pos.to_usize(); 165 let pos = pos.to_usize();
diff --git a/crates/ra_hir_expand/src/db.rs b/crates/ra_hir_expand/src/db.rs
index 2e12e126f..af5b22d1c 100644
--- a/crates/ra_hir_expand/src/db.rs
+++ b/crates/ra_hir_expand/src/db.rs
@@ -178,6 +178,7 @@ fn to_fragment_kind(db: &dyn AstDatabase, macro_call_id: MacroCallId) -> Fragmen
178 178
179 match parent.kind() { 179 match parent.kind() {
180 MACRO_ITEMS | SOURCE_FILE => FragmentKind::Items, 180 MACRO_ITEMS | SOURCE_FILE => FragmentKind::Items,
181 ITEM_LIST => FragmentKind::Items,
181 LET_STMT => { 182 LET_STMT => {
182 // FIXME: Handle Pattern 183 // FIXME: Handle Pattern
183 FragmentKind::Expr 184 FragmentKind::Expr
@@ -187,7 +188,28 @@ fn to_fragment_kind(db: &dyn AstDatabase, macro_call_id: MacroCallId) -> Fragmen
187 ARG_LIST => FragmentKind::Expr, 188 ARG_LIST => FragmentKind::Expr,
188 TRY_EXPR => FragmentKind::Expr, 189 TRY_EXPR => FragmentKind::Expr,
189 TUPLE_EXPR => FragmentKind::Expr, 190 TUPLE_EXPR => FragmentKind::Expr,
190 ITEM_LIST => FragmentKind::Items, 191 PAREN_EXPR => FragmentKind::Expr,
192
193 FOR_EXPR => FragmentKind::Expr,
194 PATH_EXPR => FragmentKind::Expr,
195 LAMBDA_EXPR => FragmentKind::Expr,
196 CONDITION => FragmentKind::Expr,
197 BREAK_EXPR => FragmentKind::Expr,
198 RETURN_EXPR => FragmentKind::Expr,
199 BLOCK_EXPR => FragmentKind::Expr,
200 MATCH_EXPR => FragmentKind::Expr,
201 MATCH_ARM => FragmentKind::Expr,
202 MATCH_GUARD => FragmentKind::Expr,
203 RECORD_FIELD => FragmentKind::Expr,
204 CALL_EXPR => FragmentKind::Expr,
205 INDEX_EXPR => FragmentKind::Expr,
206 METHOD_CALL_EXPR => FragmentKind::Expr,
207 AWAIT_EXPR => FragmentKind::Expr,
208 CAST_EXPR => FragmentKind::Expr,
209 REF_EXPR => FragmentKind::Expr,
210 PREFIX_EXPR => FragmentKind::Expr,
211 RANGE_EXPR => FragmentKind::Expr,
212 BIN_EXPR => FragmentKind::Expr,
191 _ => { 213 _ => {
192 // Unknown , Just guess it is `Items` 214 // Unknown , Just guess it is `Items`
193 FragmentKind::Items 215 FragmentKind::Items
diff --git a/crates/ra_hir_ty/src/tests/macros.rs b/crates/ra_hir_ty/src/tests/macros.rs
index 69c695cc8..9d09d93a7 100644
--- a/crates/ra_hir_ty/src/tests/macros.rs
+++ b/crates/ra_hir_ty/src/tests/macros.rs
@@ -136,6 +136,87 @@ fn main() {
136} 136}
137 137
138#[test] 138#[test]
139fn expr_macro_expanded_in_various_places() {
140 assert_snapshot!(
141 infer(r#"
142macro_rules! spam {
143 () => (1isize);
144}
145
146fn spam() {
147 spam!();
148 (spam!());
149 spam!().spam(spam!());
150 for _ in spam!() {}
151 || spam!();
152 while spam!() {}
153 break spam!();
154 return spam!();
155 match spam!() {
156 _ if spam!() => spam!(),
157 }
158 spam!()(spam!());
159 Spam { spam: spam!() };
160 spam!()[spam!()];
161 await spam!();
162 spam!() as usize;
163 &spam!();
164 -spam!();
165 spam!()..spam!();
166 spam!() + spam!();
167}
168"#),
169 @r###"
170 ![0; 6) '1isize': isize
171 ![0; 6) '1isize': isize
172 ![0; 6) '1isize': isize
173 ![0; 6) '1isize': isize
174 ![0; 6) '1isize': isize
175 ![0; 6) '1isize': isize
176 ![0; 6) '1isize': isize
177 ![0; 6) '1isize': isize
178 ![0; 6) '1isize': isize
179 ![0; 6) '1isize': isize
180 ![0; 6) '1isize': isize
181 ![0; 6) '1isize': isize
182 ![0; 6) '1isize': isize
183 ![0; 6) '1isize': isize
184 ![0; 6) '1isize': isize
185 ![0; 6) '1isize': isize
186 ![0; 6) '1isize': isize
187 ![0; 6) '1isize': isize
188 ![0; 6) '1isize': isize
189 ![0; 6) '1isize': isize
190 ![0; 6) '1isize': isize
191 ![0; 6) '1isize': isize
192 ![0; 6) '1isize': isize
193 ![0; 6) '1isize': isize
194 ![0; 6) '1isize': isize
195 [54; 457) '{ ...!(); }': !
196 [88; 109) 'spam!(...am!())': {unknown}
197 [115; 134) 'for _ ...!() {}': ()
198 [119; 120) '_': {unknown}
199 [132; 134) '{}': ()
200 [139; 149) '|| spam!()': || -> isize
201 [155; 171) 'while ...!() {}': ()
202 [169; 171) '{}': ()
203 [176; 189) 'break spam!()': !
204 [195; 209) 'return spam!()': !
205 [215; 269) 'match ... }': isize
206 [239; 240) '_': isize
207 [274; 290) 'spam!(...am!())': {unknown}
208 [296; 318) 'Spam {...m!() }': {unknown}
209 [324; 340) 'spam!(...am!()]': {unknown}
210 [365; 381) 'spam!(... usize': usize
211 [387; 395) '&spam!()': &isize
212 [401; 409) '-spam!()': isize
213 [415; 431) 'spam!(...pam!()': {unknown}
214 [437; 454) 'spam!(...pam!()': isize
215 "###
216 );
217}
218
219#[test]
139fn infer_type_value_macro_having_same_name() { 220fn infer_type_value_macro_having_same_name() {
140 assert_snapshot!( 221 assert_snapshot!(
141 infer(r#" 222 infer(r#"
diff --git a/crates/ra_hir_ty/src/tests/regression.rs b/crates/ra_hir_ty/src/tests/regression.rs
index 13c5f62e4..6c5d39549 100644
--- a/crates/ra_hir_ty/src/tests/regression.rs
+++ b/crates/ra_hir_ty/src/tests/regression.rs
@@ -310,6 +310,23 @@ fn test<R>(query_response: Canonical<QueryResponse<R>>) {
310} 310}
311 311
312#[test] 312#[test]
313fn infer_paren_macro_call() {
314 assert_snapshot!(
315 infer(r#"
316macro_rules! bar { () => {0u32} }
317fn test() {
318 let a = (bar!());
319}
320"#),
321 @r###"
322 ![0; 4) '0u32': u32
323 [45; 70) '{ ...()); }': ()
324 [55; 56) 'a': u32
325 "###
326 );
327}
328
329#[test]
313fn bug_1030() { 330fn bug_1030() {
314 assert_snapshot!(infer(r#" 331 assert_snapshot!(infer(r#"
315struct HashSet<T, H>; 332struct HashSet<T, H>;
diff --git a/crates/ra_ide/Cargo.toml b/crates/ra_ide/Cargo.toml
index e3439ae31..2c9f9dce0 100644
--- a/crates/ra_ide/Cargo.toml
+++ b/crates/ra_ide/Cargo.toml
@@ -13,6 +13,7 @@ wasm = []
13[dependencies] 13[dependencies]
14either = "1.5" 14either = "1.5"
15format-buf = "1.0.0" 15format-buf = "1.0.0"
16indexmap = "1.3.0"
16itertools = "0.8.0" 17itertools = "0.8.0"
17join_to_string = "0.1.3" 18join_to_string = "0.1.3"
18log = "0.4.5" 19log = "0.4.5"
diff --git a/crates/ra_ide/src/call_hierarchy.rs b/crates/ra_ide/src/call_hierarchy.rs
new file mode 100644
index 000000000..1cb712e32
--- /dev/null
+++ b/crates/ra_ide/src/call_hierarchy.rs
@@ -0,0 +1,337 @@
1//! Entry point for call-hierarchy
2
3use indexmap::IndexMap;
4
5use hir::db::AstDatabase;
6use ra_syntax::{
7 ast::{self, DocCommentsOwner},
8 match_ast, AstNode, TextRange,
9};
10
11use crate::{
12 call_info::FnCallNode,
13 db::RootDatabase,
14 display::{ShortLabel, ToNav},
15 expand::descend_into_macros,
16 goto_definition, references, FilePosition, NavigationTarget, RangeInfo,
17};
18
19#[derive(Debug, Clone)]
20pub struct CallItem {
21 pub target: NavigationTarget,
22 pub ranges: Vec<TextRange>,
23}
24
25impl CallItem {
26 #[cfg(test)]
27 pub(crate) fn assert_match(&self, expected: &str) {
28 let actual = self.debug_render();
29 test_utils::assert_eq_text!(expected.trim(), actual.trim(),);
30 }
31
32 #[cfg(test)]
33 pub(crate) fn debug_render(&self) -> String {
34 format!("{} : {:?}", self.target.debug_render(), self.ranges)
35 }
36}
37
38pub(crate) fn call_hierarchy(
39 db: &RootDatabase,
40 position: FilePosition,
41) -> Option<RangeInfo<Vec<NavigationTarget>>> {
42 goto_definition::goto_definition(db, position)
43}
44
45pub(crate) fn incoming_calls(db: &RootDatabase, position: FilePosition) -> Option<Vec<CallItem>> {
46 // 1. Find all refs
47 // 2. Loop through refs and determine unique fndef. This will become our `from: CallHierarchyItem,` in the reply.
48 // 3. Add ranges relative to the start of the fndef.
49 let refs = references::find_all_refs(db, position, None)?;
50
51 let mut calls = CallLocations::default();
52
53 for reference in refs.info.references() {
54 let file_id = reference.file_range.file_id;
55 let file = db.parse_or_expand(file_id.into())?;
56 let token = file.token_at_offset(reference.file_range.range.start()).next()?;
57 let token = descend_into_macros(db, file_id, token);
58 let syntax = token.value.parent();
59
60 // This target is the containing function
61 if let Some(nav) = syntax.ancestors().find_map(|node| {
62 match_ast! {
63 match node {
64 ast::FnDef(it) => {
65 Some(NavigationTarget::from_named(
66 db,
67 token.with_value(&it),
68 it.doc_comment_text(),
69 it.short_label(),
70 ))
71 },
72 _ => { None },
73 }
74 }
75 }) {
76 let relative_range = reference.file_range.range;
77 calls.add(&nav, relative_range);
78 }
79 }
80
81 Some(calls.into_items())
82}
83
84pub(crate) fn outgoing_calls(db: &RootDatabase, position: FilePosition) -> Option<Vec<CallItem>> {
85 let file_id = position.file_id;
86 let file = db.parse_or_expand(file_id.into())?;
87 let token = file.token_at_offset(position.offset).next()?;
88 let token = descend_into_macros(db, file_id, token);
89 let syntax = token.value.parent();
90
91 let mut calls = CallLocations::default();
92
93 syntax
94 .descendants()
95 .filter_map(|node| FnCallNode::with_node_exact(&node))
96 .filter_map(|call_node| {
97 let name_ref = call_node.name_ref()?;
98 let name_ref = token.with_value(name_ref.syntax());
99
100 let analyzer = hir::SourceAnalyzer::new(db, name_ref, None);
101
102 if let Some(func_target) = match &call_node {
103 FnCallNode::CallExpr(expr) => {
104 //FIXME: Type::as_callable is broken
105 let callable_def = analyzer.type_of(db, &expr.expr()?)?.as_callable()?;
106 match callable_def {
107 hir::CallableDef::FunctionId(it) => {
108 let fn_def: hir::Function = it.into();
109 let nav = fn_def.to_nav(db);
110 Some(nav)
111 }
112 _ => None,
113 }
114 }
115 FnCallNode::MethodCallExpr(expr) => {
116 let function = analyzer.resolve_method_call(&expr)?;
117 Some(function.to_nav(db))
118 }
119 FnCallNode::MacroCallExpr(expr) => {
120 let macro_def = analyzer.resolve_macro_call(db, name_ref.with_value(&expr))?;
121 Some(macro_def.to_nav(db))
122 }
123 } {
124 Some((func_target.clone(), name_ref.value.text_range()))
125 } else {
126 None
127 }
128 })
129 .for_each(|(nav, range)| calls.add(&nav, range));
130
131 Some(calls.into_items())
132}
133
134#[derive(Default)]
135struct CallLocations {
136 funcs: IndexMap<NavigationTarget, Vec<TextRange>>,
137}
138
139impl CallLocations {
140 fn add(&mut self, target: &NavigationTarget, range: TextRange) {
141 self.funcs.entry(target.clone()).or_default().push(range);
142 }
143
144 fn into_items(self) -> Vec<CallItem> {
145 self.funcs.into_iter().map(|(target, ranges)| CallItem { target, ranges }).collect()
146 }
147}
148
149#[cfg(test)]
150mod tests {
151 use ra_db::FilePosition;
152
153 use crate::mock_analysis::analysis_and_position;
154
155 fn check_hierarchy(
156 fixture: &str,
157 expected: &str,
158 expected_incoming: &[&str],
159 expected_outgoing: &[&str],
160 ) {
161 let (analysis, pos) = analysis_and_position(fixture);
162
163 let mut navs = analysis.call_hierarchy(pos).unwrap().unwrap().info;
164 assert_eq!(navs.len(), 1);
165 let nav = navs.pop().unwrap();
166 nav.assert_match(expected);
167
168 let item_pos = FilePosition { file_id: nav.file_id(), offset: nav.range().start() };
169 let incoming_calls = analysis.incoming_calls(item_pos).unwrap().unwrap();
170 assert_eq!(incoming_calls.len(), expected_incoming.len());
171
172 for call in 0..incoming_calls.len() {
173 incoming_calls[call].assert_match(expected_incoming[call]);
174 }
175
176 let outgoing_calls = analysis.outgoing_calls(item_pos).unwrap().unwrap();
177 assert_eq!(outgoing_calls.len(), expected_outgoing.len());
178
179 for call in 0..outgoing_calls.len() {
180 outgoing_calls[call].assert_match(expected_outgoing[call]);
181 }
182 }
183
184 #[test]
185 fn test_call_hierarchy_on_ref() {
186 check_hierarchy(
187 r#"
188 //- /lib.rs
189 fn callee() {}
190 fn caller() {
191 call<|>ee();
192 }
193 "#,
194 "callee FN_DEF FileId(1) [0; 14) [3; 9)",
195 &["caller FN_DEF FileId(1) [15; 44) [18; 24) : [[33; 39)]"],
196 &[],
197 );
198 }
199
200 #[test]
201 fn test_call_hierarchy_on_def() {
202 check_hierarchy(
203 r#"
204 //- /lib.rs
205 fn call<|>ee() {}
206 fn caller() {
207 callee();
208 }
209 "#,
210 "callee FN_DEF FileId(1) [0; 14) [3; 9)",
211 &["caller FN_DEF FileId(1) [15; 44) [18; 24) : [[33; 39)]"],
212 &[],
213 );
214 }
215
216 #[test]
217 fn test_call_hierarchy_in_same_fn() {
218 check_hierarchy(
219 r#"
220 //- /lib.rs
221 fn callee() {}
222 fn caller() {
223 call<|>ee();
224 callee();
225 }
226 "#,
227 "callee FN_DEF FileId(1) [0; 14) [3; 9)",
228 &["caller FN_DEF FileId(1) [15; 58) [18; 24) : [[33; 39), [47; 53)]"],
229 &[],
230 );
231 }
232
233 #[test]
234 fn test_call_hierarchy_in_different_fn() {
235 check_hierarchy(
236 r#"
237 //- /lib.rs
238 fn callee() {}
239 fn caller1() {
240 call<|>ee();
241 }
242
243 fn caller2() {
244 callee();
245 }
246 "#,
247 "callee FN_DEF FileId(1) [0; 14) [3; 9)",
248 &[
249 "caller1 FN_DEF FileId(1) [15; 45) [18; 25) : [[34; 40)]",
250 "caller2 FN_DEF FileId(1) [46; 76) [49; 56) : [[65; 71)]",
251 ],
252 &[],
253 );
254 }
255
256 #[test]
257 fn test_call_hierarchy_in_different_files() {
258 check_hierarchy(
259 r#"
260 //- /lib.rs
261 mod foo;
262 use foo::callee;
263
264 fn caller() {
265 call<|>ee();
266 }
267
268 //- /foo/mod.rs
269 pub fn callee() {}
270 "#,
271 "callee FN_DEF FileId(2) [0; 18) [7; 13)",
272 &["caller FN_DEF FileId(1) [26; 55) [29; 35) : [[44; 50)]"],
273 &[],
274 );
275 }
276
277 #[test]
278 fn test_call_hierarchy_outgoing() {
279 check_hierarchy(
280 r#"
281 //- /lib.rs
282 fn callee() {}
283 fn call<|>er() {
284 callee();
285 callee();
286 }
287 "#,
288 "caller FN_DEF FileId(1) [15; 58) [18; 24)",
289 &[],
290 &["callee FN_DEF FileId(1) [0; 14) [3; 9) : [[33; 39), [47; 53)]"],
291 );
292 }
293
294 #[test]
295 fn test_call_hierarchy_outgoing_in_different_files() {
296 check_hierarchy(
297 r#"
298 //- /lib.rs
299 mod foo;
300 use foo::callee;
301
302 fn call<|>er() {
303 callee();
304 }
305
306 //- /foo/mod.rs
307 pub fn callee() {}
308 "#,
309 "caller FN_DEF FileId(1) [26; 55) [29; 35)",
310 &[],
311 &["callee FN_DEF FileId(2) [0; 18) [7; 13) : [[44; 50)]"],
312 );
313 }
314
315 #[test]
316 fn test_call_hierarchy_incoming_outgoing() {
317 check_hierarchy(
318 r#"
319 //- /lib.rs
320 fn caller1() {
321 call<|>er2();
322 }
323
324 fn caller2() {
325 caller3();
326 }
327
328 fn caller3() {
329
330 }
331 "#,
332 "caller2 FN_DEF FileId(1) [32; 63) [35; 42)",
333 &["caller1 FN_DEF FileId(1) [0; 31) [3; 10) : [[19; 26)]"],
334 &["caller3 FN_DEF FileId(1) [64; 80) [67; 74) : [[51; 58)]"],
335 );
336 }
337}
diff --git a/crates/ra_ide/src/call_info.rs b/crates/ra_ide/src/call_info.rs
index 2c2b6fa48..a7023529b 100644
--- a/crates/ra_ide/src/call_info.rs
+++ b/crates/ra_ide/src/call_info.rs
@@ -88,7 +88,7 @@ pub(crate) fn call_info(db: &RootDatabase, position: FilePosition) -> Option<Cal
88} 88}
89 89
90#[derive(Debug)] 90#[derive(Debug)]
91enum FnCallNode { 91pub(crate) enum FnCallNode {
92 CallExpr(ast::CallExpr), 92 CallExpr(ast::CallExpr),
93 MethodCallExpr(ast::MethodCallExpr), 93 MethodCallExpr(ast::MethodCallExpr),
94 MacroCallExpr(ast::MacroCall), 94 MacroCallExpr(ast::MacroCall),
@@ -108,7 +108,18 @@ impl FnCallNode {
108 }) 108 })
109 } 109 }
110 110
111 fn name_ref(&self) -> Option<ast::NameRef> { 111 pub(crate) fn with_node_exact(node: &SyntaxNode) -> Option<FnCallNode> {
112 match_ast! {
113 match node {
114 ast::CallExpr(it) => { Some(FnCallNode::CallExpr(it)) },
115 ast::MethodCallExpr(it) => { Some(FnCallNode::MethodCallExpr(it)) },
116 ast::MacroCall(it) => { Some(FnCallNode::MacroCallExpr(it)) },
117 _ => { None },
118 }
119 }
120 }
121
122 pub(crate) fn name_ref(&self) -> Option<ast::NameRef> {
112 match self { 123 match self {
113 FnCallNode::CallExpr(call_expr) => Some(match call_expr.expr()? { 124 FnCallNode::CallExpr(call_expr) => Some(match call_expr.expr()? {
114 ast::Expr::PathExpr(path_expr) => path_expr.path()?.segment()?.name_ref()?, 125 ast::Expr::PathExpr(path_expr) => path_expr.path()?.segment()?.name_ref()?,
diff --git a/crates/ra_ide/src/change.rs b/crates/ra_ide/src/change.rs
index 8b197d642..b0aa2c8e0 100644
--- a/crates/ra_ide/src/change.rs
+++ b/crates/ra_ide/src/change.rs
@@ -176,7 +176,8 @@ impl RootDatabase {
176 if !change.new_roots.is_empty() { 176 if !change.new_roots.is_empty() {
177 let mut local_roots = Vec::clone(&self.local_roots()); 177 let mut local_roots = Vec::clone(&self.local_roots());
178 for (root_id, is_local) in change.new_roots { 178 for (root_id, is_local) in change.new_roots {
179 let root = if is_local { SourceRoot::new() } else { SourceRoot::new_library() }; 179 let root =
180 if is_local { SourceRoot::new_local() } else { SourceRoot::new_library() };
180 let durability = durability(&root); 181 let durability = durability(&root);
181 self.set_source_root_with_durability(root_id, Arc::new(root), durability); 182 self.set_source_root_with_durability(root_id, Arc::new(root), durability);
182 if is_local { 183 if is_local {
diff --git a/crates/ra_ide/src/display/navigation_target.rs b/crates/ra_ide/src/display/navigation_target.rs
index b9ae67828..f2e45fa31 100644
--- a/crates/ra_ide/src/display/navigation_target.rs
+++ b/crates/ra_ide/src/display/navigation_target.rs
@@ -19,7 +19,7 @@ use super::short_label::ShortLabel;
19/// 19///
20/// Typically, a `NavigationTarget` corresponds to some element in the source 20/// Typically, a `NavigationTarget` corresponds to some element in the source
21/// code, like a function or a struct, but this is not strictly required. 21/// code, like a function or a struct, but this is not strictly required.
22#[derive(Debug, Clone)] 22#[derive(Debug, Clone, PartialEq, Eq, Hash)]
23pub struct NavigationTarget { 23pub struct NavigationTarget {
24 file_id: FileId, 24 file_id: FileId,
25 name: SmolStr, 25 name: SmolStr,
diff --git a/crates/ra_ide/src/feature_flags.rs b/crates/ra_ide/src/feature_flags.rs
index de4ae513d..85617640d 100644
--- a/crates/ra_ide/src/feature_flags.rs
+++ b/crates/ra_ide/src/feature_flags.rs
@@ -56,6 +56,7 @@ impl Default for FeatureFlags {
56 ("completion.insertion.add-call-parenthesis", true), 56 ("completion.insertion.add-call-parenthesis", true),
57 ("completion.enable-postfix", true), 57 ("completion.enable-postfix", true),
58 ("notifications.workspace-loaded", true), 58 ("notifications.workspace-loaded", true),
59 ("notifications.cargo-toml-not-found", true),
59 ]) 60 ])
60 } 61 }
61} 62}
diff --git a/crates/ra_ide/src/hover.rs b/crates/ra_ide/src/hover.rs
index 35e39f965..5548681f1 100644
--- a/crates/ra_ide/src/hover.rs
+++ b/crates/ra_ide/src/hover.rs
@@ -128,7 +128,7 @@ fn hover_text_from_name_kind(db: &RootDatabase, name_kind: NameKind) -> Option<S
128 hir::ModuleDef::TypeAlias(it) => from_def_source(db, it), 128 hir::ModuleDef::TypeAlias(it) => from_def_source(db, it),
129 hir::ModuleDef::BuiltinType(it) => Some(it.to_string()), 129 hir::ModuleDef::BuiltinType(it) => Some(it.to_string()),
130 }, 130 },
131 Local(_) => None, 131 Local(it) => Some(rust_code_markup(it.ty(db).display_truncated(db, None).to_string())),
132 TypeParam(_) | SelfType(_) => { 132 TypeParam(_) | SelfType(_) => {
133 // FIXME: Hover for generic param 133 // FIXME: Hover for generic param
134 None 134 None
@@ -174,6 +174,8 @@ pub(crate) fn hover(db: &RootDatabase, position: FilePosition) -> Option<RangeIn
174 .value 174 .value
175 .ancestors() 175 .ancestors()
176 .find(|n| ast::Expr::cast(n.clone()).is_some() || ast::Pat::cast(n.clone()).is_some())?; 176 .find(|n| ast::Expr::cast(n.clone()).is_some() || ast::Pat::cast(n.clone()).is_some())?;
177
178 // The following logic will not work if token is coming from a macro
177 let frange = FileRange { file_id: position.file_id, range: node.text_range() }; 179 let frange = FileRange { file_id: position.file_id, range: node.text_range() };
178 res.extend(type_of(db, frange).map(rust_code_markup)); 180 res.extend(type_of(db, frange).map(rust_code_markup));
179 if res.is_empty() { 181 if res.is_empty() {
@@ -729,4 +731,20 @@ fn func(foo: i32) { if true { <|>foo; }; }
729 &["fn foo()"], 731 &["fn foo()"],
730 ); 732 );
731 } 733 }
734
735 #[test]
736 fn test_hover_through_expr_in_macro() {
737 check_hover_result(
738 "
739 //- /lib.rs
740 macro_rules! id {
741 ($($tt:tt)*) => { $($tt)* }
742 }
743 fn foo(bar:u32) {
744 let a = id!(ba<|>r);
745 }
746 ",
747 &["u32"],
748 );
749 }
732} 750}
diff --git a/crates/ra_ide/src/lib.rs b/crates/ra_ide/src/lib.rs
index 779a81b2c..4d8deb21c 100644
--- a/crates/ra_ide/src/lib.rs
+++ b/crates/ra_ide/src/lib.rs
@@ -24,6 +24,7 @@ mod goto_definition;
24mod goto_type_definition; 24mod goto_type_definition;
25mod extend_selection; 25mod extend_selection;
26mod hover; 26mod hover;
27mod call_hierarchy;
27mod call_info; 28mod call_info;
28mod syntax_highlighting; 29mod syntax_highlighting;
29mod parent_module; 30mod parent_module;
@@ -62,6 +63,7 @@ use crate::{db::LineIndexDatabase, display::ToNav, symbol_index::FileSymbol};
62 63
63pub use crate::{ 64pub use crate::{
64 assists::{Assist, AssistId}, 65 assists::{Assist, AssistId},
66 call_hierarchy::CallItem,
65 change::{AnalysisChange, LibraryData}, 67 change::{AnalysisChange, LibraryData},
66 completion::{CompletionItem, CompletionItemKind, InsertTextFormat}, 68 completion::{CompletionItem, CompletionItemKind, InsertTextFormat},
67 diagnostics::Severity, 69 diagnostics::Severity,
@@ -73,7 +75,9 @@ pub use crate::{
73 inlay_hints::{InlayHint, InlayKind}, 75 inlay_hints::{InlayHint, InlayKind},
74 line_index::{LineCol, LineIndex}, 76 line_index::{LineCol, LineIndex},
75 line_index_utils::translate_offset_with_edit, 77 line_index_utils::translate_offset_with_edit,
76 references::{ReferenceSearchResult, SearchScope}, 78 references::{
79 Declaration, Reference, ReferenceAccess, ReferenceKind, ReferenceSearchResult, SearchScope,
80 },
77 runnables::{Runnable, RunnableKind}, 81 runnables::{Runnable, RunnableKind},
78 source_change::{FileSystemEdit, SourceChange, SourceFileEdit}, 82 source_change::{FileSystemEdit, SourceChange, SourceFileEdit},
79 syntax_highlighting::HighlightedRange, 83 syntax_highlighting::HighlightedRange,
@@ -412,6 +416,24 @@ impl Analysis {
412 self.with_db(|db| call_info::call_info(db, position)) 416 self.with_db(|db| call_info::call_info(db, position))
413 } 417 }
414 418
419 /// Computes call hierarchy candidates for the given file position.
420 pub fn call_hierarchy(
421 &self,
422 position: FilePosition,
423 ) -> Cancelable<Option<RangeInfo<Vec<NavigationTarget>>>> {
424 self.with_db(|db| call_hierarchy::call_hierarchy(db, position))
425 }
426
427 /// Computes incoming calls for the given file position.
428 pub fn incoming_calls(&self, position: FilePosition) -> Cancelable<Option<Vec<CallItem>>> {
429 self.with_db(|db| call_hierarchy::incoming_calls(db, position))
430 }
431
432 /// Computes incoming calls for the given file position.
433 pub fn outgoing_calls(&self, position: FilePosition) -> Cancelable<Option<Vec<CallItem>>> {
434 self.with_db(|db| call_hierarchy::outgoing_calls(db, position))
435 }
436
415 /// Returns a `mod name;` declaration which created the current module. 437 /// Returns a `mod name;` declaration which created the current module.
416 pub fn parent_module(&self, position: FilePosition) -> Cancelable<Vec<NavigationTarget>> { 438 pub fn parent_module(&self, position: FilePosition) -> Cancelable<Vec<NavigationTarget>> {
417 self.with_db(|db| parent_module::parent_module(db, position)) 439 self.with_db(|db| parent_module::parent_module(db, position))
diff --git a/crates/ra_ide/src/references.rs b/crates/ra_ide/src/references.rs
index e3ecde50d..4e52e0e7b 100644
--- a/crates/ra_ide/src/references.rs
+++ b/crates/ra_ide/src/references.rs
@@ -18,7 +18,11 @@ use hir::InFile;
18use once_cell::unsync::Lazy; 18use once_cell::unsync::Lazy;
19use ra_db::{SourceDatabase, SourceDatabaseExt}; 19use ra_db::{SourceDatabase, SourceDatabaseExt};
20use ra_prof::profile; 20use ra_prof::profile;
21use ra_syntax::{algo::find_node_at_offset, ast, AstNode, SourceFile, SyntaxNode, TextUnit}; 21use ra_syntax::{
22 algo::find_node_at_offset,
23 ast::{self, NameOwner},
24 match_ast, AstNode, SourceFile, SyntaxKind, SyntaxNode, TextRange, TextUnit, TokenAtOffset,
25};
22 26
23use crate::{ 27use crate::{
24 db::RootDatabase, display::ToNav, FilePosition, FileRange, NavigationTarget, RangeInfo, 28 db::RootDatabase, display::ToNav, FilePosition, FileRange, NavigationTarget, RangeInfo,
@@ -34,16 +38,46 @@ pub use self::search_scope::SearchScope;
34 38
35#[derive(Debug, Clone)] 39#[derive(Debug, Clone)]
36pub struct ReferenceSearchResult { 40pub struct ReferenceSearchResult {
37 declaration: NavigationTarget, 41 declaration: Declaration,
38 references: Vec<FileRange>, 42 references: Vec<Reference>,
43}
44
45#[derive(Debug, Clone)]
46pub struct Declaration {
47 pub nav: NavigationTarget,
48 pub kind: ReferenceKind,
49 pub access: Option<ReferenceAccess>,
50}
51
52#[derive(Debug, Clone)]
53pub struct Reference {
54 pub file_range: FileRange,
55 pub kind: ReferenceKind,
56 pub access: Option<ReferenceAccess>,
57}
58
59#[derive(Debug, Clone, PartialEq)]
60pub enum ReferenceKind {
61 StructLiteral,
62 Other,
63}
64
65#[derive(Debug, Copy, Clone, PartialEq)]
66pub enum ReferenceAccess {
67 Read,
68 Write,
39} 69}
40 70
41impl ReferenceSearchResult { 71impl ReferenceSearchResult {
42 pub fn declaration(&self) -> &NavigationTarget { 72 pub fn declaration(&self) -> &Declaration {
43 &self.declaration 73 &self.declaration
44 } 74 }
45 75
46 pub fn references(&self) -> &[FileRange] { 76 pub fn decl_target(&self) -> &NavigationTarget {
77 &self.declaration.nav
78 }
79
80 pub fn references(&self) -> &[Reference] {
47 &self.references 81 &self.references
48 } 82 }
49 83
@@ -56,14 +90,21 @@ impl ReferenceSearchResult {
56} 90}
57 91
58// allow turning ReferenceSearchResult into an iterator 92// allow turning ReferenceSearchResult into an iterator
59// over FileRanges 93// over References
60impl IntoIterator for ReferenceSearchResult { 94impl IntoIterator for ReferenceSearchResult {
61 type Item = FileRange; 95 type Item = Reference;
62 type IntoIter = std::vec::IntoIter<FileRange>; 96 type IntoIter = std::vec::IntoIter<Reference>;
63 97
64 fn into_iter(mut self) -> Self::IntoIter { 98 fn into_iter(mut self) -> Self::IntoIter {
65 let mut v = Vec::with_capacity(self.len()); 99 let mut v = Vec::with_capacity(self.len());
66 v.push(FileRange { file_id: self.declaration.file_id(), range: self.declaration.range() }); 100 v.push(Reference {
101 file_range: FileRange {
102 file_id: self.declaration.nav.file_id(),
103 range: self.declaration.nav.range(),
104 },
105 kind: self.declaration.kind,
106 access: self.declaration.access,
107 });
67 v.append(&mut self.references); 108 v.append(&mut self.references);
68 v.into_iter() 109 v.into_iter()
69 } 110 }
@@ -71,11 +112,24 @@ impl IntoIterator for ReferenceSearchResult {
71 112
72pub(crate) fn find_all_refs( 113pub(crate) fn find_all_refs(
73 db: &RootDatabase, 114 db: &RootDatabase,
74 position: FilePosition, 115 mut position: FilePosition,
75 search_scope: Option<SearchScope>, 116 search_scope: Option<SearchScope>,
76) -> Option<RangeInfo<ReferenceSearchResult>> { 117) -> Option<RangeInfo<ReferenceSearchResult>> {
77 let parse = db.parse(position.file_id); 118 let parse = db.parse(position.file_id);
78 let syntax = parse.tree().syntax().clone(); 119 let syntax = parse.tree().syntax().clone();
120
121 let token = syntax.token_at_offset(position.offset);
122 let mut search_kind = ReferenceKind::Other;
123
124 if let TokenAtOffset::Between(ref left, ref right) = token {
125 if (right.kind() == SyntaxKind::L_CURLY || right.kind() == SyntaxKind::L_PAREN)
126 && left.kind() != SyntaxKind::IDENT
127 {
128 position = FilePosition { offset: left.text_range().start(), ..position };
129 search_kind = ReferenceKind::StructLiteral;
130 }
131 }
132
79 let RangeInfo { range, info: (name, def) } = find_name(db, &syntax, position)?; 133 let RangeInfo { range, info: (name, def) } = find_name(db, &syntax, position)?;
80 134
81 let declaration = match def.kind { 135 let declaration = match def.kind {
@@ -96,7 +150,18 @@ pub(crate) fn find_all_refs(
96 } 150 }
97 }; 151 };
98 152
99 let references = process_definition(db, def, name, search_scope); 153 let decl_range = declaration.range();
154
155 let declaration = Declaration {
156 nav: declaration,
157 kind: ReferenceKind::Other,
158 access: decl_access(&def.kind, &name, &syntax, decl_range),
159 };
160
161 let references = process_definition(db, def, name, search_scope)
162 .into_iter()
163 .filter(|r| search_kind == ReferenceKind::Other || search_kind == r.kind)
164 .collect();
100 165
101 Some(RangeInfo::new(range, ReferenceSearchResult { declaration, references })) 166 Some(RangeInfo::new(range, ReferenceSearchResult { declaration, references }))
102} 167}
@@ -122,7 +187,7 @@ fn process_definition(
122 def: NameDefinition, 187 def: NameDefinition,
123 name: String, 188 name: String,
124 scope: SearchScope, 189 scope: SearchScope,
125) -> Vec<FileRange> { 190) -> Vec<Reference> {
126 let _p = profile("process_definition"); 191 let _p = profile("process_definition");
127 192
128 let pat = name.as_str(); 193 let pat = name.as_str();
@@ -146,7 +211,26 @@ fn process_definition(
146 } 211 }
147 if let Some(d) = classify_name_ref(db, InFile::new(file_id.into(), &name_ref)) { 212 if let Some(d) = classify_name_ref(db, InFile::new(file_id.into(), &name_ref)) {
148 if d == def { 213 if d == def {
149 refs.push(FileRange { file_id, range }); 214 let kind = if name_ref
215 .syntax()
216 .ancestors()
217 .find_map(ast::RecordLit::cast)
218 .and_then(|l| l.path())
219 .and_then(|p| p.segment())
220 .and_then(|p| p.name_ref())
221 .map(|n| n == name_ref)
222 .unwrap_or(false)
223 {
224 ReferenceKind::StructLiteral
225 } else {
226 ReferenceKind::Other
227 };
228
229 refs.push(Reference {
230 file_range: FileRange { file_id, range },
231 kind,
232 access: reference_access(&d.kind, &name_ref),
233 });
150 } 234 }
151 } 235 }
152 } 236 }
@@ -155,14 +239,94 @@ fn process_definition(
155 refs 239 refs
156} 240}
157 241
242fn decl_access(
243 kind: &NameKind,
244 name: &str,
245 syntax: &SyntaxNode,
246 range: TextRange,
247) -> Option<ReferenceAccess> {
248 match kind {
249 NameKind::Local(_) | NameKind::Field(_) => {}
250 _ => return None,
251 };
252
253 let stmt = find_node_at_offset::<ast::LetStmt>(syntax, range.start())?;
254 if let Some(_) = stmt.initializer() {
255 let pat = stmt.pat()?;
256 match pat {
257 ast::Pat::BindPat(it) => {
258 if it.name()?.text().as_str() == name {
259 return Some(ReferenceAccess::Write);
260 }
261 }
262 _ => {}
263 }
264 }
265
266 None
267}
268
269fn reference_access(kind: &NameKind, name_ref: &ast::NameRef) -> Option<ReferenceAccess> {
270 // Only Locals and Fields have accesses for now.
271 match kind {
272 NameKind::Local(_) | NameKind::Field(_) => {}
273 _ => return None,
274 };
275
276 let mode = name_ref.syntax().ancestors().find_map(|node| {
277 match_ast! {
278 match (node) {
279 ast::BinExpr(expr) => {
280 if expr.op_kind()?.is_assignment() {
281 // If the variable or field ends on the LHS's end then it's a Write (covers fields and locals).
282 // FIXME: This is not terribly accurate.
283 if let Some(lhs) = expr.lhs() {
284 if lhs.syntax().text_range().end() == name_ref.syntax().text_range().end() {
285 return Some(ReferenceAccess::Write);
286 }
287 }
288 }
289 return Some(ReferenceAccess::Read);
290 },
291 _ => {None}
292 }
293 }
294 });
295
296 // Default Locals and Fields to read
297 mode.or(Some(ReferenceAccess::Read))
298}
299
158#[cfg(test)] 300#[cfg(test)]
159mod tests { 301mod tests {
160 use crate::{ 302 use crate::{
161 mock_analysis::{analysis_and_position, single_file_with_position, MockAnalysis}, 303 mock_analysis::{analysis_and_position, single_file_with_position, MockAnalysis},
162 ReferenceSearchResult, SearchScope, 304 Declaration, Reference, ReferenceSearchResult, SearchScope,
163 }; 305 };
164 306
165 #[test] 307 #[test]
308 fn test_struct_literal() {
309 let code = r#"
310 struct Foo <|>{
311 a: i32,
312 }
313 impl Foo {
314 fn f() -> i32 { 42 }
315 }
316 fn main() {
317 let f: Foo;
318 f = Foo {a: Foo::f()};
319 }"#;
320
321 let refs = get_all_refs(code);
322 check_result(
323 refs,
324 "Foo STRUCT_DEF FileId(1) [5; 39) [12; 15) Other",
325 &["FileId(1) [142; 145) StructLiteral"],
326 );
327 }
328
329 #[test]
166 fn test_find_all_refs_for_local() { 330 fn test_find_all_refs_for_local() {
167 let code = r#" 331 let code = r#"
168 fn main() { 332 fn main() {
@@ -178,7 +342,16 @@ mod tests {
178 }"#; 342 }"#;
179 343
180 let refs = get_all_refs(code); 344 let refs = get_all_refs(code);
181 assert_eq!(refs.len(), 5); 345 check_result(
346 refs,
347 "i BIND_PAT FileId(1) [33; 34) Other Write",
348 &[
349 "FileId(1) [67; 68) Other Write",
350 "FileId(1) [71; 72) Other Read",
351 "FileId(1) [101; 102) Other Write",
352 "FileId(1) [127; 128) Other Write",
353 ],
354 );
182 } 355 }
183 356
184 #[test] 357 #[test]
@@ -189,7 +362,11 @@ mod tests {
189 }"#; 362 }"#;
190 363
191 let refs = get_all_refs(code); 364 let refs = get_all_refs(code);
192 assert_eq!(refs.len(), 2); 365 check_result(
366 refs,
367 "i BIND_PAT FileId(1) [12; 13) Other",
368 &["FileId(1) [38; 39) Other Read"],
369 );
193 } 370 }
194 371
195 #[test] 372 #[test]
@@ -200,7 +377,11 @@ mod tests {
200 }"#; 377 }"#;
201 378
202 let refs = get_all_refs(code); 379 let refs = get_all_refs(code);
203 assert_eq!(refs.len(), 2); 380 check_result(
381 refs,
382 "i BIND_PAT FileId(1) [12; 13) Other",
383 &["FileId(1) [38; 39) Other Read"],
384 );
204 } 385 }
205 386
206 #[test] 387 #[test]
@@ -217,7 +398,11 @@ mod tests {
217 "#; 398 "#;
218 399
219 let refs = get_all_refs(code); 400 let refs = get_all_refs(code);
220 assert_eq!(refs.len(), 2); 401 check_result(
402 refs,
403 "spam RECORD_FIELD_DEF FileId(1) [66; 79) [70; 74) Other",
404 &["FileId(1) [152; 156) Other Read"],
405 );
221 } 406 }
222 407
223 #[test] 408 #[test]
@@ -231,7 +416,7 @@ mod tests {
231 "#; 416 "#;
232 417
233 let refs = get_all_refs(code); 418 let refs = get_all_refs(code);
234 assert_eq!(refs.len(), 1); 419 check_result(refs, "f FN_DEF FileId(1) [88; 104) [91; 92) Other", &[]);
235 } 420 }
236 421
237 #[test] 422 #[test]
@@ -246,7 +431,7 @@ mod tests {
246 "#; 431 "#;
247 432
248 let refs = get_all_refs(code); 433 let refs = get_all_refs(code);
249 assert_eq!(refs.len(), 1); 434 check_result(refs, "B ENUM_VARIANT FileId(1) [83; 84) [83; 84) Other", &[]);
250 } 435 }
251 436
252 #[test] 437 #[test]
@@ -285,7 +470,11 @@ mod tests {
285 470
286 let (analysis, pos) = analysis_and_position(code); 471 let (analysis, pos) = analysis_and_position(code);
287 let refs = analysis.find_all_refs(pos, None).unwrap().unwrap(); 472 let refs = analysis.find_all_refs(pos, None).unwrap().unwrap();
288 assert_eq!(refs.len(), 3); 473 check_result(
474 refs,
475 "Foo STRUCT_DEF FileId(2) [16; 50) [27; 30) Other",
476 &["FileId(1) [52; 55) StructLiteral", "FileId(3) [77; 80) StructLiteral"],
477 );
289 } 478 }
290 479
291 // `mod foo;` is not in the results because `foo` is an `ast::Name`. 480 // `mod foo;` is not in the results because `foo` is an `ast::Name`.
@@ -311,7 +500,11 @@ mod tests {
311 500
312 let (analysis, pos) = analysis_and_position(code); 501 let (analysis, pos) = analysis_and_position(code);
313 let refs = analysis.find_all_refs(pos, None).unwrap().unwrap(); 502 let refs = analysis.find_all_refs(pos, None).unwrap().unwrap();
314 assert_eq!(refs.len(), 2); 503 check_result(
504 refs,
505 "foo SOURCE_FILE FileId(2) [0; 35) Other",
506 &["FileId(1) [13; 16) Other"],
507 );
315 } 508 }
316 509
317 #[test] 510 #[test]
@@ -336,7 +529,11 @@ mod tests {
336 529
337 let (analysis, pos) = analysis_and_position(code); 530 let (analysis, pos) = analysis_and_position(code);
338 let refs = analysis.find_all_refs(pos, None).unwrap().unwrap(); 531 let refs = analysis.find_all_refs(pos, None).unwrap().unwrap();
339 assert_eq!(refs.len(), 3); 532 check_result(
533 refs,
534 "Foo STRUCT_DEF FileId(3) [0; 41) [18; 21) Other",
535 &["FileId(2) [20; 23) Other", "FileId(2) [46; 49) StructLiteral"],
536 );
340 } 537 }
341 538
342 #[test] 539 #[test]
@@ -360,11 +557,19 @@ mod tests {
360 let analysis = mock.analysis(); 557 let analysis = mock.analysis();
361 558
362 let refs = analysis.find_all_refs(pos, None).unwrap().unwrap(); 559 let refs = analysis.find_all_refs(pos, None).unwrap().unwrap();
363 assert_eq!(refs.len(), 3); 560 check_result(
561 refs,
562 "quux FN_DEF FileId(1) [18; 34) [25; 29) Other",
563 &["FileId(2) [16; 20) Other", "FileId(3) [16; 20) Other"],
564 );
364 565
365 let refs = 566 let refs =
366 analysis.find_all_refs(pos, Some(SearchScope::single_file(bar))).unwrap().unwrap(); 567 analysis.find_all_refs(pos, Some(SearchScope::single_file(bar))).unwrap().unwrap();
367 assert_eq!(refs.len(), 2); 568 check_result(
569 refs,
570 "quux FN_DEF FileId(1) [18; 34) [25; 29) Other",
571 &["FileId(3) [16; 20) Other"],
572 );
368 } 573 }
369 574
370 #[test] 575 #[test]
@@ -379,11 +584,106 @@ mod tests {
379 }"#; 584 }"#;
380 585
381 let refs = get_all_refs(code); 586 let refs = get_all_refs(code);
382 assert_eq!(refs.len(), 3); 587 check_result(
588 refs,
589 "m1 MACRO_CALL FileId(1) [9; 63) [46; 48) Other",
590 &["FileId(1) [96; 98) Other", "FileId(1) [114; 116) Other"],
591 );
592 }
593
594 #[test]
595 fn test_basic_highlight_read_write() {
596 let code = r#"
597 fn foo() {
598 let i<|> = 0;
599 i = i + 1;
600 }"#;
601
602 let refs = get_all_refs(code);
603 check_result(
604 refs,
605 "i BIND_PAT FileId(1) [36; 37) Other Write",
606 &["FileId(1) [55; 56) Other Write", "FileId(1) [59; 60) Other Read"],
607 );
608 }
609
610 #[test]
611 fn test_basic_highlight_field_read_write() {
612 let code = r#"
613 struct S {
614 f: u32,
615 }
616
617 fn foo() {
618 let mut s = S{f: 0};
619 s.f<|> = 0;
620 }"#;
621
622 let refs = get_all_refs(code);
623 check_result(
624 refs,
625 "f RECORD_FIELD_DEF FileId(1) [32; 38) [32; 33) Other",
626 &["FileId(1) [96; 97) Other Read", "FileId(1) [117; 118) Other Write"],
627 );
628 }
629
630 #[test]
631 fn test_basic_highlight_decl_no_write() {
632 let code = r#"
633 fn foo() {
634 let i<|>;
635 i = 1;
636 }"#;
637
638 let refs = get_all_refs(code);
639 check_result(
640 refs,
641 "i BIND_PAT FileId(1) [36; 37) Other",
642 &["FileId(1) [51; 52) Other Write"],
643 );
383 } 644 }
384 645
385 fn get_all_refs(text: &str) -> ReferenceSearchResult { 646 fn get_all_refs(text: &str) -> ReferenceSearchResult {
386 let (analysis, position) = single_file_with_position(text); 647 let (analysis, position) = single_file_with_position(text);
387 analysis.find_all_refs(position, None).unwrap().unwrap() 648 analysis.find_all_refs(position, None).unwrap().unwrap()
388 } 649 }
650
651 fn check_result(res: ReferenceSearchResult, expected_decl: &str, expected_refs: &[&str]) {
652 res.declaration().assert_match(expected_decl);
653 assert_eq!(res.references.len(), expected_refs.len());
654 res.references().iter().enumerate().for_each(|(i, r)| r.assert_match(expected_refs[i]));
655 }
656
657 impl Declaration {
658 fn debug_render(&self) -> String {
659 let mut s = format!("{} {:?}", self.nav.debug_render(), self.kind);
660 if let Some(access) = self.access {
661 s.push_str(&format!(" {:?}", access));
662 }
663 s
664 }
665
666 fn assert_match(&self, expected: &str) {
667 let actual = self.debug_render();
668 test_utils::assert_eq_text!(expected.trim(), actual.trim(),);
669 }
670 }
671
672 impl Reference {
673 fn debug_render(&self) -> String {
674 let mut s = format!(
675 "{:?} {:?} {:?}",
676 self.file_range.file_id, self.file_range.range, self.kind
677 );
678 if let Some(access) = self.access {
679 s.push_str(&format!(" {:?}", access));
680 }
681 s
682 }
683
684 fn assert_match(&self, expected: &str) {
685 let actual = self.debug_render();
686 test_utils::assert_eq_text!(expected.trim(), actual.trim(),);
687 }
688 }
389} 689}
diff --git a/crates/ra_ide/src/references/rename.rs b/crates/ra_ide/src/references/rename.rs
index b804d5f6d..e02985dcd 100644
--- a/crates/ra_ide/src/references/rename.rs
+++ b/crates/ra_ide/src/references/rename.rs
@@ -110,7 +110,13 @@ fn rename_reference(
110 110
111 let edit = refs 111 let edit = refs
112 .into_iter() 112 .into_iter()
113 .map(|range| source_edit_from_file_id_range(range.file_id, range.range, new_name)) 113 .map(|reference| {
114 source_edit_from_file_id_range(
115 reference.file_range.file_id,
116 reference.file_range.range,
117 new_name,
118 )
119 })
114 .collect::<Vec<_>>(); 120 .collect::<Vec<_>>();
115 121
116 if edit.is_empty() { 122 if edit.is_empty() {
diff --git a/crates/ra_lsp_server/src/caps.rs b/crates/ra_lsp_server/src/caps.rs
index db502c200..c4711076c 100644
--- a/crates/ra_lsp_server/src/caps.rs
+++ b/crates/ra_lsp_server/src/caps.rs
@@ -1,8 +1,8 @@
1//! Advertizes the capabilities of the LSP Server. 1//! Advertizes the capabilities of the LSP Server.
2 2
3use lsp_types::{ 3use lsp_types::{
4 CodeActionProviderCapability, CodeLensOptions, CompletionOptions, 4 CallHierarchyServerCapability, CodeActionProviderCapability, CodeLensOptions,
5 DocumentOnTypeFormattingOptions, FoldingRangeProviderCapability, 5 CompletionOptions, DocumentOnTypeFormattingOptions, FoldingRangeProviderCapability,
6 ImplementationProviderCapability, RenameOptions, RenameProviderCapability, SaveOptions, 6 ImplementationProviderCapability, RenameOptions, RenameProviderCapability, SaveOptions,
7 SelectionRangeProviderCapability, ServerCapabilities, SignatureHelpOptions, 7 SelectionRangeProviderCapability, ServerCapabilities, SignatureHelpOptions,
8 TextDocumentSyncCapability, TextDocumentSyncKind, TextDocumentSyncOptions, 8 TextDocumentSyncCapability, TextDocumentSyncKind, TextDocumentSyncOptions,
@@ -56,7 +56,7 @@ pub fn server_capabilities() -> ServerCapabilities {
56 color_provider: None, 56 color_provider: None,
57 execute_command_provider: None, 57 execute_command_provider: None,
58 workspace: None, 58 workspace: None,
59 call_hierarchy_provider: None, 59 call_hierarchy_provider: Some(CallHierarchyServerCapability::Simple(true)),
60 experimental: Default::default(), 60 experimental: Default::default(),
61 } 61 }
62} 62}
diff --git a/crates/ra_lsp_server/src/conv.rs b/crates/ra_lsp_server/src/conv.rs
index e93d4ea33..562699b7c 100644
--- a/crates/ra_lsp_server/src/conv.rs
+++ b/crates/ra_lsp_server/src/conv.rs
@@ -9,7 +9,7 @@ use lsp_types::{
9use ra_ide::{ 9use ra_ide::{
10 translate_offset_with_edit, CompletionItem, CompletionItemKind, FileId, FilePosition, 10 translate_offset_with_edit, CompletionItem, CompletionItemKind, FileId, FilePosition,
11 FileRange, FileSystemEdit, Fold, FoldKind, InsertTextFormat, LineCol, LineIndex, 11 FileRange, FileSystemEdit, Fold, FoldKind, InsertTextFormat, LineCol, LineIndex,
12 NavigationTarget, RangeInfo, Severity, SourceChange, SourceFileEdit, 12 NavigationTarget, RangeInfo, ReferenceAccess, Severity, SourceChange, SourceFileEdit,
13}; 13};
14use ra_syntax::{SyntaxKind, TextRange, TextUnit}; 14use ra_syntax::{SyntaxKind, TextRange, TextUnit};
15use ra_text_edit::{AtomTextEdit, TextEdit}; 15use ra_text_edit::{AtomTextEdit, TextEdit};
@@ -53,6 +53,18 @@ impl Conv for SyntaxKind {
53 } 53 }
54} 54}
55 55
56impl Conv for ReferenceAccess {
57 type Output = ::lsp_types::DocumentHighlightKind;
58
59 fn conv(self) -> Self::Output {
60 use lsp_types::DocumentHighlightKind;
61 match self {
62 ReferenceAccess::Read => DocumentHighlightKind::Read,
63 ReferenceAccess::Write => DocumentHighlightKind::Write,
64 }
65 }
66}
67
56impl Conv for CompletionItemKind { 68impl Conv for CompletionItemKind {
57 type Output = ::lsp_types::CompletionItemKind; 69 type Output = ::lsp_types::CompletionItemKind;
58 70
@@ -490,6 +502,24 @@ impl TryConvWith<&WorldSnapshot> for (FileId, RangeInfo<Vec<NavigationTarget>>)
490 } 502 }
491} 503}
492 504
505pub fn to_call_hierarchy_item(
506 file_id: FileId,
507 range: TextRange,
508 world: &WorldSnapshot,
509 line_index: &LineIndex,
510 nav: NavigationTarget,
511) -> Result<lsp_types::CallHierarchyItem> {
512 Ok(lsp_types::CallHierarchyItem {
513 name: nav.name().to_string(),
514 kind: nav.kind().conv(),
515 tags: None,
516 detail: nav.description().map(|it| it.to_string()),
517 uri: file_id.try_conv_with(&world)?,
518 range: nav.range().conv_with(&line_index),
519 selection_range: range.conv_with(&line_index),
520 })
521}
522
493pub fn to_location( 523pub fn to_location(
494 file_id: FileId, 524 file_id: FileId,
495 range: TextRange, 525 range: TextRange,
diff --git a/crates/ra_lsp_server/src/main_loop.rs b/crates/ra_lsp_server/src/main_loop.rs
index 4336583fe..7a49cad86 100644
--- a/crates/ra_lsp_server/src/main_loop.rs
+++ b/crates/ra_lsp_server/src/main_loop.rs
@@ -62,6 +62,22 @@ pub fn main_loop(
62 62
63 let mut loop_state = LoopState::default(); 63 let mut loop_state = LoopState::default();
64 let mut world_state = { 64 let mut world_state = {
65 let feature_flags = {
66 let mut ff = FeatureFlags::default();
67 for (flag, value) in config.feature_flags {
68 if ff.set(flag.as_str(), value).is_err() {
69 log::error!("unknown feature flag: {:?}", flag);
70 show_message(
71 req::MessageType::Error,
72 format!("unknown feature flag: {:?}", flag),
73 &connection.sender,
74 );
75 }
76 }
77 ff
78 };
79 log::info!("feature_flags: {:#?}", feature_flags);
80
65 // FIXME: support dynamic workspace loading. 81 // FIXME: support dynamic workspace loading.
66 let workspaces = { 82 let workspaces = {
67 let mut loaded_workspaces = Vec::new(); 83 let mut loaded_workspaces = Vec::new();
@@ -75,7 +91,12 @@ pub fn main_loop(
75 Ok(workspace) => loaded_workspaces.push(workspace), 91 Ok(workspace) => loaded_workspaces.push(workspace),
76 Err(e) => { 92 Err(e) => {
77 log::error!("loading workspace failed: {}", e); 93 log::error!("loading workspace failed: {}", e);
78 94 if let Some(ra_project_model::CargoTomlNotFoundError(_)) = e.downcast_ref()
95 {
96 if !feature_flags.get("notifications.cargo-toml-not-found") {
97 continue;
98 }
99 }
79 show_message( 100 show_message(
80 req::MessageType::Error, 101 req::MessageType::Error,
81 format!("rust-analyzer failed to load workspace: {}", e), 102 format!("rust-analyzer failed to load workspace: {}", e),
@@ -136,22 +157,6 @@ pub fn main_loop(
136 } 157 }
137 }; 158 };
138 159
139 let feature_flags = {
140 let mut ff = FeatureFlags::default();
141 for (flag, value) in config.feature_flags {
142 if ff.set(flag.as_str(), value).is_err() {
143 log::error!("unknown feature flag: {:?}", flag);
144 show_message(
145 req::MessageType::Error,
146 format!("unknown feature flag: {:?}", flag),
147 &connection.sender,
148 );
149 }
150 }
151 ff
152 };
153 log::info!("feature_flags: {:#?}", feature_flags);
154
155 WorldState::new( 160 WorldState::new(
156 ws_roots, 161 ws_roots,
157 workspaces, 162 workspaces,
@@ -499,6 +504,9 @@ fn on_request(
499 .on::<req::Formatting>(handlers::handle_formatting)? 504 .on::<req::Formatting>(handlers::handle_formatting)?
500 .on::<req::DocumentHighlightRequest>(handlers::handle_document_highlight)? 505 .on::<req::DocumentHighlightRequest>(handlers::handle_document_highlight)?
501 .on::<req::InlayHints>(handlers::handle_inlay_hints)? 506 .on::<req::InlayHints>(handlers::handle_inlay_hints)?
507 .on::<req::CallHierarchyPrepare>(handlers::handle_call_hierarchy_prepare)?
508 .on::<req::CallHierarchyIncomingCalls>(handlers::handle_call_hierarchy_incoming)?
509 .on::<req::CallHierarchyOutgoingCalls>(handlers::handle_call_hierarchy_outgoing)?
502 .finish(); 510 .finish();
503 Ok(()) 511 Ok(())
504} 512}
diff --git a/crates/ra_lsp_server/src/main_loop/handlers.rs b/crates/ra_lsp_server/src/main_loop/handlers.rs
index 331beab13..a592f0a12 100644
--- a/crates/ra_lsp_server/src/main_loop/handlers.rs
+++ b/crates/ra_lsp_server/src/main_loop/handlers.rs
@@ -5,13 +5,16 @@ use std::{fmt::Write as _, io::Write as _};
5 5
6use lsp_server::ErrorCode; 6use lsp_server::ErrorCode;
7use lsp_types::{ 7use lsp_types::{
8 CallHierarchyIncomingCall, CallHierarchyIncomingCallsParams, CallHierarchyItem,
9 CallHierarchyOutgoingCall, CallHierarchyOutgoingCallsParams, CallHierarchyPrepareParams,
8 CodeAction, CodeActionResponse, CodeLens, Command, CompletionItem, Diagnostic, 10 CodeAction, CodeActionResponse, CodeLens, Command, CompletionItem, Diagnostic,
9 DocumentFormattingParams, DocumentHighlight, DocumentSymbol, FoldingRange, FoldingRangeParams, 11 DocumentFormattingParams, DocumentHighlight, DocumentSymbol, FoldingRange, FoldingRangeParams,
10 Hover, HoverContents, Location, MarkupContent, MarkupKind, Position, PrepareRenameResponse, 12 Hover, HoverContents, Location, MarkupContent, MarkupKind, Position, PrepareRenameResponse,
11 Range, RenameParams, SymbolInformation, TextDocumentIdentifier, TextEdit, WorkspaceEdit, 13 Range, RenameParams, SymbolInformation, TextDocumentIdentifier, TextEdit, WorkspaceEdit,
12}; 14};
13use ra_ide::{ 15use ra_ide::{
14 AssistId, FileId, FilePosition, FileRange, Query, Runnable, RunnableKind, SearchScope, 16 AssistId, FileId, FilePosition, FileRange, Query, RangeInfo, Runnable, RunnableKind,
17 SearchScope,
15}; 18};
16use ra_prof::profile; 19use ra_prof::profile;
17use ra_syntax::{AstNode, SyntaxKind, TextRange, TextUnit}; 20use ra_syntax::{AstNode, SyntaxKind, TextRange, TextUnit};
@@ -21,7 +24,10 @@ use serde_json::to_value;
21 24
22use crate::{ 25use crate::{
23 cargo_target_spec::{runnable_args, CargoTargetSpec}, 26 cargo_target_spec::{runnable_args, CargoTargetSpec},
24 conv::{to_location, Conv, ConvWith, FoldConvCtx, MapConvWith, TryConvWith, TryConvWithToVec}, 27 conv::{
28 to_call_hierarchy_item, to_location, Conv, ConvWith, FoldConvCtx, MapConvWith, TryConvWith,
29 TryConvWithToVec,
30 },
25 req::{self, Decoration, InlayHint, InlayHintsParams, InlayKind}, 31 req::{self, Decoration, InlayHint, InlayHintsParams, InlayKind},
26 world::WorldSnapshot, 32 world::WorldSnapshot,
27 LspError, Result, 33 LspError, Result,
@@ -530,18 +536,32 @@ pub fn handle_references(
530 536
531 let locations = if params.context.include_declaration { 537 let locations = if params.context.include_declaration {
532 refs.into_iter() 538 refs.into_iter()
533 .filter_map(|r| { 539 .filter_map(|reference| {
534 let line_index = world.analysis().file_line_index(r.file_id).ok()?; 540 let line_index =
535 to_location(r.file_id, r.range, &world, &line_index).ok() 541 world.analysis().file_line_index(reference.file_range.file_id).ok()?;
542 to_location(
543 reference.file_range.file_id,
544 reference.file_range.range,
545 &world,
546 &line_index,
547 )
548 .ok()
536 }) 549 })
537 .collect() 550 .collect()
538 } else { 551 } else {
539 // Only iterate over the references if include_declaration was false 552 // Only iterate over the references if include_declaration was false
540 refs.references() 553 refs.references()
541 .iter() 554 .iter()
542 .filter_map(|r| { 555 .filter_map(|reference| {
543 let line_index = world.analysis().file_line_index(r.file_id).ok()?; 556 let line_index =
544 to_location(r.file_id, r.range, &world, &line_index).ok() 557 world.analysis().file_line_index(reference.file_range.file_id).ok()?;
558 to_location(
559 reference.file_range.file_id,
560 reference.file_range.range,
561 &world,
562 &line_index,
563 )
564 .ok()
545 }) 565 })
546 .collect() 566 .collect()
547 }; 567 };
@@ -830,8 +850,11 @@ pub fn handle_document_highlight(
830 850
831 Ok(Some( 851 Ok(Some(
832 refs.into_iter() 852 refs.into_iter()
833 .filter(|r| r.file_id == file_id) 853 .filter(|reference| reference.file_range.file_id == file_id)
834 .map(|r| DocumentHighlight { range: r.range.conv_with(&line_index), kind: None }) 854 .map(|reference| DocumentHighlight {
855 range: reference.file_range.range.conv_with(&line_index),
856 kind: reference.access.map(|it| it.conv()),
857 })
835 .collect(), 858 .collect(),
836 )) 859 ))
837} 860}
@@ -933,3 +956,91 @@ pub fn handle_inlay_hints(
933 }) 956 })
934 .collect()) 957 .collect())
935} 958}
959
960pub fn handle_call_hierarchy_prepare(
961 world: WorldSnapshot,
962 params: CallHierarchyPrepareParams,
963) -> Result<Option<Vec<CallHierarchyItem>>> {
964 let _p = profile("handle_call_hierarchy_prepare");
965 let position = params.text_document_position_params.try_conv_with(&world)?;
966 let file_id = position.file_id;
967
968 let nav_info = match world.analysis().call_hierarchy(position)? {
969 None => return Ok(None),
970 Some(it) => it,
971 };
972
973 let line_index = world.analysis().file_line_index(file_id)?;
974 let RangeInfo { range, info: navs } = nav_info;
975 let res = navs
976 .into_iter()
977 .filter(|it| it.kind() == SyntaxKind::FN_DEF)
978 .filter_map(|it| to_call_hierarchy_item(file_id, range, &world, &line_index, it).ok())
979 .collect();
980
981 Ok(Some(res))
982}
983
984pub fn handle_call_hierarchy_incoming(
985 world: WorldSnapshot,
986 params: CallHierarchyIncomingCallsParams,
987) -> Result<Option<Vec<CallHierarchyIncomingCall>>> {
988 let _p = profile("handle_call_hierarchy_incoming");
989 let item = params.item;
990
991 let doc = TextDocumentIdentifier::new(item.uri);
992 let frange: FileRange = (&doc, item.range).try_conv_with(&world)?;
993 let fpos = FilePosition { file_id: frange.file_id, offset: frange.range.start() };
994
995 let call_items = match world.analysis().incoming_calls(fpos)? {
996 None => return Ok(None),
997 Some(it) => it,
998 };
999
1000 let mut res = vec![];
1001
1002 for call_item in call_items.into_iter() {
1003 let file_id = call_item.target.file_id();
1004 let line_index = world.analysis().file_line_index(file_id)?;
1005 let range = call_item.target.range();
1006 let item = to_call_hierarchy_item(file_id, range, &world, &line_index, call_item.target)?;
1007 res.push(CallHierarchyIncomingCall {
1008 from: item,
1009 from_ranges: call_item.ranges.iter().map(|it| it.conv_with(&line_index)).collect(),
1010 });
1011 }
1012
1013 Ok(Some(res))
1014}
1015
1016pub fn handle_call_hierarchy_outgoing(
1017 world: WorldSnapshot,
1018 params: CallHierarchyOutgoingCallsParams,
1019) -> Result<Option<Vec<CallHierarchyOutgoingCall>>> {
1020 let _p = profile("handle_call_hierarchy_outgoing");
1021 let item = params.item;
1022
1023 let doc = TextDocumentIdentifier::new(item.uri);
1024 let frange: FileRange = (&doc, item.range).try_conv_with(&world)?;
1025 let fpos = FilePosition { file_id: frange.file_id, offset: frange.range.start() };
1026
1027 let call_items = match world.analysis().outgoing_calls(fpos)? {
1028 None => return Ok(None),
1029 Some(it) => it,
1030 };
1031
1032 let mut res = vec![];
1033
1034 for call_item in call_items.into_iter() {
1035 let file_id = call_item.target.file_id();
1036 let line_index = world.analysis().file_line_index(file_id)?;
1037 let range = call_item.target.range();
1038 let item = to_call_hierarchy_item(file_id, range, &world, &line_index, call_item.target)?;
1039 res.push(CallHierarchyOutgoingCall {
1040 to: item,
1041 from_ranges: call_item.ranges.iter().map(|it| it.conv_with(&line_index)).collect(),
1042 });
1043 }
1044
1045 Ok(Some(res))
1046}
diff --git a/crates/ra_lsp_server/src/req.rs b/crates/ra_lsp_server/src/req.rs
index 40edaf677..8098ff31d 100644
--- a/crates/ra_lsp_server/src/req.rs
+++ b/crates/ra_lsp_server/src/req.rs
@@ -6,13 +6,15 @@ use serde::{Deserialize, Serialize};
6 6
7pub use lsp_types::{ 7pub use lsp_types::{
8 notification::*, request::*, ApplyWorkspaceEditParams, CodeActionParams, CodeLens, 8 notification::*, request::*, ApplyWorkspaceEditParams, CodeActionParams, CodeLens,
9 CodeLensParams, CompletionParams, CompletionResponse, DidChangeConfigurationParams, 9 CodeLensParams, CompletionParams, CompletionResponse, DiagnosticTag,
10 DidChangeWatchedFilesParams, DidChangeWatchedFilesRegistrationOptions, 10 DidChangeConfigurationParams, DidChangeWatchedFilesParams,
11 DocumentOnTypeFormattingParams, DocumentSymbolParams, DocumentSymbolResponse, 11 DidChangeWatchedFilesRegistrationOptions, DocumentOnTypeFormattingParams, DocumentSymbolParams,
12 FileSystemWatcher, Hover, InitializeResult, MessageType, ProgressParams, ProgressParamsValue, 12 DocumentSymbolResponse, FileSystemWatcher, Hover, InitializeResult, MessageType,
13 ProgressToken, PublishDiagnosticsParams, ReferenceParams, Registration, RegistrationParams, 13 PartialResultParams, ProgressParams, ProgressParamsValue, ProgressToken,
14 SelectionRange, SelectionRangeParams, ShowMessageParams, SignatureHelp, TextDocumentEdit, 14 PublishDiagnosticsParams, ReferenceParams, Registration, RegistrationParams, SelectionRange,
15 TextDocumentPositionParams, TextEdit, WorkspaceEdit, WorkspaceSymbolParams, 15 SelectionRangeParams, ServerCapabilities, ShowMessageParams, SignatureHelp, SymbolKind,
16 TextDocumentEdit, TextDocumentPositionParams, TextEdit, WorkDoneProgressParams, WorkspaceEdit,
17 WorkspaceSymbolParams,
16}; 18};
17 19
18pub enum AnalyzerStatus {} 20pub enum AnalyzerStatus {}
diff --git a/crates/ra_parser/src/grammar/items/traits.rs b/crates/ra_parser/src/grammar/items/traits.rs
index 964fd3041..03dae3cdb 100644
--- a/crates/ra_parser/src/grammar/items/traits.rs
+++ b/crates/ra_parser/src/grammar/items/traits.rs
@@ -10,6 +10,16 @@ pub(super) fn trait_def(p: &mut Parser) {
10 p.bump(T![trait]); 10 p.bump(T![trait]);
11 name_r(p, ITEM_RECOVERY_SET); 11 name_r(p, ITEM_RECOVERY_SET);
12 type_params::opt_type_param_list(p); 12 type_params::opt_type_param_list(p);
13 // test trait_alias
14 // trait Z<U> = T<U>;
15 // trait Z<U> = T<U> where U: Copy;
16 // trait Z<U> = where Self: T<U>;
17 if p.eat(T![=]) {
18 type_params::bounds_without_colon(p);
19 type_params::opt_where_clause(p);
20 p.expect(T![;]);
21 return;
22 }
13 if p.at(T![:]) { 23 if p.at(T![:]) {
14 type_params::bounds(p); 24 type_params::bounds(p);
15 } 25 }
diff --git a/crates/ra_project_model/src/lib.rs b/crates/ra_project_model/src/lib.rs
index d71b7031a..b7f6a9b57 100644
--- a/crates/ra_project_model/src/lib.rs
+++ b/crates/ra_project_model/src/lib.rs
@@ -23,9 +23,19 @@ pub use crate::{
23 sysroot::Sysroot, 23 sysroot::Sysroot,
24}; 24};
25 25
26// FIXME use proper error enum
27pub type Result<T> = ::std::result::Result<T, Box<dyn Error + Send + Sync>>; 26pub type Result<T> = ::std::result::Result<T, Box<dyn Error + Send + Sync>>;
28 27
28#[derive(Clone, PartialEq, Eq, Hash, Debug)]
29pub struct CargoTomlNotFoundError(pub PathBuf);
30
31impl std::fmt::Display for CargoTomlNotFoundError {
32 fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
33 write!(fmt, "can't find Cargo.toml at {}", self.0.display())
34 }
35}
36
37impl Error for CargoTomlNotFoundError {}
38
29#[derive(Debug, Clone)] 39#[derive(Debug, Clone)]
30pub enum ProjectWorkspace { 40pub enum ProjectWorkspace {
31 /// Project workspace was discovered by running `cargo metadata` and `rustc --print sysroot`. 41 /// Project workspace was discovered by running `cargo metadata` and `rustc --print sysroot`.
@@ -362,7 +372,7 @@ fn find_cargo_toml(path: &Path) -> Result<PathBuf> {
362 } 372 }
363 curr = path.parent(); 373 curr = path.parent();
364 } 374 }
365 Err(format!("can't find Cargo.toml at {}", path.display()))? 375 Err(CargoTomlNotFoundError(path.to_path_buf()))?
366} 376}
367 377
368pub fn get_rustc_cfg_options() -> CfgOptions { 378pub fn get_rustc_cfg_options() -> CfgOptions {
diff --git a/crates/ra_project_model/src/sysroot.rs b/crates/ra_project_model/src/sysroot.rs
index 10ca391b6..34d066b1e 100644
--- a/crates/ra_project_model/src/sysroot.rs
+++ b/crates/ra_project_model/src/sysroot.rs
@@ -53,9 +53,10 @@ impl Sysroot {
53 if !src.exists() { 53 if !src.exists() {
54 Err(format!( 54 Err(format!(
55 "can't load standard library from sysroot\n\ 55 "can't load standard library from sysroot\n\
56 {:?}\n\ 56 {}\n\
57 (discovered via `rustc --print sysroot`)\n\
57 try running `rustup component add rust-src` or set `RUST_SRC_PATH`", 58 try running `rustup component add rust-src` or set `RUST_SRC_PATH`",
58 src, 59 src.display(),
59 ))?; 60 ))?;
60 } 61 }
61 62
diff --git a/crates/ra_syntax/Cargo.toml b/crates/ra_syntax/Cargo.toml
index b6ebb129d..c5a3d1999 100644
--- a/crates/ra_syntax/Cargo.toml
+++ b/crates/ra_syntax/Cargo.toml
@@ -12,7 +12,7 @@ doctest = false
12 12
13[dependencies] 13[dependencies]
14itertools = "0.8.0" 14itertools = "0.8.0"
15rowan = "0.8.0" 15rowan = "0.9.0"
16rustc_lexer = "0.1.0" 16rustc_lexer = "0.1.0"
17rustc-hash = "1.0.1" 17rustc-hash = "1.0.1"
18arrayvec = "0.5.1" 18arrayvec = "0.5.1"
diff --git a/crates/ra_syntax/src/algo.rs b/crates/ra_syntax/src/algo.rs
index e4061e994..2b2b295f9 100644
--- a/crates/ra_syntax/src/algo.rs
+++ b/crates/ra_syntax/src/algo.rs
@@ -214,8 +214,7 @@ fn with_children(
214 new_children: Vec<NodeOrToken<rowan::GreenNode, rowan::GreenToken>>, 214 new_children: Vec<NodeOrToken<rowan::GreenNode, rowan::GreenToken>>,
215) -> SyntaxNode { 215) -> SyntaxNode {
216 let len = new_children.iter().map(|it| it.text_len()).sum::<TextUnit>(); 216 let len = new_children.iter().map(|it| it.text_len()).sum::<TextUnit>();
217 let new_node = 217 let new_node = rowan::GreenNode::new(rowan::SyntaxKind(parent.kind() as u16), new_children);
218 rowan::GreenNode::new(rowan::cursor::SyntaxKind(parent.kind() as u16), new_children);
219 let new_root_node = parent.replace_with(new_node); 218 let new_root_node = parent.replace_with(new_node);
220 let new_root_node = SyntaxNode::new_root(new_root_node); 219 let new_root_node = SyntaxNode::new_root(new_root_node);
221 220
diff --git a/crates/ra_syntax/src/ast/expr_extensions.rs b/crates/ra_syntax/src/ast/expr_extensions.rs
index 23b6aa901..3dfecfe76 100644
--- a/crates/ra_syntax/src/ast/expr_extensions.rs
+++ b/crates/ra_syntax/src/ast/expr_extensions.rs
@@ -144,6 +144,7 @@ impl BinOp {
144 } 144 }
145 } 145 }
146} 146}
147
147impl ast::BinExpr { 148impl ast::BinExpr {
148 pub fn op_details(&self) -> Option<(SyntaxToken, BinOp)> { 149 pub fn op_details(&self) -> Option<(SyntaxToken, BinOp)> {
149 self.syntax().children_with_tokens().filter_map(|it| it.into_token()).find_map(|c| { 150 self.syntax().children_with_tokens().filter_map(|it| it.into_token()).find_map(|c| {
diff --git a/crates/ra_syntax/src/parsing/reparsing.rs b/crates/ra_syntax/src/parsing/reparsing.rs
index 65b8aa10d..06bdda11d 100644
--- a/crates/ra_syntax/src/parsing/reparsing.rs
+++ b/crates/ra_syntax/src/parsing/reparsing.rs
@@ -70,8 +70,7 @@ fn reparse_token<'node>(
70 } 70 }
71 } 71 }
72 72
73 let new_token = 73 let new_token = GreenToken::new(rowan::SyntaxKind(token.kind().into()), text.into());
74 GreenToken::new(rowan::cursor::SyntaxKind(token.kind().into()), text.into());
75 Some((token.replace_with(new_token), token.text_range())) 74 Some((token.replace_with(new_token), token.text_range()))
76 } 75 }
77 _ => None, 76 _ => None,
diff --git a/crates/ra_syntax/src/syntax_node.rs b/crates/ra_syntax/src/syntax_node.rs
index 041c6ea8d..b3eb5da63 100644
--- a/crates/ra_syntax/src/syntax_node.rs
+++ b/crates/ra_syntax/src/syntax_node.rs
@@ -21,18 +21,18 @@ pub enum RustLanguage {}
21impl Language for RustLanguage { 21impl Language for RustLanguage {
22 type Kind = SyntaxKind; 22 type Kind = SyntaxKind;
23 23
24 fn kind_from_raw(raw: rowan::cursor::SyntaxKind) -> SyntaxKind { 24 fn kind_from_raw(raw: rowan::SyntaxKind) -> SyntaxKind {
25 SyntaxKind::from(raw.0) 25 SyntaxKind::from(raw.0)
26 } 26 }
27 27
28 fn kind_to_raw(kind: SyntaxKind) -> rowan::cursor::SyntaxKind { 28 fn kind_to_raw(kind: SyntaxKind) -> rowan::SyntaxKind {
29 rowan::cursor::SyntaxKind(kind.into()) 29 rowan::SyntaxKind(kind.into())
30 } 30 }
31} 31}
32 32
33pub type SyntaxNode = rowan::SyntaxNode<RustLanguage>; 33pub type SyntaxNode = rowan::SyntaxNode<RustLanguage>;
34pub type SyntaxToken = rowan::SyntaxToken<RustLanguage>; 34pub type SyntaxToken = rowan::SyntaxToken<RustLanguage>;
35pub type SyntaxElement = rowan::NodeOrToken<SyntaxNode, SyntaxToken>; 35pub type SyntaxElement = rowan::SyntaxElement<RustLanguage>;
36pub type SyntaxNodeChildren = rowan::SyntaxNodeChildren<RustLanguage>; 36pub type SyntaxNodeChildren = rowan::SyntaxNodeChildren<RustLanguage>;
37pub type SyntaxElementChildren = rowan::SyntaxElementChildren<RustLanguage>; 37pub type SyntaxElementChildren = rowan::SyntaxElementChildren<RustLanguage>;
38 38
diff --git a/crates/ra_syntax/test_data/parser/inline/ok/0151_trait_alias.rs b/crates/ra_syntax/test_data/parser/inline/ok/0151_trait_alias.rs
new file mode 100644
index 000000000..4bd428ee4
--- /dev/null
+++ b/crates/ra_syntax/test_data/parser/inline/ok/0151_trait_alias.rs
@@ -0,0 +1,3 @@
1trait Z<U> = T<U>;
2trait Z<U> = T<U> where U: Copy;
3trait Z<U> = where Self: T<U>;
diff --git a/crates/ra_syntax/test_data/parser/inline/ok/0151_trait_alias.txt b/crates/ra_syntax/test_data/parser/inline/ok/0151_trait_alias.txt
new file mode 100644
index 000000000..de1536ef0
--- /dev/null
+++ b/crates/ra_syntax/test_data/parser/inline/ok/0151_trait_alias.txt
@@ -0,0 +1,128 @@
1SOURCE_FILE@[0; 83)
2 TRAIT_DEF@[0; 18)
3 TRAIT_KW@[0; 5) "trait"
4 WHITESPACE@[5; 6) " "
5 NAME@[6; 7)
6 IDENT@[6; 7) "Z"
7 TYPE_PARAM_LIST@[7; 10)
8 L_ANGLE@[7; 8) "<"
9 TYPE_PARAM@[8; 9)
10 NAME@[8; 9)
11 IDENT@[8; 9) "U"
12 R_ANGLE@[9; 10) ">"
13 WHITESPACE@[10; 11) " "
14 EQ@[11; 12) "="
15 WHITESPACE@[12; 13) " "
16 TYPE_BOUND_LIST@[13; 17)
17 TYPE_BOUND@[13; 17)
18 PATH_TYPE@[13; 17)
19 PATH@[13; 17)
20 PATH_SEGMENT@[13; 17)
21 NAME_REF@[13; 14)
22 IDENT@[13; 14) "T"
23 TYPE_ARG_LIST@[14; 17)
24 L_ANGLE@[14; 15) "<"
25 TYPE_ARG@[15; 16)
26 PATH_TYPE@[15; 16)
27 PATH@[15; 16)
28 PATH_SEGMENT@[15; 16)
29 NAME_REF@[15; 16)
30 IDENT@[15; 16) "U"
31 R_ANGLE@[16; 17) ">"
32 SEMI@[17; 18) ";"
33 WHITESPACE@[18; 19) "\n"
34 TRAIT_DEF@[19; 51)
35 TRAIT_KW@[19; 24) "trait"
36 WHITESPACE@[24; 25) " "
37 NAME@[25; 26)
38 IDENT@[25; 26) "Z"
39 TYPE_PARAM_LIST@[26; 29)
40 L_ANGLE@[26; 27) "<"
41 TYPE_PARAM@[27; 28)
42 NAME@[27; 28)
43 IDENT@[27; 28) "U"
44 R_ANGLE@[28; 29) ">"
45 WHITESPACE@[29; 30) " "
46 EQ@[30; 31) "="
47 WHITESPACE@[31; 32) " "
48 TYPE_BOUND_LIST@[32; 36)
49 TYPE_BOUND@[32; 36)
50 PATH_TYPE@[32; 36)
51 PATH@[32; 36)
52 PATH_SEGMENT@[32; 36)
53 NAME_REF@[32; 33)
54 IDENT@[32; 33) "T"
55 TYPE_ARG_LIST@[33; 36)
56 L_ANGLE@[33; 34) "<"
57 TYPE_ARG@[34; 35)
58 PATH_TYPE@[34; 35)
59 PATH@[34; 35)
60 PATH_SEGMENT@[34; 35)
61 NAME_REF@[34; 35)
62 IDENT@[34; 35) "U"
63 R_ANGLE@[35; 36) ">"
64 WHITESPACE@[36; 37) " "
65 WHERE_CLAUSE@[37; 50)
66 WHERE_KW@[37; 42) "where"
67 WHITESPACE@[42; 43) " "
68 WHERE_PRED@[43; 50)
69 PATH_TYPE@[43; 44)
70 PATH@[43; 44)
71 PATH_SEGMENT@[43; 44)
72 NAME_REF@[43; 44)
73 IDENT@[43; 44) "U"
74 COLON@[44; 45) ":"
75 WHITESPACE@[45; 46) " "
76 TYPE_BOUND_LIST@[46; 50)
77 TYPE_BOUND@[46; 50)
78 PATH_TYPE@[46; 50)
79 PATH@[46; 50)
80 PATH_SEGMENT@[46; 50)
81 NAME_REF@[46; 50)
82 IDENT@[46; 50) "Copy"
83 SEMI@[50; 51) ";"
84 WHITESPACE@[51; 52) "\n"
85 TRAIT_DEF@[52; 82)
86 TRAIT_KW@[52; 57) "trait"
87 WHITESPACE@[57; 58) " "
88 NAME@[58; 59)
89 IDENT@[58; 59) "Z"
90 TYPE_PARAM_LIST@[59; 62)
91 L_ANGLE@[59; 60) "<"
92 TYPE_PARAM@[60; 61)
93 NAME@[60; 61)
94 IDENT@[60; 61) "U"
95 R_ANGLE@[61; 62) ">"
96 WHITESPACE@[62; 63) " "
97 EQ@[63; 64) "="
98 WHITESPACE@[64; 65) " "
99 TYPE_BOUND_LIST@[65; 65)
100 WHERE_CLAUSE@[65; 81)
101 WHERE_KW@[65; 70) "where"
102 WHITESPACE@[70; 71) " "
103 WHERE_PRED@[71; 81)
104 PATH_TYPE@[71; 75)
105 PATH@[71; 75)
106 PATH_SEGMENT@[71; 75)
107 NAME_REF@[71; 75)
108 IDENT@[71; 75) "Self"
109 COLON@[75; 76) ":"
110 WHITESPACE@[76; 77) " "
111 TYPE_BOUND_LIST@[77; 81)
112 TYPE_BOUND@[77; 81)
113 PATH_TYPE@[77; 81)
114 PATH@[77; 81)
115 PATH_SEGMENT@[77; 81)
116 NAME_REF@[77; 78)
117 IDENT@[77; 78) "T"
118 TYPE_ARG_LIST@[78; 81)
119 L_ANGLE@[78; 79) "<"
120 TYPE_ARG@[79; 80)
121 PATH_TYPE@[79; 80)
122 PATH@[79; 80)
123 PATH_SEGMENT@[79; 80)
124 NAME_REF@[79; 80)
125 IDENT@[79; 80) "U"
126 R_ANGLE@[80; 81) ">"
127 SEMI@[81; 82) ";"
128 WHITESPACE@[82; 83) "\n"