aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_syntax/src
diff options
context:
space:
mode:
authorbors[bot] <26634292+bors[bot]@users.noreply.github.com>2020-04-30 19:37:35 +0100
committerGitHub <[email protected]>2020-04-30 19:37:35 +0100
commit745bd45ddb2f8b6165ab7eacfd482d8530cab05a (patch)
tree9e36b55112d3ea5f9d913d36597884b82aa87f68 /crates/ra_syntax/src
parent861652ffa6b6440a022a353d2e6b9f5ca780d2ec (diff)
parent513a3615f6d462852c0135dc4ac30a2086e25c5a (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]>
Diffstat (limited to 'crates/ra_syntax/src')
-rw-r--r--crates/ra_syntax/src/validation.rs29
1 files changed, 24 insertions, 5 deletions
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 }