Skip to content

Commit 623360c

Browse files
committed
rework handling of doc attributes on macro calls
1 parent f7d782a commit 623360c

26 files changed

Lines changed: 404 additions & 165 deletions

Cargo.lock

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4001,7 +4001,6 @@ name = "rustc_expand"
40014001
version = "0.0.0"
40024002
dependencies = [
40034003
"rustc_ast",
4004-
"rustc_ast_passes",
40054004
"rustc_ast_pretty",
40064005
"rustc_attr_ir",
40074006
"rustc_attr_parsing",

compiler/rustc_ast_lowering/src/lib.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ use rustc_ast::mut_visit::{self, MutVisitor};
4545
use rustc_ast::node_id::NodeMap;
4646
use rustc_ast::visit::{self, Visitor};
4747
use rustc_ast::{self as ast, *};
48-
use rustc_attr_parsing::{AttributeParser, OmitDoc, Recovery, ShouldEmit};
48+
use rustc_attr_parsing::{AttributeParser, Recovery, ShouldEmit};
4949
use rustc_data_structures::fx::FxIndexMap;
5050
use rustc_data_structures::sorted_map::SortedMap;
5151
use rustc_data_structures::stable_hash::{StableHash, StableHasher};
@@ -1231,7 +1231,6 @@ impl<'hir> LoweringContext<'_, 'hir> {
12311231
attrs,
12321232
target_span,
12331233
target,
1234-
OmitDoc::Lower,
12351234
|s| l.lower(s),
12361235
|lint_id, span, kind| {
12371236
self.delayed_lints.push(DelayedLint {

compiler/rustc_ast_passes/src/feature_gate.rs

Lines changed: 3 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -46,10 +46,6 @@ macro_rules! gate_multi {
4646
}};
4747
}
4848

49-
pub fn check_attribute(attr: &ast::Attribute, sess: &Session, features: &Features) {
50-
PostExpansionVisitor { sess, features }.visit_attribute(attr)
51-
}
52-
5349
struct PostExpansionVisitor<'a> {
5450
sess: &'a Session,
5551

@@ -152,33 +148,9 @@ impl<'a> PostExpansionVisitor<'a> {
152148
}
153149

154150
impl<'a> Visitor<'a> for PostExpansionVisitor<'a> {
155-
fn visit_attribute(&mut self, attr: &ast::Attribute) {
156-
// Check unstable flavors of the `#[doc]` attribute.
157-
if attr.has_name(sym::doc) {
158-
for meta_item_inner in attr.meta_item_list().unwrap_or_default() {
159-
macro_rules! gate_doc { ($($s:literal { $($name:ident => $feature:ident)* })*) => {
160-
$($(if meta_item_inner.has_name(sym::$name) {
161-
let msg = concat!("`#[doc(", stringify!($name), ")]` is ", $s);
162-
gate!(self, $feature, attr.span, msg);
163-
})*)*
164-
}}
165-
166-
gate_doc!(
167-
"experimental" {
168-
cfg => doc_cfg
169-
auto_cfg => doc_cfg
170-
masked => doc_masked
171-
notable_trait => doc_notable_trait
172-
}
173-
"meant for internal use only" {
174-
attribute => rustdoc_internals
175-
keyword => rustdoc_internals
176-
fake_variadic => rustdoc_internals
177-
search_unbox => rustdoc_internals
178-
}
179-
);
180-
}
181-
}
151+
fn visit_attribute(&mut self, attr: &'a ast::Attribute) {
152+
// Checked in attribute parsers, do NOT add checks here
153+
visit::walk_attribute(self, attr)
182154
}
183155

184156
fn visit_item(&mut self, i: &'a ast::Item) {

compiler/rustc_attr_parsing/src/attributes/doc.rs

Lines changed: 106 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,19 @@
1-
use rustc_ast::ast::{AttrStyle, LitKind, MetaItemLit};
1+
use rustc_ast::ExprKind;
2+
use rustc_ast::ast::{self, AttrArgs, AttrKind, AttrStyle, LitKind, MetaItemLit};
23
use rustc_attr_ir::target::Target;
34
use rustc_attr_ir::{
45
AttributeKind, CfgEntry, CfgHideShow, DocAttribute, DocCfgHideShow, DocCfgHideShowValue,
56
DocInline, HideOrShow,
67
};
78
use rustc_data_structures::fx::{FxHashSet, FxIndexMap, IndexEntry};
8-
use rustc_errors::{Applicability, msg};
9+
use rustc_errors::{Applicability, Diagnostic, MultiSpan};
910
use rustc_feature::AttributeStability;
10-
use rustc_session::diagnostics::feature_err;
11+
use rustc_lint_defs::LintId;
1112
use rustc_span::{Span, Symbol, edition, sym};
1213

1314
use super::prelude::{ALL_TARGETS, AllowedTargets};
1415
use super::{AcceptMapping, AttributeParser, template};
16+
use crate::EmitAttribute;
1517
use crate::context::{AcceptContext, FinalizeContext};
1618
use 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
};
2628
use 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+
}

compiler/rustc_attr_parsing/src/context.rs

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -865,12 +865,6 @@ impl<'p, 'sess: 'p> DerefMut for SharedContext<'p, 'sess> {
865865
}
866866
}
867867

868-
#[derive(PartialEq, Clone, Copy, Debug)]
869-
pub enum OmitDoc {
870-
Lower,
871-
Skip,
872-
}
873-
874868
#[derive(Copy, Clone, Debug)]
875869
pub enum ShouldEmit {
876870
/// The operations will emit errors, and lints, and errors are fatal.

compiler/rustc_attr_parsing/src/diagnostics.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1334,6 +1334,15 @@ pub(crate) enum InvalidTargetHelp {
13341334
UseRustcAlignStatic,
13351335
}
13361336

1337+
#[derive(Diagnostic)]
1338+
#[diag("invalid expression in `doc` attribute on macro invocation")]
1339+
pub(crate) struct InvalidExprInDocAttr {
1340+
#[primary_span]
1341+
pub expr_span: Span,
1342+
#[suggestion("remove the attribute", code = "", applicability = "machine-applicable")]
1343+
pub attr_span: Span,
1344+
}
1345+
13371346
#[derive(Diagnostic)]
13381347
#[diag("invalid alignment value: {$error_part}", code = E0589)]
13391348
pub(crate) struct InvalidAlignmentValue {

compiler/rustc_attr_parsing/src/interface.rs

Lines changed: 6 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -17,14 +17,15 @@ use rustc_session::lint::LintId;
1717
use rustc_span::{DUMMY_SP, ErrorGuaranteed, Span, Symbol, sym};
1818

1919
use crate::attributes::AttributeSafety;
20+
use crate::attributes::doc::lint_non_lit_doc_attr;
2021
use crate::context::{
2122
ATTRIBUTE_PARSERS, AcceptContext, FinalizeCheckContext, FinalizeCheckFn, FinalizeContext,
2223
FinalizeFn, FinalizeOutput, SharedContext,
2324
};
2425
use crate::diagnostics::ParsedDescription;
2526
use crate::parser::{AllowExprMetavar, ArgParser, PathParser, RefPathParser};
2627
use crate::synthetic::SyntheticAttrState;
27-
use crate::{AttributeTemplate, OmitDoc, ShouldEmit};
28+
use crate::{AttributeTemplate, ShouldEmit};
2829

2930
pub struct EmitAttribute(
3031
pub Box<
@@ -162,7 +163,6 @@ impl<'sess> AttributeParser<'sess> {
162163
attrs,
163164
target_span,
164165
target,
165-
OmitDoc::Skip,
166166
std::convert::identity,
167167
|lint_id, span, kind| {
168168
sess.psess.dyn_buffer_lint_sess(lint_id.lint, span, target_node_id, kind.0)
@@ -311,14 +311,12 @@ impl<'sess> AttributeParser<'sess> {
311311

312312
/// Parse a list of attributes.
313313
///
314-
/// `target_span` is the span of the thing this list of attributes is applied to,
315-
/// and when `omit_doc` is set, doc attributes are filtered out.
314+
/// `target_span` is the span of the thing this list of attributes is applied to.
316315
pub fn parse_attribute_list(
317316
&mut self,
318317
attrs: &[ast::Attribute],
319318
target_span: Span,
320319
target: Target,
321-
omit_doc: OmitDoc,
322320
lower_span: impl Copy + Fn(Span) -> Span,
323321
mut emit_lint: impl FnMut(LintId, MultiSpan, EmitAttribute),
324322
) -> Vec<Attribute> {
@@ -336,23 +334,14 @@ impl<'sess> AttributeParser<'sess> {
336334
}
337335
}
338336

339-
// Sometimes, for example for `#![doc = include_str!("readme.md")]`,
340-
// doc still contains a non-literal. You might say, when we're lowering attributes
341-
// that's expanded right? But no, sometimes, when parsing attributes on macros,
342-
// we already use the lowering logic and these are still there. So, when `omit_doc`
343-
// is set we *also* want to ignore these.
344-
let is_doc_attribute = attr.has_name(sym::doc);
345-
if omit_doc == OmitDoc::Skip && is_doc_attribute {
337+
// FIXME accidentally allowed on Stable Rust
338+
if target == Target::MacroCall && lint_non_lit_doc_attr(&mut emit_lint, attr) {
346339
continue;
347340
}
348341

349342
let attr_span = lower_span(attr.span);
350343
match &attr.kind {
351344
ast::AttrKind::DocComment(comment_kind, symbol) => {
352-
if omit_doc == OmitDoc::Skip {
353-
continue;
354-
}
355-
356345
attributes.push(Attribute::Parsed(AttributeKind::DocComment {
357346
style: attr.style,
358347
kind: DocFragmentKind::Sugared(*comment_kind),
@@ -408,7 +397,7 @@ impl<'sess> AttributeParser<'sess> {
408397
// bla
409398
// blob
410399
// a
411-
if is_doc_attribute
400+
if attr.has_name(sym::doc)
412401
&& let ArgParser::NameValue(nv) = &args
413402
// If not a string key/value, it should emit an error, but to make
414403
// things simpler, it's handled in `DocParser` because it's simpler to

compiler/rustc_attr_parsing/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,7 @@ pub use attributes::cfg::{
116116
};
117117
pub use attributes::cfg_select::*;
118118
pub use attributes::util::{is_builtin_attr, parse_version};
119-
pub use context::{OmitDoc, ShouldEmit};
119+
pub use context::ShouldEmit;
120120
pub use diagnostics::ParsedDescription;
121121
pub use interface::{AttributeParser, EmitAttribute};
122122
pub use rustc_parse::parser::Recovery;

0 commit comments

Comments
 (0)