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) = i.lit().and_then(|i| i.value_str()) else {
259 cx.adcx().expected_string_literal(i.span(), i.lit());
260 continue;
261 };
262
263 self.add_alias(cx, alias, i.span());
264 }
265 }
266 ArgParser::NameValue(nv) => {
267 let Some(alias) = nv.value_as_str() else {
268 cx.adcx().expected_string_literal(nv.value_span, Some(nv.value_as_lit()));
269 return;
270 };
271 self.add_alias(cx, alias, nv.value_span);
272 }
273 }
274 }
275
276 fn parse_inline(
277 &mut self,
278 cx: &mut AcceptContext<'_, '_>,
279 path: &OwnedPathParser,
280 args: &ArgParser,
281 inline: DocInline,
282 ) {
283 if let Err(span) = args.as_no_args() {
284 expected_no_args(cx, span);
285 return;
286 }
287
288 self.attribute.inline.push((inline, path.span()));
289 }
290
291 fn parse_cfg(&mut self, cx: &mut AcceptContext<'_, '_>, args: &ArgParser) {
292 fn simplify_cfg(cfg_entry: &mut CfgEntry) {
294 match cfg_entry {
295 CfgEntry::All(cfgs, span) if cfgs.is_empty() => {
296 *cfg_entry = CfgEntry::Bool(true, *span)
297 }
298 CfgEntry::Any(cfgs, span) if cfgs.is_empty() => {
299 *cfg_entry = CfgEntry::Bool(false, *span)
300 }
301 CfgEntry::Not(cfg, _) => simplify_cfg(cfg),
302 _ => {}
303 }
304 }
305 if let Some(mut cfg_entry) = super::cfg::parse_cfg(cx, args) {
306 simplify_cfg(&mut cfg_entry);
307 self.attribute.cfg.push(cfg_entry);
308 }
309 }
310
311 fn parse_auto_cfg(
312 &mut self,
313 cx: &mut AcceptContext<'_, '_>,
314 path: &OwnedPathParser,
315 args: &ArgParser,
316 ) {
317 match args {
318 ArgParser::NoArgs => {
319 self.attribute.auto_cfg_change.push((true, path.span()));
320 }
321 ArgParser::List(list) => {
322 for meta in list.mixed() {
323 let MetaItemOrLitParser::MetaItemParser(item) = meta else {
324 cx.emit_lint(
325 rustc_session::lint::builtin::INVALID_DOC_ATTRIBUTES,
326 DocAutoCfgExpectsHideOrShow,
327 meta.span(),
328 );
329 continue;
330 };
331 let (kind, attr_name) = match item.path().word_sym() {
332 Some(sym::hide) => (HideOrShow::Hide, sym::hide),
333 Some(sym::show) => (HideOrShow::Show, sym::show),
334 _ => {
335 cx.emit_lint(
336 rustc_session::lint::builtin::INVALID_DOC_ATTRIBUTES,
337 DocAutoCfgExpectsHideOrShow,
338 item.span(),
339 );
340 continue;
341 }
342 };
343 let ArgParser::List(list) = item.args() else {
344 cx.emit_lint(
345 rustc_session::lint::builtin::INVALID_DOC_ATTRIBUTES,
346 DocAutoCfgHideShowExpectsList { attr_name },
347 item.span(),
348 );
349 continue;
350 };
351
352 let mut cfg_hide_show = CfgHideShow { kind, values: ThinVec::new() };
353
354 for item in list.mixed() {
355 let MetaItemOrLitParser::MetaItemParser(sub_item) = item else {
356 cx.emit_lint(
357 rustc_session::lint::builtin::INVALID_DOC_ATTRIBUTES,
358 DocAutoCfgHideShowUnexpectedItem { attr_name },
359 item.span(),
360 );
361 continue;
362 };
363 match sub_item.args() {
364 a @ (ArgParser::NoArgs | ArgParser::NameValue(_)) => {
365 let Some(name) = sub_item.path().word_sym() else {
366 cx.emit_lint(
370 rustc_session::lint::builtin::INVALID_DOC_ATTRIBUTES,
371 MalformedDoc,
372 sub_item.path().span(),
373 );
374 continue;
375 };
376 if let Ok(CfgEntry::NameValue { name, value, .. }) =
377 super::cfg::parse_name_value(
378 name,
379 sub_item.path().span(),
380 a.as_name_value(),
381 sub_item.span(),
382 cx,
383 )
384 {
385 cfg_hide_show.values.push(CfgInfo {
386 name,
387 name_span: sub_item.path().span(),
388 value: value
391 .map(|v| (v, a.as_name_value().unwrap().value_span)),
392 })
393 }
394 }
395 _ => {
396 cx.emit_lint(
397 rustc_session::lint::builtin::INVALID_DOC_ATTRIBUTES,
398 DocAutoCfgHideShowUnexpectedItem { attr_name },
399 sub_item.span(),
400 );
401 continue;
402 }
403 }
404 }
405 self.attribute.auto_cfg.push((cfg_hide_show, path.span()));
406 }
407 }
408 ArgParser::NameValue(nv) => {
409 let MetaItemLit { kind: LitKind::Bool(bool_value), span, .. } = nv.value_as_lit()
410 else {
411 cx.emit_lint(
412 rustc_session::lint::builtin::INVALID_DOC_ATTRIBUTES,
413 DocAutoCfgWrongLiteral,
414 nv.value_span,
415 );
416 return;
417 };
418 self.attribute.auto_cfg_change.push((*bool_value, *span));
419 }
420 }
421 }
422
423 fn parse_single_doc_attr_item(&mut self, cx: &mut AcceptContext<'_, '_>, mip: &MetaItemParser) {
424 let path = mip.path();
425 let args = mip.args();
426
427 macro_rules! no_args {
428 ($ident: ident) => {{
429 if let Err(span) = args.as_no_args() {
430 expected_no_args(cx, span);
431 return;
432 }
433
434 self.attribute.$ident = Some(path.span());
444 }};
445 }
446 macro_rules! no_args_and_not_crate_level {
447 ($ident: ident) => {{
448 if let Err(span) = args.as_no_args() {
449 expected_no_args(cx, span);
450 return;
451 }
452 let span = path.span();
453 if !check_attr_not_crate_level(cx, span, sym::$ident) {
454 return;
455 }
456 self.attribute.$ident = Some(span);
457 }};
458 }
459 macro_rules! no_args_and_crate_level {
460 ($ident: ident) => {{
461 no_args_and_crate_level!($ident, |span| {});
462 }};
463 ($ident: ident, |$span:ident| $extra_validation:block) => {{
464 if let Err(span) = args.as_no_args() {
465 expected_no_args(cx, span);
466 return;
467 }
468 let $span = path.span();
469 if !check_attr_crate_level(cx, $span) {
470 return;
471 }
472 $extra_validation
473 self.attribute.$ident = Some($span);
474 }};
475 }
476 macro_rules! string_arg_and_crate_level {
477 ($ident: ident) => {{
478 let Some(nv) = args.as_name_value() else {
479 expected_name_value(cx, args.span().unwrap_or(path.span()), path.word_sym());
480 return;
481 };
482
483 let Some(s) = nv.value_as_str() else {
484 expected_string_literal(cx, nv.value_span, Some(nv.value_as_lit()));
485 return;
486 };
487
488 if !check_attr_crate_level(cx, path.span()) {
489 return;
490 }
491
492 self.attribute.$ident = Some((s, path.span()));
502 }};
503 }
504
505 match path.word_sym() {
506 Some(sym::alias) => self.parse_alias(cx, path, args),
507 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),
508 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),
509 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),
510 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),
511 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),
512 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),
513 Some(sym::issue_tracker_base_url) => {
514 {
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)
515 }
516 Some(sym::inline) => self.parse_inline(cx, path, args, DocInline::Inline),
517 Some(sym::no_inline) => self.parse_inline(cx, path, args, DocInline::NoInline),
518 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),
519 Some(sym::cfg) => self.parse_cfg(cx, args),
520 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),
521 Some(sym::keyword) => parse_keyword_and_attribute(
522 cx,
523 path,
524 args,
525 &mut self.attribute.keyword,
526 sym::keyword,
527 ),
528 Some(sym::attribute) => parse_keyword_and_attribute(
529 cx,
530 path,
531 args,
532 &mut self.attribute.attribute,
533 sym::attribute,
534 ),
535 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),
536 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),
537 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| {
538 if !cx.features().rustdoc_internals() {
539 feature_err(
540 cx.sess(),
541 sym::rustdoc_internals,
542 span,
543 msg!("the `#[doc(rust_logo)]` attribute is used for Rust branding"),
544 )
545 .emit();
546 }
547 }),
548 Some(sym::auto_cfg) => self.parse_auto_cfg(cx, path, args),
549 Some(sym::test) => {
550 let Some(list) = args.as_list() else {
551 cx.emit_lint(
552 rustc_session::lint::builtin::INVALID_DOC_ATTRIBUTES,
553 DocTestTakesList,
554 args.span().unwrap_or(path.span()),
555 );
556 return;
557 };
558
559 for i in list.mixed() {
560 match i {
561 MetaItemOrLitParser::MetaItemParser(mip) => {
562 self.parse_single_test_doc_attr_item(cx, mip);
563 }
564 MetaItemOrLitParser::Lit(lit) => {
565 cx.emit_lint(
569 rustc_session::lint::builtin::INVALID_DOC_ATTRIBUTES,
570 MalformedDoc,
571 lit.span,
572 );
573 }
574 }
575 }
576 }
577 Some(sym::spotlight) => {
578 let span = path.span();
579 cx.emit_lint(
580 rustc_session::lint::builtin::INVALID_DOC_ATTRIBUTES,
581 DocUnknownSpotlight { sugg_span: span },
582 span,
583 );
584 }
585 Some(sym::include) if let Some(nv) = args.as_name_value() => {
586 let inner = match cx.attr_style {
587 AttrStyle::Outer => "",
588 AttrStyle::Inner => "!",
589 };
590 let value = nv.value_as_lit().symbol;
591 let span = path.span();
592 cx.emit_lint(
593 rustc_session::lint::builtin::INVALID_DOC_ATTRIBUTES,
594 DocUnknownInclude { inner, value, sugg: (span, Applicability::MaybeIncorrect) },
595 span,
596 );
597 }
598 Some(name @ (sym::passes | sym::no_default_passes)) => {
599 let span = path.span();
600 cx.emit_lint(
601 rustc_session::lint::builtin::INVALID_DOC_ATTRIBUTES,
602 DocUnknownPasses { name, note_span: span },
603 span,
604 );
605 }
606 Some(sym::plugins) => {
607 let span = path.span();
608 cx.emit_lint(
609 rustc_session::lint::builtin::INVALID_DOC_ATTRIBUTES,
610 DocUnknownPlugins { label_span: span },
611 span,
612 );
613 }
614 Some(name) => {
615 cx.emit_lint(
616 rustc_session::lint::builtin::INVALID_DOC_ATTRIBUTES,
617 DocUnknownAny { name },
618 path.span(),
619 );
620 }
621 None => {
622 let full_name =
623 path.segments().map(|s| s.as_str()).intersperse("::").collect::<String>();
624 let name = Symbol::intern(&full_name);
625 cx.emit_lint(
626 rustc_session::lint::builtin::INVALID_DOC_ATTRIBUTES,
627 DocUnknownAny { name },
628 path.span(),
629 );
630 }
631 }
632 }
633
634 fn accept_single_doc_attr(&mut self, cx: &mut AcceptContext<'_, '_>, args: &ArgParser) {
635 match args {
636 ArgParser::NoArgs => {
637 let suggestions = cx.adcx().suggestions();
638 let span = cx.attr_span;
639 cx.emit_lint(
640 rustc_session::lint::builtin::INVALID_DOC_ATTRIBUTES,
641 IllFormedAttributeInput::new(&suggestions, None, None),
642 span,
643 );
644 }
645 ArgParser::List(items) => {
646 for i in items.mixed() {
647 match i {
648 MetaItemOrLitParser::MetaItemParser(mip) => {
649 if self.nb_doc_attrs == 0 {
650 self.attribute.first_span = cx.attr_span;
651 }
652 self.nb_doc_attrs += 1;
653 self.parse_single_doc_attr_item(cx, mip);
654 }
655 MetaItemOrLitParser::Lit(lit) => {
656 expected_name_value(cx, lit.span, None);
657 }
658 }
659 }
660 }
661 ArgParser::NameValue(nv) => {
662 if nv.value_as_str().is_none() {
663 expected_string_literal(cx, nv.value_span, Some(nv.value_as_lit()));
664 } else {
665 {
::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!(
666 "Should have been handled at the same time as sugar-syntaxed doc comments"
667 );
668 }
669 }
670 }
671 }
672}
673
674impl AttributeParser for DocParser {
675 const ATTRIBUTES: AcceptMapping<Self> = &[(
676 &[sym::doc],
677 ::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!(
678 List: &[
679 "alias",
680 "attribute",
681 "hidden",
682 "html_favicon_url",
683 "html_logo_url",
684 "html_no_source",
685 "html_playground_url",
686 "html_root_url",
687 "issue_tracker_base_url",
688 "inline",
689 "no_inline",
690 "masked",
691 "cfg",
692 "notable_trait",
693 "keyword",
694 "fake_variadic",
695 "search_unbox",
696 "rust_logo",
697 "auto_cfg",
698 "test",
699 "spotlight",
700 "include",
701 "no_default_passes",
702 "passes",
703 "plugins",
704 ],
705 NameValueStr: "string"
706 ),
707 |this, cx, args| {
708 this.accept_single_doc_attr(cx, args);
709 },
710 )];
711 const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(ALL_TARGETS);
713 fn finalize(self, _cx: &FinalizeContext<'_, '_>) -> Option<AttributeKind> {
746 if self.nb_doc_attrs != 0 {
747 Some(AttributeKind::Doc(Box::new(self.attribute)))
748 } else {
749 None
750 }
751 }
752}