From 3bb46042fb5b8ee421e350c54079cb68b4edc996 Mon Sep 17 00:00:00 2001 From: John Renner Date: Fri, 1 May 2020 08:59:24 -0700 Subject: Validate uses of self and super --- crates/ra_syntax/src/ast/generated/nodes.rs | 2 + crates/ra_syntax/src/validation.rs | 119 +++++++++++++++++----------- 2 files changed, 73 insertions(+), 48 deletions(-) (limited to 'crates/ra_syntax/src') diff --git a/crates/ra_syntax/src/ast/generated/nodes.rs b/crates/ra_syntax/src/ast/generated/nodes.rs index d2253d4af..2096f12f1 100644 --- a/crates/ra_syntax/src/ast/generated/nodes.rs +++ b/crates/ra_syntax/src/ast/generated/nodes.rs @@ -1251,6 +1251,8 @@ 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 self_token(&self) -> Option { support::token(&self.syntax, T![self]) } + pub fn super_token(&self) -> Option { support::token(&self.syntax, T![super]) } 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 f0b3dec63..e075cd801 100644 --- a/crates/ra_syntax/src/validation.rs +++ b/crates/ra_syntax/src/validation.rs @@ -96,7 +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), + ast::PathSegment(it) => validate_path_keywords(it, &mut errors), _ => (), } } @@ -224,59 +224,82 @@ 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"; +fn validate_path_keywords(segment: ast::PathSegment, errors: &mut Vec) { + use ast::PathSegmentKind; - let crate_token = match segment.crate_token() { - None => return, - Some(it) => it, - }; + let path = segment.parent_path(); + let is_path_start = segment.coloncolon_token().is_none() && path.qualifier().is_none(); + + if let Some(token) = segment.self_token() { + if !is_path_start { + errors.push(SyntaxError::new( + "The `self` keyword is only allowed as the first segment of a path", + token.text_range(), + )); + } + } else if let Some(token) = segment.crate_token() { + if !is_path_start || use_prefix(path).is_some() { + errors.push(SyntaxError::new( + "The `crate` keyword is only allowed as the first segment of a path", + token.text_range(), + )); + } + } else if let Some(token) = segment.super_token() { + if !all_supers(&path) { + errors.push(SyntaxError::new( + "The `super` keyword may only be preceded by other `super`s", + token.text_range(), + )); + return; + } - // Disallow both ::crate and foo::crate - let mut 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; + let mut curr_path = path; + while let Some(prefix) = use_prefix(curr_path) { + if !all_supers(&prefix) { + errors.push(SyntaxError::new( + "The `super` keyword may only be preceded by other `super`s", + token.text_range(), + )); + return; + } + curr_path = prefix; + } } - // For expressions and types, validation is complete, but we still have - // to handle invalid UseItems like this: - // - // use foo:{crate::bar::baz}; - // - // To handle this we must inspect the parent `UseItem`s and `UseTree`s - // but right now we're looking deep inside the nested `Path` nodes because - // `Path`s are left-associative: - // - // ((crate)::bar)::baz) - // ^ current value of path - // - // So we need to climb to the top - while let Some(parent) = path.parent_path() { - path = parent; + fn use_prefix(mut path: ast::Path) -> Option { + for node in path.syntax().ancestors().skip(1) { + match_ast! { + match node { + ast::UseTree(it) => if let Some(tree_path) = it.path() { + // Even a top-level path exists within a `UseTree` so we must explicitly + // allow our path but disallow anything else + if tree_path != path { + return Some(tree_path); + } + }, + ast::UseTreeList(_it) => continue, + ast::Path(parent) => path = parent, + _ => return None, + } + }; + } + return None; } - // Now that we've found the whole path we need to see if there's a prefix - // somewhere in the UseTree hierarchy. This check is arbitrarily deep - // because rust allows arbitrary nesting like so: - // - // use {foo::{{{{crate::bar::baz}}}}}; - for node in path.syntax().ancestors().skip(1) { - match_ast! { - match node { - ast::UseTree(it) => if let Some(tree_path) = it.path() { - // Even a top-level path exists within a `UseTree` so we must explicitly - // allow our path but disallow anything else - if tree_path != path { - errors.push(SyntaxError::new(ERR_MSG, crate_token.text_range())); - } - }, - ast::UseTreeList(_it) => continue, - _ => return, - } + fn all_supers(path: &ast::Path) -> bool { + let segment = match path.segment() { + Some(it) => it, + None => return false, }; + + if segment.kind() != Some(PathSegmentKind::SuperKw) { + return false; + } + + if let Some(ref subpath) = path.qualifier() { + return all_supers(subpath); + } + + return true; } } -- cgit v1.2.3