@@ -7,7 +7,8 @@ use lint::{EarlyContext, LateContext, LintContext, LintArray};
7
7
use lint:: { EarlyLintPass , LintPass , LateLintPass } ;
8
8
use syntax:: ast;
9
9
use syntax:: attr;
10
- use syntax_pos:: Span ;
10
+ use syntax:: errors:: Applicability ;
11
+ use syntax_pos:: { BytePos , symbol:: Ident , Span } ;
11
12
12
13
#[ derive( PartialEq ) ]
13
14
pub enum MethodLateContext {
@@ -179,7 +180,8 @@ impl NonSnakeCase {
179
180
words. join ( "_" )
180
181
}
181
182
182
- fn check_snake_case ( & self , cx : & LateContext , sort : & str , name : & str , span : Option < Span > ) {
183
+ /// Checks if a given identifier is snake case, and reports a diagnostic if not.
184
+ fn check_snake_case ( & self , cx : & LateContext , sort : & str , ident : & Ident ) {
183
185
fn is_snake_case ( ident : & str ) -> bool {
184
186
if ident. is_empty ( ) {
185
187
return true ;
@@ -201,20 +203,28 @@ impl NonSnakeCase {
201
203
} )
202
204
}
203
205
206
+ let name = & ident. name . as_str ( ) ;
207
+
204
208
if !is_snake_case ( name) {
205
209
let sc = NonSnakeCase :: to_snake_case ( name) ;
206
- let msg = if sc != name {
207
- format ! ( "{} `{}` should have a snake case name such as `{}`" ,
208
- sort,
209
- name,
210
- sc)
210
+
211
+ let msg = format ! ( "{} `{}` should have a snake case name" , sort, name) ;
212
+ let mut err = cx. struct_span_lint ( NON_SNAKE_CASE , ident. span , & msg) ;
213
+
214
+ // We have a valid span in almost all cases, but we don't have one when linting a crate
215
+ // name provided via the command line.
216
+ if !ident. span . is_dummy ( ) {
217
+ err. span_suggestion_with_applicability (
218
+ ident. span ,
219
+ "convert the identifier to snake case" ,
220
+ sc,
221
+ Applicability :: MaybeIncorrect ,
222
+ ) ;
211
223
} else {
212
- format ! ( "{} `{}` should have a snake case name" , sort, name)
213
- } ;
214
- match span {
215
- Some ( span) => cx. span_lint ( NON_SNAKE_CASE , span, & msg) ,
216
- None => cx. lint ( NON_SNAKE_CASE , & msg) ,
224
+ err. help ( & format ! ( "convert the identifier to snake case: `{}`" , sc) ) ;
217
225
}
226
+
227
+ err. emit ( ) ;
218
228
}
219
229
}
220
230
}
@@ -227,87 +237,111 @@ impl LintPass for NonSnakeCase {
227
237
228
238
impl < ' a , ' tcx > LateLintPass < ' a , ' tcx > for NonSnakeCase {
229
239
fn check_crate ( & mut self , cx : & LateContext , cr : & hir:: Crate ) {
230
- let attr_crate_name = attr:: find_by_name ( & cr. attrs , "crate_name" )
231
- . and_then ( |at| at. value_str ( ) . map ( |s| ( at, s) ) ) ;
232
- if let Some ( ref name) = cx. tcx . sess . opts . crate_name {
233
- self . check_snake_case ( cx, "crate" , name, None ) ;
234
- } else if let Some ( ( attr, name) ) = attr_crate_name {
235
- self . check_snake_case ( cx, "crate" , & name. as_str ( ) , Some ( attr. span ) ) ;
240
+ let crate_ident = if let Some ( name) = & cx. tcx . sess . opts . crate_name {
241
+ Some ( Ident :: from_str ( name) )
242
+ } else {
243
+ attr:: find_by_name ( & cr. attrs , "crate_name" )
244
+ . and_then ( |attr| attr. meta ( ) )
245
+ . and_then ( |meta| {
246
+ meta. name_value_literal ( ) . and_then ( |lit| {
247
+ if let ast:: LitKind :: Str ( name, ..) = lit. node {
248
+ // Discard the double quotes surrounding the literal.
249
+ let sp = cx. sess ( ) . source_map ( ) . span_to_snippet ( lit. span )
250
+ . ok ( )
251
+ . and_then ( |snippet| {
252
+ let left = snippet. find ( '"' ) ?;
253
+ let right = snippet. rfind ( '"' ) . map ( |pos| snippet. len ( ) - pos) ?;
254
+
255
+ Some (
256
+ lit. span
257
+ . with_lo ( lit. span . lo ( ) + BytePos ( left as u32 + 1 ) )
258
+ . with_hi ( lit. span . hi ( ) - BytePos ( right as u32 ) ) ,
259
+ )
260
+ } )
261
+ . unwrap_or_else ( || lit. span ) ;
262
+
263
+ Some ( Ident :: new ( name, sp) )
264
+ } else {
265
+ None
266
+ }
267
+ } )
268
+ } )
269
+ } ;
270
+
271
+ if let Some ( ident) = & crate_ident {
272
+ self . check_snake_case ( cx, "crate" , ident) ;
236
273
}
237
274
}
238
275
239
276
fn check_generic_param ( & mut self , cx : & LateContext , param : & hir:: GenericParam ) {
240
- match param. kind {
241
- GenericParamKind :: Lifetime { .. } => {
242
- let name = param. name . ident ( ) . as_str ( ) ;
243
- self . check_snake_case ( cx, "lifetime" , & name, Some ( param. span ) ) ;
244
- }
245
- GenericParamKind :: Type { .. } => { }
277
+ if let GenericParamKind :: Lifetime { .. } = param. kind {
278
+ self . check_snake_case ( cx, "lifetime" , & param. name . ident ( ) ) ;
246
279
}
247
280
}
248
281
249
- fn check_fn ( & mut self ,
250
- cx : & LateContext ,
251
- fk : FnKind ,
252
- _: & hir:: FnDecl ,
253
- _: & hir:: Body ,
254
- span : Span ,
255
- id : ast:: NodeId ) {
256
- match fk {
257
- FnKind :: Method ( name, ..) => {
282
+ fn check_fn (
283
+ & mut self ,
284
+ cx : & LateContext ,
285
+ fk : FnKind ,
286
+ _: & hir:: FnDecl ,
287
+ _: & hir:: Body ,
288
+ _: Span ,
289
+ id : ast:: NodeId ,
290
+ ) {
291
+ match & fk {
292
+ FnKind :: Method ( ident, ..) => {
258
293
match method_context ( cx, id) {
259
294
MethodLateContext :: PlainImpl => {
260
- self . check_snake_case ( cx, "method" , & name . as_str ( ) , Some ( span ) )
295
+ self . check_snake_case ( cx, "method" , ident ) ;
261
296
}
262
297
MethodLateContext :: TraitAutoImpl => {
263
- self . check_snake_case ( cx, "trait method" , & name . as_str ( ) , Some ( span ) )
298
+ self . check_snake_case ( cx, "trait method" , ident ) ;
264
299
}
265
300
_ => ( ) ,
266
301
}
267
302
}
268
- FnKind :: ItemFn ( name , _, header, _, attrs) => {
303
+ FnKind :: ItemFn ( ident , _, header, _, attrs) => {
269
304
// Skip foreign-ABI #[no_mangle] functions (Issue #31924)
270
- if header. abi != Abi :: Rust && attr:: find_by_name ( attrs, "no_mangle" ) . is_some ( ) {
305
+ if header. abi != Abi :: Rust && attr:: contains_name ( attrs, "no_mangle" ) {
271
306
return ;
272
307
}
273
- self . check_snake_case ( cx, "function" , & name . as_str ( ) , Some ( span ) )
308
+ self . check_snake_case ( cx, "function" , ident ) ;
274
309
}
275
310
FnKind :: Closure ( _) => ( ) ,
276
311
}
277
312
}
278
313
279
314
fn check_item ( & mut self , cx : & LateContext , it : & hir:: Item ) {
280
315
if let hir:: ItemKind :: Mod ( _) = it. node {
281
- self . check_snake_case ( cx, "module" , & it. ident . as_str ( ) , Some ( it . span ) ) ;
316
+ self . check_snake_case ( cx, "module" , & it. ident ) ;
282
317
}
283
318
}
284
319
285
320
fn check_trait_item ( & mut self , cx : & LateContext , item : & hir:: TraitItem ) {
286
- if let hir:: TraitItemKind :: Method ( _, hir:: TraitMethod :: Required ( ref pnames) ) = item. node {
287
- self . check_snake_case ( cx,
288
- "trait method" ,
289
- & item. ident . as_str ( ) ,
290
- Some ( item. span ) ) ;
321
+ if let hir:: TraitItemKind :: Method ( _, hir:: TraitMethod :: Required ( pnames) ) = & item. node {
322
+ self . check_snake_case ( cx, "trait method" , & item. ident ) ;
291
323
for param_name in pnames {
292
- self . check_snake_case ( cx, "variable" , & param_name. as_str ( ) , Some ( param_name . span ) ) ;
324
+ self . check_snake_case ( cx, "variable" , param_name) ;
293
325
}
294
326
}
295
327
}
296
328
297
329
fn check_pat ( & mut self , cx : & LateContext , p : & hir:: Pat ) {
298
- if let & PatKind :: Binding ( _, _, ref ident, _) = & p. node {
299
- self . check_snake_case ( cx, "variable" , & ident. as_str ( ) , Some ( p . span ) ) ;
330
+ if let & PatKind :: Binding ( _, _, ident, _) = & p. node {
331
+ self . check_snake_case ( cx, "variable" , & ident) ;
300
332
}
301
333
}
302
334
303
- fn check_struct_def ( & mut self ,
304
- cx : & LateContext ,
305
- s : & hir:: VariantData ,
306
- _: ast:: Name ,
307
- _: & hir:: Generics ,
308
- _: ast:: NodeId ) {
335
+ fn check_struct_def (
336
+ & mut self ,
337
+ cx : & LateContext ,
338
+ s : & hir:: VariantData ,
339
+ _: ast:: Name ,
340
+ _: & hir:: Generics ,
341
+ _: ast:: NodeId ,
342
+ ) {
309
343
for sf in s. fields ( ) {
310
- self . check_snake_case ( cx, "structure field" , & sf. ident . as_str ( ) , Some ( sf . span ) ) ;
344
+ self . check_snake_case ( cx, "structure field" , & sf. ident ) ;
311
345
}
312
346
}
313
347
}
0 commit comments