Skip to content

Commit 769b95d

Browse files
committed
Add diagnostic for incorrect pub (restriction)
Given the following statement ```rust pub (a) fn afn() {} ``` Provide the following diagnostic: ```rust error: incorrect restriction in `pub` --> file.rs:15:1 | 15 | pub (a) fn afn() {} | ^^^^^^^ | = help: some valid visibility restrictions are: `pub(crate)`: visible only on the current crate `pub(super)`: visible only in the current module's parent `pub(in path::to::module)`: visible only on the specified path help: to make this visible only to module `a`, add `in` before the path: | pub (in a) fn afn() {} ``` Remove cruft from old `pub(path)` syntax.
1 parent 8c4f2c6 commit 769b95d

12 files changed

+205
-35
lines changed

src/libsyntax/parse/parser.rs

+38-26
Original file line numberDiff line numberDiff line change
@@ -4626,7 +4626,7 @@ impl<'a> Parser<'a> {
46264626

46274627
let mut attrs = self.parse_outer_attributes()?;
46284628
let lo = self.span.lo;
4629-
let vis = self.parse_visibility()?;
4629+
let vis = self.parse_visibility(false)?;
46304630
let defaultness = self.parse_defaultness()?;
46314631
let (name, node) = if self.eat_keyword(keywords::Type) {
46324632
let name = self.parse_ident()?;
@@ -4939,25 +4939,8 @@ impl<'a> Parser<'a> {
49394939
|p| {
49404940
let attrs = p.parse_outer_attributes()?;
49414941
let lo = p.span.lo;
4942-
let mut vis = p.parse_visibility()?;
4943-
let ty_is_interpolated =
4944-
p.token.is_interpolated() || p.look_ahead(1, |t| t.is_interpolated());
4945-
let mut ty = p.parse_ty()?;
4946-
4947-
// Handle `pub(path) type`, in which `vis` will be `pub` and `ty` will be `(path)`.
4948-
if vis == Visibility::Public && !ty_is_interpolated &&
4949-
p.token != token::Comma && p.token != token::CloseDelim(token::Paren) {
4950-
ty = if let TyKind::Paren(ref path_ty) = ty.node {
4951-
if let TyKind::Path(None, ref path) = path_ty.node {
4952-
vis = Visibility::Restricted { path: P(path.clone()), id: path_ty.id };
4953-
Some(p.parse_ty()?)
4954-
} else {
4955-
None
4956-
}
4957-
} else {
4958-
None
4959-
}.unwrap_or(ty);
4960-
}
4942+
let vis = p.parse_visibility(true)?;
4943+
let ty = p.parse_ty()?;
49614944
Ok(StructField {
49624945
span: mk_sp(lo, p.span.hi),
49634946
vis: vis,
@@ -4996,18 +4979,25 @@ impl<'a> Parser<'a> {
49964979
fn parse_struct_decl_field(&mut self) -> PResult<'a, StructField> {
49974980
let attrs = self.parse_outer_attributes()?;
49984981
let lo = self.span.lo;
4999-
let vis = self.parse_visibility()?;
4982+
let vis = self.parse_visibility(false)?;
50004983
self.parse_single_struct_field(lo, vis, attrs)
50014984
}
50024985

5003-
// Parse `pub`, `pub(crate)` and `pub(in path)` plus shortcuts
5004-
// `pub(self)` for `pub(in self)` and `pub(super)` for `pub(in super)`.
5005-
fn parse_visibility(&mut self) -> PResult<'a, Visibility> {
4986+
/// Parse `pub`, `pub(crate)` and `pub(in path)` plus shortcuts `pub(self)` for `pub(in self)`
4987+
/// and `pub(super)` for `pub(in super)`. If the following element can't be a tuple (i.e. it's
4988+
/// a function definition, it's not a tuple struct field) and the contents within the parens
4989+
/// isn't valid, emit a proper diagnostic.
4990+
fn parse_visibility(&mut self, can_take_tuple: bool) -> PResult<'a, Visibility> {
50064991
if !self.eat_keyword(keywords::Pub) {
50074992
return Ok(Visibility::Inherited)
50084993
}
50094994

50104995
if self.check(&token::OpenDelim(token::Paren)) {
4996+
let start_span = self.span;
4997+
// We don't `self.bump()` the `(` yet because this might be a struct definition where
4998+
// `()` or a tuple might be allowed. For example, `struct Struct(pub (), pub (usize));`.
4999+
// Because of this, we only `bump` the `(` if we're assured it is appropriate to do so
5000+
// by the following tokens.
50115001
if self.look_ahead(1, |t| t.is_keyword(keywords::Crate)) {
50125002
// `pub(crate)`
50135003
self.bump(); // `(`
@@ -5032,6 +5022,28 @@ impl<'a> Parser<'a> {
50325022
let vis = Visibility::Restricted { path: P(path), id: ast::DUMMY_NODE_ID };
50335023
self.expect(&token::CloseDelim(token::Paren))?; // `)`
50345024
return Ok(vis)
5025+
} else if !can_take_tuple { // Provide this diagnostic if this is not a tuple struct
5026+
// `pub(something) fn ...` or `struct X { pub(something) y: Z }`
5027+
self.bump(); // `(`
5028+
let msg = "incorrect visibility restriction";
5029+
let suggestion = r##"some possible visibility restrictions are:
5030+
`pub(crate)`: visible only on the current crate
5031+
`pub(super)`: visible only in the current module's parent
5032+
`pub(in path::to::module)`: visible only on the specified path"##;
5033+
let path = self.parse_path(PathStyle::Mod)?;
5034+
let path_span = self.prev_span;
5035+
let help_msg = format!("to make this visible only to module `{}`, add `in` before \
5036+
the path:",
5037+
path);
5038+
self.expect(&token::CloseDelim(token::Paren))?; // `)`
5039+
let sp = Span {
5040+
lo: start_span.lo,
5041+
hi: self.prev_span.hi,
5042+
expn_id: start_span.expn_id,
5043+
};
5044+
let mut err = self.span_fatal_help(sp, &msg, &suggestion);
5045+
err.span_suggestion(path_span, &help_msg, format!("in {}", path));
5046+
err.emit(); // emit diagnostic, but continue with public visibility
50355047
}
50365048
}
50375049

@@ -5508,7 +5520,7 @@ impl<'a> Parser<'a> {
55085520

55095521
let lo = self.span.lo;
55105522

5511-
let visibility = self.parse_visibility()?;
5523+
let visibility = self.parse_visibility(false)?;
55125524

55135525
if self.eat_keyword(keywords::Use) {
55145526
// USE ITEM
@@ -5787,7 +5799,7 @@ impl<'a> Parser<'a> {
57875799
fn parse_foreign_item(&mut self) -> PResult<'a, Option<ForeignItem>> {
57885800
let attrs = self.parse_outer_attributes()?;
57895801
let lo = self.span.lo;
5790-
let visibility = self.parse_visibility()?;
5802+
let visibility = self.parse_visibility(false)?;
57915803

57925804
if self.check_keyword(keywords::Static) {
57935805
// FOREIGN STATIC ITEM

src/test/compile-fail/privacy/restricted/tuple-struct-fields/test.rs

+4-3
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@
1010

1111
mod foo {
1212
type T = ();
13-
struct S1(pub(foo) (), pub(T), pub(crate) (), pub(((), T)));
14-
struct S2(pub((foo)) ()); //~ ERROR expected `,`, found `(`
15-
//~| ERROR expected one of `;` or `where`, found `(`
13+
struct S1(pub(in foo) (), pub(T), pub(crate) (), pub(((), T)));
14+
struct S2(pub((foo)) ());
15+
//~^ ERROR expected `,`, found `(`
16+
//~| ERROR expected one of `;` or `where`, found `(`
1617
}

src/test/compile-fail/privacy/restricted/tuple-struct-fields/test2.rs

+4-3
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,10 @@
1111
macro_rules! define_struct {
1212
($t:ty) => {
1313
struct S1(pub $t);
14-
struct S2(pub (foo) ());
15-
struct S3(pub $t ()); //~ ERROR expected `,`, found `(`
16-
//~| ERROR expected one of `;` or `where`, found `(`
14+
struct S2(pub (in foo) ());
15+
struct S3(pub $t ());
16+
//~^ ERROR expected `,`, found `(`
17+
//~| ERROR expected one of `;` or `where`, found `(`
1718
}
1819
}
1920

src/test/compile-fail/privacy/restricted/tuple-struct-fields/test3.rs

+4-3
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,10 @@
1111
macro_rules! define_struct {
1212
($t:ty) => {
1313
struct S1(pub($t));
14-
struct S2(pub (foo) ());
15-
struct S3(pub($t) ()); //~ ERROR expected `,`, found `(`
16-
//~| ERROR expected one of `;` or `where`, found `(`
14+
struct S2(pub (in foo) ());
15+
struct S3(pub($t) ());
16+
//~^ ERROR expected `,`, found `(`
17+
//~| ERROR expected one of `;` or `where`, found `(`
1718
}
1819
}
1920

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
#![feature(pub_restricted)]
12+
13+
pub(crate) () fn foo() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
error: unmatched visibility `pub`
2+
--> $DIR/pub-restricted-error-fn.rs:13:10
3+
|
4+
13 | pub(crate) () fn foo() {}
5+
| ^
6+
7+
error: aborting due to previous error
8+
+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
#![feature(pub_restricted)]
12+
13+
struct Bar(pub(()));
14+
15+
struct Foo {
16+
pub(crate) () foo: usize,
17+
}
18+
19+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
error: expected identifier, found `(`
2+
--> $DIR/pub-restricted-error.rs:16:16
3+
|
4+
16 | pub(crate) () foo: usize,
5+
| ^
6+
7+
error: aborting due to previous error
8+
+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
#![feature(pub_restricted)]
12+
13+
pub (.) fn afn() {}
14+
15+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
error: expected identifier, found `.`
2+
--> $DIR/pub-restricted-non-path.rs:13:6
3+
|
4+
13 | pub (.) fn afn() {}
5+
| ^
6+
7+
error: aborting due to previous error
8+

src/test/ui/pub/pub-restricted.rs

+37
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
#![feature(pub_restricted)]
12+
13+
mod a {}
14+
15+
pub (a) fn afn() {}
16+
pub (b) fn bfn() {}
17+
pub fn privfn() {}
18+
mod x {
19+
mod y {
20+
pub (in x) fn foo() {}
21+
pub (super) fn bar() {}
22+
pub (crate) fn qux() {}
23+
}
24+
}
25+
26+
mod y {
27+
struct Foo {
28+
pub (crate) c: usize,
29+
pub (super) s: usize,
30+
valid_private: usize,
31+
pub (in y) valid_in_x: usize,
32+
pub (a) invalid: usize,
33+
pub (in x) non_parent_invalid: usize,
34+
}
35+
}
36+
37+
fn main() {}

src/test/ui/pub/pub-restricted.stderr

+47
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
error: incorrect visibility restriction
2+
--> $DIR/pub-restricted.rs:15:5
3+
|
4+
15 | pub (a) fn afn() {}
5+
| ^^^
6+
|
7+
= help: some possible visibility restrictions are:
8+
`pub(crate)`: visible only on the current crate
9+
`pub(super)`: visible only in the current module's parent
10+
`pub(in path::to::module)`: visible only on the specified path
11+
help: to make this visible only to module `a`, add `in` before the path:
12+
| pub (in a) fn afn() {}
13+
14+
error: incorrect visibility restriction
15+
--> $DIR/pub-restricted.rs:16:5
16+
|
17+
16 | pub (b) fn bfn() {}
18+
| ^^^
19+
|
20+
= help: some possible visibility restrictions are:
21+
`pub(crate)`: visible only on the current crate
22+
`pub(super)`: visible only in the current module's parent
23+
`pub(in path::to::module)`: visible only on the specified path
24+
help: to make this visible only to module `b`, add `in` before the path:
25+
| pub (in b) fn bfn() {}
26+
27+
error: incorrect visibility restriction
28+
--> $DIR/pub-restricted.rs:32:13
29+
|
30+
32 | pub (a) invalid: usize,
31+
| ^^^
32+
|
33+
= help: some possible visibility restrictions are:
34+
`pub(crate)`: visible only on the current crate
35+
`pub(super)`: visible only in the current module's parent
36+
`pub(in path::to::module)`: visible only on the specified path
37+
help: to make this visible only to module `a`, add `in` before the path:
38+
| pub (in a) invalid: usize,
39+
40+
error: visibilities can only be restricted to ancestor modules
41+
--> $DIR/pub-restricted.rs:33:17
42+
|
43+
33 | pub (in x) non_parent_invalid: usize,
44+
| ^
45+
46+
error: aborting due to 4 previous errors
47+

0 commit comments

Comments
 (0)