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