aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_ide_api
diff options
context:
space:
mode:
authorSergey Parilin <[email protected]>2019-04-02 15:55:14 +0100
committerSergey Parilin <[email protected]>2019-04-02 15:55:14 +0100
commitb74449e9952846a8ea66c3507e52c24348d6dbc9 (patch)
tree00bb1101334b0bf1b189a2e6451cb28e0af959a1 /crates/ra_ide_api
parent9b73f809596e955216dde24fcf921d6985a1a767 (diff)
parent849d7428aa6b733d452b2ebc55ec322d96345f49 (diff)
Merge remote-tracking branch 'upstream/master' into issue961_profiling
Diffstat (limited to 'crates/ra_ide_api')
-rw-r--r--crates/ra_ide_api/Cargo.toml1
-rw-r--r--crates/ra_ide_api/src/change.rs4
-rw-r--r--crates/ra_ide_api/src/diagnostics.rs87
-rw-r--r--crates/ra_ide_api/src/lib.rs11
-rw-r--r--crates/ra_ide_api/src/parent_module.rs30
-rw-r--r--crates/ra_ide_api/src/references.rs48
-rw-r--r--crates/ra_ide_api/src/snapshots/tests__file_structure.snap182
-rw-r--r--crates/ra_ide_api/src/structure.rs190
-rw-r--r--crates/ra_ide_api/src/symbol_index.rs58
-rw-r--r--crates/ra_ide_api/src/syntax_tree.rs257
-rw-r--r--crates/ra_ide_api/tests/test/main.rs392
-rw-r--r--crates/ra_ide_api/tests/test/snapshots/test__unresolved_module_diagnostic.snap28
12 files changed, 827 insertions, 461 deletions
diff --git a/crates/ra_ide_api/Cargo.toml b/crates/ra_ide_api/Cargo.toml
index c64226801..45bab4e28 100644
--- a/crates/ra_ide_api/Cargo.toml
+++ b/crates/ra_ide_api/Cargo.toml
@@ -20,7 +20,6 @@ jemallocator = { version = "0.1.9", optional = true }
20jemalloc-ctl = { version = "0.2.0", optional = true } 20jemalloc-ctl = { version = "0.2.0", optional = true }
21 21
22ra_syntax = { path = "../ra_syntax" } 22ra_syntax = { path = "../ra_syntax" }
23ra_ide_api_light = { path = "../ra_ide_api_light" }
24ra_text_edit = { path = "../ra_text_edit" } 23ra_text_edit = { path = "../ra_text_edit" }
25ra_db = { path = "../ra_db" } 24ra_db = { path = "../ra_db" }
26ra_fmt = { path = "../ra_fmt" } 25ra_fmt = { path = "../ra_fmt" }
diff --git a/crates/ra_ide_api/src/change.rs b/crates/ra_ide_api/src/change.rs
index 26fde91bc..a4a086931 100644
--- a/crates/ra_ide_api/src/change.rs
+++ b/crates/ra_ide_api/src/change.rs
@@ -220,8 +220,8 @@ impl RootDatabase {
220 self.query(ra_db::ParseQuery).sweep(sweep); 220 self.query(ra_db::ParseQuery).sweep(sweep);
221 221
222 self.query(hir::db::HirParseQuery).sweep(sweep); 222 self.query(hir::db::HirParseQuery).sweep(sweep);
223 self.query(hir::db::FileItemsQuery).sweep(sweep); 223 self.query(hir::db::AstIdMapQuery).sweep(sweep);
224 self.query(hir::db::FileItemQuery).sweep(sweep); 224 self.query(hir::db::AstIdToNodeQuery).sweep(sweep);
225 225
226 self.query(hir::db::RawItemsWithSourceMapQuery).sweep(sweep); 226 self.query(hir::db::RawItemsWithSourceMapQuery).sweep(sweep);
227 self.query(hir::db::BodyWithSourceMapQuery).sweep(sweep); 227 self.query(hir::db::BodyWithSourceMapQuery).sweep(sweep);
diff --git a/crates/ra_ide_api/src/diagnostics.rs b/crates/ra_ide_api/src/diagnostics.rs
index 156f28ca3..5a78e94d8 100644
--- a/crates/ra_ide_api/src/diagnostics.rs
+++ b/crates/ra_ide_api/src/diagnostics.rs
@@ -1,10 +1,11 @@
1use std::cell::RefCell;
2
1use itertools::Itertools; 3use itertools::Itertools;
2use hir::{Problem, source_binder}; 4use hir::{source_binder, diagnostics::{Diagnostic as _, DiagnosticSink}};
3use ra_db::SourceDatabase; 5use ra_db::SourceDatabase;
4use ra_syntax::{ 6use ra_syntax::{
5 Location, SourceFile, SyntaxKind, TextRange, SyntaxNode, 7 Location, SourceFile, SyntaxKind, TextRange, SyntaxNode,
6 ast::{self, AstNode}, 8 ast::{self, AstNode},
7
8}; 9};
9use ra_text_edit::{TextEdit, TextEditBuilder}; 10use ra_text_edit::{TextEdit, TextEditBuilder};
10 11
@@ -26,11 +27,31 @@ pub(crate) fn diagnostics(db: &RootDatabase, file_id: FileId) -> Vec<Diagnostic>
26 check_unnecessary_braces_in_use_statement(&mut res, file_id, node); 27 check_unnecessary_braces_in_use_statement(&mut res, file_id, node);
27 check_struct_shorthand_initialization(&mut res, file_id, node); 28 check_struct_shorthand_initialization(&mut res, file_id, node);
28 } 29 }
29 30 let res = RefCell::new(res);
31 let mut sink = DiagnosticSink::new(|d| {
32 res.borrow_mut().push(Diagnostic {
33 message: d.message(),
34 range: d.highlight_range(),
35 severity: Severity::Error,
36 fix: None,
37 })
38 })
39 .on::<hir::diagnostics::UnresolvedModule, _>(|d| {
40 let source_root = db.file_source_root(d.file().original_file(db));
41 let create_file = FileSystemEdit::CreateFile { source_root, path: d.candidate.clone() };
42 let fix = SourceChange::file_system_edit("create module", create_file);
43 res.borrow_mut().push(Diagnostic {
44 range: d.highlight_range(),
45 message: d.message(),
46 severity: Severity::Error,
47 fix: Some(fix),
48 })
49 });
30 if let Some(m) = source_binder::module_from_file_id(db, file_id) { 50 if let Some(m) = source_binder::module_from_file_id(db, file_id) {
31 check_module(&mut res, db, file_id, m); 51 m.diagnostics(db, &mut sink);
32 }; 52 };
33 res 53 drop(sink);
54 res.into_inner()
34} 55}
35 56
36fn syntax_errors(acc: &mut Vec<Diagnostic>, source_file: &SourceFile) { 57fn syntax_errors(acc: &mut Vec<Diagnostic>, source_file: &SourceFile) {
@@ -128,34 +149,12 @@ fn check_struct_shorthand_initialization(
128 Some(()) 149 Some(())
129} 150}
130 151
131fn check_module(
132 acc: &mut Vec<Diagnostic>,
133 db: &RootDatabase,
134 file_id: FileId,
135 module: hir::Module,
136) {
137 let source_root = db.file_source_root(file_id);
138 for (name_node, problem) in module.problems(db) {
139 let diag = match problem {
140 Problem::UnresolvedModule { candidate } => {
141 let create_file =
142 FileSystemEdit::CreateFile { source_root, path: candidate.clone() };
143 let fix = SourceChange::file_system_edit("create module", create_file);
144 Diagnostic {
145 range: name_node.range(),
146 message: "unresolved module".to_string(),
147 severity: Severity::Error,
148 fix: Some(fix),
149 }
150 }
151 };
152 acc.push(diag)
153 }
154}
155
156#[cfg(test)] 152#[cfg(test)]
157mod tests { 153mod tests {
158 use test_utils::assert_eq_text; 154 use test_utils::assert_eq_text;
155 use insta::assert_debug_snapshot_matches;
156
157 use crate::mock_analysis::single_file;
159 158
160 use super::*; 159 use super::*;
161 160
@@ -185,6 +184,34 @@ mod tests {
185 } 184 }
186 185
187 #[test] 186 #[test]
187 fn test_unresolved_module_diagnostic() {
188 let (analysis, file_id) = single_file("mod foo;");
189 let diagnostics = analysis.diagnostics(file_id).unwrap();
190 assert_debug_snapshot_matches!(diagnostics, @r####"[
191 Diagnostic {
192 message: "unresolved module",
193 range: [0; 8),
194 fix: Some(
195 SourceChange {
196 label: "create module",
197 source_file_edits: [],
198 file_system_edits: [
199 CreateFile {
200 source_root: SourceRootId(
201 0
202 ),
203 path: "foo.rs"
204 }
205 ],
206 cursor_position: None
207 }
208 ),
209 severity: Error
210 }
211]"####);
212 }
213
214 #[test]
188 fn test_check_unnecessary_braces_in_use_statement() { 215 fn test_check_unnecessary_braces_in_use_statement() {
189 check_not_applicable( 216 check_not_applicable(
190 " 217 "
diff --git a/crates/ra_ide_api/src/lib.rs b/crates/ra_ide_api/src/lib.rs
index 8aa3eb088..9063f78a9 100644
--- a/crates/ra_ide_api/src/lib.rs
+++ b/crates/ra_ide_api/src/lib.rs
@@ -6,9 +6,6 @@
6//! database, and the `ra_hir` crate, where majority of the analysis happens. 6//! database, and the `ra_hir` crate, where majority of the analysis happens.
7//! However, IDE specific bits of the analysis (most notably completion) happen 7//! However, IDE specific bits of the analysis (most notably completion) happen
8//! in this crate. 8//! in this crate.
9//!
10//! The sibling `ra_ide_api_light` handles those bits of IDE functionality
11//! which are restricted to a single file and need only syntax.
12 9
13// For proving that RootDatabase is RefUnwindSafe. 10// For proving that RootDatabase is RefUnwindSafe.
14#![recursion_limit = "128"] 11#![recursion_limit = "128"]
@@ -33,10 +30,11 @@ mod impls;
33mod assists; 30mod assists;
34mod diagnostics; 31mod diagnostics;
35mod syntax_tree; 32mod syntax_tree;
36mod line_index;
37mod folding_ranges; 33mod folding_ranges;
34mod line_index;
38mod line_index_utils; 35mod line_index_utils;
39mod join_lines; 36mod join_lines;
37mod structure;
40mod typing; 38mod typing;
41mod matching_brace; 39mod matching_brace;
42 40
@@ -72,9 +70,10 @@ pub use crate::{
72 line_index_utils::translate_offset_with_edit, 70 line_index_utils::translate_offset_with_edit,
73 folding_ranges::{Fold, FoldKind}, 71 folding_ranges::{Fold, FoldKind},
74 syntax_highlighting::HighlightedRange, 72 syntax_highlighting::HighlightedRange,
73 structure::{StructureNode, file_structure},
75 diagnostics::Severity, 74 diagnostics::Severity,
76}; 75};
77pub use ra_ide_api_light::StructureNode; 76
78pub use ra_db::{ 77pub use ra_db::{
79 Canceled, CrateGraph, CrateId, FileId, FilePosition, FileRange, SourceRootId, 78 Canceled, CrateGraph, CrateId, FileId, FilePosition, FileRange, SourceRootId,
80 Edition 79 Edition
@@ -388,7 +387,7 @@ impl Analysis {
388 /// file outline. 387 /// file outline.
389 pub fn file_structure(&self, file_id: FileId) -> Vec<StructureNode> { 388 pub fn file_structure(&self, file_id: FileId) -> Vec<StructureNode> {
390 let file = self.db.parse(file_id); 389 let file = self.db.parse(file_id);
391 ra_ide_api_light::file_structure(&file) 390 structure::file_structure(&file)
392 } 391 }
393 392
394 /// Returns the set of folding ranges. 393 /// Returns the set of folding ranges.
diff --git a/crates/ra_ide_api/src/parent_module.rs b/crates/ra_ide_api/src/parent_module.rs
index 603c3db6a..27788c984 100644
--- a/crates/ra_ide_api/src/parent_module.rs
+++ b/crates/ra_ide_api/src/parent_module.rs
@@ -28,7 +28,11 @@ pub(crate) fn crate_for(db: &RootDatabase, file_id: FileId) -> Vec<CrateId> {
28 28
29#[cfg(test)] 29#[cfg(test)]
30mod tests { 30mod tests {
31 use crate::mock_analysis::analysis_and_position; 31 use crate::{
32 AnalysisChange, CrateGraph,
33 mock_analysis::{analysis_and_position, MockAnalysis},
34 Edition::Edition2018,
35};
32 36
33 #[test] 37 #[test]
34 fn test_resolve_parent_module() { 38 fn test_resolve_parent_module() {
@@ -59,4 +63,28 @@ mod tests {
59 let nav = analysis.parent_module(pos).unwrap().pop().unwrap(); 63 let nav = analysis.parent_module(pos).unwrap().pop().unwrap();
60 nav.assert_match("baz MODULE FileId(1) [32; 44)"); 64 nav.assert_match("baz MODULE FileId(1) [32; 44)");
61 } 65 }
66
67 #[test]
68 fn test_resolve_crate_root() {
69 let mock = MockAnalysis::with_files(
70 "
71 //- /bar.rs
72 mod foo;
73 //- /foo.rs
74 // empty <|>
75 ",
76 );
77 let root_file = mock.id_of("/bar.rs");
78 let mod_file = mock.id_of("/foo.rs");
79 let mut host = mock.analysis_host();
80 assert!(host.analysis().crate_for(mod_file).unwrap().is_empty());
81
82 let mut crate_graph = CrateGraph::default();
83 let crate_id = crate_graph.add_crate_root(root_file, Edition2018);
84 let mut change = AnalysisChange::new();
85 change.set_crate_graph(crate_graph);
86 host.apply_change(change);
87
88 assert_eq!(host.analysis().crate_for(mod_file).unwrap(), vec![crate_id]);
89 }
62} 90}
diff --git a/crates/ra_ide_api/src/references.rs b/crates/ra_ide_api/src/references.rs
index 22741445a..20bbf11a3 100644
--- a/crates/ra_ide_api/src/references.rs
+++ b/crates/ra_ide_api/src/references.rs
@@ -216,10 +216,56 @@ mod tests {
216 use crate::{ 216 use crate::{
217 mock_analysis::single_file_with_position, 217 mock_analysis::single_file_with_position,
218 mock_analysis::analysis_and_position, 218 mock_analysis::analysis_and_position,
219 FileId 219 FileId, ReferenceSearchResult
220}; 220};
221 221
222 #[test] 222 #[test]
223 fn test_find_all_refs_for_local() {
224 let code = r#"
225 fn main() {
226 let mut i = 1;
227 let j = 1;
228 i = i<|> + j;
229
230 {
231 i = 0;
232 }
233
234 i = 5;
235 }"#;
236
237 let refs = get_all_refs(code);
238 assert_eq!(refs.len(), 5);
239 }
240
241 #[test]
242 fn test_find_all_refs_for_param_inside() {
243 let code = r#"
244 fn foo(i : u32) -> u32 {
245 i<|>
246 }"#;
247
248 let refs = get_all_refs(code);
249 assert_eq!(refs.len(), 2);
250 }
251
252 #[test]
253 fn test_find_all_refs_for_fn_param() {
254 let code = r#"
255 fn foo(i<|> : u32) -> u32 {
256 i
257 }"#;
258
259 let refs = get_all_refs(code);
260 assert_eq!(refs.len(), 2);
261 }
262
263 fn get_all_refs(text: &str) -> ReferenceSearchResult {
264 let (analysis, position) = single_file_with_position(text);
265 analysis.find_all_refs(position).unwrap().unwrap()
266 }
267
268 #[test]
223 fn test_rename_for_local() { 269 fn test_rename_for_local() {
224 test_rename( 270 test_rename(
225 r#" 271 r#"
diff --git a/crates/ra_ide_api/src/snapshots/tests__file_structure.snap b/crates/ra_ide_api/src/snapshots/tests__file_structure.snap
new file mode 100644
index 000000000..2efa8e22c
--- /dev/null
+++ b/crates/ra_ide_api/src/snapshots/tests__file_structure.snap
@@ -0,0 +1,182 @@
1---
2created: "2019-02-05T22:03:50.763530100Z"
3creator: [email protected]
4source: crates/ra_ide_api/src/structure.rs
5expression: structure
6---
7[
8 StructureNode {
9 parent: None,
10 label: "Foo",
11 navigation_range: [8; 11),
12 node_range: [1; 26),
13 kind: STRUCT_DEF,
14 detail: None,
15 deprecated: false
16 },
17 StructureNode {
18 parent: Some(
19 0
20 ),
21 label: "x",
22 navigation_range: [18; 19),
23 node_range: [18; 24),
24 kind: NAMED_FIELD_DEF,
25 detail: Some(
26 "i32"
27 ),
28 deprecated: false
29 },
30 StructureNode {
31 parent: None,
32 label: "m",
33 navigation_range: [32; 33),
34 node_range: [28; 158),
35 kind: MODULE,
36 detail: None,
37 deprecated: false
38 },
39 StructureNode {
40 parent: Some(
41 2
42 ),
43 label: "bar1",
44 navigation_range: [43; 47),
45 node_range: [40; 52),
46 kind: FN_DEF,
47 detail: Some(
48 "fn()"
49 ),
50 deprecated: false
51 },
52 StructureNode {
53 parent: Some(
54 2
55 ),
56 label: "bar2",
57 navigation_range: [60; 64),
58 node_range: [57; 81),
59 kind: FN_DEF,
60 detail: Some(
61 "fn<T>(t: T) -> T"
62 ),
63 deprecated: false
64 },
65 StructureNode {
66 parent: Some(
67 2
68 ),
69 label: "bar3",
70 navigation_range: [89; 93),
71 node_range: [86; 156),
72 kind: FN_DEF,
73 detail: Some(
74 "fn<A, B>(a: A, b: B) -> Vec< u32 >"
75 ),
76 deprecated: false
77 },
78 StructureNode {
79 parent: None,
80 label: "E",
81 navigation_range: [165; 166),
82 node_range: [160; 180),
83 kind: ENUM_DEF,
84 detail: None,
85 deprecated: false
86 },
87 StructureNode {
88 parent: Some(
89 6
90 ),
91 label: "X",
92 navigation_range: [169; 170),
93 node_range: [169; 170),
94 kind: ENUM_VARIANT,
95 detail: None,
96 deprecated: false
97 },
98 StructureNode {
99 parent: Some(
100 6
101 ),
102 label: "Y",
103 navigation_range: [172; 173),
104 node_range: [172; 178),
105 kind: ENUM_VARIANT,
106 detail: None,
107 deprecated: false
108 },
109 StructureNode {
110 parent: None,
111 label: "T",
112 navigation_range: [186; 187),
113 node_range: [181; 193),
114 kind: TYPE_ALIAS_DEF,
115 detail: Some(
116 "()"
117 ),
118 deprecated: false
119 },
120 StructureNode {
121 parent: None,
122 label: "S",
123 navigation_range: [201; 202),
124 node_range: [194; 213),
125 kind: STATIC_DEF,
126 detail: Some(
127 "i32"
128 ),
129 deprecated: false
130 },
131 StructureNode {
132 parent: None,
133 label: "C",
134 navigation_range: [220; 221),
135 node_range: [214; 232),
136 kind: CONST_DEF,
137 detail: Some(
138 "i32"
139 ),
140 deprecated: false
141 },
142 StructureNode {
143 parent: None,
144 label: "impl E",
145 navigation_range: [239; 240),
146 node_range: [234; 243),
147 kind: IMPL_BLOCK,
148 detail: None,
149 deprecated: false
150 },
151 StructureNode {
152 parent: None,
153 label: "impl fmt::Debug for E",
154 navigation_range: [265; 266),
155 node_range: [245; 269),
156 kind: IMPL_BLOCK,
157 detail: None,
158 deprecated: false
159 },
160 StructureNode {
161 parent: None,
162 label: "obsolete",
163 navigation_range: [288; 296),
164 node_range: [271; 301),
165 kind: FN_DEF,
166 detail: Some(
167 "fn()"
168 ),
169 deprecated: true
170 },
171 StructureNode {
172 parent: None,
173 label: "very_obsolete",
174 navigation_range: [341; 354),
175 node_range: [303; 359),
176 kind: FN_DEF,
177 detail: Some(
178 "fn()"
179 ),
180 deprecated: true
181 }
182]
diff --git a/crates/ra_ide_api/src/structure.rs b/crates/ra_ide_api/src/structure.rs
new file mode 100644
index 000000000..ec2c9bbc6
--- /dev/null
+++ b/crates/ra_ide_api/src/structure.rs
@@ -0,0 +1,190 @@
1use crate::TextRange;
2
3use ra_syntax::{
4 algo::visit::{visitor, Visitor},
5 ast::{self, AttrsOwner, NameOwner, TypeParamsOwner, TypeAscriptionOwner},
6 AstNode, SourceFile, SyntaxKind, SyntaxNode, WalkEvent,
7};
8
9#[derive(Debug, Clone)]
10pub struct StructureNode {
11 pub parent: Option<usize>,
12 pub label: String,
13 pub navigation_range: TextRange,
14 pub node_range: TextRange,
15 pub kind: SyntaxKind,
16 pub detail: Option<String>,
17 pub deprecated: bool,
18}
19
20pub fn file_structure(file: &SourceFile) -> Vec<StructureNode> {
21 let mut res = Vec::new();
22 let mut stack = Vec::new();
23
24 for event in file.syntax().preorder() {
25 match event {
26 WalkEvent::Enter(node) => {
27 if let Some(mut symbol) = structure_node(node) {
28 symbol.parent = stack.last().map(|&n| n);
29 stack.push(res.len());
30 res.push(symbol);
31 }
32 }
33 WalkEvent::Leave(node) => {
34 if structure_node(node).is_some() {
35 stack.pop().unwrap();
36 }
37 }
38 }
39 }
40 res
41}
42
43fn structure_node(node: &SyntaxNode) -> Option<StructureNode> {
44 fn decl<N: NameOwner + AttrsOwner>(node: &N) -> Option<StructureNode> {
45 decl_with_detail(node, None)
46 }
47
48 fn decl_with_ascription<N: NameOwner + AttrsOwner + TypeAscriptionOwner>(
49 node: &N,
50 ) -> Option<StructureNode> {
51 decl_with_type_ref(node, node.ascribed_type())
52 }
53
54 fn decl_with_type_ref<N: NameOwner + AttrsOwner>(
55 node: &N,
56 type_ref: Option<&ast::TypeRef>,
57 ) -> Option<StructureNode> {
58 let detail = type_ref.map(|type_ref| {
59 let mut detail = String::new();
60 collapse_ws(type_ref.syntax(), &mut detail);
61 detail
62 });
63 decl_with_detail(node, detail)
64 }
65
66 fn decl_with_detail<N: NameOwner + AttrsOwner>(
67 node: &N,
68 detail: Option<String>,
69 ) -> Option<StructureNode> {
70 let name = node.name()?;
71
72 Some(StructureNode {
73 parent: None,
74 label: name.text().to_string(),
75 navigation_range: name.syntax().range(),
76 node_range: node.syntax().range(),
77 kind: node.syntax().kind(),
78 detail,
79 deprecated: node.attrs().filter_map(|x| x.as_named()).any(|x| x == "deprecated"),
80 })
81 }
82
83 fn collapse_ws(node: &SyntaxNode, output: &mut String) {
84 let mut can_insert_ws = false;
85 for line in node.text().chunks().flat_map(|chunk| chunk.lines()) {
86 let line = line.trim();
87 if line.is_empty() {
88 if can_insert_ws {
89 output.push_str(" ");
90 can_insert_ws = false;
91 }
92 } else {
93 output.push_str(line);
94 can_insert_ws = true;
95 }
96 }
97 }
98
99 visitor()
100 .visit(|fn_def: &ast::FnDef| {
101 let mut detail = String::from("fn");
102 if let Some(type_param_list) = fn_def.type_param_list() {
103 collapse_ws(type_param_list.syntax(), &mut detail);
104 }
105 if let Some(param_list) = fn_def.param_list() {
106 collapse_ws(param_list.syntax(), &mut detail);
107 }
108 if let Some(ret_type) = fn_def.ret_type() {
109 detail.push_str(" ");
110 collapse_ws(ret_type.syntax(), &mut detail);
111 }
112
113 decl_with_detail(fn_def, Some(detail))
114 })
115 .visit(decl::<ast::StructDef>)
116 .visit(decl::<ast::EnumDef>)
117 .visit(decl::<ast::EnumVariant>)
118 .visit(decl::<ast::TraitDef>)
119 .visit(decl::<ast::Module>)
120 .visit(|td: &ast::TypeAliasDef| decl_with_type_ref(td, td.type_ref()))
121 .visit(decl_with_ascription::<ast::NamedFieldDef>)
122 .visit(decl_with_ascription::<ast::ConstDef>)
123 .visit(decl_with_ascription::<ast::StaticDef>)
124 .visit(|im: &ast::ImplBlock| {
125 let target_type = im.target_type()?;
126 let target_trait = im.target_trait();
127 let label = match target_trait {
128 None => format!("impl {}", target_type.syntax().text()),
129 Some(t) => {
130 format!("impl {} for {}", t.syntax().text(), target_type.syntax().text(),)
131 }
132 };
133
134 let node = StructureNode {
135 parent: None,
136 label,
137 navigation_range: target_type.syntax().range(),
138 node_range: im.syntax().range(),
139 kind: im.syntax().kind(),
140 detail: None,
141 deprecated: false,
142 };
143 Some(node)
144 })
145 .accept(node)?
146}
147
148#[cfg(test)]
149mod tests {
150 use super::*;
151 use insta::assert_debug_snapshot_matches;
152
153 #[test]
154 fn test_file_structure() {
155 let file = SourceFile::parse(
156 r#"
157struct Foo {
158 x: i32
159}
160
161mod m {
162 fn bar1() {}
163 fn bar2<T>(t: T) -> T {}
164 fn bar3<A,
165 B>(a: A,
166 b: B) -> Vec<
167 u32
168 > {}
169}
170
171enum E { X, Y(i32) }
172type T = ();
173static S: i32 = 92;
174const C: i32 = 92;
175
176impl E {}
177
178impl fmt::Debug for E {}
179
180#[deprecated]
181fn obsolete() {}
182
183#[deprecated(note = "for awhile")]
184fn very_obsolete() {}
185"#,
186 );
187 let structure = file_structure(&file);
188 assert_debug_snapshot_matches!("file_structure", structure);
189 }
190}
diff --git a/crates/ra_ide_api/src/symbol_index.rs b/crates/ra_ide_api/src/symbol_index.rs
index 0978d164a..0eadc4e71 100644
--- a/crates/ra_ide_api/src/symbol_index.rs
+++ b/crates/ra_ide_api/src/symbol_index.rs
@@ -270,3 +270,61 @@ fn to_file_symbol(node: &SyntaxNode, file_id: FileId) -> Option<FileSymbol> {
270 container_name: None, 270 container_name: None,
271 }) 271 })
272} 272}
273
274#[cfg(test)]
275mod tests {
276 use ra_syntax::SmolStr;
277 use crate::{
278 navigation_target::NavigationTarget,
279 mock_analysis::single_file,
280 Query,
281};
282
283 #[test]
284 fn test_world_symbols_with_no_container() {
285 let code = r#"
286 enum FooInner { }
287 "#;
288
289 let mut symbols = get_symbols_matching(code, "FooInner");
290
291 let s = symbols.pop().unwrap();
292
293 assert_eq!(s.name(), "FooInner");
294 assert!(s.container_name().is_none());
295 }
296
297 #[test]
298 fn test_world_symbols_include_container_name() {
299 let code = r#"
300fn foo() {
301 enum FooInner { }
302}
303 "#;
304
305 let mut symbols = get_symbols_matching(code, "FooInner");
306
307 let s = symbols.pop().unwrap();
308
309 assert_eq!(s.name(), "FooInner");
310 assert_eq!(s.container_name(), Some(&SmolStr::new("foo")));
311
312 let code = r#"
313mod foo {
314 struct FooInner;
315}
316 "#;
317
318 let mut symbols = get_symbols_matching(code, "FooInner");
319
320 let s = symbols.pop().unwrap();
321
322 assert_eq!(s.name(), "FooInner");
323 assert_eq!(s.container_name(), Some(&SmolStr::new("foo")));
324 }
325
326 fn get_symbols_matching(text: &str, query: &str) -> Vec<NavigationTarget> {
327 let (analysis, _) = single_file(text);
328 analysis.symbol_search(Query::new(query.into())).unwrap()
329 }
330}
diff --git a/crates/ra_ide_api/src/syntax_tree.rs b/crates/ra_ide_api/src/syntax_tree.rs
index bbe9222b4..276f8a8c8 100644
--- a/crates/ra_ide_api/src/syntax_tree.rs
+++ b/crates/ra_ide_api/src/syntax_tree.rs
@@ -85,3 +85,260 @@ fn syntax_tree_for_token<T: AstToken>(node: &T, text_range: TextRange) -> Option
85 85
86 None 86 None
87} 87}
88
89#[cfg(test)]
90mod tests {
91 use crate::mock_analysis::{single_file, single_file_with_range};
92
93 #[test]
94 fn test_syntax_tree_without_range() {
95 // Basic syntax
96 let (analysis, file_id) = single_file(r#"fn foo() {}"#);
97 let syn = analysis.syntax_tree(file_id, None);
98
99 assert_eq!(
100 syn.trim(),
101 r#"
102SOURCE_FILE@[0; 11)
103 FN_DEF@[0; 11)
104 FN_KW@[0; 2)
105 WHITESPACE@[2; 3)
106 NAME@[3; 6)
107 IDENT@[3; 6) "foo"
108 PARAM_LIST@[6; 8)
109 L_PAREN@[6; 7)
110 R_PAREN@[7; 8)
111 WHITESPACE@[8; 9)
112 BLOCK@[9; 11)
113 L_CURLY@[9; 10)
114 R_CURLY@[10; 11)
115 "#
116 .trim()
117 );
118
119 let (analysis, file_id) = single_file(
120 r#"
121fn test() {
122 assert!("
123 fn foo() {
124 }
125 ", "");
126}"#
127 .trim(),
128 );
129 let syn = analysis.syntax_tree(file_id, None);
130
131 assert_eq!(
132 syn.trim(),
133 r#"
134SOURCE_FILE@[0; 60)
135 FN_DEF@[0; 60)
136 FN_KW@[0; 2)
137 WHITESPACE@[2; 3)
138 NAME@[3; 7)
139 IDENT@[3; 7) "test"
140 PARAM_LIST@[7; 9)
141 L_PAREN@[7; 8)
142 R_PAREN@[8; 9)
143 WHITESPACE@[9; 10)
144 BLOCK@[10; 60)
145 L_CURLY@[10; 11)
146 WHITESPACE@[11; 16)
147 EXPR_STMT@[16; 58)
148 MACRO_CALL@[16; 57)
149 PATH@[16; 22)
150 PATH_SEGMENT@[16; 22)
151 NAME_REF@[16; 22)
152 IDENT@[16; 22) "assert"
153 EXCL@[22; 23)
154 TOKEN_TREE@[23; 57)
155 L_PAREN@[23; 24)
156 STRING@[24; 52)
157 COMMA@[52; 53)
158 WHITESPACE@[53; 54)
159 STRING@[54; 56)
160 R_PAREN@[56; 57)
161 SEMI@[57; 58)
162 WHITESPACE@[58; 59)
163 R_CURLY@[59; 60)
164 "#
165 .trim()
166 );
167 }
168
169 #[test]
170 fn test_syntax_tree_with_range() {
171 let (analysis, range) = single_file_with_range(r#"<|>fn foo() {}<|>"#.trim());
172 let syn = analysis.syntax_tree(range.file_id, Some(range.range));
173
174 assert_eq!(
175 syn.trim(),
176 r#"
177FN_DEF@[0; 11)
178 FN_KW@[0; 2)
179 WHITESPACE@[2; 3)
180 NAME@[3; 6)
181 IDENT@[3; 6) "foo"
182 PARAM_LIST@[6; 8)
183 L_PAREN@[6; 7)
184 R_PAREN@[7; 8)
185 WHITESPACE@[8; 9)
186 BLOCK@[9; 11)
187 L_CURLY@[9; 10)
188 R_CURLY@[10; 11)
189 "#
190 .trim()
191 );
192
193 let (analysis, range) = single_file_with_range(
194 r#"fn test() {
195 <|>assert!("
196 fn foo() {
197 }
198 ", "");<|>
199}"#
200 .trim(),
201 );
202 let syn = analysis.syntax_tree(range.file_id, Some(range.range));
203
204 assert_eq!(
205 syn.trim(),
206 r#"
207EXPR_STMT@[16; 58)
208 MACRO_CALL@[16; 57)
209 PATH@[16; 22)
210 PATH_SEGMENT@[16; 22)
211 NAME_REF@[16; 22)
212 IDENT@[16; 22) "assert"
213 EXCL@[22; 23)
214 TOKEN_TREE@[23; 57)
215 L_PAREN@[23; 24)
216 STRING@[24; 52)
217 COMMA@[52; 53)
218 WHITESPACE@[53; 54)
219 STRING@[54; 56)
220 R_PAREN@[56; 57)
221 SEMI@[57; 58)
222 "#
223 .trim()
224 );
225 }
226
227 #[test]
228 fn test_syntax_tree_inside_string() {
229 let (analysis, range) = single_file_with_range(
230 r#"fn test() {
231 assert!("
232<|>fn foo() {
233}<|>
234fn bar() {
235}
236 ", "");
237}"#
238 .trim(),
239 );
240 let syn = analysis.syntax_tree(range.file_id, Some(range.range));
241 assert_eq!(
242 syn.trim(),
243 r#"
244SOURCE_FILE@[0; 12)
245 FN_DEF@[0; 12)
246 FN_KW@[0; 2)
247 WHITESPACE@[2; 3)
248 NAME@[3; 6)
249 IDENT@[3; 6) "foo"
250 PARAM_LIST@[6; 8)
251 L_PAREN@[6; 7)
252 R_PAREN@[7; 8)
253 WHITESPACE@[8; 9)
254 BLOCK@[9; 12)
255 L_CURLY@[9; 10)
256 WHITESPACE@[10; 11)
257 R_CURLY@[11; 12)
258"#
259 .trim()
260 );
261
262 // With a raw string
263 let (analysis, range) = single_file_with_range(
264 r###"fn test() {
265 assert!(r#"
266<|>fn foo() {
267}<|>
268fn bar() {
269}
270 "#, "");
271}"###
272 .trim(),
273 );
274 let syn = analysis.syntax_tree(range.file_id, Some(range.range));
275 assert_eq!(
276 syn.trim(),
277 r#"
278SOURCE_FILE@[0; 12)
279 FN_DEF@[0; 12)
280 FN_KW@[0; 2)
281 WHITESPACE@[2; 3)
282 NAME@[3; 6)
283 IDENT@[3; 6) "foo"
284 PARAM_LIST@[6; 8)
285 L_PAREN@[6; 7)
286 R_PAREN@[7; 8)
287 WHITESPACE@[8; 9)
288 BLOCK@[9; 12)
289 L_CURLY@[9; 10)
290 WHITESPACE@[10; 11)
291 R_CURLY@[11; 12)
292"#
293 .trim()
294 );
295
296 // With a raw string
297 let (analysis, range) = single_file_with_range(
298 r###"fn test() {
299 assert!(r<|>#"
300fn foo() {
301}
302fn bar() {
303}"<|>#, "");
304}"###
305 .trim(),
306 );
307 let syn = analysis.syntax_tree(range.file_id, Some(range.range));
308 assert_eq!(
309 syn.trim(),
310 r#"
311SOURCE_FILE@[0; 25)
312 FN_DEF@[0; 12)
313 FN_KW@[0; 2)
314 WHITESPACE@[2; 3)
315 NAME@[3; 6)
316 IDENT@[3; 6) "foo"
317 PARAM_LIST@[6; 8)
318 L_PAREN@[6; 7)
319 R_PAREN@[7; 8)
320 WHITESPACE@[8; 9)
321 BLOCK@[9; 12)
322 L_CURLY@[9; 10)
323 WHITESPACE@[10; 11)
324 R_CURLY@[11; 12)
325 WHITESPACE@[12; 13)
326 FN_DEF@[13; 25)
327 FN_KW@[13; 15)
328 WHITESPACE@[15; 16)
329 NAME@[16; 19)
330 IDENT@[16; 19) "bar"
331 PARAM_LIST@[19; 21)
332 L_PAREN@[19; 20)
333 R_PAREN@[20; 21)
334 WHITESPACE@[21; 22)
335 BLOCK@[22; 25)
336 L_CURLY@[22; 23)
337 WHITESPACE@[23; 24)
338 R_CURLY@[24; 25)
339
340"#
341 .trim()
342 );
343 }
344}
diff --git a/crates/ra_ide_api/tests/test/main.rs b/crates/ra_ide_api/tests/test/main.rs
deleted file mode 100644
index 0f0766f62..000000000
--- a/crates/ra_ide_api/tests/test/main.rs
+++ /dev/null
@@ -1,392 +0,0 @@
1use insta::assert_debug_snapshot_matches;
2use ra_ide_api::{
3 mock_analysis::{single_file, single_file_with_position, single_file_with_range, MockAnalysis},
4 AnalysisChange, CrateGraph, Edition::Edition2018, Query, NavigationTarget,
5 ReferenceSearchResult,
6};
7use ra_syntax::SmolStr;
8
9#[test]
10fn test_unresolved_module_diagnostic() {
11 let (analysis, file_id) = single_file("mod foo;");
12 let diagnostics = analysis.diagnostics(file_id).unwrap();
13 assert_debug_snapshot_matches!("unresolved_module_diagnostic", &diagnostics);
14}
15
16// FIXME: move this test to hir
17#[test]
18fn test_unresolved_module_diagnostic_no_diag_for_inline_mode() {
19 let (analysis, file_id) = single_file("mod foo {}");
20 let diagnostics = analysis.diagnostics(file_id).unwrap();
21 assert!(diagnostics.is_empty());
22}
23
24#[test]
25fn test_resolve_crate_root() {
26 let mock = MockAnalysis::with_files(
27 "
28 //- /bar.rs
29 mod foo;
30 //- /foo.rs
31 // empty <|>
32 ",
33 );
34 let root_file = mock.id_of("/bar.rs");
35 let mod_file = mock.id_of("/foo.rs");
36 let mut host = mock.analysis_host();
37 assert!(host.analysis().crate_for(mod_file).unwrap().is_empty());
38
39 let mut crate_graph = CrateGraph::default();
40 let crate_id = crate_graph.add_crate_root(root_file, Edition2018);
41 let mut change = AnalysisChange::new();
42 change.set_crate_graph(crate_graph);
43 host.apply_change(change);
44
45 assert_eq!(host.analysis().crate_for(mod_file).unwrap(), vec![crate_id]);
46}
47
48fn get_all_refs(text: &str) -> ReferenceSearchResult {
49 let (analysis, position) = single_file_with_position(text);
50 analysis.find_all_refs(position).unwrap().unwrap()
51}
52
53fn get_symbols_matching(text: &str, query: &str) -> Vec<NavigationTarget> {
54 let (analysis, _) = single_file(text);
55 analysis.symbol_search(Query::new(query.into())).unwrap()
56}
57
58#[test]
59fn test_find_all_refs_for_local() {
60 let code = r#"
61 fn main() {
62 let mut i = 1;
63 let j = 1;
64 i = i<|> + j;
65
66 {
67 i = 0;
68 }
69
70 i = 5;
71 }"#;
72
73 let refs = get_all_refs(code);
74 assert_eq!(refs.len(), 5);
75}
76
77#[test]
78fn test_find_all_refs_for_param_inside() {
79 let code = r#"
80 fn foo(i : u32) -> u32 {
81 i<|>
82 }"#;
83
84 let refs = get_all_refs(code);
85 assert_eq!(refs.len(), 2);
86}
87
88#[test]
89fn test_find_all_refs_for_fn_param() {
90 let code = r#"
91 fn foo(i<|> : u32) -> u32 {
92 i
93 }"#;
94
95 let refs = get_all_refs(code);
96 assert_eq!(refs.len(), 2);
97}
98
99#[test]
100fn test_world_symbols_with_no_container() {
101 let code = r#"
102 enum FooInner { }
103 "#;
104
105 let mut symbols = get_symbols_matching(code, "FooInner");
106
107 let s = symbols.pop().unwrap();
108
109 assert_eq!(s.name(), "FooInner");
110 assert!(s.container_name().is_none());
111}
112
113#[test]
114fn test_world_symbols_include_container_name() {
115 let code = r#"
116fn foo() {
117 enum FooInner { }
118}
119 "#;
120
121 let mut symbols = get_symbols_matching(code, "FooInner");
122
123 let s = symbols.pop().unwrap();
124
125 assert_eq!(s.name(), "FooInner");
126 assert_eq!(s.container_name(), Some(&SmolStr::new("foo")));
127
128 let code = r#"
129mod foo {
130 struct FooInner;
131}
132 "#;
133
134 let mut symbols = get_symbols_matching(code, "FooInner");
135
136 let s = symbols.pop().unwrap();
137
138 assert_eq!(s.name(), "FooInner");
139 assert_eq!(s.container_name(), Some(&SmolStr::new("foo")));
140}
141
142#[test]
143fn test_syntax_tree_without_range() {
144 // Basic syntax
145 let (analysis, file_id) = single_file(r#"fn foo() {}"#);
146 let syn = analysis.syntax_tree(file_id, None);
147
148 assert_eq!(
149 syn.trim(),
150 r#"
151SOURCE_FILE@[0; 11)
152 FN_DEF@[0; 11)
153 FN_KW@[0; 2)
154 WHITESPACE@[2; 3)
155 NAME@[3; 6)
156 IDENT@[3; 6) "foo"
157 PARAM_LIST@[6; 8)
158 L_PAREN@[6; 7)
159 R_PAREN@[7; 8)
160 WHITESPACE@[8; 9)
161 BLOCK@[9; 11)
162 L_CURLY@[9; 10)
163 R_CURLY@[10; 11)
164 "#
165 .trim()
166 );
167
168 let (analysis, file_id) = single_file(
169 r#"
170fn test() {
171 assert!("
172 fn foo() {
173 }
174 ", "");
175}"#
176 .trim(),
177 );
178 let syn = analysis.syntax_tree(file_id, None);
179
180 assert_eq!(
181 syn.trim(),
182 r#"
183SOURCE_FILE@[0; 60)
184 FN_DEF@[0; 60)
185 FN_KW@[0; 2)
186 WHITESPACE@[2; 3)
187 NAME@[3; 7)
188 IDENT@[3; 7) "test"
189 PARAM_LIST@[7; 9)
190 L_PAREN@[7; 8)
191 R_PAREN@[8; 9)
192 WHITESPACE@[9; 10)
193 BLOCK@[10; 60)
194 L_CURLY@[10; 11)
195 WHITESPACE@[11; 16)
196 EXPR_STMT@[16; 58)
197 MACRO_CALL@[16; 57)
198 PATH@[16; 22)
199 PATH_SEGMENT@[16; 22)
200 NAME_REF@[16; 22)
201 IDENT@[16; 22) "assert"
202 EXCL@[22; 23)
203 TOKEN_TREE@[23; 57)
204 L_PAREN@[23; 24)
205 STRING@[24; 52)
206 COMMA@[52; 53)
207 WHITESPACE@[53; 54)
208 STRING@[54; 56)
209 R_PAREN@[56; 57)
210 SEMI@[57; 58)
211 WHITESPACE@[58; 59)
212 R_CURLY@[59; 60)
213 "#
214 .trim()
215 );
216}
217
218#[test]
219fn test_syntax_tree_with_range() {
220 let (analysis, range) = single_file_with_range(r#"<|>fn foo() {}<|>"#.trim());
221 let syn = analysis.syntax_tree(range.file_id, Some(range.range));
222
223 assert_eq!(
224 syn.trim(),
225 r#"
226FN_DEF@[0; 11)
227 FN_KW@[0; 2)
228 WHITESPACE@[2; 3)
229 NAME@[3; 6)
230 IDENT@[3; 6) "foo"
231 PARAM_LIST@[6; 8)
232 L_PAREN@[6; 7)
233 R_PAREN@[7; 8)
234 WHITESPACE@[8; 9)
235 BLOCK@[9; 11)
236 L_CURLY@[9; 10)
237 R_CURLY@[10; 11)
238 "#
239 .trim()
240 );
241
242 let (analysis, range) = single_file_with_range(
243 r#"fn test() {
244 <|>assert!("
245 fn foo() {
246 }
247 ", "");<|>
248}"#
249 .trim(),
250 );
251 let syn = analysis.syntax_tree(range.file_id, Some(range.range));
252
253 assert_eq!(
254 syn.trim(),
255 r#"
256EXPR_STMT@[16; 58)
257 MACRO_CALL@[16; 57)
258 PATH@[16; 22)
259 PATH_SEGMENT@[16; 22)
260 NAME_REF@[16; 22)
261 IDENT@[16; 22) "assert"
262 EXCL@[22; 23)
263 TOKEN_TREE@[23; 57)
264 L_PAREN@[23; 24)
265 STRING@[24; 52)
266 COMMA@[52; 53)
267 WHITESPACE@[53; 54)
268 STRING@[54; 56)
269 R_PAREN@[56; 57)
270 SEMI@[57; 58)
271 "#
272 .trim()
273 );
274}
275
276#[test]
277fn test_syntax_tree_inside_string() {
278 let (analysis, range) = single_file_with_range(
279 r#"fn test() {
280 assert!("
281<|>fn foo() {
282}<|>
283fn bar() {
284}
285 ", "");
286}"#
287 .trim(),
288 );
289 let syn = analysis.syntax_tree(range.file_id, Some(range.range));
290 assert_eq!(
291 syn.trim(),
292 r#"
293SOURCE_FILE@[0; 12)
294 FN_DEF@[0; 12)
295 FN_KW@[0; 2)
296 WHITESPACE@[2; 3)
297 NAME@[3; 6)
298 IDENT@[3; 6) "foo"
299 PARAM_LIST@[6; 8)
300 L_PAREN@[6; 7)
301 R_PAREN@[7; 8)
302 WHITESPACE@[8; 9)
303 BLOCK@[9; 12)
304 L_CURLY@[9; 10)
305 WHITESPACE@[10; 11)
306 R_CURLY@[11; 12)
307"#
308 .trim()
309 );
310
311 // With a raw string
312 let (analysis, range) = single_file_with_range(
313 r###"fn test() {
314 assert!(r#"
315<|>fn foo() {
316}<|>
317fn bar() {
318}
319 "#, "");
320}"###
321 .trim(),
322 );
323 let syn = analysis.syntax_tree(range.file_id, Some(range.range));
324 assert_eq!(
325 syn.trim(),
326 r#"
327SOURCE_FILE@[0; 12)
328 FN_DEF@[0; 12)
329 FN_KW@[0; 2)
330 WHITESPACE@[2; 3)
331 NAME@[3; 6)
332 IDENT@[3; 6) "foo"
333 PARAM_LIST@[6; 8)
334 L_PAREN@[6; 7)
335 R_PAREN@[7; 8)
336 WHITESPACE@[8; 9)
337 BLOCK@[9; 12)
338 L_CURLY@[9; 10)
339 WHITESPACE@[10; 11)
340 R_CURLY@[11; 12)
341"#
342 .trim()
343 );
344
345 // With a raw string
346 let (analysis, range) = single_file_with_range(
347 r###"fn test() {
348 assert!(r<|>#"
349fn foo() {
350}
351fn bar() {
352}"<|>#, "");
353}"###
354 .trim(),
355 );
356 let syn = analysis.syntax_tree(range.file_id, Some(range.range));
357 assert_eq!(
358 syn.trim(),
359 r#"
360SOURCE_FILE@[0; 25)
361 FN_DEF@[0; 12)
362 FN_KW@[0; 2)
363 WHITESPACE@[2; 3)
364 NAME@[3; 6)
365 IDENT@[3; 6) "foo"
366 PARAM_LIST@[6; 8)
367 L_PAREN@[6; 7)
368 R_PAREN@[7; 8)
369 WHITESPACE@[8; 9)
370 BLOCK@[9; 12)
371 L_CURLY@[9; 10)
372 WHITESPACE@[10; 11)
373 R_CURLY@[11; 12)
374 WHITESPACE@[12; 13)
375 FN_DEF@[13; 25)
376 FN_KW@[13; 15)
377 WHITESPACE@[15; 16)
378 NAME@[16; 19)
379 IDENT@[16; 19) "bar"
380 PARAM_LIST@[19; 21)
381 L_PAREN@[19; 20)
382 R_PAREN@[20; 21)
383 WHITESPACE@[21; 22)
384 BLOCK@[22; 25)
385 L_CURLY@[22; 23)
386 WHITESPACE@[23; 24)
387 R_CURLY@[24; 25)
388
389"#
390 .trim()
391 );
392}
diff --git a/crates/ra_ide_api/tests/test/snapshots/test__unresolved_module_diagnostic.snap b/crates/ra_ide_api/tests/test/snapshots/test__unresolved_module_diagnostic.snap
deleted file mode 100644
index 5bb953892..000000000
--- a/crates/ra_ide_api/tests/test/snapshots/test__unresolved_module_diagnostic.snap
+++ /dev/null
@@ -1,28 +0,0 @@
1---
2created: "2019-01-22T14:45:01.486985900+00:00"
3creator: [email protected]
4expression: "&diagnostics"
5source: "crates\\ra_ide_api\\tests\\test\\main.rs"
6---
7[
8 Diagnostic {
9 message: "unresolved module",
10 range: [0; 8),
11 fix: Some(
12 SourceChange {
13 label: "create module",
14 source_file_edits: [],
15 file_system_edits: [
16 CreateFile {
17 source_root: SourceRootId(
18 0
19 ),
20 path: "foo.rs"
21 }
22 ],
23 cursor_position: None
24 }
25 ),
26 severity: Error
27 }
28]