diff options
author | John Renner <[email protected]> | 2020-04-30 18:41:24 +0100 |
---|---|---|
committer | John Renner <[email protected]> | 2020-04-30 19:16:09 +0100 |
commit | 513a3615f6d462852c0135dc4ac30a2086e25c5a (patch) | |
tree | 31b5de91f47cf315074dcf7b1a7282accdbfad1d /crates/ra_syntax/src | |
parent | fec1e7c8e10e1c592642fac0c497cd57bd3f003c (diff) |
Report invalid, nested, multi-segment crate-paths
Specifically, things like:
use foo::{crate::bar};
Are now being caught, when before we only caught:
use foo::{crate};
Diffstat (limited to 'crates/ra_syntax/src')
-rw-r--r-- | crates/ra_syntax/src/validation.rs | 29 |
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 | } |