aboutsummaryrefslogtreecommitdiff
path: root/crates
diff options
context:
space:
mode:
authorbors[bot] <26634292+bors[bot]@users.noreply.github.com>2020-04-30 11:17:40 +0100
committerGitHub <[email protected]>2020-04-30 11:17:40 +0100
commit95e8766db60be2a00bce9978e2680a769771a546 (patch)
treeadd99db171c000d164056c56885ea2ce0741eaa6 /crates
parentc2425fd88b13b8aeaaaca4e4933a647526f8511f (diff)
parent0af727da91e7ff3c8ed5518cb7e005e8d4f939b0 (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')
-rw-r--r--crates/ra_syntax/src/ast/generated/nodes.rs1
-rw-r--r--crates/ra_syntax/src/validation.rs39
-rw-r--r--crates/ra_syntax/test_data/parser/err/0040_illegal_crate_kw_location.rast76
-rw-r--r--crates/ra_syntax/test_data/parser/err/0040_illegal_crate_kw_location.rs4
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}
1250impl PathSegment { 1250impl 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
227fn 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"
73error 6..11: The `crate` keyword is only allowed as the first segment of a path
74error 31..36: The `crate` keyword is only allowed as the first segment of a path
75error 51..56: The `crate` keyword is only allowed as the first segment of a path
76error 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 @@
1use ::crate;
2use {crate, foo::{crate}};
3use hello::crate;
4use hello::crate::there;