1- use rustc_ast:: ast:: { AttrStyle , LitKind , MetaItemLit } ;
1+ use rustc_ast:: ExprKind ;
2+ use rustc_ast:: ast:: { self , AttrArgs , AttrKind , AttrStyle , LitKind , MetaItemLit } ;
23use rustc_attr_ir:: target:: Target ;
34use rustc_attr_ir:: {
45 AttributeKind , CfgEntry , CfgHideShow , DocAttribute , DocCfgHideShow , DocCfgHideShowValue ,
56 DocInline , HideOrShow ,
67} ;
78use rustc_data_structures:: fx:: { FxHashSet , FxIndexMap , IndexEntry } ;
8- use rustc_errors:: { Applicability , msg } ;
9+ use rustc_errors:: { Applicability , Diagnostic , MultiSpan } ;
910use rustc_feature:: AttributeStability ;
10- use rustc_session :: diagnostics :: feature_err ;
11+ use rustc_lint_defs :: LintId ;
1112use rustc_span:: { Span , Symbol , edition, sym} ;
1213
1314use super :: prelude:: { ALL_TARGETS , AllowedTargets } ;
1415use super :: { AcceptMapping , AttributeParser , template} ;
16+ use crate :: EmitAttribute ;
1517use crate :: context:: { AcceptContext , FinalizeContext } ;
1618use crate :: diagnostics:: {
1719 AttrCrateLevelOnly , DocAliasBadChar , DocAliasDuplicated , DocAliasEmpty , DocAliasMalformed ,
@@ -21,7 +23,7 @@ use crate::diagnostics::{
2123 DocAutoCfgHideShowValuesMix , DocAutoCfgWrongLiteral , DocKeywordNotKeyword , DocTestLiteral ,
2224 DocTestTakesList , DocTestUnknown , DocUnknownAny , DocUnknownInclude , DocUnknownPasses ,
2325 DocUnknownPlugins , DocUnknownSpotlight , ExpectedNameValue , ExpectedNoArgs ,
24- IllFormedAttributeInput , MalformedDoc , UnusedDuplicate ,
26+ IllFormedAttributeInput , InvalidExprInDocAttr , MalformedDoc , UnusedDuplicate ,
2527} ;
2628use crate :: parser:: {
2729 ArgParser , MetaItemListParser , MetaItemOrLitParser , MetaItemParser , OwnedPathParser ,
@@ -549,19 +551,15 @@ impl DocParser {
549551 }
550552 macro_rules! no_args_and_crate_level {
551553 ( $ident: ident) => { {
552- no_args_and_crate_level!( $ident, |span| { } ) ;
553- } } ;
554- ( $ident: ident, |$span: ident| $extra_validation: block) => { {
555554 if let Err ( span) = args. as_no_args( ) {
556555 expected_no_args( cx, span) ;
557556 return ;
558557 }
559- let $ span = path. span( ) ;
560- if !check_attr_crate_level( cx, $ span) {
558+ let span = path. span( ) ;
559+ if !check_attr_crate_level( cx, span) {
561560 return ;
562561 }
563- $extra_validation
564- self . attribute. $ident = Some ( $span) ;
562+ self . attribute. $ident = Some ( span) ;
565563 } } ;
566564 }
567565 macro_rules! string_arg_and_crate_level {
@@ -592,6 +590,12 @@ impl DocParser {
592590 self . attribute. $ident = Some ( ( s, path. span( ) ) ) ;
593591 } } ;
594592 }
593+ macro_rules! gated {
594+ ( $feature: ident $( , $notes: expr) * ) => {
595+ let stability = $crate:: unstable!( $feature $( , $notes) * ) ;
596+ cx. shared. cx. check_attribute_stability( & cx. attr_path, path. span( ) , stability) ;
597+ } ;
598+ }
595599
596600 match path. word_sym ( ) {
597601 Some ( sym:: alias) => self . parse_alias ( cx, path, args) ,
@@ -606,37 +610,60 @@ impl DocParser {
606610 }
607611 Some ( sym:: inline) => self . parse_inline ( cx, path, args, DocInline :: Inline ) ,
608612 Some ( sym:: no_inline) => self . parse_inline ( cx, path, args, DocInline :: NoInline ) ,
609- Some ( sym:: masked) => no_args ! ( masked) ,
610- Some ( sym:: cfg) => self . parse_cfg ( cx, args) ,
611- Some ( sym:: notable_trait) => no_args ! ( notable_trait) ,
612- Some ( sym:: keyword) => parse_keyword_and_attribute (
613- cx,
614- path,
615- args,
616- & mut self . attribute . keyword ,
617- sym:: keyword,
618- ) ,
619- Some ( sym:: attribute) => parse_keyword_and_attribute (
620- cx,
621- path,
622- args,
623- & mut self . attribute . attribute ,
624- sym:: attribute,
625- ) ,
626- Some ( sym:: fake_variadic) => no_args_and_not_crate_level ! ( fake_variadic) ,
627- Some ( sym:: search_unbox) => no_args_and_not_crate_level ! ( search_unbox) ,
628- Some ( sym:: rust_logo) => no_args_and_crate_level ! ( rust_logo, |span| {
629- if !cx. features( ) . rustdoc_internals( ) {
630- feature_err(
631- cx. sess( ) ,
632- sym:: rustdoc_internals,
633- span,
634- msg!( "the `#[doc(rust_logo)]` attribute is used for Rust branding" ) ,
635- )
636- . emit( ) ;
613+ Some ( sym:: masked) => {
614+ gated ! ( doc_masked) ;
615+ no_args ! ( masked)
616+ }
617+ Some ( sym:: cfg) => {
618+ gated ! ( doc_cfg) ;
619+ self . parse_cfg ( cx, args)
620+ }
621+ Some ( sym:: notable_trait) => {
622+ gated ! ( doc_notable_trait) ;
623+ no_args ! ( notable_trait)
624+ }
625+ Some ( sym:: keyword) => {
626+ gated ! ( rustdoc_internals) ;
627+ parse_keyword_and_attribute (
628+ cx,
629+ path,
630+ args,
631+ & mut self . attribute . keyword ,
632+ sym:: keyword,
633+ )
634+ }
635+ Some ( sym:: attribute) => {
636+ gated ! ( rustdoc_internals) ;
637+ parse_keyword_and_attribute (
638+ cx,
639+ path,
640+ args,
641+ & mut self . attribute . attribute ,
642+ sym:: attribute,
643+ )
644+ }
645+ Some ( sym:: fake_variadic) => {
646+ gated ! ( rustdoc_internals) ;
647+ no_args_and_not_crate_level ! ( fake_variadic)
648+ }
649+ Some ( sym:: search_unbox) => {
650+ gated ! ( rustdoc_internals) ;
651+ no_args_and_not_crate_level ! ( search_unbox)
652+ }
653+ Some ( sym:: rust_logo) => {
654+ // FIXME: Only feature gated at the crate level (!!)
655+ if cx. target == Target :: Crate {
656+ gated ! (
657+ rustdoc_internals,
658+ "the `#[doc(rust_logo)]` attribute is used for Rust branding"
659+ ) ;
637660 }
638- } ) ,
639- Some ( sym:: auto_cfg) => self . parse_auto_cfg ( cx, path, args) ,
661+ no_args_and_crate_level ! ( rust_logo)
662+ }
663+ Some ( sym:: auto_cfg) => {
664+ gated ! ( doc_cfg) ;
665+ self . parse_auto_cfg ( cx, path, args)
666+ }
640667 Some ( sym:: test) => {
641668 let Some ( list) = args. as_list ( ) else {
642669 cx. emit_lint (
@@ -842,3 +869,41 @@ impl AttributeParser for DocParser {
842869 }
843870 }
844871}
872+
873+ /// Is this a `#[doc = mac!()]`?
874+ ///
875+ /// Or perhaps something as spicy as this?
876+ /// ```ignore,_
877+ /// #[doc = {
878+ /// let a = 1;
879+ /// let b = 1;
880+ /// let sum = a + b;
881+ /// assert_eq!(sum, 2);
882+ /// }]
883+ /// println!();
884+ /// ```
885+ pub ( crate ) fn lint_non_lit_doc_attr (
886+ mut emit_lint : impl FnMut ( LintId , MultiSpan , EmitAttribute ) ,
887+ attr : & ast:: Attribute ,
888+ ) -> bool {
889+ if !attr. has_name ( sym:: doc) {
890+ return false ;
891+ }
892+ let AttrKind :: Normal ( n) = & attr. kind else { return false } ;
893+ let AttrArgs :: Eq { expr, .. } = & n. item . args else { return false } ;
894+ if matches ! ( expr. kind, ExprKind :: Lit ( _) ) {
895+ return false ;
896+ } ;
897+
898+ let attr_span = attr. span ;
899+ let expr_span = expr. span ;
900+
901+ emit_lint (
902+ LintId :: of ( rustc_session:: lint:: builtin:: ILL_FORMED_ATTRIBUTE_INPUT ) ,
903+ attr_span. into ( ) ,
904+ EmitAttribute ( Box :: new ( move |dcx, level, _| {
905+ InvalidExprInDocAttr { attr_span, expr_span } . into_diag ( dcx, level)
906+ } ) ) ,
907+ ) ;
908+ true
909+ }
0 commit comments