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