From 0af727da91e7ff3c8ed5518cb7e005e8d4f939b0 Mon Sep 17 00:00:00 2001 From: John Renner Date: Mon, 27 Apr 2020 10:02:47 -0700 Subject: Validate the location of `crate` in paths --- crates/ra_syntax/src/ast/generated/nodes.rs | 1 + crates/ra_syntax/src/validation.rs | 39 +++++++++++ .../parser/err/0040_illegal_crate_kw_location.rast | 76 ++++++++++++++++++++++ .../parser/err/0040_illegal_crate_kw_location.rs | 4 ++ 4 files changed, 120 insertions(+) create mode 100644 crates/ra_syntax/test_data/parser/err/0040_illegal_crate_kw_location.rast create mode 100644 crates/ra_syntax/test_data/parser/err/0040_illegal_crate_kw_location.rs (limited to 'crates/ra_syntax') 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 { } impl PathSegment { pub fn coloncolon_token(&self) -> Option { support::token(&self.syntax, T![::]) } + pub fn crate_token(&self) -> Option { support::token(&self.syntax, T![crate]) } pub fn l_angle_token(&self) -> Option { support::token(&self.syntax, T![<]) } pub fn name_ref(&self) -> Option { support::child(&self.syntax) } pub fn type_arg_list(&self) -> Option { 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 { ast::RecordField(it) => validate_numeric_name(it.name_ref(), &mut errors), ast::Visibility(it) => validate_visibility(it, &mut errors), ast::RangeExpr(it) => validate_range_expr(it, &mut errors), + ast::PathSegment(it) => validate_crate_keyword_in_path_segment(it, &mut errors), _ => (), } } @@ -222,3 +223,41 @@ fn validate_range_expr(expr: ast::RangeExpr, errors: &mut Vec) { )); } } + +fn validate_crate_keyword_in_path_segment( + segment: ast::PathSegment, + errors: &mut Vec, +) { + const ERR_MSG: &str = "The `crate` keyword is only allowed as the first segment of a path"; + + let crate_token = match segment.crate_token() { + None => return, + Some(it) => it, + }; + + // Disallow both ::crate and foo::crate + let path = segment.parent_path(); + if segment.coloncolon_token().is_some() || path.qualifier().is_some() { + errors.push(SyntaxError::new(ERR_MSG, crate_token.text_range())); + return; + } + + // We now know that the path variable describes a complete path. + // For expressions and types, validation is complete, but we still have + // to handle UseItems like this: + // use foo:{crate}; + // so we crawl upwards looking for any preceding paths on `UseTree`s + for node in path.syntax().ancestors().skip(1) { + match_ast! { + match node { + ast::UseTree(it) => if let Some(tree_path) = it.path() { + if tree_path != path { + errors.push(SyntaxError::new(ERR_MSG, crate_token.text_range())); + } + }, + ast::UseTreeList(_it) => continue, + _ => return, + } + }; + } +} 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 @@ +SOURCE_FILE@0..83 + USE_ITEM@0..12 + USE_KW@0..3 "use" + WHITESPACE@3..4 " " + USE_TREE@4..11 + PATH@4..11 + PATH_SEGMENT@4..11 + COLON2@4..6 "::" + CRATE_KW@6..11 "crate" + SEMICOLON@11..12 ";" + WHITESPACE@12..13 "\n" + USE_ITEM@13..39 + USE_KW@13..16 "use" + WHITESPACE@16..17 " " + USE_TREE@17..38 + USE_TREE_LIST@17..38 + L_CURLY@17..18 "{" + USE_TREE@18..23 + PATH@18..23 + PATH_SEGMENT@18..23 + CRATE_KW@18..23 "crate" + COMMA@23..24 "," + WHITESPACE@24..25 " " + USE_TREE@25..37 + PATH@25..28 + PATH_SEGMENT@25..28 + NAME_REF@25..28 + IDENT@25..28 "foo" + COLON2@28..30 "::" + USE_TREE_LIST@30..37 + L_CURLY@30..31 "{" + USE_TREE@31..36 + PATH@31..36 + PATH_SEGMENT@31..36 + CRATE_KW@31..36 "crate" + R_CURLY@36..37 "}" + R_CURLY@37..38 "}" + SEMICOLON@38..39 ";" + WHITESPACE@39..40 "\n" + USE_ITEM@40..57 + USE_KW@40..43 "use" + WHITESPACE@43..44 " " + USE_TREE@44..56 + PATH@44..56 + PATH@44..49 + PATH_SEGMENT@44..49 + NAME_REF@44..49 + IDENT@44..49 "hello" + COLON2@49..51 "::" + PATH_SEGMENT@51..56 + CRATE_KW@51..56 "crate" + SEMICOLON@56..57 ";" + WHITESPACE@57..58 "\n" + USE_ITEM@58..82 + USE_KW@58..61 "use" + WHITESPACE@61..62 " " + USE_TREE@62..81 + PATH@62..81 + PATH@62..74 + PATH@62..67 + PATH_SEGMENT@62..67 + NAME_REF@62..67 + IDENT@62..67 "hello" + COLON2@67..69 "::" + PATH_SEGMENT@69..74 + CRATE_KW@69..74 "crate" + COLON2@74..76 "::" + PATH_SEGMENT@76..81 + NAME_REF@76..81 + IDENT@76..81 "there" + SEMICOLON@81..82 ";" + WHITESPACE@82..83 "\n" +error 6..11: The `crate` keyword is only allowed as the first segment of a path +error 31..36: The `crate` keyword is only allowed as the first segment of a path +error 51..56: The `crate` keyword is only allowed as the first segment of a path +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 @@ +use ::crate; +use {crate, foo::{crate}}; +use hello::crate; +use hello::crate::there; -- cgit v1.2.3