@@ -47,8 +47,32 @@ declare_lint! {
47
47
"type names prefixed/postfixed with their containing module's name"
48
48
}
49
49
50
+ /// **What it does:** Checks for modules that have the same name as their parent module
51
+ ///
52
+ /// **Why is this bad?** A typical beginner mistake is to have `mod foo;` and again `mod foo { .. }` in `foo.rs`.
53
+ /// The expectation is that items inside the inner `mod foo { .. }` are then available
54
+ /// through `foo::x`, but they are only available through `foo::foo::x`.
55
+ /// If this is done on purpose, it would be better to choose a more representative module name.
56
+ ///
57
+ /// **Known problems:** None.
58
+ ///
59
+ /// **Example:**
60
+ /// ```rust
61
+ /// // lib.rs
62
+ /// mod foo;
63
+ /// // foo.rs
64
+ /// mod foo {
65
+ /// ...
66
+ /// }
67
+ /// ```
68
+ declare_lint ! {
69
+ pub MODULE_INCEPTION ,
70
+ Warn ,
71
+ "modules that have the same name as their parent module"
72
+ }
73
+
50
74
pub struct EnumVariantNames {
51
- modules : Vec < String > ,
75
+ modules : Vec < ( InternedString , String ) > ,
52
76
threshold : u64 ,
53
77
}
54
78
@@ -60,7 +84,7 @@ impl EnumVariantNames {
60
84
61
85
impl LintPass for EnumVariantNames {
62
86
fn get_lints ( & self ) -> LintArray {
63
- lint_array ! ( ENUM_VARIANT_NAMES , STUTTER )
87
+ lint_array ! ( ENUM_VARIANT_NAMES , STUTTER , MODULE_INCEPTION )
64
88
}
65
89
}
66
90
@@ -170,25 +194,32 @@ impl EarlyLintPass for EnumVariantNames {
170
194
let item_name = item. ident . name . as_str ( ) ;
171
195
let item_name_chars = item_name. chars ( ) . count ( ) ;
172
196
let item_camel = to_camel_case ( & item_name) ;
173
- if item . vis == Visibility :: Public && !in_macro ( cx, item. span ) {
174
- if let Some ( mod_camel) = self . modules . last ( ) {
197
+ if !in_macro ( cx, item. span ) {
198
+ if let Some ( & ( ref mod_name , ref mod_camel) ) = self . modules . last ( ) {
175
199
// constants don't have surrounding modules
176
200
if !mod_camel. is_empty ( ) {
177
- let matching = partial_match ( mod_camel, & item_camel) ;
178
- let rmatching = partial_rmatch ( mod_camel, & item_camel) ;
179
- let nchars = mod_camel. chars ( ) . count ( ) ;
180
- if matching == nchars {
181
- span_lint ( cx, STUTTER , item. span , & format ! ( "Item name ({}) starts with its containing module's name ({})" , item_camel, mod_camel) ) ;
201
+ if mod_name == & item_name {
202
+ if let ItemKind :: Mod ( ..) = item. node {
203
+ span_lint ( cx, MODULE_INCEPTION , item. span , "module has the same name as its containing module" ) ;
204
+ }
182
205
}
183
- if rmatching == nchars {
184
- span_lint ( cx, STUTTER , item. span , & format ! ( "Item name ({}) ends with its containing module's name ({})" , item_camel, mod_camel) ) ;
206
+ if item. vis == Visibility :: Public {
207
+ let matching = partial_match ( mod_camel, & item_camel) ;
208
+ let rmatching = partial_rmatch ( mod_camel, & item_camel) ;
209
+ let nchars = mod_camel. chars ( ) . count ( ) ;
210
+ if matching == nchars {
211
+ span_lint ( cx, STUTTER , item. span , "item name starts with its containing module's name" ) ;
212
+ }
213
+ if rmatching == nchars {
214
+ span_lint ( cx, STUTTER , item. span , "item name ends with its containing module's name" ) ;
215
+ }
185
216
}
186
217
}
187
218
}
188
219
}
189
220
if let ItemKind :: Enum ( ref def, _) = item. node {
190
221
check_variant ( cx, self . threshold , def, & item_name, item_name_chars, item. span ) ;
191
222
}
192
- self . modules . push ( item_camel) ;
223
+ self . modules . push ( ( item_name , item_camel) ) ;
193
224
}
194
225
}
0 commit comments