Skip to content

Commit 3bb4604

Browse files
committed
Validate uses of self and super
1 parent 6d49c7d commit 3bb4604

File tree

9 files changed

+178
-75
lines changed

9 files changed

+178
-75
lines changed

crates/ra_syntax/src/ast/generated/nodes.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1251,6 +1251,8 @@ pub struct PathSegment {
12511251
impl PathSegment {
12521252
pub fn coloncolon_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![::]) }
12531253
pub fn crate_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![crate]) }
1254+
pub fn self_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![self]) }
1255+
pub fn super_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![super]) }
12541256
pub fn l_angle_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![<]) }
12551257
pub fn name_ref(&self) -> Option<NameRef> { support::child(&self.syntax) }
12561258
pub fn type_arg_list(&self) -> Option<TypeArgList> { support::child(&self.syntax) }

crates/ra_syntax/src/validation.rs

Lines changed: 71 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ pub(crate) fn validate(root: &SyntaxNode) -> Vec<SyntaxError> {
9696
ast::RecordField(it) => validate_numeric_name(it.name_ref(), &mut errors),
9797
ast::Visibility(it) => validate_visibility(it, &mut errors),
9898
ast::RangeExpr(it) => validate_range_expr(it, &mut errors),
99-
ast::PathSegment(it) => validate_crate_keyword_in_path_segment(it, &mut errors),
99+
ast::PathSegment(it) => validate_path_keywords(it, &mut errors),
100100
_ => (),
101101
}
102102
}
@@ -224,59 +224,82 @@ fn validate_range_expr(expr: ast::RangeExpr, errors: &mut Vec<SyntaxError>) {
224224
}
225225
}
226226

227-
fn validate_crate_keyword_in_path_segment(
228-
segment: ast::PathSegment,
229-
errors: &mut Vec<SyntaxError>,
230-
) {
231-
const ERR_MSG: &str = "The `crate` keyword is only allowed as the first segment of a path";
227+
fn validate_path_keywords(segment: ast::PathSegment, errors: &mut Vec<SyntaxError>) {
228+
use ast::PathSegmentKind;
232229

233-
let crate_token = match segment.crate_token() {
234-
None => return,
235-
Some(it) => it,
236-
};
230+
let path = segment.parent_path();
231+
let is_path_start = segment.coloncolon_token().is_none() && path.qualifier().is_none();
232+
233+
if let Some(token) = segment.self_token() {
234+
if !is_path_start {
235+
errors.push(SyntaxError::new(
236+
"The `self` keyword is only allowed as the first segment of a path",
237+
token.text_range(),
238+
));
239+
}
240+
} else if let Some(token) = segment.crate_token() {
241+
if !is_path_start || use_prefix(path).is_some() {
242+
errors.push(SyntaxError::new(
243+
"The `crate` keyword is only allowed as the first segment of a path",
244+
token.text_range(),
245+
));
246+
}
247+
} else if let Some(token) = segment.super_token() {
248+
if !all_supers(&path) {
249+
errors.push(SyntaxError::new(
250+
"The `super` keyword may only be preceded by other `super`s",
251+
token.text_range(),
252+
));
253+
return;
254+
}
237255

238-
// Disallow both ::crate and foo::crate
239-
let mut path = segment.parent_path();
240-
if segment.coloncolon_token().is_some() || path.qualifier().is_some() {
241-
errors.push(SyntaxError::new(ERR_MSG, crate_token.text_range()));
242-
return;
256+
let mut curr_path = path;
257+
while let Some(prefix) = use_prefix(curr_path) {
258+
if !all_supers(&prefix) {
259+
errors.push(SyntaxError::new(
260+
"The `super` keyword may only be preceded by other `super`s",
261+
token.text_range(),
262+
));
263+
return;
264+
}
265+
curr_path = prefix;
266+
}
243267
}
244268

245-
// For expressions and types, validation is complete, but we still have
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;
269+
fn use_prefix(mut path: ast::Path) -> Option<ast::Path> {
270+
for node in path.syntax().ancestors().skip(1) {
271+
match_ast! {
272+
match node {
273+
ast::UseTree(it) => if let Some(tree_path) = it.path() {
274+
// Even a top-level path exists within a `UseTree` so we must explicitly
275+
// allow our path but disallow anything else
276+
if tree_path != path {
277+
return Some(tree_path);
278+
}
279+
},
280+
ast::UseTreeList(_it) => continue,
281+
ast::Path(parent) => path = parent,
282+
_ => return None,
283+
}
284+
};
285+
}
286+
return None;
260287
}
261288

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}}}}};
267-
for node in path.syntax().ancestors().skip(1) {
268-
match_ast! {
269-
match node {
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
273-
if tree_path != path {
274-
errors.push(SyntaxError::new(ERR_MSG, crate_token.text_range()));
275-
}
276-
},
277-
ast::UseTreeList(_it) => continue,
278-
_ => return,
279-
}
289+
fn all_supers(path: &ast::Path) -> bool {
290+
let segment = match path.segment() {
291+
Some(it) => it,
292+
None => return false,
280293
};
294+
295+
if segment.kind() != Some(PathSegmentKind::SuperKw) {
296+
return false;
297+
}
298+
299+
if let Some(ref subpath) = path.qualifier() {
300+
return all_supers(subpath);
301+
}
302+
303+
return true;
281304
}
282305
}
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
2+
3+
4+
5+
6+
7+
8+
9+
10+
11+
12+
13+
14+
15+
16+
17+
18+
19+
20+
21+
22+
23+
24+
25+
26+
27+
28+
29+
30+
31+
32+
33+
34+
35+
36+
37+
38+
39+
40+
41+
42+
43+
44+
45+
46+
47+
48+
49+
50+
51+
52+
53+
54+
55+
56+
57+
58+
59+
60+
61+
62+
63+
64+
65+
66+
67+
error 6..11: The `super` keyword may only be preceded by other `super`s
68+
error 20..25: The `super` keyword may only be preceded by other `super`s
69+
error 41..46: The `super` keyword may only be preceded by other `super`s
70+
error 56..61: The `super` keyword may only be preceded by other `super`s
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
use ::super;
2+
use a::super;
3+
use super::a::super;
4+
use a::{super::b};
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
2+
3+
4+
5+
6+
7+
8+
9+
10+
11+
12+
13+
14+
15+
16+
17+
18+
19+
20+
21+
22+
23+
24+
25+
26+
error 6..10: The `self` keyword is only allowed as the first segment of a path
27+
error 19..23: The `self` keyword is only allowed as the first segment of a path
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
use ::self;
2+
use a::self;
Lines changed: 1 addition & 25 deletions
Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,2 @@
11
use self::foo;
22
use super::super::bar;
3-
use ::self::a::super::bar;

xtask/src/ast_src.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -595,7 +595,7 @@ pub(crate) const AST_SRC: AstSrc = AstSrc {
595595
qualifier: Path,
596596
}
597597
struct PathSegment {
598-
T![::], T![crate], T![<], NameRef, TypeArgList, ParamList, RetType, PathType, T![>]
598+
T![::], T![crate], T![self], T![super], T![<], NameRef, TypeArgList, ParamList, RetType, PathType, T![>]
599599
}
600600
struct TypeArgList {
601601
T![::],

0 commit comments

Comments
 (0)