Skip to content

Commit de80188

Browse files
committed
Auto merge of #7543 - DevinR528:disallowed-mod, r=Manishearth
Add module_style lint to style changelog: Add new [`module_style`] style lint This is a configurable (no mod file/mod file) lint that determines if `mod.rs` is used consistently or if `mod.rs` is never used (using the new mod layout).
2 parents 3051773 + 31738b1 commit de80188

File tree

23 files changed

+296
-0
lines changed

23 files changed

+296
-0
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2796,6 +2796,7 @@ Released 2018-09-13
27962796
[`missing_safety_doc`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_safety_doc
27972797
[`mistyped_literal_suffixes`]: https://rust-lang.github.io/rust-clippy/master/index.html#mistyped_literal_suffixes
27982798
[`mixed_case_hex_literals`]: https://rust-lang.github.io/rust-clippy/master/index.html#mixed_case_hex_literals
2799+
[`mod_module_files`]: https://rust-lang.github.io/rust-clippy/master/index.html#mod_module_files
27992800
[`module_inception`]: https://rust-lang.github.io/rust-clippy/master/index.html#module_inception
28002801
[`module_name_repetitions`]: https://rust-lang.github.io/rust-clippy/master/index.html#module_name_repetitions
28012802
[`modulo_arithmetic`]: https://rust-lang.github.io/rust-clippy/master/index.html#modulo_arithmetic
@@ -2906,6 +2907,7 @@ Released 2018-09-13
29062907
[`search_is_some`]: https://rust-lang.github.io/rust-clippy/master/index.html#search_is_some
29072908
[`self_assignment`]: https://rust-lang.github.io/rust-clippy/master/index.html#self_assignment
29082909
[`self_named_constructors`]: https://rust-lang.github.io/rust-clippy/master/index.html#self_named_constructors
2910+
[`self_named_module_files`]: https://rust-lang.github.io/rust-clippy/master/index.html#self_named_module_files
29092911
[`semicolon_if_nothing_returned`]: https://rust-lang.github.io/rust-clippy/master/index.html#semicolon_if_nothing_returned
29102912
[`serde_api_misuse`]: https://rust-lang.github.io/rust-clippy/master/index.html#serde_api_misuse
29112913
[`shadow_reuse`]: https://rust-lang.github.io/rust-clippy/master/index.html#shadow_reuse

clippy_lints/src/lib.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -275,6 +275,7 @@ mod missing_const_for_fn;
275275
mod missing_doc;
276276
mod missing_enforced_import_rename;
277277
mod missing_inline;
278+
mod module_style;
278279
mod modulo_arithmetic;
279280
mod multiple_crate_versions;
280281
mod mut_key;
@@ -829,6 +830,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
829830
missing_doc::MISSING_DOCS_IN_PRIVATE_ITEMS,
830831
missing_enforced_import_rename::MISSING_ENFORCED_IMPORT_RENAMES,
831832
missing_inline::MISSING_INLINE_IN_PUBLIC_ITEMS,
833+
module_style::MOD_MODULE_FILES,
834+
module_style::SELF_NAMED_MODULE_FILES,
832835
modulo_arithmetic::MODULO_ARITHMETIC,
833836
multiple_crate_versions::MULTIPLE_CRATE_VERSIONS,
834837
mut_key::MUTABLE_KEY_TYPE,
@@ -1038,6 +1041,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
10381041
LintId::of(missing_doc::MISSING_DOCS_IN_PRIVATE_ITEMS),
10391042
LintId::of(missing_enforced_import_rename::MISSING_ENFORCED_IMPORT_RENAMES),
10401043
LintId::of(missing_inline::MISSING_INLINE_IN_PUBLIC_ITEMS),
1044+
LintId::of(module_style::MOD_MODULE_FILES),
1045+
LintId::of(module_style::SELF_NAMED_MODULE_FILES),
10411046
LintId::of(modulo_arithmetic::MODULO_ARITHMETIC),
10421047
LintId::of(panic_in_result_fn::PANIC_IN_RESULT_FN),
10431048
LintId::of(panic_unimplemented::PANIC),
@@ -2111,6 +2116,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
21112116
store.register_late_pass(|| box manual_map::ManualMap);
21122117
store.register_late_pass(move || box if_then_some_else_none::IfThenSomeElseNone::new(msrv));
21132118
store.register_early_pass(|| box bool_assert_comparison::BoolAssertComparison);
2119+
store.register_early_pass(move || box module_style::ModStyle);
21142120
store.register_late_pass(|| box unused_async::UnusedAsync);
21152121
let disallowed_types = conf.disallowed_types.iter().cloned().collect::<FxHashSet<_>>();
21162122
store.register_late_pass(move || box disallowed_type::DisallowedType::new(&disallowed_types));

clippy_lints/src/module_style.rs

Lines changed: 178 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,178 @@
1+
use std::{
2+
ffi::OsString,
3+
path::{Component, Path},
4+
};
5+
6+
use rustc_ast::ast;
7+
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
8+
use rustc_lint::{EarlyContext, EarlyLintPass, Level, LintContext};
9+
use rustc_session::{declare_tool_lint, impl_lint_pass};
10+
use rustc_span::{FileName, RealFileName, SourceFile, Span, SyntaxContext};
11+
12+
declare_clippy_lint! {
13+
/// ### What it does
14+
/// Checks that module layout uses only self named module files, bans mod.rs files.
15+
///
16+
/// ### Why is this bad?
17+
/// Having multiple module layout styles in a project can be confusing.
18+
///
19+
/// ### Example
20+
/// ```text
21+
/// src/
22+
/// stuff/
23+
/// stuff_files.rs
24+
/// mod.rs
25+
/// lib.rs
26+
/// ```
27+
/// Use instead:
28+
/// ```text
29+
/// src/
30+
/// stuff/
31+
/// stuff_files.rs
32+
/// stuff.rs
33+
/// lib.rs
34+
/// ```
35+
pub MOD_MODULE_FILES,
36+
restriction,
37+
"checks that module layout is consistent"
38+
}
39+
40+
declare_clippy_lint! {
41+
/// ### What it does
42+
/// Checks that module layout uses only mod.rs files.
43+
///
44+
/// ### Why is this bad?
45+
/// Having multiple module layout styles in a project can be confusing.
46+
///
47+
/// ### Example
48+
/// ```text
49+
/// src/
50+
/// stuff/
51+
/// stuff_files.rs
52+
/// stuff.rs
53+
/// lib.rs
54+
/// ```
55+
/// Use instead:
56+
/// ```text
57+
/// src/
58+
/// stuff/
59+
/// stuff_files.rs
60+
/// mod.rs
61+
/// lib.rs
62+
/// ```
63+
64+
pub SELF_NAMED_MODULE_FILES,
65+
restriction,
66+
"checks that module layout is consistent"
67+
}
68+
69+
pub struct ModStyle;
70+
71+
impl_lint_pass!(ModStyle => [MOD_MODULE_FILES, SELF_NAMED_MODULE_FILES]);
72+
73+
impl EarlyLintPass for ModStyle {
74+
fn check_crate(&mut self, cx: &EarlyContext<'_>, _: &ast::Crate) {
75+
if cx.builder.lint_level(MOD_MODULE_FILES).0 == Level::Allow
76+
&& cx.builder.lint_level(SELF_NAMED_MODULE_FILES).0 == Level::Allow
77+
{
78+
return;
79+
}
80+
81+
let files = cx.sess.source_map().files();
82+
83+
let trim_to_src = if let RealFileName::LocalPath(p) = &cx.sess.working_dir {
84+
p.to_string_lossy()
85+
} else {
86+
return;
87+
};
88+
89+
// `folder_segments` is all unique folder path segments `path/to/foo.rs` gives
90+
// `[path, to]` but not foo
91+
let mut folder_segments = FxHashSet::default();
92+
// `mod_folders` is all the unique folder names that contain a mod.rs file
93+
let mut mod_folders = FxHashSet::default();
94+
// `file_map` maps file names to the full path including the file name
95+
// `{ foo => path/to/foo.rs, .. }
96+
let mut file_map = FxHashMap::default();
97+
for file in files.iter() {
98+
match &file.name {
99+
FileName::Real(RealFileName::LocalPath(lp))
100+
if lp.to_string_lossy().starts_with(trim_to_src.as_ref()) =>
101+
{
102+
let p = lp.to_string_lossy();
103+
let path = Path::new(p.trim_start_matches(trim_to_src.as_ref()));
104+
if let Some(stem) = path.file_stem() {
105+
file_map.insert(stem.to_os_string(), (file, path.to_owned()));
106+
}
107+
process_paths_for_mod_files(path, &mut folder_segments, &mut mod_folders);
108+
check_self_named_mod_exists(cx, path, file);
109+
}
110+
_ => {},
111+
}
112+
}
113+
114+
for folder in &folder_segments {
115+
if !mod_folders.contains(folder) {
116+
if let Some((file, path)) = file_map.get(folder) {
117+
let mut correct = path.clone();
118+
correct.pop();
119+
correct.push(folder);
120+
correct.push("mod.rs");
121+
cx.struct_span_lint(
122+
SELF_NAMED_MODULE_FILES,
123+
Span::new(file.start_pos, file.start_pos, SyntaxContext::root()),
124+
|build| {
125+
let mut lint =
126+
build.build(&format!("`mod.rs` files are required, found `{}`", path.display()));
127+
lint.help(&format!("move `{}` to `{}`", path.display(), correct.display(),));
128+
lint.emit();
129+
},
130+
);
131+
}
132+
}
133+
}
134+
}
135+
}
136+
137+
/// For each `path` we add each folder component to `folder_segments` and if the file name
138+
/// is `mod.rs` we add it's parent folder to `mod_folders`.
139+
fn process_paths_for_mod_files(
140+
path: &Path,
141+
folder_segments: &mut FxHashSet<OsString>,
142+
mod_folders: &mut FxHashSet<OsString>,
143+
) {
144+
let mut comp = path.components().rev().peekable();
145+
let _ = comp.next();
146+
if path.ends_with("mod.rs") {
147+
mod_folders.insert(comp.peek().map(|c| c.as_os_str().to_owned()).unwrap_or_default());
148+
}
149+
let folders = comp
150+
.filter_map(|c| {
151+
if let Component::Normal(s) = c {
152+
Some(s.to_os_string())
153+
} else {
154+
None
155+
}
156+
})
157+
.collect::<Vec<_>>();
158+
folder_segments.extend(folders);
159+
}
160+
161+
/// Checks every path for the presence of `mod.rs` files and emits the lint if found.
162+
fn check_self_named_mod_exists(cx: &EarlyContext<'_>, path: &Path, file: &SourceFile) {
163+
if path.ends_with("mod.rs") {
164+
let mut mod_file = path.to_path_buf();
165+
mod_file.pop();
166+
mod_file.set_extension("rs");
167+
168+
cx.struct_span_lint(
169+
MOD_MODULE_FILES,
170+
Span::new(file.start_pos, file.start_pos, SyntaxContext::root()),
171+
|build| {
172+
let mut lint = build.build(&format!("`mod.rs` files are not allowed, found `{}`", path.display()));
173+
lint.help(&format!("move `{}` to `{}`", path.display(), mod_file.display(),));
174+
lint.emit();
175+
},
176+
);
177+
}
178+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
[package]
2+
name = "fail"
3+
version = "0.1.0"
4+
edition = "2018"
5+
6+
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
7+
8+
[dependencies]
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
pub mod stuff;
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
pub mod most;
2+
3+
pub struct Inner;
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
pub struct Snarks;
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
pub mod inner;
2+
3+
pub struct Thing;
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
#![warn(clippy::self_named_module_files)]
2+
3+
mod bad;
4+
5+
fn main() {
6+
let _ = bad::Thing;
7+
let _ = bad::inner::stuff::Inner;
8+
let _ = bad::inner::stuff::most::Snarks;
9+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
error: `mod.rs` files are required, found `/bad/inner.rs`
2+
--> $DIR/bad/inner.rs:1:1
3+
|
4+
LL | pub mod stuff;
5+
| ^
6+
|
7+
= note: `-D clippy::self-named-module-files` implied by `-D warnings`
8+
= help: move `/bad/inner.rs` to `/bad/inner/mod.rs`
9+
10+
error: `mod.rs` files are required, found `/bad/inner/stuff.rs`
11+
--> $DIR/bad/inner/stuff.rs:1:1
12+
|
13+
LL | pub mod most;
14+
| ^
15+
|
16+
= help: move `/bad/inner/stuff.rs` to `/bad/inner/stuff/mod.rs`
17+
18+
error: aborting due to 2 previous errors
19+
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
[package]
2+
name = "fail"
3+
version = "0.1.0"
4+
edition = "2018"
5+
6+
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
7+
8+
[dependencies]
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
pub struct Thing;
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
#![warn(clippy::mod_module_files)]
2+
3+
mod bad;
4+
5+
fn main() {
6+
let _ = bad::Thing;
7+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
error: `mod.rs` files are not allowed, found `/bad/mod.rs`
2+
--> $DIR/bad/mod.rs:1:1
3+
|
4+
LL | pub struct Thing;
5+
| ^
6+
|
7+
= note: `-D clippy::mod-module-files` implied by `-D warnings`
8+
= help: move `/bad/mod.rs` to `/bad.rs`
9+
10+
error: aborting due to previous error
11+
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
[package]
2+
name = "fail"
3+
version = "0.1.0"
4+
edition = "2018"
5+
6+
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
7+
8+
[dependencies]
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
pub struct Thing;
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
#![warn(clippy::self_named_module_files)]
2+
3+
mod bad;
4+
mod more;
5+
6+
fn main() {
7+
let _ = bad::Thing;
8+
let _ = more::foo::Foo;
9+
let _ = more::inner::Inner;
10+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
pub struct Foo;
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
pub struct Inner;
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
pub mod foo;
2+
pub mod inner;
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
[package]
2+
name = "pass"
3+
version = "0.1.0"
4+
edition = "2018"
5+
6+
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
7+
8+
[dependencies]
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
pub struct Thing;
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
#![warn(clippy::mod_module_files)]
2+
3+
mod good;
4+
5+
fn main() {
6+
let _ = good::Thing;
7+
}

0 commit comments

Comments
 (0)