Skip to content

Commit f26b792

Browse files
authored
Merge pull request #4495 from vsrs/fixture_meta
Test fixtures parsing improvements
2 parents ce7144a + cd45c73 commit f26b792

File tree

7 files changed

+326
-77
lines changed

7 files changed

+326
-77
lines changed

crates/ra_db/src/fixture.rs

Lines changed: 21 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ use std::sync::Arc;
6363

6464
use ra_cfg::CfgOptions;
6565
use rustc_hash::FxHashMap;
66-
use test_utils::{extract_offset, parse_fixture, parse_single_fixture, CURSOR_MARKER};
66+
use test_utils::{extract_offset, parse_fixture, parse_single_fixture, FixtureMeta, CURSOR_MARKER};
6767

6868
use crate::{
6969
input::CrateName, CrateGraph, CrateId, Edition, Env, FileId, FilePosition, RelativePathBuf,
@@ -113,7 +113,7 @@ fn with_single_file(db: &mut dyn SourceDatabaseExt, ra_fixture: &str) -> FileId
113113
let fixture = parse_single_fixture(ra_fixture);
114114

115115
let crate_graph = if let Some(entry) = fixture {
116-
let meta = match parse_meta(&entry.meta) {
116+
let meta = match ParsedMeta::from(&entry.meta) {
117117
ParsedMeta::File(it) => it,
118118
_ => panic!("with_single_file only support file meta"),
119119
};
@@ -170,7 +170,7 @@ fn with_files(db: &mut dyn SourceDatabaseExt, fixture: &str) -> Option<FilePosit
170170
let mut file_position = None;
171171

172172
for entry in fixture.iter() {
173-
let meta = match parse_meta(&entry.meta) {
173+
let meta = match ParsedMeta::from(&entry.meta) {
174174
ParsedMeta::Root { path } => {
175175
let source_root = std::mem::replace(&mut source_root, SourceRoot::new_local());
176176
db.set_source_root(source_root_id, Arc::new(source_root));
@@ -258,53 +258,25 @@ struct FileMeta {
258258
env: Env,
259259
}
260260

261-
//- /lib.rs crate:foo deps:bar,baz cfg:foo=a,bar=b env:OUTDIR=path/to,OTHER=foo)
262-
fn parse_meta(meta: &str) -> ParsedMeta {
263-
let components = meta.split_ascii_whitespace().collect::<Vec<_>>();
264-
265-
if components[0] == "root" {
266-
let path: RelativePathBuf = components[1].into();
267-
assert!(path.starts_with("/") && path.ends_with("/"));
268-
return ParsedMeta::Root { path };
269-
}
270-
271-
let path: RelativePathBuf = components[0].into();
272-
assert!(path.starts_with("/"));
273-
274-
let mut krate = None;
275-
let mut deps = Vec::new();
276-
let mut edition = Edition::Edition2018;
277-
let mut cfg = CfgOptions::default();
278-
let mut env = Env::default();
279-
for component in components[1..].iter() {
280-
let (key, value) = split1(component, ':').unwrap();
281-
match key {
282-
"crate" => krate = Some(value.to_string()),
283-
"deps" => deps = value.split(',').map(|it| it.to_string()).collect(),
284-
"edition" => edition = Edition::from_str(&value).unwrap(),
285-
"cfg" => {
286-
for key in value.split(',') {
287-
match split1(key, '=') {
288-
None => cfg.insert_atom(key.into()),
289-
Some((k, v)) => cfg.insert_key_value(k.into(), v.into()),
290-
}
291-
}
292-
}
293-
"env" => {
294-
for key in value.split(',') {
295-
if let Some((k, v)) = split1(key, '=') {
296-
env.set(k, v.into());
297-
}
298-
}
261+
impl From<&FixtureMeta> for ParsedMeta {
262+
fn from(meta: &FixtureMeta) -> Self {
263+
match meta {
264+
FixtureMeta::Root { path } => {
265+
// `Self::Root` causes a false warning: 'variant is never constructed: `Root` '
266+
// see https://github.com/rust-lang/rust/issues/69018
267+
ParsedMeta::Root { path: path.to_owned() }
299268
}
300-
_ => panic!("bad component: {:?}", component),
269+
FixtureMeta::File(f) => Self::File(FileMeta {
270+
path: f.path.to_owned().into(),
271+
krate: f.crate_name.to_owned().into(),
272+
deps: f.deps.to_owned(),
273+
cfg: f.cfg.to_owned(),
274+
edition: f
275+
.edition
276+
.as_ref()
277+
.map_or(Edition::Edition2018, |v| Edition::from_str(&v).unwrap()),
278+
env: Env::from(f.env.iter()),
279+
}),
301280
}
302281
}
303-
304-
ParsedMeta::File(FileMeta { path, krate, deps, edition, cfg, env })
305-
}
306-
307-
fn split1(haystack: &str, delim: char) -> Option<(&str, &str)> {
308-
let idx = haystack.find(delim)?;
309-
Some((&haystack[..idx], &haystack[idx + delim.len_utf8()..]))
310282
}

crates/ra_db/src/input.rs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -311,6 +311,21 @@ impl fmt::Display for Edition {
311311
}
312312
}
313313

314+
impl<'a, T> From<T> for Env
315+
where
316+
T: Iterator<Item = (&'a String, &'a String)>,
317+
{
318+
fn from(iter: T) -> Self {
319+
let mut result = Self::default();
320+
321+
for (k, v) in iter {
322+
result.entries.insert(k.to_owned(), v.to_owned());
323+
}
324+
325+
result
326+
}
327+
}
328+
314329
impl Env {
315330
pub fn set(&mut self, env: &str, value: String) {
316331
self.entries.insert(env.to_owned(), value);

crates/ra_ide/src/call_hierarchy.rs

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -245,6 +245,35 @@ mod tests {
245245
);
246246
}
247247

248+
#[test]
249+
fn test_call_hierarchy_in_tests_mod() {
250+
check_hierarchy(
251+
r#"
252+
//- /lib.rs cfg:test
253+
fn callee() {}
254+
fn caller1() {
255+
call<|>ee();
256+
}
257+
258+
#[cfg(test)]
259+
mod tests {
260+
use super::*;
261+
262+
#[test]
263+
fn test_caller() {
264+
callee();
265+
}
266+
}
267+
"#,
268+
"callee FN_DEF FileId(1) 0..14 3..9",
269+
&[
270+
"caller1 FN_DEF FileId(1) 15..45 18..25 : [34..40]",
271+
"test_caller FN_DEF FileId(1) 93..147 108..119 : [132..138]",
272+
],
273+
&[],
274+
);
275+
}
276+
248277
#[test]
249278
fn test_call_hierarchy_in_different_files() {
250279
check_hierarchy(

crates/ra_ide/src/mock_analysis.rs

Lines changed: 102 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,81 @@
11
//! FIXME: write short doc here
22
3+
use std::str::FromStr;
34
use std::sync::Arc;
45

56
use ra_cfg::CfgOptions;
67
use ra_db::{CrateName, Env, RelativePathBuf};
7-
use test_utils::{extract_offset, extract_range, parse_fixture, CURSOR_MARKER};
8+
use test_utils::{extract_offset, extract_range, parse_fixture, FixtureEntry, CURSOR_MARKER};
89

910
use crate::{
10-
Analysis, AnalysisChange, AnalysisHost, CrateGraph, Edition::Edition2018, FileId, FilePosition,
11-
FileRange, SourceRootId,
11+
Analysis, AnalysisChange, AnalysisHost, CrateGraph, Edition, FileId, FilePosition, FileRange,
12+
SourceRootId,
1213
};
1314

15+
#[derive(Debug)]
16+
enum MockFileData {
17+
Plain { path: String, content: String },
18+
Fixture(FixtureEntry),
19+
}
20+
21+
impl MockFileData {
22+
fn new(path: String, content: String) -> Self {
23+
// `Self::Plain` causes a false warning: 'variant is never constructed: `Plain` '
24+
// see https://github.com/rust-lang/rust/issues/69018
25+
MockFileData::Plain { path, content }
26+
}
27+
28+
fn path(&self) -> &str {
29+
match self {
30+
MockFileData::Plain { path, .. } => path.as_str(),
31+
MockFileData::Fixture(f) => f.meta.path().as_str(),
32+
}
33+
}
34+
35+
fn content(&self) -> &str {
36+
match self {
37+
MockFileData::Plain { content, .. } => content,
38+
MockFileData::Fixture(f) => f.text.as_str(),
39+
}
40+
}
41+
42+
fn cfg_options(&self) -> CfgOptions {
43+
match self {
44+
MockFileData::Fixture(f) => {
45+
f.meta.cfg_options().map_or_else(Default::default, |o| o.clone())
46+
}
47+
_ => CfgOptions::default(),
48+
}
49+
}
50+
51+
fn edition(&self) -> Edition {
52+
match self {
53+
MockFileData::Fixture(f) => {
54+
f.meta.edition().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.meta.env()),
63+
_ => Env::default(),
64+
}
65+
}
66+
}
67+
68+
impl From<FixtureEntry> for MockFileData {
69+
fn from(fixture: FixtureEntry) -> Self {
70+
Self::Fixture(fixture)
71+
}
72+
}
73+
1474
/// Mock analysis is used in test to bootstrap an AnalysisHost/Analysis
1575
/// from a set of in-memory files.
1676
#[derive(Debug, Default)]
1777
pub struct MockAnalysis {
18-
files: Vec<(String, String)>,
78+
files: Vec<MockFileData>,
1979
}
2080

2181
impl MockAnalysis {
@@ -35,7 +95,7 @@ impl MockAnalysis {
3595
pub fn with_files(fixture: &str) -> MockAnalysis {
3696
let mut res = MockAnalysis::new();
3797
for entry in parse_fixture(fixture) {
38-
res.add_file(&entry.meta, &entry.text);
98+
res.add_file_fixture(entry);
3999
}
40100
res
41101
}
@@ -48,38 +108,52 @@ impl MockAnalysis {
48108
for entry in parse_fixture(fixture) {
49109
if entry.text.contains(CURSOR_MARKER) {
50110
assert!(position.is_none(), "only one marker (<|>) per fixture is allowed");
51-
position = Some(res.add_file_with_position(&entry.meta, &entry.text));
111+
position = Some(res.add_file_fixture_with_position(entry));
52112
} else {
53-
res.add_file(&entry.meta, &entry.text);
113+
res.add_file_fixture(entry);
54114
}
55115
}
56116
let position = position.expect("expected a marker (<|>)");
57117
(res, position)
58118
}
59119

120+
pub fn add_file_fixture(&mut self, fixture: FixtureEntry) -> FileId {
121+
let file_id = self.next_id();
122+
self.files.push(MockFileData::from(fixture));
123+
file_id
124+
}
125+
126+
pub fn add_file_fixture_with_position(&mut self, mut fixture: FixtureEntry) -> 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+
60134
pub fn add_file(&mut self, path: &str, text: &str) -> FileId {
61-
let file_id = FileId((self.files.len() + 1) as u32);
62-
self.files.push((path.to_string(), text.to_string()));
135+
let file_id = self.next_id();
136+
self.files.push(MockFileData::new(path.to_string(), text.to_string()));
63137
file_id
64138
}
65139
pub fn add_file_with_position(&mut self, path: &str, text: &str) -> FilePosition {
66140
let (offset, text) = extract_offset(text);
67-
let file_id = FileId((self.files.len() + 1) as u32);
68-
self.files.push((path.to_string(), text));
141+
let file_id = self.next_id();
142+
self.files.push(MockFileData::new(path.to_string(), text));
69143
FilePosition { file_id, offset }
70144
}
71145
pub fn add_file_with_range(&mut self, path: &str, text: &str) -> FileRange {
72146
let (range, text) = extract_range(text);
73-
let file_id = FileId((self.files.len() + 1) as u32);
74-
self.files.push((path.to_string(), text));
147+
let file_id = self.next_id();
148+
self.files.push(MockFileData::new(path.to_string(), text));
75149
FileRange { file_id, range }
76150
}
77151
pub fn id_of(&self, path: &str) -> FileId {
78152
let (idx, _) = self
79153
.files
80154
.iter()
81155
.enumerate()
82-
.find(|(_, (p, _text))| path == p)
156+
.find(|(_, data)| path == data.path())
83157
.expect("no file in this mock");
84158
FileId(idx as u32 + 1)
85159
}
@@ -90,29 +164,32 @@ impl MockAnalysis {
90164
change.add_root(source_root, true);
91165
let mut crate_graph = CrateGraph::default();
92166
let mut root_crate = None;
93-
for (i, (path, contents)) in self.files.into_iter().enumerate() {
167+
for (i, data) in self.files.into_iter().enumerate() {
168+
let path = data.path();
94169
assert!(path.starts_with('/'));
95170
let path = RelativePathBuf::from_path(&path[1..]).unwrap();
171+
let cfg_options = data.cfg_options();
96172
let file_id = FileId(i as u32 + 1);
97-
let cfg_options = CfgOptions::default();
173+
let edition = data.edition();
174+
let env = data.env();
98175
if path == "/lib.rs" || path == "/main.rs" {
99176
root_crate = Some(crate_graph.add_crate_root(
100177
file_id,
101-
Edition2018,
178+
edition,
102179
None,
103180
cfg_options,
104-
Env::default(),
181+
env,
105182
Default::default(),
106183
Default::default(),
107184
));
108185
} else if path.ends_with("/lib.rs") {
109186
let crate_name = path.parent().unwrap().file_name().unwrap();
110187
let other_crate = crate_graph.add_crate_root(
111188
file_id,
112-
Edition2018,
189+
edition,
113190
Some(CrateName::new(crate_name).unwrap()),
114191
cfg_options,
115-
Env::default(),
192+
env,
116193
Default::default(),
117194
Default::default(),
118195
);
@@ -122,7 +199,7 @@ impl MockAnalysis {
122199
.unwrap();
123200
}
124201
}
125-
change.add_file(source_root, file_id, path, Arc::new(contents));
202+
change.add_file(source_root, file_id, path, Arc::new(data.content().to_owned()));
126203
}
127204
change.set_crate_graph(crate_graph);
128205
host.apply_change(change);
@@ -131,6 +208,10 @@ impl MockAnalysis {
131208
pub fn analysis(self) -> Analysis {
132209
self.analysis_host().analysis()
133210
}
211+
212+
fn next_id(&self) -> FileId {
213+
FileId((self.files.len() + 1) as u32)
214+
}
134215
}
135216

136217
/// Creates analysis from a multi-file fixture, returns positions marked with <|>.

crates/rust-analyzer/tests/heavy_tests/support.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ impl<'a> Project<'a> {
6868
let mut paths = vec![];
6969

7070
for entry in parse_fixture(self.fixture) {
71-
let path = tmp_dir.path().join(entry.meta);
71+
let path = tmp_dir.path().join(entry.meta.path().as_str());
7272
fs::create_dir_all(path.parent().unwrap()).unwrap();
7373
fs::write(path.as_path(), entry.text.as_bytes()).unwrap();
7474
paths.push((path, entry.text));

crates/test_utils/Cargo.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,3 +11,7 @@ doctest = false
1111
difference = "2.0.0"
1212
text-size = "1.0.0"
1313
serde_json = "1.0.48"
14+
relative-path = "1.0.0"
15+
rustc-hash = "1.1.0"
16+
17+
ra_cfg = { path = "../ra_cfg" }

0 commit comments

Comments
 (0)