@@ -78,7 +78,7 @@ impl CheckAttrVisitor<'tcx> {
78
78
} else if self . tcx . sess . check_name ( attr, sym:: track_caller) {
79
79
self . check_track_caller ( & attr. span , attrs, span, target)
80
80
} else if self . tcx . sess . check_name ( attr, sym:: doc) {
81
- self . check_doc_alias ( attr, hir_id, target)
81
+ self . check_doc_attrs ( attr, hir_id, target)
82
82
} else if self . tcx . sess . check_name ( attr, sym:: no_link) {
83
83
self . check_no_link ( & attr, span, target)
84
84
} else if self . tcx . sess . check_name ( attr, sym:: export_name) {
@@ -287,99 +287,159 @@ impl CheckAttrVisitor<'tcx> {
287
287
}
288
288
}
289
289
290
- fn doc_alias_str_error ( & self , meta : & NestedMetaItem ) {
290
+ fn doc_attr_str_error ( & self , meta : & NestedMetaItem , attr_name : & str ) {
291
291
self . tcx
292
292
. sess
293
293
. struct_span_err (
294
294
meta. span ( ) ,
295
- "doc alias attribute expects a string: #[doc(alias = \" 0 \" )]" ,
295
+ & format ! ( "doc {0} attribute expects a string: #[doc({0} = \" a \" )]" , attr_name ) ,
296
296
)
297
297
. emit ( ) ;
298
298
}
299
299
300
- fn check_doc_alias ( & self , attr : & Attribute , hir_id : HirId , target : Target ) -> bool {
300
+ fn check_doc_alias ( & self , meta : & NestedMetaItem , hir_id : HirId , target : Target ) -> bool {
301
+ let doc_alias = meta. value_str ( ) . map ( |s| s. to_string ( ) ) . unwrap_or_else ( String :: new) ;
302
+ if doc_alias. is_empty ( ) {
303
+ self . doc_attr_str_error ( meta, "alias" ) ;
304
+ return false ;
305
+ }
306
+ if let Some ( c) =
307
+ doc_alias. chars ( ) . find ( |& c| c == '"' || c == '\'' || ( c. is_whitespace ( ) && c != ' ' ) )
308
+ {
309
+ self . tcx
310
+ . sess
311
+ . struct_span_err (
312
+ meta. name_value_literal_span ( ) . unwrap_or_else ( || meta. span ( ) ) ,
313
+ & format ! ( "{:?} character isn't allowed in `#[doc(alias = \" ...\" )]`" , c, ) ,
314
+ )
315
+ . emit ( ) ;
316
+ return false ;
317
+ }
318
+ if doc_alias. starts_with ( ' ' ) || doc_alias. ends_with ( ' ' ) {
319
+ self . tcx
320
+ . sess
321
+ . struct_span_err (
322
+ meta. name_value_literal_span ( ) . unwrap_or_else ( || meta. span ( ) ) ,
323
+ "`#[doc(alias = \" ...\" )]` cannot start or end with ' '" ,
324
+ )
325
+ . emit ( ) ;
326
+ return false ;
327
+ }
328
+ if let Some ( err) = match target {
329
+ Target :: Impl => Some ( "implementation block" ) ,
330
+ Target :: ForeignMod => Some ( "extern block" ) ,
331
+ Target :: AssocTy => {
332
+ let parent_hir_id = self . tcx . hir ( ) . get_parent_item ( hir_id) ;
333
+ let containing_item = self . tcx . hir ( ) . expect_item ( parent_hir_id) ;
334
+ if Target :: from_item ( containing_item) == Target :: Impl {
335
+ Some ( "type alias in implementation block" )
336
+ } else {
337
+ None
338
+ }
339
+ }
340
+ Target :: AssocConst => {
341
+ let parent_hir_id = self . tcx . hir ( ) . get_parent_item ( hir_id) ;
342
+ let containing_item = self . tcx . hir ( ) . expect_item ( parent_hir_id) ;
343
+ // We can't link to trait impl's consts.
344
+ let err = "associated constant in trait implementation block" ;
345
+ match containing_item. kind {
346
+ ItemKind :: Impl { of_trait : Some ( _) , .. } => Some ( err) ,
347
+ _ => None ,
348
+ }
349
+ }
350
+ _ => None ,
351
+ } {
352
+ self . tcx
353
+ . sess
354
+ . struct_span_err (
355
+ meta. span ( ) ,
356
+ & format ! ( "`#[doc(alias = \" ...\" )]` isn't allowed on {}" , err) ,
357
+ )
358
+ . emit ( ) ;
359
+ return false ;
360
+ }
361
+ true
362
+ }
363
+
364
+ fn check_doc_keyword ( & self , meta : & NestedMetaItem , hir_id : HirId ) -> bool {
365
+ let doc_keyword = meta. value_str ( ) . map ( |s| s. to_string ( ) ) . unwrap_or_else ( String :: new) ;
366
+ if doc_keyword. is_empty ( ) {
367
+ self . doc_attr_str_error ( meta, "keyword" ) ;
368
+ return false ;
369
+ }
370
+ match self . tcx . hir ( ) . expect_item ( hir_id) . kind {
371
+ ItemKind :: Mod ( ref module) => {
372
+ if !module. item_ids . is_empty ( ) {
373
+ self . tcx
374
+ . sess
375
+ . struct_span_err (
376
+ meta. span ( ) ,
377
+ "`#[doc(keyword = \" ...\" )]` can only be used on empty modules" ,
378
+ )
379
+ . emit ( ) ;
380
+ return false ;
381
+ }
382
+ }
383
+ _ => {
384
+ self . tcx
385
+ . sess
386
+ . struct_span_err (
387
+ meta. span ( ) ,
388
+ "`#[doc(keyword = \" ...\" )]` can only be used on modules" ,
389
+ )
390
+ . emit ( ) ;
391
+ return false ;
392
+ }
393
+ }
394
+ if !rustc_lexer:: is_ident ( & doc_keyword) {
395
+ self . tcx
396
+ . sess
397
+ . struct_span_err (
398
+ meta. name_value_literal_span ( ) . unwrap_or_else ( || meta. span ( ) ) ,
399
+ & format ! ( "`{}` is not a valid identifier" , doc_keyword) ,
400
+ )
401
+ . emit ( ) ;
402
+ return false ;
403
+ }
404
+ true
405
+ }
406
+
407
+ fn check_attr_crate_level (
408
+ & self ,
409
+ meta : & NestedMetaItem ,
410
+ hir_id : HirId ,
411
+ attr_name : & str ,
412
+ ) -> bool {
413
+ if CRATE_HIR_ID == hir_id {
414
+ self . tcx
415
+ . sess
416
+ . struct_span_err (
417
+ meta. span ( ) ,
418
+ & format ! (
419
+ "`#![doc({} = \" ...\" )]` isn't allowed as a crate level attribute" ,
420
+ attr_name,
421
+ ) ,
422
+ )
423
+ . emit ( ) ;
424
+ return false ;
425
+ }
426
+ true
427
+ }
428
+
429
+ fn check_doc_attrs ( & self , attr : & Attribute , hir_id : HirId , target : Target ) -> bool {
301
430
if let Some ( mi) = attr. meta ( ) {
302
431
if let Some ( list) = mi. meta_item_list ( ) {
303
432
for meta in list {
304
433
if meta. has_name ( sym:: alias) {
305
- if !meta. is_value_str ( ) {
306
- self . doc_alias_str_error ( meta) ;
307
- return false ;
308
- }
309
- let doc_alias =
310
- meta. value_str ( ) . map ( |s| s. to_string ( ) ) . unwrap_or_else ( String :: new) ;
311
- if doc_alias. is_empty ( ) {
312
- self . doc_alias_str_error ( meta) ;
313
- return false ;
314
- }
315
- if let Some ( c) = doc_alias
316
- . chars ( )
317
- . find ( |& c| c == '"' || c == '\'' || ( c. is_whitespace ( ) && c != ' ' ) )
434
+ if !self . check_attr_crate_level ( meta, hir_id, "alias" )
435
+ || !self . check_doc_alias ( meta, hir_id, target)
318
436
{
319
- self . tcx
320
- . sess
321
- . struct_span_err (
322
- meta. name_value_literal_span ( ) . unwrap_or_else ( || meta. span ( ) ) ,
323
- & format ! (
324
- "{:?} character isn't allowed in `#[doc(alias = \" ...\" )]`" ,
325
- c,
326
- ) ,
327
- )
328
- . emit ( ) ;
329
437
return false ;
330
438
}
331
- if doc_alias. starts_with ( ' ' ) || doc_alias. ends_with ( ' ' ) {
332
- self . tcx
333
- . sess
334
- . struct_span_err (
335
- meta. name_value_literal_span ( ) . unwrap_or_else ( || meta. span ( ) ) ,
336
- "`#[doc(alias = \" ...\" )]` cannot start or end with ' '" ,
337
- )
338
- . emit ( ) ;
339
- return false ;
340
- }
341
- if let Some ( err) = match target {
342
- Target :: Impl => Some ( "implementation block" ) ,
343
- Target :: ForeignMod => Some ( "extern block" ) ,
344
- Target :: AssocTy => {
345
- let parent_hir_id = self . tcx . hir ( ) . get_parent_item ( hir_id) ;
346
- let containing_item = self . tcx . hir ( ) . expect_item ( parent_hir_id) ;
347
- if Target :: from_item ( containing_item) == Target :: Impl {
348
- Some ( "type alias in implementation block" )
349
- } else {
350
- None
351
- }
352
- }
353
- Target :: AssocConst => {
354
- let parent_hir_id = self . tcx . hir ( ) . get_parent_item ( hir_id) ;
355
- let containing_item = self . tcx . hir ( ) . expect_item ( parent_hir_id) ;
356
- // We can't link to trait impl's consts.
357
- let err = "associated constant in trait implementation block" ;
358
- match containing_item. kind {
359
- ItemKind :: Impl { of_trait : Some ( _) , .. } => Some ( err) ,
360
- _ => None ,
361
- }
362
- }
363
- _ => None ,
364
- } {
365
- self . tcx
366
- . sess
367
- . struct_span_err (
368
- meta. span ( ) ,
369
- & format ! ( "`#[doc(alias = \" ...\" )]` isn't allowed on {}" , err) ,
370
- )
371
- . emit ( ) ;
372
- return false ;
373
- }
374
- if CRATE_HIR_ID == hir_id {
375
- self . tcx
376
- . sess
377
- . struct_span_err (
378
- meta. span ( ) ,
379
- "`#![doc(alias = \" ...\" )]` isn't allowed as a crate \
380
- level attribute",
381
- )
382
- . emit ( ) ;
439
+ } else if meta. has_name ( sym:: keyword) {
440
+ if !self . check_attr_crate_level ( meta, hir_id, "keyword" )
441
+ || !self . check_doc_keyword ( meta, hir_id)
442
+ {
383
443
return false ;
384
444
}
385
445
}
0 commit comments