diff options
Diffstat (limited to 'crates/ra_ide/src/mock_analysis.rs')
-rw-r--r-- | crates/ra_ide/src/mock_analysis.rs | 178 |
1 files changed, 48 insertions, 130 deletions
diff --git a/crates/ra_ide/src/mock_analysis.rs b/crates/ra_ide/src/mock_analysis.rs index 981bdf924..889b84c59 100644 --- a/crates/ra_ide/src/mock_analysis.rs +++ b/crates/ra_ide/src/mock_analysis.rs | |||
@@ -1,87 +1,22 @@ | |||
1 | //! FIXME: write short doc here | 1 | //! FIXME: write short doc here |
2 | use std::{str::FromStr, sync::Arc}; | 2 | use std::sync::Arc; |
3 | 3 | ||
4 | use ra_cfg::CfgOptions; | 4 | use ra_cfg::CfgOptions; |
5 | use ra_db::{CrateName, Env, FileSet, SourceRoot, VfsPath}; | 5 | use ra_db::{CrateName, Env, FileSet, SourceRoot, VfsPath}; |
6 | use test_utils::{extract_offset, extract_range, Fixture, CURSOR_MARKER}; | 6 | use test_utils::{extract_range_or_offset, Fixture, RangeOrOffset, CURSOR_MARKER}; |
7 | 7 | ||
8 | use crate::{ | 8 | use crate::{ |
9 | Analysis, AnalysisChange, AnalysisHost, CrateGraph, Edition, FileId, FilePosition, FileRange, | 9 | Analysis, AnalysisChange, AnalysisHost, CrateGraph, Edition, FileId, FilePosition, FileRange, |
10 | }; | 10 | }; |
11 | 11 | ||
12 | #[derive(Debug)] | ||
13 | enum MockFileData { | ||
14 | Plain { path: String, content: String }, | ||
15 | Fixture(Fixture), | ||
16 | } | ||
17 | |||
18 | impl MockFileData { | ||
19 | fn new(path: String, content: String) -> Self { | ||
20 | // `Self::Plain` causes a false warning: 'variant is never constructed: `Plain` ' | ||
21 | // see https://github.com/rust-lang/rust/issues/69018 | ||
22 | MockFileData::Plain { path, content } | ||
23 | } | ||
24 | |||
25 | fn path(&self) -> &str { | ||
26 | match self { | ||
27 | MockFileData::Plain { path, .. } => path.as_str(), | ||
28 | MockFileData::Fixture(f) => f.path.as_str(), | ||
29 | } | ||
30 | } | ||
31 | |||
32 | fn content(&self) -> &str { | ||
33 | match self { | ||
34 | MockFileData::Plain { content, .. } => content, | ||
35 | MockFileData::Fixture(f) => f.text.as_str(), | ||
36 | } | ||
37 | } | ||
38 | |||
39 | fn cfg_options(&self) -> CfgOptions { | ||
40 | match self { | ||
41 | MockFileData::Fixture(f) => { | ||
42 | let mut cfg = CfgOptions::default(); | ||
43 | f.cfg_atoms.iter().for_each(|it| cfg.insert_atom(it.into())); | ||
44 | f.cfg_key_values.iter().for_each(|(k, v)| cfg.insert_key_value(k.into(), v.into())); | ||
45 | cfg | ||
46 | } | ||
47 | _ => CfgOptions::default(), | ||
48 | } | ||
49 | } | ||
50 | |||
51 | fn edition(&self) -> Edition { | ||
52 | match self { | ||
53 | MockFileData::Fixture(f) => { | ||
54 | f.edition.as_ref().map_or(Edition::Edition2018, |v| Edition::from_str(&v).unwrap()) | ||
55 | } | ||
56 | _ => Edition::Edition2018, | ||
57 | } | ||
58 | } | ||
59 | |||
60 | fn env(&self) -> Env { | ||
61 | match self { | ||
62 | MockFileData::Fixture(f) => Env::from(f.env.iter()), | ||
63 | _ => Env::default(), | ||
64 | } | ||
65 | } | ||
66 | } | ||
67 | |||
68 | impl From<Fixture> for MockFileData { | ||
69 | fn from(fixture: Fixture) -> Self { | ||
70 | Self::Fixture(fixture) | ||
71 | } | ||
72 | } | ||
73 | |||
74 | /// Mock analysis is used in test to bootstrap an AnalysisHost/Analysis | 12 | /// Mock analysis is used in test to bootstrap an AnalysisHost/Analysis |
75 | /// from a set of in-memory files. | 13 | /// from a set of in-memory files. |
76 | #[derive(Debug, Default)] | 14 | #[derive(Debug, Default)] |
77 | pub struct MockAnalysis { | 15 | pub struct MockAnalysis { |
78 | files: Vec<MockFileData>, | 16 | files: Vec<Fixture>, |
79 | } | 17 | } |
80 | 18 | ||
81 | impl MockAnalysis { | 19 | impl MockAnalysis { |
82 | pub fn new() -> MockAnalysis { | ||
83 | MockAnalysis::default() | ||
84 | } | ||
85 | /// Creates `MockAnalysis` using a fixture data in the following format: | 20 | /// Creates `MockAnalysis` using a fixture data in the following format: |
86 | /// | 21 | /// |
87 | /// ```not_rust | 22 | /// ```not_rust |
@@ -92,68 +27,53 @@ impl MockAnalysis { | |||
92 | /// //- /foo.rs | 27 | /// //- /foo.rs |
93 | /// struct Baz; | 28 | /// struct Baz; |
94 | /// ``` | 29 | /// ``` |
95 | pub fn with_files(fixture: &str) -> MockAnalysis { | 30 | pub fn with_files(ra_fixture: &str) -> MockAnalysis { |
96 | let mut res = MockAnalysis::new(); | 31 | let (res, pos) = MockAnalysis::with_fixture(ra_fixture); |
97 | for entry in Fixture::parse(fixture) { | 32 | assert!(pos.is_none()); |
98 | res.add_file_fixture(entry); | ||
99 | } | ||
100 | res | 33 | res |
101 | } | 34 | } |
102 | 35 | ||
103 | /// Same as `with_files`, but requires that a single file contains a `<|>` marker, | 36 | /// Same as `with_files`, but requires that a single file contains a `<|>` marker, |
104 | /// whose position is also returned. | 37 | /// whose position is also returned. |
105 | pub fn with_files_and_position(fixture: &str) -> (MockAnalysis, FilePosition) { | 38 | pub fn with_files_and_position(fixture: &str) -> (MockAnalysis, FilePosition) { |
39 | let (res, position) = MockAnalysis::with_fixture(fixture); | ||
40 | let (file_id, range_or_offset) = position.expect("expected a marker (<|>)"); | ||
41 | let offset = match range_or_offset { | ||
42 | RangeOrOffset::Range(_) => panic!(), | ||
43 | RangeOrOffset::Offset(it) => it, | ||
44 | }; | ||
45 | (res, FilePosition { file_id, offset }) | ||
46 | } | ||
47 | |||
48 | fn with_fixture(fixture: &str) -> (MockAnalysis, Option<(FileId, RangeOrOffset)>) { | ||
106 | let mut position = None; | 49 | let mut position = None; |
107 | let mut res = MockAnalysis::new(); | 50 | let mut res = MockAnalysis::default(); |
108 | for entry in Fixture::parse(fixture) { | 51 | for mut entry in Fixture::parse(fixture) { |
109 | if entry.text.contains(CURSOR_MARKER) { | 52 | if entry.text.contains(CURSOR_MARKER) { |
110 | assert!(position.is_none(), "only one marker (<|>) per fixture is allowed"); | 53 | assert!(position.is_none(), "only one marker (<|>) per fixture is allowed"); |
111 | position = Some(res.add_file_fixture_with_position(entry)); | 54 | let (range_or_offset, text) = extract_range_or_offset(&entry.text); |
55 | entry.text = text; | ||
56 | let file_id = res.add_file_fixture(entry); | ||
57 | position = Some((file_id, range_or_offset)); | ||
112 | } else { | 58 | } else { |
113 | res.add_file_fixture(entry); | 59 | res.add_file_fixture(entry); |
114 | } | 60 | } |
115 | } | 61 | } |
116 | let position = position.expect("expected a marker (<|>)"); | ||
117 | (res, position) | 62 | (res, position) |
118 | } | 63 | } |
119 | 64 | ||
120 | pub fn add_file_fixture(&mut self, fixture: Fixture) -> FileId { | 65 | fn add_file_fixture(&mut self, fixture: Fixture) -> FileId { |
121 | let file_id = self.next_id(); | 66 | let file_id = FileId((self.files.len() + 1) as u32); |
122 | self.files.push(MockFileData::from(fixture)); | 67 | self.files.push(fixture); |
123 | file_id | 68 | file_id |
124 | } | 69 | } |
125 | 70 | ||
126 | pub fn add_file_fixture_with_position(&mut self, mut fixture: Fixture) -> FilePosition { | ||
127 | let (offset, text) = extract_offset(&fixture.text); | ||
128 | fixture.text = text; | ||
129 | let file_id = self.next_id(); | ||
130 | self.files.push(MockFileData::from(fixture)); | ||
131 | FilePosition { file_id, offset } | ||
132 | } | ||
133 | |||
134 | pub fn add_file(&mut self, path: &str, text: &str) -> FileId { | ||
135 | let file_id = self.next_id(); | ||
136 | self.files.push(MockFileData::new(path.to_string(), text.to_string())); | ||
137 | file_id | ||
138 | } | ||
139 | pub fn add_file_with_position(&mut self, path: &str, text: &str) -> FilePosition { | ||
140 | let (offset, text) = extract_offset(text); | ||
141 | let file_id = self.next_id(); | ||
142 | self.files.push(MockFileData::new(path.to_string(), text)); | ||
143 | FilePosition { file_id, offset } | ||
144 | } | ||
145 | pub fn add_file_with_range(&mut self, path: &str, text: &str) -> FileRange { | ||
146 | let (range, text) = extract_range(text); | ||
147 | let file_id = self.next_id(); | ||
148 | self.files.push(MockFileData::new(path.to_string(), text)); | ||
149 | FileRange { file_id, range } | ||
150 | } | ||
151 | pub fn id_of(&self, path: &str) -> FileId { | 71 | pub fn id_of(&self, path: &str) -> FileId { |
152 | let (idx, _) = self | 72 | let (idx, _) = self |
153 | .files | 73 | .files |
154 | .iter() | 74 | .iter() |
155 | .enumerate() | 75 | .enumerate() |
156 | .find(|(_, data)| path == data.path()) | 76 | .find(|(_, data)| path == data.path) |
157 | .expect("no file in this mock"); | 77 | .expect("no file in this mock"); |
158 | FileId(idx as u32 + 1) | 78 | FileId(idx as u32 + 1) |
159 | } | 79 | } |
@@ -164,18 +84,23 @@ impl MockAnalysis { | |||
164 | let mut crate_graph = CrateGraph::default(); | 84 | let mut crate_graph = CrateGraph::default(); |
165 | let mut root_crate = None; | 85 | let mut root_crate = None; |
166 | for (i, data) in self.files.into_iter().enumerate() { | 86 | for (i, data) in self.files.into_iter().enumerate() { |
167 | let path = data.path(); | 87 | let path = data.path; |
168 | assert!(path.starts_with('/')); | 88 | assert!(path.starts_with('/')); |
169 | let cfg_options = data.cfg_options(); | 89 | |
90 | let mut cfg = CfgOptions::default(); | ||
91 | data.cfg_atoms.iter().for_each(|it| cfg.insert_atom(it.into())); | ||
92 | data.cfg_key_values.iter().for_each(|(k, v)| cfg.insert_key_value(k.into(), v.into())); | ||
93 | let edition: Edition = | ||
94 | data.edition.and_then(|it| it.parse().ok()).unwrap_or(Edition::Edition2018); | ||
95 | |||
170 | let file_id = FileId(i as u32 + 1); | 96 | let file_id = FileId(i as u32 + 1); |
171 | let edition = data.edition(); | 97 | let env = Env::from(data.env.iter()); |
172 | let env = data.env(); | ||
173 | if path == "/lib.rs" || path == "/main.rs" { | 98 | if path == "/lib.rs" || path == "/main.rs" { |
174 | root_crate = Some(crate_graph.add_crate_root( | 99 | root_crate = Some(crate_graph.add_crate_root( |
175 | file_id, | 100 | file_id, |
176 | edition, | 101 | edition, |
177 | None, | 102 | None, |
178 | cfg_options, | 103 | cfg, |
179 | env, | 104 | env, |
180 | Default::default(), | 105 | Default::default(), |
181 | )); | 106 | )); |
@@ -186,7 +111,7 @@ impl MockAnalysis { | |||
186 | file_id, | 111 | file_id, |
187 | edition, | 112 | edition, |
188 | Some(CrateName::new(crate_name).unwrap()), | 113 | Some(CrateName::new(crate_name).unwrap()), |
189 | cfg_options, | 114 | cfg, |
190 | env, | 115 | env, |
191 | Default::default(), | 116 | Default::default(), |
192 | ); | 117 | ); |
@@ -198,7 +123,7 @@ impl MockAnalysis { | |||
198 | } | 123 | } |
199 | let path = VfsPath::new_virtual_path(path.to_string()); | 124 | let path = VfsPath::new_virtual_path(path.to_string()); |
200 | file_set.insert(file_id, path); | 125 | file_set.insert(file_id, path); |
201 | change.change_file(file_id, Some(Arc::new(data.content().to_owned()))); | 126 | change.change_file(file_id, Some(Arc::new(data.text).to_owned())); |
202 | } | 127 | } |
203 | change.set_crate_graph(crate_graph); | 128 | change.set_crate_graph(crate_graph); |
204 | change.set_roots(vec![SourceRoot::new_local(file_set)]); | 129 | change.set_roots(vec![SourceRoot::new_local(file_set)]); |
@@ -208,10 +133,6 @@ impl MockAnalysis { | |||
208 | pub fn analysis(self) -> Analysis { | 133 | pub fn analysis(self) -> Analysis { |
209 | self.analysis_host().analysis() | 134 | self.analysis_host().analysis() |
210 | } | 135 | } |
211 | |||
212 | fn next_id(&self) -> FileId { | ||
213 | FileId((self.files.len() + 1) as u32) | ||
214 | } | ||
215 | } | 136 | } |
216 | 137 | ||
217 | /// Creates analysis from a multi-file fixture, returns positions marked with <|>. | 138 | /// Creates analysis from a multi-file fixture, returns positions marked with <|>. |
@@ -222,21 +143,18 @@ pub fn analysis_and_position(ra_fixture: &str) -> (Analysis, FilePosition) { | |||
222 | 143 | ||
223 | /// Creates analysis for a single file. | 144 | /// Creates analysis for a single file. |
224 | pub fn single_file(ra_fixture: &str) -> (Analysis, FileId) { | 145 | pub fn single_file(ra_fixture: &str) -> (Analysis, FileId) { |
225 | let mut mock = MockAnalysis::new(); | 146 | let mock = MockAnalysis::with_files(ra_fixture); |
226 | let file_id = mock.add_file("/main.rs", ra_fixture); | 147 | let file_id = mock.id_of("/main.rs"); |
227 | (mock.analysis(), file_id) | 148 | (mock.analysis(), file_id) |
228 | } | 149 | } |
229 | 150 | ||
230 | /// Creates analysis for a single file, returns position marked with <|>. | ||
231 | pub fn single_file_with_position(ra_fixture: &str) -> (Analysis, FilePosition) { | ||
232 | let mut mock = MockAnalysis::new(); | ||
233 | let pos = mock.add_file_with_position("/main.rs", ra_fixture); | ||
234 | (mock.analysis(), pos) | ||
235 | } | ||
236 | |||
237 | /// Creates analysis for a single file, returns range marked with a pair of <|>. | 151 | /// Creates analysis for a single file, returns range marked with a pair of <|>. |
238 | pub fn single_file_with_range(ra_fixture: &str) -> (Analysis, FileRange) { | 152 | pub fn analysis_and_range(ra_fixture: &str) -> (Analysis, FileRange) { |
239 | let mut mock = MockAnalysis::new(); | 153 | let (res, position) = MockAnalysis::with_fixture(ra_fixture); |
240 | let pos = mock.add_file_with_range("/main.rs", ra_fixture); | 154 | let (file_id, range_or_offset) = position.expect("expected a marker (<|>)"); |
241 | (mock.analysis(), pos) | 155 | let range = match range_or_offset { |
156 | RangeOrOffset::Range(it) => it, | ||
157 | RangeOrOffset::Offset(_) => panic!(), | ||
158 | }; | ||
159 | (res.analysis(), FileRange { file_id, range }) | ||
242 | } | 160 | } |