Unnamed repository; edit this file 'description' to name the repository.
-rw-r--r--crates/ide-completion/src/context/analysis.rs4
-rw-r--r--crates/parser/src/grammar.rs85
-rw-r--r--crates/parser/src/grammar/items.rs19
-rw-r--r--crates/parser/src/grammar/items/adt.rs37
-rw-r--r--crates/parser/src/syntax_kind/generated.rs6
-rw-r--r--crates/parser/test_data/generated/runner.rs16
-rw-r--r--crates/parser/test_data/parser/inline/err/crate_visibility_empty_recover.rast5
-rw-r--r--crates/parser/test_data/parser/inline/ok/crate_visibility.rast39
-rw-r--r--crates/parser/test_data/parser/inline/ok/crate_visibility_in.rast38
-rw-r--r--crates/parser/test_data/parser/inline/ok/impl_restrictions.rast80
-rw-r--r--crates/parser/test_data/parser/inline/ok/impl_restrictions.rs4
-rw-r--r--crates/parser/test_data/parser/inline/ok/record_mut_restrictions_after.rast35
-rw-r--r--crates/parser/test_data/parser/inline/ok/record_mut_restrictions_after.rs1
-rw-r--r--crates/parser/test_data/parser/inline/ok/record_mut_restrictions_before.rast35
-rw-r--r--crates/parser/test_data/parser/inline/ok/record_mut_restrictions_before.rs1
-rw-r--r--crates/parser/test_data/parser/inline/ok/tuple_mut_restrictions.rast37
-rw-r--r--crates/parser/test_data/parser/inline/ok/tuple_mut_restrictions.rs1
-rw-r--r--crates/parser/test_data/parser/ok/0012_visibility.rast49
-rw-r--r--crates/syntax/rust.ungram13
-rw-r--r--crates/syntax/src/ast/generated/nodes.rs146
-rw-r--r--crates/syntax/src/ast/node_ext.rs9
-rw-r--r--crates/syntax/src/validation.rs12
-rw-r--r--crates/syntax/test_data/parser/validation/0037_visibility_in_traits.rast26
23 files changed, 581 insertions, 117 deletions
diff --git a/crates/ide-completion/src/context/analysis.rs b/crates/ide-completion/src/context/analysis.rs
index 2a293313f2..60f5dabefc 100644
--- a/crates/ide-completion/src/context/analysis.rs
+++ b/crates/ide-completion/src/context/analysis.rs
@@ -1592,7 +1592,7 @@ fn classify_name_ref<'db>(
kind_macro_call(it)?
},
ast::Meta(meta) => make_path_kind_attr(meta)?,
- ast::Visibility(it) => PathKind::Vis { has_in_token: it.in_token().is_some() },
+ ast::VisibilityInner(it) => PathKind::Vis { has_in_token: it.in_token().is_some() },
ast::UseTree(_) => PathKind::Use,
// completing inside a qualifier
ast::Path(parent) => {
@@ -1621,7 +1621,7 @@ fn classify_name_ref<'db>(
kind_macro_call(it)?
},
ast::Meta(meta) => make_path_kind_attr(meta)?,
- ast::Visibility(it) => PathKind::Vis { has_in_token: it.in_token().is_some() },
+ ast::VisibilityInner(it) => PathKind::Vis { has_in_token: it.in_token().is_some() },
ast::UseTree(_) => PathKind::Use,
ast::RecordExpr(it) => make_path_kind_expr(it.into()),
_ => return None,
diff --git a/crates/parser/src/grammar.rs b/crates/parser/src/grammar.rs
index 1ff8a56b58..e8a9ddde69 100644
--- a/crates/parser/src/grammar.rs
+++ b/crates/parser/src/grammar.rs
@@ -228,48 +228,61 @@ fn opt_visibility(p: &mut Parser<'_>, in_tuple_field: bool) -> bool {
let m = p.start();
p.bump(T![pub]);
- if p.at(T!['(']) {
- match p.nth(1) {
- // test crate_visibility
- // pub(crate) struct S;
- // pub(self) struct S;
- // pub(super) struct S;
-
- // test_err crate_visibility_empty_recover
- // pub() struct S;
-
- // test pub_parens_typepath
- // struct B(pub (super::A));
- // struct B(pub (crate::A,));
- T![crate] | T![self] | T![super] | T![ident] | T![')'] if p.nth(2) != T![:] => {
- // If we are in a tuple struct, then the parens following `pub`
- // might be an tuple field, not part of the visibility. So in that
- // case we don't want to consume an identifier.
-
- // test pub_tuple_field
- // struct MyStruct(pub (u32, u32));
- // struct MyStruct(pub (u32));
- // struct MyStruct(pub ());
- if !(in_tuple_field && matches!(p.nth(1), T![ident] | T![')'])) {
- p.bump(T!['(']);
- paths::vis_path(p);
- p.expect(T![')']);
- }
- }
- // test crate_visibility_in
- // pub(in super::A) struct S;
- // pub(in crate) struct S;
- T![in] => {
+ opt_visibility_inner(p, in_tuple_field);
+ m.complete(p, VISIBILITY);
+ true
+}
+
+fn opt_visibility_inner(p: &mut Parser<'_>, in_tuple_field: bool) -> bool {
+ if !p.at(T!['(']) {
+ return false;
+ }
+
+ match p.nth(1) {
+ // test crate_visibility
+ // pub(crate) struct S;
+ // pub(self) struct S;
+ // pub(super) struct S;
+
+ // test_err crate_visibility_empty_recover
+ // pub() struct S;
+
+ // test pub_parens_typepath
+ // struct B(pub (super::A));
+ // struct B(pub (crate::A,));
+ T![crate] | T![self] | T![super] | T![ident] | T![')'] if p.nth(2) != T![:] => {
+ // If we are in a tuple struct, then the parens following `pub`
+ // might be an tuple field, not part of the visibility. So in that
+ // case we don't want to consume an identifier.
+
+ // test pub_tuple_field
+ // struct MyStruct(pub (u32, u32));
+ // struct MyStruct(pub (u32));
+ // struct MyStruct(pub ());
+ if !(in_tuple_field && matches!(p.nth(1), T![ident] | T![')'])) {
+ let m = p.start();
p.bump(T!['(']);
- p.bump(T![in]);
paths::vis_path(p);
p.expect(T![')']);
+ m.complete(p, VISIBILITY_INNER);
+ return true;
}
- _ => {}
}
+ // test crate_visibility_in
+ // pub(in super::A) struct S;
+ // pub(in crate) struct S;
+ T![in] => {
+ let m = p.start();
+ p.bump(T!['(']);
+ p.bump(T![in]);
+ paths::vis_path(p);
+ p.expect(T![')']);
+ m.complete(p, VISIBILITY_INNER);
+ return true;
+ }
+ _ => {}
}
- m.complete(p, VISIBILITY);
- true
+ false
}
fn opt_rename(p: &mut Parser<'_>) {
diff --git a/crates/parser/src/grammar/items.rs b/crates/parser/src/grammar/items.rs
index c0acdde2a7..6c46fac290 100644
--- a/crates/parser/src/grammar/items.rs
+++ b/crates/parser/src/grammar/items.rs
@@ -167,6 +167,25 @@ pub(super) fn opt_item(p: &mut Parser<'_>, m: Marker, is_in_extern: bool) -> Res
has_mods = true;
}
+ if p.at(T![impl])
+ && p.nth(1) == T!['(']
+ && ((matches!(p.nth(2), T![crate] | T![super] | T![self]) && p.nth(3) == T![')'])
+ || p.nth(2) == T![in])
+ {
+ // test impl_restrictions
+ // pub unsafe impl(crate) trait Foo {}
+ // impl(in super::bar) trait Bar {}
+ // impl () {}
+ // impl (i32) {}
+ let m = p.start();
+ p.bump(T![impl]);
+ if !opt_visibility_inner(p, false) {
+ p.error("expected an impl restriction");
+ }
+ m.complete(p, IMPL_RESTRICTION);
+ has_mods = true;
+ }
+
// test default_item
// default impl T for Foo {}
if p.at_contextual_kw(T![default]) {
diff --git a/crates/parser/src/grammar/items/adt.rs b/crates/parser/src/grammar/items/adt.rs
index cfba4c3a77..a030190ad3 100644
--- a/crates/parser/src/grammar/items/adt.rs
+++ b/crates/parser/src/grammar/items/adt.rs
@@ -133,7 +133,30 @@ pub(crate) fn record_field_list(p: &mut Parser<'_>) {
// struct S { #[attr] f: f32 }
attributes::outer_attrs(p);
opt_visibility(p, false);
- p.eat(T![unsafe]);
+
+ if p.at(T![mut]) && p.nth(1) == T!['('] {
+ // test record_mut_restrictions_before
+ // struct Foo { mut(super) unsafe i: i32 }
+ let m = p.start();
+ p.bump(T![mut]);
+ if !opt_visibility_inner(p, false) {
+ p.error("expected a mut restriction");
+ }
+ m.complete(p, MUT_RESTRICTION);
+ }
+
+ // We accept mut restriction both after and before `unsafe`, as the order is undecided yet.
+ if p.eat(T![unsafe]) && p.at(T![mut]) && p.nth(1) == T!['('] {
+ // test record_mut_restrictions_after
+ // struct Foo { unsafe mut(super) i: i32 }
+ let m = p.start();
+ p.bump(T![mut]);
+ if !opt_visibility_inner(p, false) {
+ p.error("expected a mut restriction");
+ }
+ m.complete(p, MUT_RESTRICTION);
+ }
+
if p.at(IDENT) {
name(p);
p.expect(T![:]);
@@ -175,6 +198,18 @@ fn tuple_field_list(p: &mut Parser<'_>) {
// struct S (#[attr] f32);
attributes::outer_attrs(p);
let has_vis = opt_visibility(p, true);
+
+ if p.at(T![mut]) && p.nth(1) == T!['('] {
+ // test tuple_mut_restrictions
+ // struct Foo(pub(crate) mut(super) i32);
+ let m = p.start();
+ p.bump(T![mut]);
+ if !opt_visibility_inner(p, false) {
+ p.error("expected a mut restriction");
+ }
+ m.complete(p, MUT_RESTRICTION);
+ }
+
if !p.at_ts(types::TYPE_FIRST) {
p.error("expected a type");
if has_vis {
diff --git a/crates/parser/src/syntax_kind/generated.rs b/crates/parser/src/syntax_kind/generated.rs
index 9cd48f2aa4..59fa3ee773 100644
--- a/crates/parser/src/syntax_kind/generated.rs
+++ b/crates/parser/src/syntax_kind/generated.rs
@@ -218,6 +218,7 @@ pub enum SyntaxKind {
IDENT_PAT,
IF_EXPR,
IMPL,
+ IMPL_RESTRICTION,
IMPL_TRAIT_TYPE,
INDEX_EXPR,
INFER_TYPE,
@@ -247,6 +248,7 @@ pub enum SyntaxKind {
MATCH_GUARD,
METHOD_CALL_EXPR,
MODULE,
+ MUT_RESTRICTION,
NAME,
NAME_REF,
NEVER_TYPE,
@@ -318,6 +320,7 @@ pub enum SyntaxKind {
VARIANT,
VARIANT_LIST,
VISIBILITY,
+ VISIBILITY_INNER,
WHERE_CLAUSE,
WHERE_PRED,
WHILE_EXPR,
@@ -399,6 +402,7 @@ impl SyntaxKind {
| IDENT_PAT
| IF_EXPR
| IMPL
+ | IMPL_RESTRICTION
| IMPL_TRAIT_TYPE
| INDEX_EXPR
| INFER_TYPE
@@ -428,6 +432,7 @@ impl SyntaxKind {
| MATCH_GUARD
| METHOD_CALL_EXPR
| MODULE
+ | MUT_RESTRICTION
| NAME
| NAME_REF
| NEVER_TYPE
@@ -499,6 +504,7 @@ impl SyntaxKind {
| VARIANT
| VARIANT_LIST
| VISIBILITY
+ | VISIBILITY_INNER
| WHERE_CLAUSE
| WHERE_PRED
| WHILE_EXPR
diff --git a/crates/parser/test_data/generated/runner.rs b/crates/parser/test_data/generated/runner.rs
index 71978390df..10b1f85d39 100644
--- a/crates/parser/test_data/generated/runner.rs
+++ b/crates/parser/test_data/generated/runner.rs
@@ -348,6 +348,10 @@ mod ok {
run_and_expect_no_errors("test_data/parser/inline/ok/impl_item_never_type.rs");
}
#[test]
+ fn impl_restrictions() {
+ run_and_expect_no_errors("test_data/parser/inline/ok/impl_restrictions.rs");
+ }
+ #[test]
fn impl_trait_type() {
run_and_expect_no_errors("test_data/parser/inline/ok/impl_trait_type.rs");
}
@@ -556,6 +560,14 @@ mod ok {
run_and_expect_no_errors("test_data/parser/inline/ok/record_literal_field_with_attr.rs");
}
#[test]
+ fn record_mut_restrictions_after() {
+ run_and_expect_no_errors("test_data/parser/inline/ok/record_mut_restrictions_after.rs");
+ }
+ #[test]
+ fn record_mut_restrictions_before() {
+ run_and_expect_no_errors("test_data/parser/inline/ok/record_mut_restrictions_before.rs");
+ }
+ #[test]
fn record_pat_field() {
run_and_expect_no_errors("test_data/parser/inline/ok/record_pat_field.rs");
}
@@ -658,6 +670,10 @@ mod ok {
run_and_expect_no_errors("test_data/parser/inline/ok/tuple_field_attrs.rs");
}
#[test]
+ fn tuple_mut_restrictions() {
+ run_and_expect_no_errors("test_data/parser/inline/ok/tuple_mut_restrictions.rs");
+ }
+ #[test]
fn tuple_pat() { run_and_expect_no_errors("test_data/parser/inline/ok/tuple_pat.rs"); }
#[test]
fn tuple_pat_fields() {
diff --git a/crates/parser/test_data/parser/inline/err/crate_visibility_empty_recover.rast b/crates/parser/test_data/parser/inline/err/crate_visibility_empty_recover.rast
index 172bc099b5..37116ca895 100644
--- a/crates/parser/test_data/parser/inline/err/crate_visibility_empty_recover.rast
+++ b/crates/parser/test_data/parser/inline/err/crate_visibility_empty_recover.rast
@@ -2,8 +2,9 @@ SOURCE_FILE
STRUCT
VISIBILITY
PUB_KW "pub"
- L_PAREN "("
- R_PAREN ")"
+ VISIBILITY_INNER
+ L_PAREN "("
+ R_PAREN ")"
WHITESPACE " "
STRUCT_KW "struct"
WHITESPACE " "
diff --git a/crates/parser/test_data/parser/inline/ok/crate_visibility.rast b/crates/parser/test_data/parser/inline/ok/crate_visibility.rast
index 8738292a9f..c946f19321 100644
--- a/crates/parser/test_data/parser/inline/ok/crate_visibility.rast
+++ b/crates/parser/test_data/parser/inline/ok/crate_visibility.rast
@@ -2,12 +2,13 @@ SOURCE_FILE
STRUCT
VISIBILITY
PUB_KW "pub"
- L_PAREN "("
- PATH
- PATH_SEGMENT
- NAME_REF
- CRATE_KW "crate"
- R_PAREN ")"
+ VISIBILITY_INNER
+ L_PAREN "("
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ CRATE_KW "crate"
+ R_PAREN ")"
WHITESPACE " "
STRUCT_KW "struct"
WHITESPACE " "
@@ -18,12 +19,13 @@ SOURCE_FILE
STRUCT
VISIBILITY
PUB_KW "pub"
- L_PAREN "("
- PATH
- PATH_SEGMENT
- NAME_REF
- SELF_KW "self"
- R_PAREN ")"
+ VISIBILITY_INNER
+ L_PAREN "("
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ SELF_KW "self"
+ R_PAREN ")"
WHITESPACE " "
STRUCT_KW "struct"
WHITESPACE " "
@@ -34,12 +36,13 @@ SOURCE_FILE
STRUCT
VISIBILITY
PUB_KW "pub"
- L_PAREN "("
- PATH
- PATH_SEGMENT
- NAME_REF
- SUPER_KW "super"
- R_PAREN ")"
+ VISIBILITY_INNER
+ L_PAREN "("
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ SUPER_KW "super"
+ R_PAREN ")"
WHITESPACE " "
STRUCT_KW "struct"
WHITESPACE " "
diff --git a/crates/parser/test_data/parser/inline/ok/crate_visibility_in.rast b/crates/parser/test_data/parser/inline/ok/crate_visibility_in.rast
index ac45c56956..1a551ea221 100644
--- a/crates/parser/test_data/parser/inline/ok/crate_visibility_in.rast
+++ b/crates/parser/test_data/parser/inline/ok/crate_visibility_in.rast
@@ -2,19 +2,20 @@ SOURCE_FILE
STRUCT
VISIBILITY
PUB_KW "pub"
- L_PAREN "("
- IN_KW "in"
- WHITESPACE " "
- PATH
+ VISIBILITY_INNER
+ L_PAREN "("
+ IN_KW "in"
+ WHITESPACE " "
PATH
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ SUPER_KW "super"
+ COLON2 "::"
PATH_SEGMENT
NAME_REF
- SUPER_KW "super"
- COLON2 "::"
- PATH_SEGMENT
- NAME_REF
- IDENT "A"
- R_PAREN ")"
+ IDENT "A"
+ R_PAREN ")"
WHITESPACE " "
STRUCT_KW "struct"
WHITESPACE " "
@@ -25,14 +26,15 @@ SOURCE_FILE
STRUCT
VISIBILITY
PUB_KW "pub"
- L_PAREN "("
- IN_KW "in"
- WHITESPACE " "
- PATH
- PATH_SEGMENT
- NAME_REF
- CRATE_KW "crate"
- R_PAREN ")"
+ VISIBILITY_INNER
+ L_PAREN "("
+ IN_KW "in"
+ WHITESPACE " "
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ CRATE_KW "crate"
+ R_PAREN ")"
WHITESPACE " "
STRUCT_KW "struct"
WHITESPACE " "
diff --git a/crates/parser/test_data/parser/inline/ok/impl_restrictions.rast b/crates/parser/test_data/parser/inline/ok/impl_restrictions.rast
new file mode 100644
index 0000000000..5f2680cbaa
--- /dev/null
+++ b/crates/parser/test_data/parser/inline/ok/impl_restrictions.rast
@@ -0,0 +1,80 @@
+SOURCE_FILE
+ TRAIT
+ VISIBILITY
+ PUB_KW "pub"
+ WHITESPACE " "
+ UNSAFE_KW "unsafe"
+ WHITESPACE " "
+ IMPL_RESTRICTION
+ IMPL_KW "impl"
+ VISIBILITY_INNER
+ L_PAREN "("
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ CRATE_KW "crate"
+ R_PAREN ")"
+ WHITESPACE " "
+ TRAIT_KW "trait"
+ WHITESPACE " "
+ NAME
+ IDENT "Foo"
+ WHITESPACE " "
+ ASSOC_ITEM_LIST
+ L_CURLY "{"
+ R_CURLY "}"
+ WHITESPACE "\n"
+ TRAIT
+ IMPL_RESTRICTION
+ IMPL_KW "impl"
+ VISIBILITY_INNER
+ L_PAREN "("
+ IN_KW "in"
+ WHITESPACE " "
+ PATH
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ SUPER_KW "super"
+ COLON2 "::"
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "bar"
+ R_PAREN ")"
+ WHITESPACE " "
+ TRAIT_KW "trait"
+ WHITESPACE " "
+ NAME
+ IDENT "Bar"
+ WHITESPACE " "
+ ASSOC_ITEM_LIST
+ L_CURLY "{"
+ R_CURLY "}"
+ WHITESPACE "\n"
+ IMPL
+ IMPL_KW "impl"
+ WHITESPACE " "
+ TUPLE_TYPE
+ L_PAREN "("
+ R_PAREN ")"
+ WHITESPACE " "
+ ASSOC_ITEM_LIST
+ L_CURLY "{"
+ R_CURLY "}"
+ WHITESPACE "\n"
+ IMPL
+ IMPL_KW "impl"
+ WHITESPACE " "
+ PAREN_TYPE
+ L_PAREN "("
+ PATH_TYPE
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "i32"
+ R_PAREN ")"
+ WHITESPACE " "
+ ASSOC_ITEM_LIST
+ L_CURLY "{"
+ R_CURLY "}"
+ WHITESPACE "\n"
diff --git a/crates/parser/test_data/parser/inline/ok/impl_restrictions.rs b/crates/parser/test_data/parser/inline/ok/impl_restrictions.rs
new file mode 100644
index 0000000000..0a46b158af
--- /dev/null
+++ b/crates/parser/test_data/parser/inline/ok/impl_restrictions.rs
@@ -0,0 +1,4 @@
+pub unsafe impl(crate) trait Foo {}
+impl(in super::bar) trait Bar {}
+impl () {}
+impl (i32) {}
diff --git a/crates/parser/test_data/parser/inline/ok/record_mut_restrictions_after.rast b/crates/parser/test_data/parser/inline/ok/record_mut_restrictions_after.rast
new file mode 100644
index 0000000000..ebe3c81468
--- /dev/null
+++ b/crates/parser/test_data/parser/inline/ok/record_mut_restrictions_after.rast
@@ -0,0 +1,35 @@
+SOURCE_FILE
+ STRUCT
+ STRUCT_KW "struct"
+ WHITESPACE " "
+ NAME
+ IDENT "Foo"
+ WHITESPACE " "
+ RECORD_FIELD_LIST
+ L_CURLY "{"
+ WHITESPACE " "
+ RECORD_FIELD
+ UNSAFE_KW "unsafe"
+ WHITESPACE " "
+ MUT_RESTRICTION
+ MUT_KW "mut"
+ VISIBILITY_INNER
+ L_PAREN "("
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ SUPER_KW "super"
+ R_PAREN ")"
+ WHITESPACE " "
+ NAME
+ IDENT "i"
+ COLON ":"
+ WHITESPACE " "
+ PATH_TYPE
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "i32"
+ WHITESPACE " "
+ R_CURLY "}"
+ WHITESPACE "\n"
diff --git a/crates/parser/test_data/parser/inline/ok/record_mut_restrictions_after.rs b/crates/parser/test_data/parser/inline/ok/record_mut_restrictions_after.rs
new file mode 100644
index 0000000000..bf37f60f85
--- /dev/null
+++ b/crates/parser/test_data/parser/inline/ok/record_mut_restrictions_after.rs
@@ -0,0 +1 @@
+struct Foo { unsafe mut(super) i: i32 }
diff --git a/crates/parser/test_data/parser/inline/ok/record_mut_restrictions_before.rast b/crates/parser/test_data/parser/inline/ok/record_mut_restrictions_before.rast
new file mode 100644
index 0000000000..7f76e737f7
--- /dev/null
+++ b/crates/parser/test_data/parser/inline/ok/record_mut_restrictions_before.rast
@@ -0,0 +1,35 @@
+SOURCE_FILE
+ STRUCT
+ STRUCT_KW "struct"
+ WHITESPACE " "
+ NAME
+ IDENT "Foo"
+ WHITESPACE " "
+ RECORD_FIELD_LIST
+ L_CURLY "{"
+ WHITESPACE " "
+ RECORD_FIELD
+ MUT_RESTRICTION
+ MUT_KW "mut"
+ VISIBILITY_INNER
+ L_PAREN "("
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ SUPER_KW "super"
+ R_PAREN ")"
+ WHITESPACE " "
+ UNSAFE_KW "unsafe"
+ WHITESPACE " "
+ NAME
+ IDENT "i"
+ COLON ":"
+ WHITESPACE " "
+ PATH_TYPE
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "i32"
+ WHITESPACE " "
+ R_CURLY "}"
+ WHITESPACE "\n"
diff --git a/crates/parser/test_data/parser/inline/ok/record_mut_restrictions_before.rs b/crates/parser/test_data/parser/inline/ok/record_mut_restrictions_before.rs
new file mode 100644
index 0000000000..9bbb80f205
--- /dev/null
+++ b/crates/parser/test_data/parser/inline/ok/record_mut_restrictions_before.rs
@@ -0,0 +1 @@
+struct Foo { mut(super) unsafe i: i32 }
diff --git a/crates/parser/test_data/parser/inline/ok/tuple_mut_restrictions.rast b/crates/parser/test_data/parser/inline/ok/tuple_mut_restrictions.rast
new file mode 100644
index 0000000000..944133ff85
--- /dev/null
+++ b/crates/parser/test_data/parser/inline/ok/tuple_mut_restrictions.rast
@@ -0,0 +1,37 @@
+SOURCE_FILE
+ STRUCT
+ STRUCT_KW "struct"
+ WHITESPACE " "
+ NAME
+ IDENT "Foo"
+ TUPLE_FIELD_LIST
+ L_PAREN "("
+ TUPLE_FIELD
+ VISIBILITY
+ PUB_KW "pub"
+ VISIBILITY_INNER
+ L_PAREN "("
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ CRATE_KW "crate"
+ R_PAREN ")"
+ WHITESPACE " "
+ MUT_RESTRICTION
+ MUT_KW "mut"
+ VISIBILITY_INNER
+ L_PAREN "("
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ SUPER_KW "super"
+ R_PAREN ")"
+ WHITESPACE " "
+ PATH_TYPE
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "i32"
+ R_PAREN ")"
+ SEMICOLON ";"
+ WHITESPACE "\n"
diff --git a/crates/parser/test_data/parser/inline/ok/tuple_mut_restrictions.rs b/crates/parser/test_data/parser/inline/ok/tuple_mut_restrictions.rs
new file mode 100644
index 0000000000..42653b0043
--- /dev/null
+++ b/crates/parser/test_data/parser/inline/ok/tuple_mut_restrictions.rs
@@ -0,0 +1 @@
+struct Foo(pub(crate) mut(super) i32);
diff --git a/crates/parser/test_data/parser/ok/0012_visibility.rast b/crates/parser/test_data/parser/ok/0012_visibility.rast
index 3d9322947b..348498daa9 100644
--- a/crates/parser/test_data/parser/ok/0012_visibility.rast
+++ b/crates/parser/test_data/parser/ok/0012_visibility.rast
@@ -52,12 +52,13 @@ SOURCE_FILE
FN
VISIBILITY
PUB_KW "pub"
- L_PAREN "("
- PATH
- PATH_SEGMENT
- NAME_REF
- CRATE_KW "crate"
- R_PAREN ")"
+ VISIBILITY_INNER
+ L_PAREN "("
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ CRATE_KW "crate"
+ R_PAREN ")"
WHITESPACE " "
FN_KW "fn"
WHITESPACE " "
@@ -75,12 +76,13 @@ SOURCE_FILE
FN
VISIBILITY
PUB_KW "pub"
- L_PAREN "("
- PATH
- PATH_SEGMENT
- NAME_REF
- SUPER_KW "super"
- R_PAREN ")"
+ VISIBILITY_INNER
+ L_PAREN "("
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ SUPER_KW "super"
+ R_PAREN ")"
WHITESPACE " "
FN_KW "fn"
WHITESPACE " "
@@ -98,24 +100,25 @@ SOURCE_FILE
FN
VISIBILITY
PUB_KW "pub"
- L_PAREN "("
- IN_KW "in"
- WHITESPACE " "
- PATH
+ VISIBILITY_INNER
+ L_PAREN "("
+ IN_KW "in"
+ WHITESPACE " "
PATH
PATH
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "foo"
+ COLON2 "::"
PATH_SEGMENT
NAME_REF
- IDENT "foo"
+ IDENT "bar"
COLON2 "::"
PATH_SEGMENT
NAME_REF
- IDENT "bar"
- COLON2 "::"
- PATH_SEGMENT
- NAME_REF
- IDENT "baz"
- R_PAREN ")"
+ IDENT "baz"
+ R_PAREN ")"
WHITESPACE " "
FN_KW "fn"
WHITESPACE " "
diff --git a/crates/syntax/rust.ungram b/crates/syntax/rust.ungram
index 324b2bbd58..caf92aca87 100644
--- a/crates/syntax/rust.ungram
+++ b/crates/syntax/rust.ungram
@@ -277,6 +277,7 @@ RecordFieldList =
RecordField =
Attr* Visibility? 'unsafe'?
+ MutRestriction?
Name ':' Type ('=' default_val:ConstArg)?
TupleFieldList =
@@ -284,12 +285,16 @@ TupleFieldList =
TupleField =
Attr* Visibility?
+ MutRestriction?
Type
FieldList =
RecordFieldList
| TupleFieldList
+MutRestriction =
+ 'mut' VisibilityInner
+
Enum =
Attr* Visibility?
'enum' Name GenericParamList? WhereClause?
@@ -336,10 +341,14 @@ Static =
Trait =
Attr* Visibility?
'unsafe'? 'auto'?
+ ImplRestriction?
'trait' Name GenericParamList?
(((':' TypeBoundList?)? WhereClause? AssocItemList) |
('=' TypeBoundList? WhereClause? ';'))
+ImplRestriction =
+ 'impl' VisibilityInner
+
AssocItemList =
'{' Attr* AssocItem* '}'
@@ -368,8 +377,10 @@ ExternItem =
| TypeAlias
Visibility =
- 'pub' ('(' 'in'? Path ')')?
+ 'pub' VisibilityInner?
+VisibilityInner =
+ '(' 'in'? Path ')'
//****************************//
// Statements and Expressions //
diff --git a/crates/syntax/src/ast/generated/nodes.rs b/crates/syntax/src/ast/generated/nodes.rs
index cd7f6a018a..69fd703c03 100644
--- a/crates/syntax/src/ast/generated/nodes.rs
+++ b/crates/syntax/src/ast/generated/nodes.rs
@@ -808,6 +808,15 @@ impl Impl {
#[inline]
pub fn unsafe_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![unsafe]) }
}
+pub struct ImplRestriction {
+ pub(crate) syntax: SyntaxNode,
+}
+impl ImplRestriction {
+ #[inline]
+ pub fn visibility_inner(&self) -> Option<VisibilityInner> { support::child(&self.syntax) }
+ #[inline]
+ pub fn impl_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![impl]) }
+}
pub struct ImplTraitType {
pub(crate) syntax: SyntaxNode,
}
@@ -1114,6 +1123,15 @@ impl Module {
#[inline]
pub fn mod_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![mod]) }
}
+pub struct MutRestriction {
+ pub(crate) syntax: SyntaxNode,
+}
+impl MutRestriction {
+ #[inline]
+ pub fn visibility_inner(&self) -> Option<VisibilityInner> { support::child(&self.syntax) }
+ #[inline]
+ pub fn mut_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![mut]) }
+}
pub struct Name {
pub(crate) syntax: SyntaxNode,
}
@@ -1400,6 +1418,8 @@ impl RecordField {
#[inline]
pub fn default_val(&self) -> Option<ConstArg> { support::child(&self.syntax) }
#[inline]
+ pub fn mut_restriction(&self) -> Option<MutRestriction> { support::child(&self.syntax) }
+ #[inline]
pub fn ty(&self) -> Option<Type> { support::child(&self.syntax) }
#[inline]
pub fn colon_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![:]) }
@@ -1690,6 +1710,8 @@ impl Trait {
#[inline]
pub fn assoc_item_list(&self) -> Option<AssocItemList> { support::child(&self.syntax) }
#[inline]
+ pub fn impl_restriction(&self) -> Option<ImplRestriction> { support::child(&self.syntax) }
+ #[inline]
pub fn semicolon_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![;]) }
#[inline]
pub fn eq_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![=]) }
@@ -1743,6 +1765,8 @@ impl ast::HasDocComments for TupleField {}
impl ast::HasVisibility for TupleField {}
impl TupleField {
#[inline]
+ pub fn mut_restriction(&self) -> Option<MutRestriction> { support::child(&self.syntax) }
+ #[inline]
pub fn ty(&self) -> Option<Type> { support::child(&self.syntax) }
}
pub struct TupleFieldList {
@@ -2001,6 +2025,15 @@ pub struct Visibility {
}
impl Visibility {
#[inline]
+ pub fn visibility_inner(&self) -> Option<VisibilityInner> { support::child(&self.syntax) }
+ #[inline]
+ pub fn pub_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![pub]) }
+}
+pub struct VisibilityInner {
+ pub(crate) syntax: SyntaxNode,
+}
+impl VisibilityInner {
+ #[inline]
pub fn path(&self) -> Option<Path> { support::child(&self.syntax) }
#[inline]
pub fn l_paren_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T!['(']) }
@@ -2008,8 +2041,6 @@ impl Visibility {
pub fn r_paren_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![')']) }
#[inline]
pub fn in_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![in]) }
- #[inline]
- pub fn pub_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![pub]) }
}
pub struct WhereClause {
pub(crate) syntax: SyntaxNode,
@@ -4192,6 +4223,38 @@ impl fmt::Debug for Impl {
f.debug_struct("Impl").field("syntax", &self.syntax).finish()
}
}
+impl AstNode for ImplRestriction {
+ #[inline]
+ fn kind() -> SyntaxKind
+ where
+ Self: Sized,
+ {
+ IMPL_RESTRICTION
+ }
+ #[inline]
+ fn can_cast(kind: SyntaxKind) -> bool { kind == IMPL_RESTRICTION }
+ #[inline]
+ fn cast(syntax: SyntaxNode) -> Option<Self> {
+ if Self::can_cast(syntax.kind()) { Some(Self { syntax }) } else { None }
+ }
+ #[inline]
+ fn syntax(&self) -> &SyntaxNode { &self.syntax }
+}
+impl hash::Hash for ImplRestriction {
+ fn hash<H: hash::Hasher>(&self, state: &mut H) { self.syntax.hash(state); }
+}
+impl Eq for ImplRestriction {}
+impl PartialEq for ImplRestriction {
+ fn eq(&self, other: &Self) -> bool { self.syntax == other.syntax }
+}
+impl Clone for ImplRestriction {
+ fn clone(&self) -> Self { Self { syntax: self.syntax.clone() } }
+}
+impl fmt::Debug for ImplRestriction {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ f.debug_struct("ImplRestriction").field("syntax", &self.syntax).finish()
+ }
+}
impl AstNode for ImplTraitType {
#[inline]
fn kind() -> SyntaxKind
@@ -5120,6 +5183,38 @@ impl fmt::Debug for Module {
f.debug_struct("Module").field("syntax", &self.syntax).finish()
}
}
+impl AstNode for MutRestriction {
+ #[inline]
+ fn kind() -> SyntaxKind
+ where
+ Self: Sized,
+ {
+ MUT_RESTRICTION
+ }
+ #[inline]
+ fn can_cast(kind: SyntaxKind) -> bool { kind == MUT_RESTRICTION }
+ #[inline]
+ fn cast(syntax: SyntaxNode) -> Option<Self> {
+ if Self::can_cast(syntax.kind()) { Some(Self { syntax }) } else { None }
+ }
+ #[inline]
+ fn syntax(&self) -> &SyntaxNode { &self.syntax }
+}
+impl hash::Hash for MutRestriction {
+ fn hash<H: hash::Hasher>(&self, state: &mut H) { self.syntax.hash(state); }
+}
+impl Eq for MutRestriction {}
+impl PartialEq for MutRestriction {
+ fn eq(&self, other: &Self) -> bool { self.syntax == other.syntax }
+}
+impl Clone for MutRestriction {
+ fn clone(&self) -> Self { Self { syntax: self.syntax.clone() } }
+}
+impl fmt::Debug for MutRestriction {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ f.debug_struct("MutRestriction").field("syntax", &self.syntax).finish()
+ }
+}
impl AstNode for Name {
#[inline]
fn kind() -> SyntaxKind
@@ -7392,6 +7487,38 @@ impl fmt::Debug for Visibility {
f.debug_struct("Visibility").field("syntax", &self.syntax).finish()
}
}
+impl AstNode for VisibilityInner {
+ #[inline]
+ fn kind() -> SyntaxKind
+ where
+ Self: Sized,
+ {
+ VISIBILITY_INNER
+ }
+ #[inline]
+ fn can_cast(kind: SyntaxKind) -> bool { kind == VISIBILITY_INNER }
+ #[inline]
+ fn cast(syntax: SyntaxNode) -> Option<Self> {
+ if Self::can_cast(syntax.kind()) { Some(Self { syntax }) } else { None }
+ }
+ #[inline]
+ fn syntax(&self) -> &SyntaxNode { &self.syntax }
+}
+impl hash::Hash for VisibilityInner {
+ fn hash<H: hash::Hasher>(&self, state: &mut H) { self.syntax.hash(state); }
+}
+impl Eq for VisibilityInner {}
+impl PartialEq for VisibilityInner {
+ fn eq(&self, other: &Self) -> bool { self.syntax == other.syntax }
+}
+impl Clone for VisibilityInner {
+ fn clone(&self) -> Self { Self { syntax: self.syntax.clone() } }
+}
+impl fmt::Debug for VisibilityInner {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ f.debug_struct("VisibilityInner").field("syntax", &self.syntax).finish()
+ }
+}
impl AstNode for WhereClause {
#[inline]
fn kind() -> SyntaxKind
@@ -10092,6 +10219,11 @@ impl std::fmt::Display for Impl {
std::fmt::Display::fmt(self.syntax(), f)
}
}
+impl std::fmt::Display for ImplRestriction {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ std::fmt::Display::fmt(self.syntax(), f)
+ }
+}
impl std::fmt::Display for ImplTraitType {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
std::fmt::Display::fmt(self.syntax(), f)
@@ -10237,6 +10369,11 @@ impl std::fmt::Display for Module {
std::fmt::Display::fmt(self.syntax(), f)
}
}
+impl std::fmt::Display for MutRestriction {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ std::fmt::Display::fmt(self.syntax(), f)
+ }
+}
impl std::fmt::Display for Name {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
std::fmt::Display::fmt(self.syntax(), f)
@@ -10592,6 +10729,11 @@ impl std::fmt::Display for Visibility {
std::fmt::Display::fmt(self.syntax(), f)
}
}
+impl std::fmt::Display for VisibilityInner {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ std::fmt::Display::fmt(self.syntax(), f)
+ }
+}
impl std::fmt::Display for WhereClause {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
std::fmt::Display::fmt(self.syntax(), f)
diff --git a/crates/syntax/src/ast/node_ext.rs b/crates/syntax/src/ast/node_ext.rs
index 03118d01dc..751f8d7e1c 100644
--- a/crates/syntax/src/ast/node_ext.rs
+++ b/crates/syntax/src/ast/node_ext.rs
@@ -948,6 +948,15 @@ pub enum VisibilityKind {
impl ast::Visibility {
pub fn kind(&self) -> VisibilityKind {
+ match self.visibility_inner() {
+ Some(inner) => inner.kind(),
+ None => VisibilityKind::Pub,
+ }
+ }
+}
+
+impl ast::VisibilityInner {
+ pub fn kind(&self) -> VisibilityKind {
match self.path() {
Some(path) => {
if let Some(segment) =
diff --git a/crates/syntax/src/validation.rs b/crates/syntax/src/validation.rs
index 485140be8f..4622590656 100644
--- a/crates/syntax/src/validation.rs
+++ b/crates/syntax/src/validation.rs
@@ -240,8 +240,16 @@ fn validate_numeric_name(name_ref: Option<ast::NameRef>, errors: &mut Vec<Syntax
}
fn validate_visibility(vis: ast::Visibility, errors: &mut Vec<SyntaxError>) {
- let path_without_in_token = vis.in_token().is_none()
- && vis.path().and_then(|p| p.as_single_name_ref()).and_then(|n| n.ident_token()).is_some();
+ let path_without_in_token = if let Some(inner) = vis.visibility_inner() {
+ inner.in_token().is_none()
+ && inner
+ .path()
+ .and_then(|p| p.as_single_name_ref())
+ .and_then(|n| n.ident_token())
+ .is_some()
+ } else {
+ false
+ };
if path_without_in_token {
errors.push(SyntaxError::new("incorrect visibility restriction", vis.syntax.text_range()));
}
diff --git a/crates/syntax/test_data/parser/validation/0037_visibility_in_traits.rast b/crates/syntax/test_data/parser/validation/0037_visibility_in_traits.rast
index 90c258cd1a..2d6d4b2681 100644
--- a/crates/syntax/test_data/parser/validation/0037_visibility_in_traits.rast
+++ b/crates/syntax/test_data/parser/validation/0037_visibility_in_traits.rast
@@ -51,12 +51,13 @@ [email protected]
@@ -73,12 +74,13 @@ [email protected]