1use rustc_ast::ast::{AttrStyle, LitKind, MetaItemLit};
2use rustc_errors::{Applicability, Diagnostic, msg};
3use rustc_feature::template;
4use rustc_hir::Target;
5use rustc_hir::attrs::{
6 AttributeKind, CfgEntry, CfgHideShow, CfgInfo, DocAttribute, DocInline, HideOrShow,
7};
8use rustc_hir::lints::AttributeLintKind;
9use rustc_session::parse::feature_err;
10use rustc_span::{Span, Symbol, edition, sym};
11use thin_vec::ThinVec;
12
13use super::prelude::{ALL_TARGETS, AllowedTargets};
14use super::{AcceptMapping, AttributeParser};
15use crate::context::{AcceptContext, FinalizeContext, Stage};
16use crate::errors::{
17 DocAliasDuplicated, DocAutoCfgExpectsHideOrShow, DocAutoCfgHideShowExpectsList,
18 DocAutoCfgHideShowUnexpectedItem, DocAutoCfgWrongLiteral, DocUnknownAny, DocUnknownInclude,
19 DocUnknownPasses, DocUnknownPlugins, DocUnknownSpotlight, IllFormedAttributeInput,
20};
21use crate::parser::{ArgParser, MetaItemOrLitParser, MetaItemParser, OwnedPathParser};
22use crate::session_diagnostics::{
23 DocAliasBadChar, DocAliasEmpty, DocAliasMalformed, DocAliasStartEnd, DocAttrNotCrateLevel,
24 DocAttributeNotAttribute, DocKeywordNotKeyword,
25};
26
27fn check_keyword<S: Stage>(cx: &mut AcceptContext<'_, '_, S>, keyword: Symbol, span: Span) -> bool {
28 if keyword.is_reserved(|| edition::LATEST_STABLE_EDITION)
32 || keyword.is_weak()
33 || keyword == sym::SelfTy
34 {
35 return true;
36 }
37 cx.emit_err(DocKeywordNotKeyword { span, keyword });
38 false
39}
40
41fn check_attribute<S: Stage>(
42 cx: &mut AcceptContext<'_, '_, S>,
43 attribute: Symbol,
44 span: Span,
45) -> bool {
46 if rustc_feature::BUILTIN_ATTRIBUTE_MAP.contains_key(&attribute) {
48 return true;
49 }
50 cx.emit_err(DocAttributeNotAttribute { span, attribute });
51 false
52}
53
54fn check_attr_not_crate_level<S: Stage>(
56 cx: &mut AcceptContext<'_, '_, S>,
57 span: Span,
58 attr_name: Symbol,
59) -> bool {
60 if cx.shared.target == Target::Crate {
61 cx.emit_err(DocAttrNotCrateLevel { span, attr_name });
62 return false;
63 }
64 true
65}
66
67fn check_attr_crate_level<S: Stage>(cx: &mut AcceptContext<'_, '_, S>, span: Span) -> bool {
69 if cx.shared.target != Target::Crate {
70 cx.emit_lint(
71 rustc_session::lint::builtin::INVALID_DOC_ATTRIBUTES,
72 AttributeLintKind::AttrCrateLevelOnly,
73 span,
74 );
75 return false;
76 }
77 true
78}
79
80fn expected_name_value<S: Stage>(
82 cx: &mut AcceptContext<'_, '_, S>,
83 span: Span,
84 _name: Option<Symbol>,
85) {
86 cx.emit_lint(
87 rustc_session::lint::builtin::INVALID_DOC_ATTRIBUTES,
88 AttributeLintKind::ExpectedNameValue,
89 span,
90 );
91}
92
93fn expected_no_args<S: Stage>(cx: &mut AcceptContext<'_, '_, S>, span: Span) {
95 cx.emit_lint(
96 rustc_session::lint::builtin::INVALID_DOC_ATTRIBUTES,
97 AttributeLintKind::ExpectedNoArgs,
98 span,
99 );
100}
101
102fn expected_string_literal<S: Stage>(
105 cx: &mut AcceptContext<'_, '_, S>,
106 span: Span,
107 _actual_literal: Option<&MetaItemLit>,
108) {
109 cx.emit_lint(
110 rustc_session::lint::builtin::INVALID_DOC_ATTRIBUTES,
111 AttributeLintKind::MalformedDoc,
112 span,
113 );
114}
115
116fn parse_keyword_and_attribute<S: Stage>(
117 cx: &mut AcceptContext<'_, '_, S>,
118 path: &OwnedPathParser,
119 args: &ArgParser,
120 attr_value: &mut Option<(Symbol, Span)>,
121 attr_name: Symbol,
122) {
123 let Some(nv) = args.name_value() else {
124 expected_name_value(cx, args.span().unwrap_or(path.span()), path.word_sym());
125 return;
126 };
127
128 let Some(value) = nv.value_as_str() else {
129 expected_string_literal(cx, nv.value_span, Some(nv.value_as_lit()));
130 return;
131 };
132
133 let ret = if attr_name == sym::keyword {
134 check_keyword(cx, value, nv.value_span)
135 } else {
136 check_attribute(cx, value, nv.value_span)
137 };
138 if !ret {
139 return;
140 }
141
142 let span = path.span();
143 if attr_value.is_some() {
144 cx.adcx().duplicate_key(span, path.word_sym().unwrap());
145 return;
146 }
147
148 if !check_attr_not_crate_level(cx, span, attr_name) {
149 return;
150 }
151
152 *attr_value = Some((value, span));
153}
154
155#[derive(#[automatically_derived]
impl ::core::default::Default for DocParser {
#[inline]
fn default() -> DocParser {
DocParser {
attribute: ::core::default::Default::default(),
nb_doc_attrs: ::core::default::Default::default(),
}
}
}Default, #[automatically_derived]
impl ::core::fmt::Debug for DocParser {
#[inline]
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
::core::fmt::Formatter::debug_struct_field2_finish(f, "DocParser",
"attribute", &self.attribute, "nb_doc_attrs", &&self.nb_doc_attrs)
}
}Debug)]
156pub(crate) struct DocParser {
157 attribute: DocAttribute,
158 nb_doc_attrs: usize,
159}
160
161impl DocParser {
162 fn parse_single_test_doc_attr_item<S: Stage>(
163 &mut self,
164 cx: &mut AcceptContext<'_, '_, S>,
165 mip: &MetaItemParser,
166 ) {
167 let path = mip.path();
168 let args = mip.args();
169
170 match path.word_sym() {
171 Some(sym::no_crate_inject) => {
172 if let Err(span) = args.no_args() {
173 expected_no_args(cx, span);
174 return;
175 }
176
177 if let Some(used_span) = self.attribute.no_crate_inject {
178 let unused_span = path.span();
179 cx.emit_dyn_lint(
180 rustc_session::lint::builtin::INVALID_DOC_ATTRIBUTES,
181 move |dcx, level| {
182 rustc_errors::lints::UnusedDuplicate {
183 this: unused_span,
184 other: used_span,
185 warning: true,
186 }
187 .into_diag(dcx, level)
188 },
189 unused_span,
190 );
191 return;
192 }
193
194 if !check_attr_crate_level(cx, path.span()) {
195 return;
196 }
197
198 self.attribute.no_crate_inject = Some(path.span())
199 }
200 Some(sym::attr) => {
201 let Some(list) = args.list() else {
202 let span = cx.attr_span;
205 cx.emit_lint(
206 rustc_session::lint::builtin::INVALID_DOC_ATTRIBUTES,
207 AttributeLintKind::MalformedDoc,
208 span,
209 );
210 return;
211 };
212
213 for attr in list.mixed() {
215 self.attribute.test_attrs.push(attr.span());
216 }
217 }
218 Some(name) => {
219 cx.emit_lint(
220 rustc_session::lint::builtin::INVALID_DOC_ATTRIBUTES,
221 AttributeLintKind::DocTestUnknown { name },
222 path.span(),
223 );
224 }
225 None => {
226 cx.emit_lint(
227 rustc_session::lint::builtin::INVALID_DOC_ATTRIBUTES,
228 AttributeLintKind::DocTestLiteral,
229 path.span(),
230 );
231 }
232 }
233 }
234
235 fn add_alias<S: Stage>(
236 &mut self,
237 cx: &mut AcceptContext<'_, '_, S>,
238 alias: Symbol,
239 span: Span,
240 ) {
241 let attr_str = "`#[doc(alias = \"...\")]`";
242 if alias == sym::empty {
243 cx.emit_err(DocAliasEmpty { span, attr_str });
244 return;
245 }
246
247 let alias_str = alias.as_str();
248 if let Some(c) =
249 alias_str.chars().find(|&c| c == '"' || c == '\'' || (c.is_whitespace() && c != ' '))
250 {
251 cx.emit_err(DocAliasBadChar { span, attr_str, char_: c });
252 return;
253 }
254 if alias_str.starts_with(' ') || alias_str.ends_with(' ') {
255 cx.emit_err(DocAliasStartEnd { span, attr_str });
256 return;
257 }
258 if !check_attr_not_crate_level(cx, span, sym::alias) {
259 return;
260 }
261
262 if let Some(first_definition) = self.attribute.aliases.get(&alias).copied() {
263 cx.emit_dyn_lint(
264 rustc_session::lint::builtin::UNUSED_ATTRIBUTES,
265 move |dcx, level| DocAliasDuplicated { first_definition }.into_diag(dcx, level),
266 span,
267 );
268 }
269
270 self.attribute.aliases.insert(alias, span);
271 }
272
273 fn parse_alias<S: Stage>(
274 &mut self,
275 cx: &mut AcceptContext<'_, '_, S>,
276 path: &OwnedPathParser,
277 args: &ArgParser,
278 ) {
279 match args {
280 ArgParser::NoArgs => {
281 cx.emit_err(DocAliasMalformed { span: args.span().unwrap_or(path.span()) });
282 }
283 ArgParser::List(list) => {
284 for i in list.mixed() {
285 let Some(alias) = i.lit().and_then(|i| i.value_str()) else {
286 cx.adcx().expected_string_literal(i.span(), i.lit());
287 continue;
288 };
289
290 self.add_alias(cx, alias, i.span());
291 }
292 }
293 ArgParser::NameValue(nv) => {
294 let Some(alias) = nv.value_as_str() else {
295 cx.adcx().expected_string_literal(nv.value_span, Some(nv.value_as_lit()));
296 return;
297 };
298 self.add_alias(cx, alias, nv.value_span);
299 }
300 }
301 }
302
303 fn parse_inline<S: Stage>(
304 &mut self,
305 cx: &mut AcceptContext<'_, '_, S>,
306 path: &OwnedPathParser,
307 args: &ArgParser,
308 inline: DocInline,
309 ) {
310 if let Err(span) = args.no_args() {
311 expected_no_args(cx, span);
312 return;
313 }
314
315 self.attribute.inline.push((inline, path.span()));
316 }
317
318 fn parse_cfg<S: Stage>(&mut self, cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) {
319 fn simplify_cfg(cfg_entry: &mut CfgEntry) {
321 match cfg_entry {
322 CfgEntry::All(cfgs, span) if cfgs.is_empty() => {
323 *cfg_entry = CfgEntry::Bool(true, *span)
324 }
325 CfgEntry::Any(cfgs, span) if cfgs.is_empty() => {
326 *cfg_entry = CfgEntry::Bool(false, *span)
327 }
328 CfgEntry::Not(cfg, _) => simplify_cfg(cfg),
329 _ => {}
330 }
331 }
332 if let Some(mut cfg_entry) = super::cfg::parse_cfg(cx, args) {
333 simplify_cfg(&mut cfg_entry);
334 self.attribute.cfg.push(cfg_entry);
335 }
336 }
337
338 fn parse_auto_cfg<S: Stage>(
339 &mut self,
340 cx: &mut AcceptContext<'_, '_, S>,
341 path: &OwnedPathParser,
342 args: &ArgParser,
343 ) {
344 match args {
345 ArgParser::NoArgs => {
346 self.attribute.auto_cfg_change.push((true, path.span()));
347 }
348 ArgParser::List(list) => {
349 for meta in list.mixed() {
350 let MetaItemOrLitParser::MetaItemParser(item) = meta else {
351 cx.emit_dyn_lint(
352 rustc_session::lint::builtin::INVALID_DOC_ATTRIBUTES,
353 |dcx, level| DocAutoCfgExpectsHideOrShow.into_diag(dcx, level),
354 meta.span(),
355 );
356 continue;
357 };
358 let (kind, attr_name) = match item.path().word_sym() {
359 Some(sym::hide) => (HideOrShow::Hide, sym::hide),
360 Some(sym::show) => (HideOrShow::Show, sym::show),
361 _ => {
362 cx.emit_dyn_lint(
363 rustc_session::lint::builtin::INVALID_DOC_ATTRIBUTES,
364 |dcx, level| DocAutoCfgExpectsHideOrShow.into_diag(dcx, level),
365 item.span(),
366 );
367 continue;
368 }
369 };
370 let ArgParser::List(list) = item.args() else {
371 cx.emit_dyn_lint(
372 rustc_session::lint::builtin::INVALID_DOC_ATTRIBUTES,
373 move |dcx, level| {
374 DocAutoCfgHideShowExpectsList { attr_name }.into_diag(dcx, level)
375 },
376 item.span(),
377 );
378 continue;
379 };
380
381 let mut cfg_hide_show = CfgHideShow { kind, values: ThinVec::new() };
382
383 for item in list.mixed() {
384 let MetaItemOrLitParser::MetaItemParser(sub_item) = item else {
385 cx.emit_dyn_lint(
386 rustc_session::lint::builtin::INVALID_DOC_ATTRIBUTES,
387 move |dcx, level| {
388 DocAutoCfgHideShowUnexpectedItem { attr_name }
389 .into_diag(dcx, level)
390 },
391 item.span(),
392 );
393 continue;
394 };
395 match sub_item.args() {
396 a @ (ArgParser::NoArgs | ArgParser::NameValue(_)) => {
397 let Some(name) = sub_item.path().word_sym() else {
398 cx.emit_lint(
402 rustc_session::lint::builtin::INVALID_DOC_ATTRIBUTES,
403 AttributeLintKind::MalformedDoc,
404 sub_item.path().span(),
405 );
406 continue;
407 };
408 if let Ok(CfgEntry::NameValue { name, value, .. }) =
409 super::cfg::parse_name_value(
410 name,
411 sub_item.path().span(),
412 a.name_value(),
413 sub_item.span(),
414 cx,
415 )
416 {
417 cfg_hide_show.values.push(CfgInfo {
418 name,
419 name_span: sub_item.path().span(),
420 value: value
423 .map(|v| (v, a.name_value().unwrap().value_span)),
424 })
425 }
426 }
427 _ => {
428 cx.emit_dyn_lint(
429 rustc_session::lint::builtin::INVALID_DOC_ATTRIBUTES,
430 move |dcx, level| {
431 DocAutoCfgHideShowUnexpectedItem { attr_name }
432 .into_diag(dcx, level)
433 },
434 sub_item.span(),
435 );
436 continue;
437 }
438 }
439 }
440 self.attribute.auto_cfg.push((cfg_hide_show, path.span()));
441 }
442 }
443 ArgParser::NameValue(nv) => {
444 let MetaItemLit { kind: LitKind::Bool(bool_value), span, .. } = nv.value_as_lit()
445 else {
446 cx.emit_dyn_lint(
447 rustc_session::lint::builtin::INVALID_DOC_ATTRIBUTES,
448 move |dcx, level| DocAutoCfgWrongLiteral.into_diag(dcx, level),
449 nv.value_span,
450 );
451 return;
452 };
453 self.attribute.auto_cfg_change.push((*bool_value, *span));
454 }
455 }
456 }
457
458 fn parse_single_doc_attr_item<S: Stage>(
459 &mut self,
460 cx: &mut AcceptContext<'_, '_, S>,
461 mip: &MetaItemParser,
462 ) {
463 let path = mip.path();
464 let args = mip.args();
465
466 macro_rules! no_args {
467 ($ident: ident) => {{
468 if let Err(span) = args.no_args() {
469 expected_no_args(cx, span);
470 return;
471 }
472
473 self.attribute.$ident = Some(path.span());
483 }};
484 }
485 macro_rules! no_args_and_not_crate_level {
486 ($ident: ident) => {{
487 if let Err(span) = args.no_args() {
488 expected_no_args(cx, span);
489 return;
490 }
491 let span = path.span();
492 if !check_attr_not_crate_level(cx, span, sym::$ident) {
493 return;
494 }
495 self.attribute.$ident = Some(span);
496 }};
497 }
498 macro_rules! no_args_and_crate_level {
499 ($ident: ident) => {{
500 no_args_and_crate_level!($ident, |span| {});
501 }};
502 ($ident: ident, |$span:ident| $extra_validation:block) => {{
503 if let Err(span) = args.no_args() {
504 expected_no_args(cx, span);
505 return;
506 }
507 let $span = path.span();
508 if !check_attr_crate_level(cx, $span) {
509 return;
510 }
511 $extra_validation
512 self.attribute.$ident = Some($span);
513 }};
514 }
515 macro_rules! string_arg_and_crate_level {
516 ($ident: ident) => {{
517 let Some(nv) = args.name_value() else {
518 expected_name_value(cx, args.span().unwrap_or(path.span()), path.word_sym());
519 return;
520 };
521
522 let Some(s) = nv.value_as_str() else {
523 expected_string_literal(cx, nv.value_span, Some(nv.value_as_lit()));
524 return;
525 };
526
527 if !check_attr_crate_level(cx, path.span()) {
528 return;
529 }
530
531 self.attribute.$ident = Some((s, path.span()));
541 }};
542 }
543
544 match path.word_sym() {
545 Some(sym::alias) => self.parse_alias(cx, path, args),
546 Some(sym::hidden) => {
if let Err(span) = args.no_args() { expected_no_args(cx, span); return; }
self.attribute.hidden = Some(path.span());
}no_args!(hidden),
547 Some(sym::html_favicon_url) => {
let Some(nv) =
args.name_value() else {
expected_name_value(cx, args.span().unwrap_or(path.span()),
path.word_sym());
return;
};
let Some(s) =
nv.value_as_str() else {
expected_string_literal(cx, nv.value_span,
Some(nv.value_as_lit()));
return;
};
if !check_attr_crate_level(cx, path.span()) { return; }
self.attribute.html_favicon_url = Some((s, path.span()));
}string_arg_and_crate_level!(html_favicon_url),
548 Some(sym::html_logo_url) => {
let Some(nv) =
args.name_value() else {
expected_name_value(cx, args.span().unwrap_or(path.span()),
path.word_sym());
return;
};
let Some(s) =
nv.value_as_str() else {
expected_string_literal(cx, nv.value_span,
Some(nv.value_as_lit()));
return;
};
if !check_attr_crate_level(cx, path.span()) { return; }
self.attribute.html_logo_url = Some((s, path.span()));
}string_arg_and_crate_level!(html_logo_url),
549 Some(sym::html_no_source) => {
{
if let Err(span) = args.no_args() {
expected_no_args(cx, span);
return;
}
let span = path.span();
if !check_attr_crate_level(cx, span) { return; }
{}
self.attribute.html_no_source = Some(span);
};
}no_args_and_crate_level!(html_no_source),
550 Some(sym::html_playground_url) => {
let Some(nv) =
args.name_value() else {
expected_name_value(cx, args.span().unwrap_or(path.span()),
path.word_sym());
return;
};
let Some(s) =
nv.value_as_str() else {
expected_string_literal(cx, nv.value_span,
Some(nv.value_as_lit()));
return;
};
if !check_attr_crate_level(cx, path.span()) { return; }
self.attribute.html_playground_url = Some((s, path.span()));
}string_arg_and_crate_level!(html_playground_url),
551 Some(sym::html_root_url) => {
let Some(nv) =
args.name_value() else {
expected_name_value(cx, args.span().unwrap_or(path.span()),
path.word_sym());
return;
};
let Some(s) =
nv.value_as_str() else {
expected_string_literal(cx, nv.value_span,
Some(nv.value_as_lit()));
return;
};
if !check_attr_crate_level(cx, path.span()) { return; }
self.attribute.html_root_url = Some((s, path.span()));
}string_arg_and_crate_level!(html_root_url),
552 Some(sym::issue_tracker_base_url) => {
553 {
let Some(nv) =
args.name_value() else {
expected_name_value(cx, args.span().unwrap_or(path.span()),
path.word_sym());
return;
};
let Some(s) =
nv.value_as_str() else {
expected_string_literal(cx, nv.value_span,
Some(nv.value_as_lit()));
return;
};
if !check_attr_crate_level(cx, path.span()) { return; }
self.attribute.issue_tracker_base_url = Some((s, path.span()));
}string_arg_and_crate_level!(issue_tracker_base_url)
554 }
555 Some(sym::inline) => self.parse_inline(cx, path, args, DocInline::Inline),
556 Some(sym::no_inline) => self.parse_inline(cx, path, args, DocInline::NoInline),
557 Some(sym::masked) => {
if let Err(span) = args.no_args() { expected_no_args(cx, span); return; }
self.attribute.masked = Some(path.span());
}no_args!(masked),
558 Some(sym::cfg) => self.parse_cfg(cx, args),
559 Some(sym::notable_trait) => {
if let Err(span) = args.no_args() { expected_no_args(cx, span); return; }
self.attribute.notable_trait = Some(path.span());
}no_args!(notable_trait),
560 Some(sym::keyword) => parse_keyword_and_attribute(
561 cx,
562 path,
563 args,
564 &mut self.attribute.keyword,
565 sym::keyword,
566 ),
567 Some(sym::attribute) => parse_keyword_and_attribute(
568 cx,
569 path,
570 args,
571 &mut self.attribute.attribute,
572 sym::attribute,
573 ),
574 Some(sym::fake_variadic) => {
if let Err(span) = args.no_args() { expected_no_args(cx, span); return; }
let span = path.span();
if !check_attr_not_crate_level(cx, span, sym::fake_variadic) { return; }
self.attribute.fake_variadic = Some(span);
}no_args_and_not_crate_level!(fake_variadic),
575 Some(sym::search_unbox) => {
if let Err(span) = args.no_args() { expected_no_args(cx, span); return; }
let span = path.span();
if !check_attr_not_crate_level(cx, span, sym::search_unbox) { return; }
self.attribute.search_unbox = Some(span);
}no_args_and_not_crate_level!(search_unbox),
576 Some(sym::rust_logo) => {
if let Err(span) = args.no_args() { expected_no_args(cx, span); return; }
let span = path.span();
if !check_attr_crate_level(cx, span) { return; }
{
if !cx.features().rustdoc_internals() {
feature_err(cx.sess(), sym::rustdoc_internals, span,
rustc_errors::DiagMessage::Inline(std::borrow::Cow::Borrowed("the `#[doc(rust_logo)]` attribute is used for Rust branding"))).emit();
}
}
self.attribute.rust_logo = Some(span);
}no_args_and_crate_level!(rust_logo, |span| {
577 if !cx.features().rustdoc_internals() {
578 feature_err(
579 cx.sess(),
580 sym::rustdoc_internals,
581 span,
582 msg!("the `#[doc(rust_logo)]` attribute is used for Rust branding"),
583 )
584 .emit();
585 }
586 }),
587 Some(sym::auto_cfg) => self.parse_auto_cfg(cx, path, args),
588 Some(sym::test) => {
589 let Some(list) = args.list() else {
590 cx.emit_lint(
591 rustc_session::lint::builtin::INVALID_DOC_ATTRIBUTES,
592 AttributeLintKind::DocTestTakesList,
593 args.span().unwrap_or(path.span()),
594 );
595 return;
596 };
597
598 for i in list.mixed() {
599 match i {
600 MetaItemOrLitParser::MetaItemParser(mip) => {
601 self.parse_single_test_doc_attr_item(cx, mip);
602 }
603 MetaItemOrLitParser::Lit(lit) => {
604 cx.emit_lint(
608 rustc_session::lint::builtin::INVALID_DOC_ATTRIBUTES,
609 AttributeLintKind::MalformedDoc,
610 lit.span,
611 );
612 }
613 }
614 }
615 }
616 Some(sym::spotlight) => {
617 let span = path.span();
618 cx.emit_dyn_lint(
619 rustc_session::lint::builtin::INVALID_DOC_ATTRIBUTES,
620 move |dcx, level| DocUnknownSpotlight { sugg_span: span }.into_diag(dcx, level),
621 span,
622 );
623 }
624 Some(sym::include) if let Some(nv) = args.name_value() => {
625 let inner = match cx.attr_style {
626 AttrStyle::Outer => "",
627 AttrStyle::Inner => "!",
628 };
629 let value = nv.value_as_lit().symbol;
630 let span = path.span();
631 cx.emit_dyn_lint(
632 rustc_session::lint::builtin::INVALID_DOC_ATTRIBUTES,
633 move |dcx, level| {
634 DocUnknownInclude {
635 inner,
636 value,
637 sugg: (span, Applicability::MaybeIncorrect),
638 }
639 .into_diag(dcx, level)
640 },
641 span,
642 );
643 }
644 Some(name @ (sym::passes | sym::no_default_passes)) => {
645 let span = path.span();
646 cx.emit_dyn_lint(
647 rustc_session::lint::builtin::INVALID_DOC_ATTRIBUTES,
648 move |dcx, level| {
649 DocUnknownPasses { name, note_span: span }.into_diag(dcx, level)
650 },
651 span,
652 );
653 }
654 Some(sym::plugins) => {
655 let span = path.span();
656 cx.emit_dyn_lint(
657 rustc_session::lint::builtin::INVALID_DOC_ATTRIBUTES,
658 move |dcx, level| DocUnknownPlugins { label_span: span }.into_diag(dcx, level),
659 span,
660 );
661 }
662 Some(name) => {
663 cx.emit_dyn_lint(
664 rustc_session::lint::builtin::INVALID_DOC_ATTRIBUTES,
665 move |dcx, level| DocUnknownAny { name }.into_diag(dcx, level),
666 path.span(),
667 );
668 }
669 None => {
670 let full_name =
671 path.segments().map(|s| s.as_str()).intersperse("::").collect::<String>();
672 let name = Symbol::intern(&full_name);
673 cx.emit_dyn_lint(
674 rustc_session::lint::builtin::INVALID_DOC_ATTRIBUTES,
675 move |dcx, level| DocUnknownAny { name }.into_diag(dcx, level),
676 path.span(),
677 );
678 }
679 }
680 }
681
682 fn accept_single_doc_attr<S: Stage>(
683 &mut self,
684 cx: &mut AcceptContext<'_, '_, S>,
685 args: &ArgParser,
686 ) {
687 match args {
688 ArgParser::NoArgs => {
689 let suggestions = cx.adcx().suggestions();
690 let span = cx.attr_span;
691 cx.emit_dyn_lint(
692 rustc_session::lint::builtin::INVALID_DOC_ATTRIBUTES,
693 move |dcx, level| {
694 IllFormedAttributeInput::new(&suggestions, None, None).into_diag(dcx, level)
695 },
696 span,
697 );
698 }
699 ArgParser::List(items) => {
700 for i in items.mixed() {
701 match i {
702 MetaItemOrLitParser::MetaItemParser(mip) => {
703 self.nb_doc_attrs += 1;
704 self.parse_single_doc_attr_item(cx, mip);
705 }
706 MetaItemOrLitParser::Lit(lit) => {
707 expected_name_value(cx, lit.span, None);
708 }
709 }
710 }
711 }
712 ArgParser::NameValue(nv) => {
713 if nv.value_as_str().is_none() {
714 expected_string_literal(cx, nv.value_span, Some(nv.value_as_lit()));
715 } else {
716 {
::core::panicking::panic_fmt(format_args!("internal error: entered unreachable code: {0}",
format_args!("Should have been handled at the same time as sugar-syntaxed doc comments")));
};unreachable!(
717 "Should have been handled at the same time as sugar-syntaxed doc comments"
718 );
719 }
720 }
721 }
722 }
723}
724
725impl<S: Stage> AttributeParser<S> for DocParser {
726 const ATTRIBUTES: AcceptMapping<Self, S> = &[(
727 &[sym::doc],
728 ::rustc_feature::AttributeTemplate {
word: false,
list: Some(&["alias", "attribute", "hidden", "html_favicon_url",
"html_logo_url", "html_no_source", "html_playground_url",
"html_root_url", "issue_tracker_base_url", "inline",
"no_inline", "masked", "cfg", "notable_trait", "keyword",
"fake_variadic", "search_unbox", "rust_logo", "auto_cfg",
"test", "spotlight", "include", "no_default_passes",
"passes", "plugins"]),
one_of: &[],
name_value_str: Some(&["string"]),
docs: None,
}template!(
729 List: &[
730 "alias",
731 "attribute",
732 "hidden",
733 "html_favicon_url",
734 "html_logo_url",
735 "html_no_source",
736 "html_playground_url",
737 "html_root_url",
738 "issue_tracker_base_url",
739 "inline",
740 "no_inline",
741 "masked",
742 "cfg",
743 "notable_trait",
744 "keyword",
745 "fake_variadic",
746 "search_unbox",
747 "rust_logo",
748 "auto_cfg",
749 "test",
750 "spotlight",
751 "include",
752 "no_default_passes",
753 "passes",
754 "plugins",
755 ],
756 NameValueStr: "string"
757 ),
758 |this, cx, args| {
759 this.accept_single_doc_attr(cx, args);
760 },
761 )];
762 const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(ALL_TARGETS);
764 fn finalize(self, _cx: &FinalizeContext<'_, '_, S>) -> Option<AttributeKind> {
797 if self.nb_doc_attrs != 0 {
798 Some(AttributeKind::Doc(Box::new(self.attribute)))
799 } else {
800 None
801 }
802 }
803}