diff options
author | bors[bot] <26634292+bors[bot]@users.noreply.github.com> | 2020-04-30 19:37:35 +0100 |
---|---|---|
committer | GitHub <[email protected]> | 2020-04-30 19:37:35 +0100 |
commit | 745bd45ddb2f8b6165ab7eacfd482d8530cab05a (patch) | |
tree | 9e36b55112d3ea5f9d913d36597884b82aa87f68 | |
parent | 861652ffa6b6440a022a353d2e6b9f5ca780d2ec (diff) | |
parent | 513a3615f6d462852c0135dc4ac30a2086e25c5a (diff) |
Merge #4227
4227: Report invalid, nested, multi-segment crate-paths r=matklad a=djrenren
There was a bug in the previous path-validating code that didn't detect multi-segment paths that started with `crate`.
```rust
// Successfully reported
use foo::{crate};
// BUG: was not being reported
use foo::{crate::bar};
```
This was due to my confusion about path-associativity. That is, the path with no qualifier is the innermost path, not the outermost. I've updated the code with a lot of comments to explain what's going on.
This bug was discovered when I found an erroneous `ok` test which I reported here:
https://github.com/rust-analyzer/rust-analyzer/issues/4226
This test now fails and has been modified, hopefully in the spirit of the original test, to be correct. Sorry about submitting the bug in the first place!
Co-authored-by: John Renner <[email protected]>
6 files changed, 116 insertions, 81 deletions
diff --git a/crates/ra_parser/src/grammar/items/use_item.rs b/crates/ra_parser/src/grammar/items/use_item.rs index e3b991c8c..3a0c7a31a 100644 --- a/crates/ra_parser/src/grammar/items/use_item.rs +++ b/crates/ra_parser/src/grammar/items/use_item.rs | |||
@@ -47,7 +47,7 @@ fn use_tree(p: &mut Parser, top_level: bool) { | |||
47 | // use {crate::path::from::root, or::path::from::crate_name}; // Rust 2018 (with a crate named `or`) | 47 | // use {crate::path::from::root, or::path::from::crate_name}; // Rust 2018 (with a crate named `or`) |
48 | // use {path::from::root}; // Rust 2015 | 48 | // use {path::from::root}; // Rust 2015 |
49 | // use ::{some::arbritrary::path}; // Rust 2015 | 49 | // use ::{some::arbritrary::path}; // Rust 2015 |
50 | // use ::{{{crate::export}}}; // Nonsensical but perfectly legal nestnig | 50 | // use ::{{{root::export}}}; // Nonsensical but perfectly legal nesting |
51 | T!['{'] => { | 51 | T!['{'] => { |
52 | use_tree_list(p); | 52 | use_tree_list(p); |
53 | } | 53 | } |
diff --git a/crates/ra_syntax/src/validation.rs b/crates/ra_syntax/src/validation.rs index a30bc97bb..f0b3dec63 100644 --- a/crates/ra_syntax/src/validation.rs +++ b/crates/ra_syntax/src/validation.rs | |||
@@ -236,21 +236,40 @@ fn validate_crate_keyword_in_path_segment( | |||
236 | }; | 236 | }; |
237 | 237 | ||
238 | // Disallow both ::crate and foo::crate | 238 | // Disallow both ::crate and foo::crate |
239 | let path = segment.parent_path(); | 239 | let mut path = segment.parent_path(); |
240 | if segment.coloncolon_token().is_some() || path.qualifier().is_some() { | 240 | if segment.coloncolon_token().is_some() || path.qualifier().is_some() { |
241 | errors.push(SyntaxError::new(ERR_MSG, crate_token.text_range())); | 241 | errors.push(SyntaxError::new(ERR_MSG, crate_token.text_range())); |
242 | return; | 242 | return; |
243 | } | 243 | } |
244 | 244 | ||
245 | // We now know that the path variable describes a complete path. | ||
246 | // For expressions and types, validation is complete, but we still have | 245 | // For expressions and types, validation is complete, but we still have |
247 | // to handle UseItems like this: | 246 | // to handle invalid UseItems like this: |
248 | // use foo:{crate}; | 247 | // |
249 | // so we crawl upwards looking for any preceding paths on `UseTree`s | 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}}}}}; | ||
250 | for node in path.syntax().ancestors().skip(1) { | 267 | for node in path.syntax().ancestors().skip(1) { |
251 | match_ast! { | 268 | match_ast! { |
252 | match node { | 269 | match node { |
253 | ast::UseTree(it) => if let Some(tree_path) = it.path() { | 270 | 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 | ||
254 | if tree_path != path { | 273 | if tree_path != path { |
255 | errors.push(SyntaxError::new(ERR_MSG, crate_token.text_range())); | 274 | errors.push(SyntaxError::new(ERR_MSG, crate_token.text_range())); |
256 | } | 275 | } |
diff --git a/crates/ra_syntax/test_data/parser/err/0040_illegal_crate_kw_location.rast b/crates/ra_syntax/test_data/parser/err/0040_illegal_crate_kw_location.rast index 8306f7361..d2a549273 100644 --- a/crates/ra_syntax/test_data/parser/err/0040_illegal_crate_kw_location.rast +++ b/crates/ra_syntax/test_data/parser/err/0040_illegal_crate_kw_location.rast | |||
@@ -1,4 +1,4 @@ | |||
1 | [email protected]3 | 1 | SOURCE_FILE@0..98 |
2 | [email protected] | 2 | [email protected] |
3 | [email protected] "use" | 3 | [email protected] "use" |
4 | [email protected] " " | 4 | [email protected] " " |
@@ -9,11 +9,11 @@ [email protected] | |||
9 | [email protected] "crate" | 9 | [email protected] "crate" |
10 | [email protected] ";" | 10 | [email protected] ";" |
11 | [email protected] "\n" | 11 | [email protected] "\n" |
12 | USE_ITEM@13..39 | 12 | USE_ITEM@13..54 |
13 | [email protected] "use" | 13 | [email protected] "use" |
14 | [email protected] " " | 14 | [email protected] " " |
15 | [email protected]8 | 15 | USE_TREE@17..53 |
16 | [email protected]8 | 16 | USE_TREE_LIST@17..53 |
17 | [email protected] "{" | 17 | [email protected] "{" |
18 | [email protected] | 18 | [email protected] |
19 | [email protected] | 19 | [email protected] |
@@ -21,56 +21,71 @@ [email protected] | |||
21 | [email protected] "crate" | 21 | [email protected] "crate" |
22 | [email protected] "," | 22 | [email protected] "," |
23 | [email protected] " " | 23 | [email protected] " " |
24 | USE_TREE@25..37 | 24 | USE_TREE@25..52 |
25 | [email protected] | 25 | [email protected] |
26 | [email protected] | 26 | [email protected] |
27 | [email protected] | 27 | [email protected] |
28 | [email protected] "foo" | 28 | [email protected] "foo" |
29 | [email protected] "::" | 29 | [email protected] "::" |
30 | USE_TREE_LIST@30..37 | 30 | USE_TREE_LIST@30..52 |
31 | [email protected] "{" | 31 | [email protected] "{" |
32 | [email protected] | 32 | [email protected] |
33 | [email protected] | 33 | [email protected] |
34 | [email protected] | 34 | [email protected] |
35 | [email protected] "crate" | 35 | [email protected] |
36 | [email protected] "}" | 36 | [email protected] |
37 | [email protected] "}" | 37 | [email protected] |
38 | [email protected] ";" | 38 | [email protected] "crate" |
39 | [email protected] "\n" | 39 | [email protected] "::" |
40 | [email protected] | 40 | [email protected] |
41 | [email protected] "use" | 41 | [email protected] |
42 | [email protected] " " | 42 | [email protected] "foo" |
43 | [email protected] | 43 | [email protected] "::" |
44 | [email protected] | 44 | [email protected] |
45 | [email protected] | 45 | [email protected] |
46 | [email protected] | 46 | [email protected] "bar" |
47 | [email protected] | 47 | [email protected] "::" |
48 | [email protected] "hello" | 48 | [email protected] |
49 | [email protected] "::" | 49 | [email protected] |
50 | [email protected] | 50 | [email protected] "baz" |
51 | [email protected] "crate" | 51 | [email protected] "}" |
52 | [email protected] ";" | 52 | [email protected] "}" |
53 | [email protected] "\n" | 53 | [email protected] ";" |
54 | [email protected] | 54 | [email protected] "\n" |
55 | [email protected] "use" | 55 | [email protected] |
56 | [email protected] " " | 56 | [email protected] "use" |
57 | [email protected] | 57 | [email protected] " " |
58 | [email protected] | 58 | [email protected] |
59 | [email protected] | 59 | [email protected] |
60 | [email protected] | 60 | [email protected] |
61 | [email protected] | 61 | [email protected] |
62 | [email protected] | 62 | [email protected] |
63 | [email protected] "hello" | 63 | [email protected] "hello" |
64 | [email protected] "::" | 64 | [email protected] "::" |
65 | [email protected] | 65 | [email protected] |
66 | [email protected] "crate" | 66 | [email protected] "crate" |
67 | [email protected] "::" | 67 | [email protected] ";" |
68 | [email protected] | 68 | [email protected] "\n" |
69 | [email protected] | 69 | [email protected] |
70 | [email protected] "there" | 70 | [email protected] "use" |
71 | [email protected] ";" | 71 | [email protected] " " |
72 | [email protected] "\n" | 72 | [email protected] |
73 | [email protected] | ||
74 | [email protected] | ||
75 | [email protected] | ||
76 | [email protected] | ||
77 | [email protected] | ||
78 | [email protected] "hello" | ||
79 | [email protected] "::" | ||
80 | [email protected] | ||
81 | [email protected] "crate" | ||
82 | [email protected] "::" | ||
83 | [email protected] | ||
84 | [email protected] | ||
85 | [email protected] "there" | ||
86 | [email protected] ";" | ||
87 | [email protected] "\n" | ||
73 | error 6..11: The `crate` keyword is only allowed as the first segment of a path | 88 | error 6..11: The `crate` keyword is only allowed as the first segment of a path |
74 | error 31..36: The `crate` keyword is only allowed as the first segment of a path | 89 | 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 | 90 | error 66..71: 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 | 91 | error 84..89: The `crate` keyword is only allowed as the first segment of a path |
diff --git a/crates/ra_syntax/test_data/parser/err/0040_illegal_crate_kw_location.rs b/crates/ra_syntax/test_data/parser/err/0040_illegal_crate_kw_location.rs index bead4c0b6..508def2c7 100644 --- a/crates/ra_syntax/test_data/parser/err/0040_illegal_crate_kw_location.rs +++ b/crates/ra_syntax/test_data/parser/err/0040_illegal_crate_kw_location.rs | |||
@@ -1,4 +1,4 @@ | |||
1 | use ::crate; | 1 | use ::crate; |
2 | use {crate, foo::{crate}}; | 2 | use {crate, foo::{crate::foo::bar::baz}}; |
3 | use hello::crate; | 3 | use hello::crate; |
4 | use hello::crate::there; | 4 | use hello::crate::there; |
diff --git a/crates/ra_syntax/test_data/parser/inline/ok/0002_use_tree_list.rast b/crates/ra_syntax/test_data/parser/inline/ok/0002_use_tree_list.rast index bd74b44a6..cf3a90400 100644 --- a/crates/ra_syntax/test_data/parser/inline/ok/0002_use_tree_list.rast +++ b/crates/ra_syntax/test_data/parser/inline/ok/0002_use_tree_list.rast | |||
@@ -1,4 +1,4 @@ | |||
1 | [email protected]50 | 1 | [email protected]49 |
2 | [email protected] | 2 | [email protected] |
3 | [email protected] "use" | 3 | [email protected] "use" |
4 | [email protected] " " | 4 | [email protected] " " |
@@ -104,32 +104,33 @@ [email protected] | |||
104 | [email protected] " " | 104 | [email protected] " " |
105 | [email protected] "// Rust 2015" | 105 | [email protected] "// Rust 2015" |
106 | [email protected] "\n" | 106 | [email protected] "\n" |
107 | [email protected]6 | 107 | [email protected]5 |
108 | [email protected] "use" | 108 | [email protected] "use" |
109 | [email protected] " " | 109 | [email protected] " " |
110 | [email protected]5 | 110 | [email protected]4 |
111 | [email protected] "::" | 111 | [email protected] "::" |
112 | [email protected]5 | 112 | [email protected]4 |
113 | [email protected] "{" | 113 | [email protected] "{" |
114 | [email protected]4 | 114 | [email protected]3 |
115 | [email protected]4 | 115 | [email protected]3 |
116 | [email protected] "{" | 116 | [email protected] "{" |
117 | [email protected]3 | 117 | [email protected]2 |
118 | [email protected]3 | 118 | [email protected]2 |
119 | [email protected] "{" | 119 | [email protected] "{" |
120 | [email protected] | 120 | [email protected] |
121 | [email protected] | 121 | [email protected] |
122 | [email protected] | 122 | [email protected] |
123 | [email protected] | 123 | [email protected] |
124 | [email protected] "crate" | 124 | [email protected] |
125 | [email protected] "::" | 125 | [email protected] "root" |
126 | [email protected] | 126 | [email protected] "::" |
127 | [email protected] | 127 | [email protected] |
128 | [email protected] "export" | 128 | [email protected] |
129 | [email protected] "}" | 129 | [email protected] "export" |
130 | [email protected] "}" | 130 | [email protected] "}" |
131 | [email protected] "}" | 131 | [email protected] "}" |
132 | [email protected] ";" | 132 | [email protected] "}" |
133 | [email protected] " " | 133 | [email protected] ";" |
134 | [email protected] "// Nonsensical but pe ..." | 134 | [email protected] " " |
135 | [email protected] "\n" | 135 | [email protected] "// Nonsensical but pe ..." |
136 | [email protected] "\n" | ||
diff --git a/crates/ra_syntax/test_data/parser/inline/ok/0002_use_tree_list.rs b/crates/ra_syntax/test_data/parser/inline/ok/0002_use_tree_list.rs index 06c387cee..381cba1e2 100644 --- a/crates/ra_syntax/test_data/parser/inline/ok/0002_use_tree_list.rs +++ b/crates/ra_syntax/test_data/parser/inline/ok/0002_use_tree_list.rs | |||
@@ -1,4 +1,4 @@ | |||
1 | use {crate::path::from::root, or::path::from::crate_name}; // Rust 2018 (with a crate named `or`) | 1 | use {crate::path::from::root, or::path::from::crate_name}; // Rust 2018 (with a crate named `or`) |
2 | use {path::from::root}; // Rust 2015 | 2 | use {path::from::root}; // Rust 2015 |
3 | use ::{some::arbritrary::path}; // Rust 2015 | 3 | use ::{some::arbritrary::path}; // Rust 2015 |
4 | use ::{{{crate::export}}}; // Nonsensical but perfectly legal nestnig | 4 | use ::{{{root::export}}}; // Nonsensical but perfectly legal nesting |