11use rustc_ast:: ast:: { AttrStyle , LitKind , MetaItemLit } ;
22use rustc_feature:: template;
3+ use rustc_hir:: Target ;
34use rustc_hir:: attrs:: {
45 AttributeKind , CfgEntry , CfgHideShow , CfgInfo , DocAttribute , DocInline , HideOrShow ,
56} ;
@@ -12,8 +13,8 @@ use super::{AcceptMapping, AttributeParser};
1213use crate :: context:: { AcceptContext , FinalizeContext , Stage } ;
1314use crate :: parser:: { ArgParser , MetaItemOrLitParser , MetaItemParser , OwnedPathParser } ;
1415use crate :: session_diagnostics:: {
15- DocAliasBadChar , DocAliasEmpty , DocAliasMalformed , DocAliasStartEnd , DocAttributeNotAttribute ,
16- DocKeywordNotKeyword ,
16+ DocAliasBadChar , DocAliasEmpty , DocAliasMalformed , DocAliasStartEnd , DocAttrNotCrateLevel ,
17+ DocAttributeNotAttribute , DocKeywordNotKeyword ,
1718} ;
1819
1920fn check_keyword < S : Stage > ( cx : & mut AcceptContext < ' _ , ' _ , S > , keyword : Symbol , span : Span ) -> bool {
@@ -43,16 +44,39 @@ fn check_attribute<S: Stage>(
4344 false
4445}
4546
46- fn parse_keyword_and_attribute < S , F > (
47+ /// Checks that an attribute is *not* used at the crate level. Returns `true` if valid.
48+ fn check_attr_not_crate_level < S : Stage > (
49+ cx : & mut AcceptContext < ' _ , ' _ , S > ,
50+ span : Span ,
51+ attr_name : Symbol ,
52+ ) -> bool {
53+ if cx. shared . target . is_some_and ( |target| target == Target :: Crate ) {
54+ cx. emit_err ( DocAttrNotCrateLevel { span, attr_name } ) ;
55+ return false ;
56+ }
57+ true
58+ }
59+
60+ /// Checks that an attribute is used at the crate level. Returns `true` if valid.
61+ fn check_attr_crate_level < S : Stage > ( cx : & mut AcceptContext < ' _ , ' _ , S > , span : Span ) -> bool {
62+ if cx. shared . target . is_some_and ( |target| target != Target :: Crate ) {
63+ cx. emit_lint (
64+ rustc_session:: lint:: builtin:: INVALID_DOC_ATTRIBUTES ,
65+ AttributeLintKind :: AttrCrateLevelOnly ,
66+ span,
67+ ) ;
68+ return false ;
69+ }
70+ true
71+ }
72+
73+ fn parse_keyword_and_attribute < S : Stage > (
4774 cx : & mut AcceptContext < ' _ , ' _ , S > ,
4875 path : & OwnedPathParser ,
4976 args : & ArgParser ,
5077 attr_value : & mut Option < ( Symbol , Span ) > ,
51- callback : F ,
52- ) where
53- S : Stage ,
54- F : FnOnce ( & mut AcceptContext < ' _ , ' _ , S > , Symbol , Span ) -> bool ,
55- {
78+ attr_name : Symbol ,
79+ ) {
5680 let Some ( nv) = args. name_value ( ) else {
5781 cx. expected_name_value ( args. span ( ) . unwrap_or ( path. span ( ) ) , path. word_sym ( ) ) ;
5882 return ;
@@ -63,16 +87,26 @@ fn parse_keyword_and_attribute<S, F>(
6387 return ;
6488 } ;
6589
66- if !callback ( cx, value, nv. value_span ) {
90+ let ret = if attr_name == sym:: keyword {
91+ check_keyword ( cx, value, nv. value_span )
92+ } else {
93+ check_attribute ( cx, value, nv. value_span )
94+ } ;
95+ if !ret {
6796 return ;
6897 }
6998
99+ let span = path. span ( ) ;
70100 if attr_value. is_some ( ) {
71- cx. duplicate_key ( path . span ( ) , path. word_sym ( ) . unwrap ( ) ) ;
101+ cx. duplicate_key ( span, path. word_sym ( ) . unwrap ( ) ) ;
72102 return ;
73103 }
74104
75- * attr_value = Some ( ( value, path. span ( ) ) ) ;
105+ if !check_attr_not_crate_level ( cx, span, attr_name) {
106+ return ;
107+ }
108+
109+ * attr_value = Some ( ( value, span) ) ;
76110}
77111
78112#[ derive( Default , Debug ) ]
@@ -102,6 +136,10 @@ impl DocParser {
102136 return ;
103137 }
104138
139+ if !check_attr_crate_level ( cx, path. span ( ) ) {
140+ return ;
141+ }
142+
105143 self . attribute . no_crate_inject = Some ( path. span ( ) )
106144 }
107145 Some ( sym:: attr) => {
@@ -155,6 +193,9 @@ impl DocParser {
155193 cx. emit_err ( DocAliasStartEnd { span, attr_str } ) ;
156194 return ;
157195 }
196+ if !check_attr_not_crate_level ( cx, span, sym:: alias) {
197+ return ;
198+ }
158199
159200 if let Some ( first_definition) = self . attribute . aliases . get ( & alias) . copied ( ) {
160201 cx. emit_lint (
@@ -366,7 +407,33 @@ impl DocParser {
366407 self . attribute. $ident = Some ( path. span( ) ) ;
367408 } } ;
368409 }
369- macro_rules! string_arg {
410+ macro_rules! no_args_and_not_crate_level {
411+ ( $ident: ident) => { {
412+ if let Err ( span) = args. no_args( ) {
413+ cx. expected_no_args( span) ;
414+ return ;
415+ }
416+ let span = path. span( ) ;
417+ if !check_attr_not_crate_level( cx, span, sym:: $ident) {
418+ return ;
419+ }
420+ self . attribute. $ident = Some ( span) ;
421+ } } ;
422+ }
423+ macro_rules! no_args_and_crate_level {
424+ ( $ident: ident) => { {
425+ if let Err ( span) = args. no_args( ) {
426+ cx. expected_no_args( span) ;
427+ return ;
428+ }
429+ let span = path. span( ) ;
430+ if !check_attr_crate_level( cx, span) {
431+ return ;
432+ }
433+ self . attribute. $ident = Some ( span) ;
434+ } } ;
435+ }
436+ macro_rules! string_arg_and_crate_level {
370437 ( $ident: ident) => { {
371438 let Some ( nv) = args. name_value( ) else {
372439 cx. expected_name_value( args. span( ) . unwrap_or( path. span( ) ) , path. word_sym( ) ) ;
@@ -378,6 +445,10 @@ impl DocParser {
378445 return ;
379446 } ;
380447
448+ if !check_attr_crate_level( cx, path. span( ) ) {
449+ return ;
450+ }
451+
381452 // FIXME: It's errorring when the attribute is passed multiple times on the command
382453 // line.
383454 // The right fix for this would be to only check this rule if the attribute is
@@ -394,12 +465,14 @@ impl DocParser {
394465 match path. word_sym ( ) {
395466 Some ( sym:: alias) => self . parse_alias ( cx, path, args) ,
396467 Some ( sym:: hidden) => no_args ! ( hidden) ,
397- Some ( sym:: html_favicon_url) => string_arg ! ( html_favicon_url) ,
398- Some ( sym:: html_logo_url) => string_arg ! ( html_logo_url) ,
399- Some ( sym:: html_no_source) => no_args ! ( html_no_source) ,
400- Some ( sym:: html_playground_url) => string_arg ! ( html_playground_url) ,
401- Some ( sym:: html_root_url) => string_arg ! ( html_root_url) ,
402- Some ( sym:: issue_tracker_base_url) => string_arg ! ( issue_tracker_base_url) ,
468+ Some ( sym:: html_favicon_url) => string_arg_and_crate_level ! ( html_favicon_url) ,
469+ Some ( sym:: html_logo_url) => string_arg_and_crate_level ! ( html_logo_url) ,
470+ Some ( sym:: html_no_source) => no_args_and_crate_level ! ( html_no_source) ,
471+ Some ( sym:: html_playground_url) => string_arg_and_crate_level ! ( html_playground_url) ,
472+ Some ( sym:: html_root_url) => string_arg_and_crate_level ! ( html_root_url) ,
473+ Some ( sym:: issue_tracker_base_url) => {
474+ string_arg_and_crate_level ! ( issue_tracker_base_url)
475+ }
403476 Some ( sym:: inline) => self . parse_inline ( cx, path, args, DocInline :: Inline ) ,
404477 Some ( sym:: no_inline) => self . parse_inline ( cx, path, args, DocInline :: NoInline ) ,
405478 Some ( sym:: masked) => no_args ! ( masked) ,
@@ -410,18 +483,18 @@ impl DocParser {
410483 path,
411484 args,
412485 & mut self . attribute . keyword ,
413- check_keyword ,
486+ sym :: keyword ,
414487 ) ,
415488 Some ( sym:: attribute) => parse_keyword_and_attribute (
416489 cx,
417490 path,
418491 args,
419492 & mut self . attribute . attribute ,
420- check_attribute ,
493+ sym :: attribute ,
421494 ) ,
422- Some ( sym:: fake_variadic) => no_args ! ( fake_variadic) ,
423- Some ( sym:: search_unbox) => no_args ! ( search_unbox) ,
424- Some ( sym:: rust_logo) => no_args ! ( rust_logo) ,
495+ Some ( sym:: fake_variadic) => no_args_and_not_crate_level ! ( fake_variadic) ,
496+ Some ( sym:: search_unbox) => no_args_and_not_crate_level ! ( search_unbox) ,
497+ Some ( sym:: rust_logo) => no_args_and_crate_level ! ( rust_logo) ,
425498 Some ( sym:: auto_cfg) => self . parse_auto_cfg ( cx, path, args) ,
426499 Some ( sym:: test) => {
427500 let Some ( list) = args. list ( ) else {
0 commit comments