diff options
author | bors[bot] <26634292+bors[bot]@users.noreply.github.com> | 2020-04-30 11:17:40 +0100 |
---|---|---|
committer | GitHub <[email protected]> | 2020-04-30 11:17:40 +0100 |
commit | 95e8766db60be2a00bce9978e2680a769771a546 (patch) | |
tree | add99db171c000d164056c56885ea2ce0741eaa6 /crates | |
parent | c2425fd88b13b8aeaaaca4e4933a647526f8511f (diff) | |
parent | 0af727da91e7ff3c8ed5518cb7e005e8d4f939b0 (diff) |
Merge #4178
4178: Validate the location of `crate` in paths r=matklad a=djrenren
**This solution does not fully handle `use` statements. See below**
This pull requests implements simple validation of usages of the `crate` keyword in `Path`s. Specifically it validates that:
- If a `PathSegment` is starts with the `crate` keyword, it is also the first segment of the `Path`
- All other usages of `crate` in `Path`s are considered errors.
This aligns with `rustc`'s rules. Unlike rustc this implementation does not issue a special error message in the case of `::crate` but it does catch the error.
Furthermore, this change does not cover all error cases. Specifically the following is not caught:
```rust
use foo::{crate}
```
This is because this check is context sensitive. From an AST perspective, `crate` is the root of the `Path`. Only by inspecting the full `UseItem` do we see that it is not in fact the root. This problem becomes worse because `UseTree`s are allowed to be arbitrarily nested:
```rust
use {crate, {{crate, foo::{crate}}}
```
So this is a hard problem to solve without essentially a breadth-first search. In a traditional compiler, I'd say this error is most easily found during the AST -> HIR conversion pass but within rust-analyzer I'm not sure where it belongs.
Under the implementation in this PR, such errors are ignored so we're *more correct* just not *entirely correct*.
Co-authored-by: John Renner <[email protected]>
Diffstat (limited to 'crates')
4 files changed, 120 insertions, 0 deletions
diff --git a/crates/ra_syntax/src/ast/generated/nodes.rs b/crates/ra_syntax/src/ast/generated/nodes.rs index 2cb3ad011..3b5e05af9 100644 --- a/crates/ra_syntax/src/ast/generated/nodes.rs +++ b/crates/ra_syntax/src/ast/generated/nodes.rs | |||
@@ -1249,6 +1249,7 @@ pub struct PathSegment { | |||
1249 | } | 1249 | } |
1250 | impl PathSegment { | 1250 | impl PathSegment { |
1251 | pub fn coloncolon_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![::]) } | 1251 | pub fn coloncolon_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![::]) } |
1252 | pub fn crate_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![crate]) } | ||
1252 | pub fn l_angle_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![<]) } | 1253 | pub fn l_angle_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![<]) } |
1253 | pub fn name_ref(&self) -> Option<NameRef> { support::child(&self.syntax) } | 1254 | pub fn name_ref(&self) -> Option<NameRef> { support::child(&self.syntax) } |
1254 | pub fn type_arg_list(&self) -> Option<TypeArgList> { support::child(&self.syntax) } | 1255 | pub fn type_arg_list(&self) -> Option<TypeArgList> { support::child(&self.syntax) } |
diff --git a/crates/ra_syntax/src/validation.rs b/crates/ra_syntax/src/validation.rs index 5e93895ec..a30bc97bb 100644 --- a/crates/ra_syntax/src/validation.rs +++ b/crates/ra_syntax/src/validation.rs | |||
@@ -96,6 +96,7 @@ pub(crate) fn validate(root: &SyntaxNode) -> Vec<SyntaxError> { | |||
96 | ast::RecordField(it) => validate_numeric_name(it.name_ref(), &mut errors), | 96 | ast::RecordField(it) => validate_numeric_name(it.name_ref(), &mut errors), |
97 | ast::Visibility(it) => validate_visibility(it, &mut errors), | 97 | ast::Visibility(it) => validate_visibility(it, &mut errors), |
98 | ast::RangeExpr(it) => validate_range_expr(it, &mut errors), | 98 | ast::RangeExpr(it) => validate_range_expr(it, &mut errors), |
99 | ast::PathSegment(it) => validate_crate_keyword_in_path_segment(it, &mut errors), | ||
99 | _ => (), | 100 | _ => (), |
100 | } | 101 | } |
101 | } | 102 | } |
@@ -222,3 +223,41 @@ fn validate_range_expr(expr: ast::RangeExpr, errors: &mut Vec<SyntaxError>) { | |||
222 | )); | 223 | )); |
223 | } | 224 | } |
224 | } | 225 | } |
226 | |||
227 | fn validate_crate_keyword_in_path_segment( | ||
228 | segment: ast::PathSegment, | ||
229 | errors: &mut Vec<SyntaxError>, | ||
230 | ) { | ||
231 | const ERR_MSG: &str = "The `crate` keyword is only allowed as the first segment of a path"; | ||
232 | |||
233 | let crate_token = match segment.crate_token() { | ||
234 | None => return, | ||
235 | Some(it) => it, | ||
236 | }; | ||
237 | |||
238 | // Disallow both ::crate and foo::crate | ||
239 | let path = segment.parent_path(); | ||
240 | if segment.coloncolon_token().is_some() || path.qualifier().is_some() { | ||
241 | errors.push(SyntaxError::new(ERR_MSG, crate_token.text_range())); | ||
242 | return; | ||
243 | } | ||
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 | ||
247 | // to handle UseItems like this: | ||
248 | // use foo:{crate}; | ||
249 | // so we crawl upwards looking for any preceding paths on `UseTree`s | ||
250 | for node in path.syntax().ancestors().skip(1) { | ||
251 | match_ast! { | ||
252 | match node { | ||
253 | ast::UseTree(it) => if let Some(tree_path) = it.path() { | ||
254 | if tree_path != path { | ||
255 | errors.push(SyntaxError::new(ERR_MSG, crate_token.text_range())); | ||
256 | } | ||
257 | }, | ||
258 | ast::UseTreeList(_it) => continue, | ||
259 | _ => return, | ||
260 | } | ||
261 | }; | ||
262 | } | ||
263 | } | ||
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 new file mode 100644 index 000000000..8306f7361 --- /dev/null +++ b/crates/ra_syntax/test_data/parser/err/0040_illegal_crate_kw_location.rast | |||
@@ -0,0 +1,76 @@ | |||
1 | [email protected] | ||
2 | [email protected] | ||
3 | [email protected] "use" | ||
4 | [email protected] " " | ||
5 | [email protected] | ||
6 | [email protected] | ||
7 | [email protected] | ||
8 | [email protected] "::" | ||
9 | [email protected] "crate" | ||
10 | [email protected] ";" | ||
11 | [email protected] "\n" | ||
12 | [email protected] | ||
13 | [email protected] "use" | ||
14 | [email protected] " " | ||
15 | [email protected] | ||
16 | [email protected] | ||
17 | [email protected] "{" | ||
18 | [email protected] | ||
19 | [email protected] | ||
20 | [email protected] | ||
21 | [email protected] "crate" | ||
22 | [email protected] "," | ||
23 | [email protected] " " | ||
24 | [email protected] | ||
25 | [email protected] | ||
26 | [email protected] | ||
27 | [email protected] | ||
28 | [email protected] "foo" | ||
29 | [email protected] "::" | ||
30 | [email protected] | ||
31 | [email protected] "{" | ||
32 | [email protected] | ||
33 | [email protected] | ||
34 | [email protected] | ||
35 | [email protected] "crate" | ||
36 | [email protected] "}" | ||
37 | [email protected] "}" | ||
38 | [email protected] ";" | ||
39 | [email protected] "\n" | ||
40 | [email protected] | ||
41 | [email protected] "use" | ||
42 | [email protected] " " | ||
43 | [email protected] | ||
44 | [email protected] | ||
45 | [email protected] | ||
46 | [email protected] | ||
47 | [email protected] | ||
48 | [email protected] "hello" | ||
49 | [email protected] "::" | ||
50 | [email protected] | ||
51 | [email protected] "crate" | ||
52 | [email protected] ";" | ||
53 | [email protected] "\n" | ||
54 | [email protected] | ||
55 | [email protected] "use" | ||
56 | [email protected] " " | ||
57 | [email protected] | ||
58 | [email protected] | ||
59 | [email protected] | ||
60 | [email protected] | ||
61 | [email protected] | ||
62 | [email protected] | ||
63 | [email protected] "hello" | ||
64 | [email protected] "::" | ||
65 | [email protected] | ||
66 | [email protected] "crate" | ||
67 | [email protected] "::" | ||
68 | [email protected] | ||
69 | [email protected] | ||
70 | [email protected] "there" | ||
71 | [email protected] ";" | ||
72 | [email protected] "\n" | ||
73 | 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 | ||
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 | ||
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 new file mode 100644 index 000000000..bead4c0b6 --- /dev/null +++ b/crates/ra_syntax/test_data/parser/err/0040_illegal_crate_kw_location.rs | |||
@@ -0,0 +1,4 @@ | |||
1 | use ::crate; | ||
2 | use {crate, foo::{crate}}; | ||
3 | use hello::crate; | ||
4 | use hello::crate::there; | ||