Skip to content

Commit 085ec6d

Browse files
committed
unreachable-pub lint for pub items not reachable from crate root
This is with deepest thanks to Vadim Petrochenkov for thorough review, and resolves #45521.
1 parent e340996 commit 085ec6d

File tree

6 files changed

+469
-0
lines changed

6 files changed

+469
-0
lines changed

src/librustc_lint/builtin.rs

+57
Original file line numberDiff line numberDiff line change
@@ -1301,3 +1301,60 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnionsWithDropFields {
13011301
}
13021302
}
13031303
}
1304+
1305+
/// Lint for items marked `pub` that aren't reachable from other crates
1306+
pub struct UnreachablePub;
1307+
1308+
declare_lint! {
1309+
UNREACHABLE_PUB,
1310+
Allow,
1311+
"`pub` items not reachable from crate root"
1312+
}
1313+
1314+
impl LintPass for UnreachablePub {
1315+
fn get_lints(&self) -> LintArray {
1316+
lint_array!(UNREACHABLE_PUB)
1317+
}
1318+
}
1319+
1320+
impl UnreachablePub {
1321+
fn perform_lint(&self, cx: &LateContext, what: &str, id: ast::NodeId,
1322+
vis: &hir::Visibility, span: Span, exportable: bool) {
1323+
if !cx.access_levels.is_reachable(id) && *vis == hir::Visibility::Public {
1324+
let def_span = cx.tcx.sess.codemap().def_span(span);
1325+
let mut err = cx.struct_span_lint(UNREACHABLE_PUB, def_span,
1326+
&format!("unreachable `pub` {}", what));
1327+
// visibility is token at start of declaration (can be macro
1328+
// variable rather than literal `pub`)
1329+
let pub_span = cx.tcx.sess.codemap().span_until_char(def_span, ' ');
1330+
let replacement = if cx.tcx.sess.features.borrow().crate_visibility_modifier {
1331+
"crate"
1332+
} else {
1333+
"pub(crate)"
1334+
}.to_owned();
1335+
err.span_suggestion(pub_span, "consider restricting its visibility", replacement);
1336+
if exportable {
1337+
err.help("or consider exporting it for use by other crates");
1338+
}
1339+
err.emit();
1340+
}
1341+
}
1342+
}
1343+
1344+
impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnreachablePub {
1345+
fn check_item(&mut self, cx: &LateContext, item: &hir::Item) {
1346+
self.perform_lint(cx, "item", item.id, &item.vis, item.span, true);
1347+
}
1348+
1349+
fn check_foreign_item(&mut self, cx: &LateContext, foreign_item: &hir::ForeignItem) {
1350+
self.perform_lint(cx, "item", foreign_item.id, &foreign_item.vis, foreign_item.span, true);
1351+
}
1352+
1353+
fn check_struct_field(&mut self, cx: &LateContext, field: &hir::StructField) {
1354+
self.perform_lint(cx, "field", field.id, &field.vis, field.span, false);
1355+
}
1356+
1357+
fn check_impl_item(&mut self, cx: &LateContext, impl_item: &hir::ImplItem) {
1358+
self.perform_lint(cx, "item", impl_item.id, &impl_item.vis, impl_item.span, false);
1359+
}
1360+
}

src/librustc_lint/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,7 @@ pub fn register_builtins(store: &mut lint::LintStore, sess: Option<&Session>) {
137137
PluginAsLibrary,
138138
MutableTransmutes,
139139
UnionsWithDropFields,
140+
UnreachablePub,
140141
);
141142

142143
add_builtin_with_new!(sess,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
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+
// This is just like unreachable_pub.rs, but without the
12+
// `crate_visibility_modifier` feature (so that we can test the suggestions to
13+
// use `pub(crate)` that are given when that feature is off, as opposed to the
14+
// suggestions to use `crate` given when it is on). When that feature becomes
15+
// stable, this test can be deleted.
16+
17+
#![feature(macro_vis_matcher)]
18+
19+
#![allow(unused)]
20+
#![warn(unreachable_pub)]
21+
22+
mod private_mod {
23+
// non-leaked `pub` items in private module should be linted
24+
pub use std::fmt;
25+
26+
pub struct Hydrogen {
27+
// `pub` struct fields, too
28+
pub neutrons: usize,
29+
// (... but not more-restricted fields)
30+
pub(crate) electrons: usize
31+
}
32+
impl Hydrogen {
33+
// impls, too
34+
pub fn count_neutrons(&self) -> usize { self.neutrons }
35+
pub(crate) fn count_electrons(&self) -> usize { self.electrons }
36+
}
37+
38+
pub enum Helium {}
39+
pub union Lithium { c1: usize, c2: u8 }
40+
pub fn beryllium() {}
41+
pub trait Boron {}
42+
pub const CARBON: usize = 1;
43+
pub static NITROGEN: usize = 2;
44+
pub type Oxygen = bool;
45+
46+
macro_rules! define_empty_struct_with_visibility {
47+
($visibility: vis, $name: ident) => { $visibility struct $name {} }
48+
}
49+
define_empty_struct_with_visibility!(pub, Fluorine);
50+
51+
extern {
52+
pub fn catalyze() -> bool;
53+
}
54+
55+
// items leaked through signatures (see `get_neon` below) are OK
56+
pub struct Neon {}
57+
58+
// crate-visible items are OK
59+
pub(crate) struct Sodium {}
60+
}
61+
62+
pub mod public_mod {
63+
// module is public: these are OK, too
64+
pub struct Magnesium {}
65+
pub(crate) struct Aluminum {}
66+
}
67+
68+
pub fn get_neon() -> private_mod::Neon {
69+
private_mod::Neon {}
70+
}
71+
72+
fn main() {
73+
let _ = get_neon();
74+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
warning: unreachable `pub` item
2+
--> $DIR/unreachable_pub-pub_crate.rs:24:5
3+
|
4+
24 | pub use std::fmt;
5+
| ---^^^^^^^^^^^^^^
6+
| |
7+
| help: consider restricting its visibility: `pub(crate)`
8+
|
9+
note: lint level defined here
10+
--> $DIR/unreachable_pub-pub_crate.rs:20:9
11+
|
12+
20 | #![warn(unreachable_pub)]
13+
| ^^^^^^^^^^^^^^^
14+
= help: or consider exporting it for use by other crates
15+
16+
warning: unreachable `pub` item
17+
--> $DIR/unreachable_pub-pub_crate.rs:26:5
18+
|
19+
26 | pub struct Hydrogen {
20+
| ---^^^^^^^^^^^^^^^^
21+
| |
22+
| help: consider restricting its visibility: `pub(crate)`
23+
|
24+
= help: or consider exporting it for use by other crates
25+
26+
warning: unreachable `pub` field
27+
--> $DIR/unreachable_pub-pub_crate.rs:28:9
28+
|
29+
28 | pub neutrons: usize,
30+
| ---^^^^^^^^^^^^^^^^
31+
| |
32+
| help: consider restricting its visibility: `pub(crate)`
33+
34+
warning: unreachable `pub` item
35+
--> $DIR/unreachable_pub-pub_crate.rs:34:9
36+
|
37+
34 | pub fn count_neutrons(&self) -> usize { self.neutrons }
38+
| ---^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
39+
| |
40+
| help: consider restricting its visibility: `pub(crate)`
41+
42+
warning: unreachable `pub` item
43+
--> $DIR/unreachable_pub-pub_crate.rs:38:5
44+
|
45+
38 | pub enum Helium {}
46+
| ---^^^^^^^^^^^^
47+
| |
48+
| help: consider restricting its visibility: `pub(crate)`
49+
|
50+
= help: or consider exporting it for use by other crates
51+
52+
warning: unreachable `pub` item
53+
--> $DIR/unreachable_pub-pub_crate.rs:39:5
54+
|
55+
39 | pub union Lithium { c1: usize, c2: u8 }
56+
| ---^^^^^^^^^^^^^^
57+
| |
58+
| help: consider restricting its visibility: `pub(crate)`
59+
|
60+
= help: or consider exporting it for use by other crates
61+
62+
warning: unreachable `pub` item
63+
--> $DIR/unreachable_pub-pub_crate.rs:40:5
64+
|
65+
40 | pub fn beryllium() {}
66+
| ---^^^^^^^^^^^^^^^
67+
| |
68+
| help: consider restricting its visibility: `pub(crate)`
69+
|
70+
= help: or consider exporting it for use by other crates
71+
72+
warning: unreachable `pub` item
73+
--> $DIR/unreachable_pub-pub_crate.rs:41:5
74+
|
75+
41 | pub trait Boron {}
76+
| ---^^^^^^^^^^^^
77+
| |
78+
| help: consider restricting its visibility: `pub(crate)`
79+
|
80+
= help: or consider exporting it for use by other crates
81+
82+
warning: unreachable `pub` item
83+
--> $DIR/unreachable_pub-pub_crate.rs:42:5
84+
|
85+
42 | pub const CARBON: usize = 1;
86+
| ---^^^^^^^^^^^^^^^^^^^^^^^^^
87+
| |
88+
| help: consider restricting its visibility: `pub(crate)`
89+
|
90+
= help: or consider exporting it for use by other crates
91+
92+
warning: unreachable `pub` item
93+
--> $DIR/unreachable_pub-pub_crate.rs:43:5
94+
|
95+
43 | pub static NITROGEN: usize = 2;
96+
| ---^^^^^^^^^^^^^^^^^^^^^^^^^^^^
97+
| |
98+
| help: consider restricting its visibility: `pub(crate)`
99+
|
100+
= help: or consider exporting it for use by other crates
101+
102+
warning: unreachable `pub` item
103+
--> $DIR/unreachable_pub-pub_crate.rs:44:5
104+
|
105+
44 | pub type Oxygen = bool;
106+
| ---^^^^^^^^^^^^^^^^^^^^
107+
| |
108+
| help: consider restricting its visibility: `pub(crate)`
109+
|
110+
= help: or consider exporting it for use by other crates
111+
112+
warning: unreachable `pub` item
113+
--> $DIR/unreachable_pub-pub_crate.rs:47:47
114+
|
115+
47 | ($visibility: vis, $name: ident) => { $visibility struct $name {} }
116+
| -----------^^^^^^^^^^^^^
117+
| |
118+
| help: consider restricting its visibility: `pub(crate)`
119+
48 | }
120+
49 | define_empty_struct_with_visibility!(pub, Fluorine);
121+
| ---------------------------------------------------- in this macro invocation
122+
|
123+
= help: or consider exporting it for use by other crates
124+
125+
warning: unreachable `pub` item
126+
--> $DIR/unreachable_pub-pub_crate.rs:52:9
127+
|
128+
52 | pub fn catalyze() -> bool;
129+
| ---^^^^^^^^^^^^^^^^^^^^^^^
130+
| |
131+
| help: consider restricting its visibility: `pub(crate)`
132+
|
133+
= help: or consider exporting it for use by other crates
134+

src/test/ui/lint/unreachable_pub.rs

+69
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
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(crate_visibility_modifier)]
12+
#![feature(macro_vis_matcher)]
13+
14+
#![allow(unused)]
15+
#![warn(unreachable_pub)]
16+
17+
mod private_mod {
18+
// non-leaked `pub` items in private module should be linted
19+
pub use std::fmt;
20+
21+
pub struct Hydrogen {
22+
// `pub` struct fields, too
23+
pub neutrons: usize,
24+
// (... but not more-restricted fields)
25+
crate electrons: usize
26+
}
27+
impl Hydrogen {
28+
// impls, too
29+
pub fn count_neutrons(&self) -> usize { self.neutrons }
30+
crate fn count_electrons(&self) -> usize { self.electrons }
31+
}
32+
33+
pub enum Helium {}
34+
pub union Lithium { c1: usize, c2: u8 }
35+
pub fn beryllium() {}
36+
pub trait Boron {}
37+
pub const CARBON: usize = 1;
38+
pub static NITROGEN: usize = 2;
39+
pub type Oxygen = bool;
40+
41+
macro_rules! define_empty_struct_with_visibility {
42+
($visibility: vis, $name: ident) => { $visibility struct $name {} }
43+
}
44+
define_empty_struct_with_visibility!(pub, Fluorine);
45+
46+
extern {
47+
pub fn catalyze() -> bool;
48+
}
49+
50+
// items leaked through signatures (see `get_neon` below) are OK
51+
pub struct Neon {}
52+
53+
// crate-visible items are OK
54+
crate struct Sodium {}
55+
}
56+
57+
pub mod public_mod {
58+
// module is public: these are OK, too
59+
pub struct Magnesium {}
60+
crate struct Aluminum {}
61+
}
62+
63+
pub fn get_neon() -> private_mod::Neon {
64+
private_mod::Neon {}
65+
}
66+
67+
fn main() {
68+
let _ = get_neon();
69+
}

0 commit comments

Comments
 (0)