@@ -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) {
@@ -297,89 +297,103 @@ impl CheckAttrVisitor<'tcx> {
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+ if !meta. is_value_str ( ) {
302+ self . doc_alias_str_error ( meta) ;
303+ return false ;
304+ }
305+ let doc_alias = meta. value_str ( ) . map ( |s| s. to_string ( ) ) . unwrap_or_else ( String :: new) ;
306+ if doc_alias. is_empty ( ) {
307+ self . doc_alias_str_error ( meta) ;
308+ return false ;
309+ }
310+ if let Some ( c) =
311+ doc_alias. chars ( ) . find ( |& c| c == '"' || c == '\'' || ( c. is_whitespace ( ) && c != ' ' ) )
312+ {
313+ self . tcx
314+ . sess
315+ . struct_span_err (
316+ meta. name_value_literal_span ( ) . unwrap_or_else ( || meta. span ( ) ) ,
317+ & format ! ( "{:?} character isn't allowed in `#[doc(alias = \" ...\" )]`" , c, ) ,
318+ )
319+ . emit ( ) ;
320+ return false ;
321+ }
322+ if doc_alias. starts_with ( ' ' ) || doc_alias. ends_with ( ' ' ) {
323+ self . tcx
324+ . sess
325+ . struct_span_err (
326+ meta. name_value_literal_span ( ) . unwrap_or_else ( || meta. span ( ) ) ,
327+ "`#[doc(alias = \" ...\" )]` cannot start or end with ' '" ,
328+ )
329+ . emit ( ) ;
330+ return false ;
331+ }
332+ if let Some ( err) = match target {
333+ Target :: Impl => Some ( "implementation block" ) ,
334+ Target :: ForeignMod => Some ( "extern block" ) ,
335+ Target :: AssocTy => {
336+ let parent_hir_id = self . tcx . hir ( ) . get_parent_item ( hir_id) ;
337+ let containing_item = self . tcx . hir ( ) . expect_item ( parent_hir_id) ;
338+ if Target :: from_item ( containing_item) == Target :: Impl {
339+ Some ( "type alias in implementation block" )
340+ } else {
341+ None
342+ }
343+ }
344+ Target :: AssocConst => {
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+ // We can't link to trait impl's consts.
348+ let err = "associated constant in trait implementation block" ;
349+ match containing_item. kind {
350+ ItemKind :: Impl { of_trait : Some ( _) , .. } => Some ( err) ,
351+ _ => None ,
352+ }
353+ }
354+ _ => None ,
355+ } {
356+ self . tcx
357+ . sess
358+ . struct_span_err (
359+ meta. span ( ) ,
360+ & format ! ( "`#[doc(alias = \" ...\" )]` isn't allowed on {}" , err) ,
361+ )
362+ . emit ( ) ;
363+ return false ;
364+ }
365+ true
366+ }
367+
368+ fn check_attr_crate_level (
369+ & self ,
370+ meta : & NestedMetaItem ,
371+ hir_id : HirId ,
372+ attr_name : & str ,
373+ ) -> bool {
374+ if CRATE_HIR_ID == hir_id {
375+ self . tcx
376+ . sess
377+ . struct_span_err (
378+ meta. span ( ) ,
379+ & format ! (
380+ "`#![doc({} = \" ...\" )]` isn't allowed as a crate level attribute" ,
381+ attr_name,
382+ ) ,
383+ )
384+ . emit ( ) ;
385+ return false ;
386+ }
387+ }
388+
389+ fn check_doc_attrs ( & self , attr : & Attribute , hir_id : HirId , target : Target ) -> bool {
301390 if let Some ( mi) = attr. meta ( ) {
302391 if let Some ( list) = mi. meta_item_list ( ) {
303392 for meta in list {
304393 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 != ' ' ) )
394+ if !self . check_attr_crate_level ( meta, hir_id, "alias" )
395+ || !self . check_doc_alias ( meta, hir_id, target)
318396 {
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- return false ;
330- }
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 ( ) ;
383397 return false ;
384398 }
385399 }
0 commit comments