@@ -78,7 +78,7 @@ impl CheckAttrVisitor<'tcx> {
7878 } else if self . tcx . sess . check_name ( attr, sym:: track_caller) {
7979 self . check_track_caller ( & attr. span , attrs, span, target)
8080 } 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)
8282 } else if self . tcx . sess . check_name ( attr, sym:: no_link) {
8383 self . check_no_link ( & attr, span, target)
8484 } else if self . tcx . sess . check_name ( attr, sym:: export_name) {
@@ -287,99 +287,159 @@ impl CheckAttrVisitor<'tcx> {
287287 }
288288 }
289289
290- fn doc_alias_str_error ( & self , meta : & NestedMetaItem ) {
290+ fn doc_attr_str_error ( & self , meta : & NestedMetaItem , attr_name : & str ) {
291291 self . tcx
292292 . sess
293293 . struct_span_err (
294294 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 ) ,
296296 )
297297 . emit ( ) ;
298298 }
299299
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 {
301430 if let Some ( mi) = attr. meta ( ) {
302431 if let Some ( list) = mi. meta_item_list ( ) {
303432 for meta in list {
304433 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)
318436 {
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 ( ) ;
329437 return false ;
330438 }
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+ {
383443 return false ;
384444 }
385445 }
0 commit comments