Skip to content

Commit 513a361

Browse files
committed
Report invalid, nested, multi-segment crate-paths
Specifically, things like: use foo::{crate::bar}; Are now being caught, when before we only caught: use foo::{crate};
1 parent fec1e7c commit 513a361

File tree

6 files changed

+116
-81
lines changed

6 files changed

+116
-81
lines changed

crates/ra_parser/src/grammar/items/use_item.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ fn use_tree(p: &mut Parser, top_level: bool) {
4747
// use {crate::path::from::root, or::path::from::crate_name}; // Rust 2018 (with a crate named `or`)
4848
// use {path::from::root}; // Rust 2015
4949
// use ::{some::arbritrary::path}; // Rust 2015
50-
// use ::{{{crate::export}}}; // Nonsensical but perfectly legal nestnig
50+
// use ::{{{root::export}}}; // Nonsensical but perfectly legal nesting
5151
T!['{'] => {
5252
use_tree_list(p);
5353
}

crates/ra_syntax/src/validation.rs

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -236,21 +236,40 @@ fn validate_crate_keyword_in_path_segment(
236236
};
237237

238238
// Disallow both ::crate and foo::crate
239-
let path = segment.parent_path();
239+
let mut path = segment.parent_path();
240240
if segment.coloncolon_token().is_some() || path.qualifier().is_some() {
241241
errors.push(SyntaxError::new(ERR_MSG, crate_token.text_range()));
242242
return;
243243
}
244244

245-
// We now know that the path variable describes a complete path.
246245
// For expressions and types, validation is complete, but we still have
247-
// to handle UseItems like this:
248-
// use foo:{crate};
249-
// so we crawl upwards looking for any preceding paths on `UseTree`s
246+
// to handle invalid UseItems like this:
247+
//
248+
// use foo:{crate::bar::baz};
249+
//
250+
// To handle this we must inspect the parent `UseItem`s and `UseTree`s
251+
// but right now we're looking deep inside the nested `Path` nodes because
252+
// `Path`s are left-associative:
253+
//
254+
// ((crate)::bar)::baz)
255+
// ^ current value of path
256+
//
257+
// So we need to climb to the top
258+
while let Some(parent) = path.parent_path() {
259+
path = parent;
260+
}
261+
262+
// Now that we've found the whole path we need to see if there's a prefix
263+
// somewhere in the UseTree hierarchy. This check is arbitrarily deep
264+
// because rust allows arbitrary nesting like so:
265+
//
266+
// use {foo::{{{{crate::bar::baz}}}}};
250267
for node in path.syntax().ancestors().skip(1) {
251268
match_ast! {
252269
match node {
253270
ast::UseTree(it) => if let Some(tree_path) = it.path() {
271+
// Even a top-level path exists within a `UseTree` so we must explicitly
272+
// allow our path but disallow anything else
254273
if tree_path != path {
255274
errors.push(SyntaxError::new(ERR_MSG, crate_token.text_range()));
256275
}
Lines changed: 64 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
SOURCE_FILE@0..83
1+
SOURCE_FILE@0..98
22
33
44
@@ -9,68 +9,83 @@ [email protected]
99
1010
1111
12-
USE_ITEM@13..39
12+
USE_ITEM@13..54
1313
1414
15-
USE_TREE@17..38
16-
USE_TREE_LIST@17..38
15+
USE_TREE@17..53
16+
USE_TREE_LIST@17..53
1717
1818
1919
2020
2121
2222
2323
24-
USE_TREE@25..37
24+
USE_TREE@25..52
2525
2626
2727
2828
2929
30-
USE_TREE_LIST@30..37
30+
USE_TREE_LIST@30..52
3131
32-
33-
34-
35-
36-
37-
38-
39-
40-
41-
42-
43-
44-
45-
46-
47-
48-
49-
50-
51-
52-
53-
54-
55-
56-
57-
58-
59-
60-
61-
62-
63-
64-
65-
66-
67-
68-
69-
70-
71-
72-
32+
33+
34+
35+
36+
37+
38+
39+
40+
41+
42+
43+
44+
45+
46+
47+
48+
49+
50+
51+
52+
53+
54+
55+
56+
57+
58+
59+
60+
61+
62+
63+
64+
65+
66+
67+
68+
69+
70+
71+
72+
73+
74+
75+
76+
77+
78+
79+
80+
81+
82+
83+
84+
85+
86+
87+
7388
error 6..11: The `crate` keyword is only allowed as the first segment of a path
7489
error 31..36: The `crate` keyword is only allowed as the first segment of a path
75-
error 51..56: The `crate` keyword is only allowed as the first segment of a path
76-
error 69..74: The `crate` keyword is only allowed as the first segment of a path
90+
error 66..71: The `crate` keyword is only allowed as the first segment of a path
91+
error 84..89: The `crate` keyword is only allowed as the first segment of a path
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
use ::crate;
2-
use {crate, foo::{crate}};
2+
use {crate, foo::{crate::foo::bar::baz}};
33
use hello::crate;
44
use hello::crate::there;

crates/ra_syntax/test_data/parser/inline/ok/0002_use_tree_list.rast

Lines changed: 25 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
SOURCE_FILE@0..250
1+
SOURCE_FILE@0..249
22
33
44
@@ -104,32 +104,33 @@ [email protected]
104104
105105
[email protected] "// Rust 2015"
106106
107-
USE_ITEM@180..206
107+
USE_ITEM@180..205
108108
109109
110-
USE_TREE@184..205
110+
USE_TREE@184..204
111111
112-
USE_TREE_LIST@186..205
112+
USE_TREE_LIST@186..204
113113
114-
USE_TREE@187..204
115-
USE_TREE_LIST@187..204
114+
USE_TREE@187..203
115+
USE_TREE_LIST@187..203
116116
117-
USE_TREE@188..203
118-
USE_TREE_LIST@188..203
117+
USE_TREE@188..202
118+
USE_TREE_LIST@188..202
119119
120-
121-
122-
123-
124-
125-
126-
127-
128-
129-
130-
131-
132-
133-
134-
[email protected] "// Nonsensical but pe ..."
135-
120+
121+
122+
123+
124+
125+
126+
127+
128+
129+
130+
131+
132+
133+
134+
135+
[email protected] "// Nonsensical but pe ..."
136+
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
use {crate::path::from::root, or::path::from::crate_name}; // Rust 2018 (with a crate named `or`)
22
use {path::from::root}; // Rust 2015
33
use ::{some::arbritrary::path}; // Rust 2015
4-
use ::{{{crate::export}}}; // Nonsensical but perfectly legal nestnig
4+
use ::{{{root::export}}}; // Nonsensical but perfectly legal nesting

0 commit comments

Comments
 (0)