Skip to content

Commit 745bd45

Browse files
bors[bot]djrenren
andauthored
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: #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 <john@jrenner.net>
2 parents 861652f + 513a361 commit 745bd45

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
USE_ITEM@0..12
33
USE_KW@0..3 "use"
44
WHITESPACE@3..4 " "
@@ -9,68 +9,83 @@ SOURCE_FILE@0..83
99
CRATE_KW@6..11 "crate"
1010
SEMICOLON@11..12 ";"
1111
WHITESPACE@12..13 "\n"
12-
USE_ITEM@13..39
12+
USE_ITEM@13..54
1313
USE_KW@13..16 "use"
1414
WHITESPACE@16..17 " "
15-
USE_TREE@17..38
16-
USE_TREE_LIST@17..38
15+
USE_TREE@17..53
16+
USE_TREE_LIST@17..53
1717
L_CURLY@17..18 "{"
1818
USE_TREE@18..23
1919
PATH@18..23
2020
PATH_SEGMENT@18..23
2121
CRATE_KW@18..23 "crate"
2222
COMMA@23..24 ","
2323
WHITESPACE@24..25 " "
24-
USE_TREE@25..37
24+
USE_TREE@25..52
2525
PATH@25..28
2626
PATH_SEGMENT@25..28
2727
NAME_REF@25..28
2828
IDENT@25..28 "foo"
2929
COLON2@28..30 "::"
30-
USE_TREE_LIST@30..37
30+
USE_TREE_LIST@30..52
3131
L_CURLY@30..31 "{"
32-
USE_TREE@31..36
33-
PATH@31..36
34-
PATH_SEGMENT@31..36
35-
CRATE_KW@31..36 "crate"
36-
R_CURLY@36..37 "}"
37-
R_CURLY@37..38 "}"
38-
SEMICOLON@38..39 ";"
39-
WHITESPACE@39..40 "\n"
40-
USE_ITEM@40..57
41-
USE_KW@40..43 "use"
42-
WHITESPACE@43..44 " "
43-
USE_TREE@44..56
44-
PATH@44..56
45-
PATH@44..49
46-
PATH_SEGMENT@44..49
47-
NAME_REF@44..49
48-
IDENT@44..49 "hello"
49-
COLON2@49..51 "::"
50-
PATH_SEGMENT@51..56
51-
CRATE_KW@51..56 "crate"
52-
SEMICOLON@56..57 ";"
53-
WHITESPACE@57..58 "\n"
54-
USE_ITEM@58..82
55-
USE_KW@58..61 "use"
56-
WHITESPACE@61..62 " "
57-
USE_TREE@62..81
58-
PATH@62..81
59-
PATH@62..74
60-
PATH@62..67
61-
PATH_SEGMENT@62..67
62-
NAME_REF@62..67
63-
IDENT@62..67 "hello"
64-
COLON2@67..69 "::"
65-
PATH_SEGMENT@69..74
66-
CRATE_KW@69..74 "crate"
67-
COLON2@74..76 "::"
68-
PATH_SEGMENT@76..81
69-
NAME_REF@76..81
70-
IDENT@76..81 "there"
71-
SEMICOLON@81..82 ";"
72-
WHITESPACE@82..83 "\n"
32+
USE_TREE@31..51
33+
PATH@31..51
34+
PATH@31..46
35+
PATH@31..41
36+
PATH@31..36
37+
PATH_SEGMENT@31..36
38+
CRATE_KW@31..36 "crate"
39+
COLON2@36..38 "::"
40+
PATH_SEGMENT@38..41
41+
NAME_REF@38..41
42+
IDENT@38..41 "foo"
43+
COLON2@41..43 "::"
44+
PATH_SEGMENT@43..46
45+
NAME_REF@43..46
46+
IDENT@43..46 "bar"
47+
COLON2@46..48 "::"
48+
PATH_SEGMENT@48..51
49+
NAME_REF@48..51
50+
IDENT@48..51 "baz"
51+
R_CURLY@51..52 "}"
52+
R_CURLY@52..53 "}"
53+
SEMICOLON@53..54 ";"
54+
WHITESPACE@54..55 "\n"
55+
USE_ITEM@55..72
56+
USE_KW@55..58 "use"
57+
WHITESPACE@58..59 " "
58+
USE_TREE@59..71
59+
PATH@59..71
60+
PATH@59..64
61+
PATH_SEGMENT@59..64
62+
NAME_REF@59..64
63+
IDENT@59..64 "hello"
64+
COLON2@64..66 "::"
65+
PATH_SEGMENT@66..71
66+
CRATE_KW@66..71 "crate"
67+
SEMICOLON@71..72 ";"
68+
WHITESPACE@72..73 "\n"
69+
USE_ITEM@73..97
70+
USE_KW@73..76 "use"
71+
WHITESPACE@76..77 " "
72+
USE_TREE@77..96
73+
PATH@77..96
74+
PATH@77..89
75+
PATH@77..82
76+
PATH_SEGMENT@77..82
77+
NAME_REF@77..82
78+
IDENT@77..82 "hello"
79+
COLON2@82..84 "::"
80+
PATH_SEGMENT@84..89
81+
CRATE_KW@84..89 "crate"
82+
COLON2@89..91 "::"
83+
PATH_SEGMENT@91..96
84+
NAME_REF@91..96
85+
IDENT@91..96 "there"
86+
SEMICOLON@96..97 ";"
87+
WHITESPACE@97..98 "\n"
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
USE_ITEM@0..58
33
USE_KW@0..3 "use"
44
WHITESPACE@3..4 " "
@@ -104,32 +104,33 @@ SOURCE_FILE@0..250
104104
WHITESPACE@166..167 " "
105105
COMMENT@167..179 "// Rust 2015"
106106
WHITESPACE@179..180 "\n"
107-
USE_ITEM@180..206
107+
USE_ITEM@180..205
108108
USE_KW@180..183 "use"
109109
WHITESPACE@183..184 " "
110-
USE_TREE@184..205
110+
USE_TREE@184..204
111111
COLON2@184..186 "::"
112-
USE_TREE_LIST@186..205
112+
USE_TREE_LIST@186..204
113113
L_CURLY@186..187 "{"
114-
USE_TREE@187..204
115-
USE_TREE_LIST@187..204
114+
USE_TREE@187..203
115+
USE_TREE_LIST@187..203
116116
L_CURLY@187..188 "{"
117-
USE_TREE@188..203
118-
USE_TREE_LIST@188..203
117+
USE_TREE@188..202
118+
USE_TREE_LIST@188..202
119119
L_CURLY@188..189 "{"
120-
USE_TREE@189..202
121-
PATH@189..202
122-
PATH@189..194
123-
PATH_SEGMENT@189..194
124-
CRATE_KW@189..194 "crate"
125-
COLON2@194..196 "::"
126-
PATH_SEGMENT@196..202
127-
NAME_REF@196..202
128-
IDENT@196..202 "export"
129-
R_CURLY@202..203 "}"
130-
R_CURLY@203..204 "}"
131-
R_CURLY@204..205 "}"
132-
SEMICOLON@205..206 ";"
133-
WHITESPACE@206..207 " "
134-
COMMENT@207..249 "// Nonsensical but pe ..."
135-
WHITESPACE@249..250 "\n"
120+
USE_TREE@189..201
121+
PATH@189..201
122+
PATH@189..193
123+
PATH_SEGMENT@189..193
124+
NAME_REF@189..193
125+
IDENT@189..193 "root"
126+
COLON2@193..195 "::"
127+
PATH_SEGMENT@195..201
128+
NAME_REF@195..201
129+
IDENT@195..201 "export"
130+
R_CURLY@201..202 "}"
131+
R_CURLY@202..203 "}"
132+
R_CURLY@203..204 "}"
133+
SEMICOLON@204..205 ";"
134+
WHITESPACE@205..206 " "
135+
COMMENT@206..248 "// Nonsensical but pe ..."
136+
WHITESPACE@248..249 "\n"
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)