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