1use rustc_ast::ast::{AttrStyle, LitKind, MetaItemLit};
2use rustc_errors::{Applicability, msg};
3use rustc_feature::AttributeStability;
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, template};
14use crate::context::{AcceptContext, FinalizeContext};
15use crate::diagnostics::{
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, UnusedDuplicate,
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(&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 UnusedDuplicate { this: unused_span, other: used_span, warning: true },
163 unused_span,
164 );
165 return;
166 }
167
168 if !check_attr_crate_level(cx, path.span()) {
169 return;
170 }
171
172 self.attribute.no_crate_inject = Some(path.span())
173 }
174 Some(sym::attr) => {
175 let Some(list) = args.as_list() else {
176 let span = cx.attr_span;
179 cx.emit_lint(
180 rustc_session::lint::builtin::INVALID_DOC_ATTRIBUTES,
181 MalformedDoc,
182 span,
183 );
184 return;
185 };
186
187 for attr in list.mixed() {
189 attr.ignore_args();
191 self.attribute.test_attrs.push(attr.span());
192 }
193 }
194 Some(name) => {
195 cx.emit_lint(
196 rustc_session::lint::builtin::INVALID_DOC_ATTRIBUTES,
197 DocTestUnknown { name },
198 path.span(),
199 );
200 }
201 None => {
202 cx.emit_lint(
203 rustc_session::lint::builtin::INVALID_DOC_ATTRIBUTES,
204 DocTestLiteral,
205 path.span(),
206 );
207 }
208 }
209 }
210
211 fn add_alias(&mut self, cx: &mut AcceptContext<'_, '_>, alias: Symbol, span: Span) {
212 let attr_str = "`#[doc(alias = \"...\")]`";
213 if alias == sym::empty {
214 cx.emit_err(DocAliasEmpty { span, attr_str });
215 return;
216 }
217
218 let alias_str = alias.as_str();
219 if let Some(c) =
220 alias_str.chars().find(|&c| c == '"' || c == '\'' || (c.is_whitespace() && c != ' '))
221 {
222 cx.emit_err(DocAliasBadChar { span, attr_str, char_: c });
223 return;
224 }
225 if alias_str.starts_with(' ') || alias_str.ends_with(' ') {
226 cx.emit_err(DocAliasStartEnd { span, attr_str });
227 return;
228 }
229 if !check_attr_not_crate_level(cx, span, sym::alias) {
230 return;
231 }
232
233 if let Some(first_definition) = self.attribute.aliases.get(&alias).copied() {
234 cx.emit_lint(
235 rustc_session::lint::builtin::UNUSED_ATTRIBUTES,
236 DocAliasDuplicated { first_definition },
237 span,
238 );
239 }
240
241 self.attribute.aliases.insert(alias, span);
242 }
243
244 fn parse_alias(
245 &mut self,
246 cx: &mut AcceptContext<'_, '_>,
247 path: &OwnedPathParser,
248 args: &ArgParser,
249 ) {
250 match args {
251 ArgParser::NoArgs => {
252 cx.emit_err(DocAliasMalformed { span: args.span().unwrap_or(path.span()) });
253 }
254 ArgParser::List(list) => {
255 for i in list.mixed() {
256 let Some(alias) = cx.expect_string_literal(i) else {
257 continue;
258 };
259
260 self.add_alias(cx, alias, i.span());
261 }
262 }
263 ArgParser::NameValue(nv) => {
264 let Some(alias) = cx.expect_string_literal(nv) else {
265 return;
266 };
267 self.add_alias(cx, alias, nv.value_span);
268 }
269 }
270 }
271
272 fn parse_inline(
273 &mut self,
274 cx: &mut AcceptContext<'_, '_>,
275 path: &OwnedPathParser,
276 args: &ArgParser,
277 inline: DocInline,
278 ) {
279 if let Err(span) = args.as_no_args() {
280 expected_no_args(cx, span);
281 return;
282 }
283
284 self.attribute.inline.push((inline, path.span()));
285 }
286
287 fn parse_cfg(&mut self, cx: &mut AcceptContext<'_, '_>, args: &ArgParser) {
288 fn simplify_cfg(cfg_entry: &mut CfgEntry) {
290 match cfg_entry {
291 CfgEntry::All(cfgs, span) if cfgs.is_empty() => {
292 *cfg_entry = CfgEntry::Bool(true, *span)
293 }
294 CfgEntry::Any(cfgs, span) if cfgs.is_empty() => {
295 *cfg_entry = CfgEntry::Bool(false, *span)
296 }
297 CfgEntry::Not(cfg, _) => simplify_cfg(cfg),
298 _ => {}
299 }
300 }
301 if let Some(mut cfg_entry) = super::cfg::parse_cfg(cx, args) {
302 simplify_cfg(&mut cfg_entry);
303 self.attribute.cfg.push(cfg_entry);
304 }
305 }
306
307 fn parse_auto_cfg(
308 &mut self,
309 cx: &mut AcceptContext<'_, '_>,
310 path: &OwnedPathParser,
311 args: &ArgParser,
312 ) {
313 match args {
314 ArgParser::NoArgs => {
315 self.attribute.auto_cfg_change.push((true, path.span()));
316 }
317 ArgParser::List(list) => {
318 for meta in list.mixed() {
319 let MetaItemOrLitParser::MetaItemParser(item) = meta else {
320 cx.emit_lint(
321 rustc_session::lint::builtin::INVALID_DOC_ATTRIBUTES,
322 DocAutoCfgExpectsHideOrShow,
323 meta.span(),
324 );
325 continue;
326 };
327 let (kind, attr_name) = match item.path().word_sym() {
328 Some(sym::hide) => (HideOrShow::Hide, sym::hide),
329 Some(sym::show) => (HideOrShow::Show, sym::show),
330 _ => {
331 cx.emit_lint(
332 rustc_session::lint::builtin::INVALID_DOC_ATTRIBUTES,
333 DocAutoCfgExpectsHideOrShow,
334 item.span(),
335 );
336 continue;
337 }
338 };
339 let ArgParser::List(list) = item.args() else {
340 cx.emit_lint(
341 rustc_session::lint::builtin::INVALID_DOC_ATTRIBUTES,
342 DocAutoCfgHideShowExpectsList { attr_name },
343 item.span(),
344 );
345 continue;
346 };
347
348 let mut cfg_hide_show = CfgHideShow { kind, values: ThinVec::new() };
349
350 for item in list.mixed() {
351 let MetaItemOrLitParser::MetaItemParser(sub_item) = item else {
352 cx.emit_lint(
353 rustc_session::lint::builtin::INVALID_DOC_ATTRIBUTES,
354 DocAutoCfgHideShowUnexpectedItem { attr_name },
355 item.span(),
356 );
357 continue;
358 };
359 match sub_item.args() {
360 a @ (ArgParser::NoArgs | ArgParser::NameValue(_)) => {
361 let Some(name) = sub_item.path().word_sym() else {
362 cx.emit_lint(
366 rustc_session::lint::builtin::INVALID_DOC_ATTRIBUTES,
367 MalformedDoc,
368 sub_item.path().span(),
369 );
370 continue;
371 };
372 if let Ok(CfgEntry::NameValue { name, value, .. }) =
373 super::cfg::parse_name_value(
374 name,
375 sub_item.path().span(),
376 a.as_name_value(),
377 sub_item.span(),
378 cx,
379 )
380 {
381 cfg_hide_show.values.push(CfgInfo {
382 name,
383 name_span: sub_item.path().span(),
384 value: value
387 .map(|v| (v, a.as_name_value().unwrap().value_span)),
388 })
389 }
390 }
391 _ => {
392 cx.emit_lint(
393 rustc_session::lint::builtin::INVALID_DOC_ATTRIBUTES,
394 DocAutoCfgHideShowUnexpectedItem { attr_name },
395 sub_item.span(),
396 );
397 continue;
398 }
399 }
400 }
401 self.attribute.auto_cfg.push((cfg_hide_show, path.span()));
402 }
403 }
404 ArgParser::NameValue(nv) => {
405 let MetaItemLit { kind: LitKind::Bool(bool_value), span, .. } = nv.value_as_lit()
406 else {
407 cx.emit_lint(
408 rustc_session::lint::builtin::INVALID_DOC_ATTRIBUTES,
409 DocAutoCfgWrongLiteral,
410 nv.value_span,
411 );
412 return;
413 };
414 self.attribute.auto_cfg_change.push((*bool_value, *span));
415 }
416 }
417 }
418
419 fn parse_single_doc_attr_item(&mut self, cx: &mut AcceptContext<'_, '_>, mip: &MetaItemParser) {
420 let path = mip.path();
421 let args = mip.args();
422
423 macro_rules! no_args {
424 ($ident: ident) => {{
425 if let Err(span) = args.as_no_args() {
426 expected_no_args(cx, span);
427 return;
428 }
429
430 self.attribute.$ident = Some(path.span());
440 }};
441 }
442 macro_rules! no_args_and_not_crate_level {
443 ($ident: ident) => {{
444 if let Err(span) = args.as_no_args() {
445 expected_no_args(cx, span);
446 return;
447 }
448 let span = path.span();
449 if !check_attr_not_crate_level(cx, span, sym::$ident) {
450 return;
451 }
452 self.attribute.$ident = Some(span);
453 }};
454 }
455 macro_rules! no_args_and_crate_level {
456 ($ident: ident) => {{
457 no_args_and_crate_level!($ident, |span| {});
458 }};
459 ($ident: ident, |$span:ident| $extra_validation:block) => {{
460 if let Err(span) = args.as_no_args() {
461 expected_no_args(cx, span);
462 return;
463 }
464 let $span = path.span();
465 if !check_attr_crate_level(cx, $span) {
466 return;
467 }
468 $extra_validation
469 self.attribute.$ident = Some($span);
470 }};
471 }
472 macro_rules! string_arg_and_crate_level {
473 ($ident: ident) => {{
474 let Some(nv) = args.as_name_value() else {
475 expected_name_value(cx, args.span().unwrap_or(path.span()), path.word_sym());
476 return;
477 };
478
479 let Some(s) = nv.value_as_str() else {
480 expected_string_literal(cx, nv.value_span, Some(nv.value_as_lit()));
481 return;
482 };
483
484 if !check_attr_crate_level(cx, path.span()) {
485 return;
486 }
487
488 self.attribute.$ident = Some((s, path.span()));
498 }};
499 }
500
501 match path.word_sym() {
502 Some(sym::alias) => self.parse_alias(cx, path, args),
503 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),
504 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),
505 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),
506 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),
507 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),
508 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),
509 Some(sym::issue_tracker_base_url) => {
510 {
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)
511 }
512 Some(sym::inline) => self.parse_inline(cx, path, args, DocInline::Inline),
513 Some(sym::no_inline) => self.parse_inline(cx, path, args, DocInline::NoInline),
514 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),
515 Some(sym::cfg) => self.parse_cfg(cx, args),
516 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),
517 Some(sym::keyword) => parse_keyword_and_attribute(
518 cx,
519 path,
520 args,
521 &mut self.attribute.keyword,
522 sym::keyword,
523 ),
524 Some(sym::attribute) => parse_keyword_and_attribute(
525 cx,
526 path,
527 args,
528 &mut self.attribute.attribute,
529 sym::attribute,
530 ),
531 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),
532 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),
533 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| {
534 if !cx.features().rustdoc_internals() {
535 feature_err(
536 cx.sess(),
537 sym::rustdoc_internals,
538 span,
539 msg!("the `#[doc(rust_logo)]` attribute is used for Rust branding"),
540 )
541 .emit();
542 }
543 }),
544 Some(sym::auto_cfg) => self.parse_auto_cfg(cx, path, args),
545 Some(sym::test) => {
546 let Some(list) = args.as_list() else {
547 cx.emit_lint(
548 rustc_session::lint::builtin::INVALID_DOC_ATTRIBUTES,
549 DocTestTakesList,
550 args.span().unwrap_or(path.span()),
551 );
552 return;
553 };
554
555 for i in list.mixed() {
556 match i {
557 MetaItemOrLitParser::MetaItemParser(mip) => {
558 self.parse_single_test_doc_attr_item(cx, mip);
559 }
560 MetaItemOrLitParser::Lit(lit) => {
561 cx.emit_lint(
565 rustc_session::lint::builtin::INVALID_DOC_ATTRIBUTES,
566 MalformedDoc,
567 lit.span,
568 );
569 }
570 }
571 }
572 }
573 Some(sym::spotlight) => {
574 let span = path.span();
575 cx.emit_lint(
576 rustc_session::lint::builtin::INVALID_DOC_ATTRIBUTES,
577 DocUnknownSpotlight { sugg_span: span },
578 span,
579 );
580 }
581 Some(sym::include) if let Some(nv) = args.as_name_value() => {
582 let inner = match cx.attr_style {
583 AttrStyle::Outer => "",
584 AttrStyle::Inner => "!",
585 };
586 let value = nv.value_as_lit().symbol;
587 let span = path.span();
588 cx.emit_lint(
589 rustc_session::lint::builtin::INVALID_DOC_ATTRIBUTES,
590 DocUnknownInclude { inner, value, sugg: (span, Applicability::MaybeIncorrect) },
591 span,
592 );
593 }
594 Some(name @ (sym::passes | sym::no_default_passes)) => {
595 let span = path.span();
596 cx.emit_lint(
597 rustc_session::lint::builtin::INVALID_DOC_ATTRIBUTES,
598 DocUnknownPasses { name, note_span: span },
599 span,
600 );
601 }
602 Some(sym::plugins) => {
603 let span = path.span();
604 cx.emit_lint(
605 rustc_session::lint::builtin::INVALID_DOC_ATTRIBUTES,
606 DocUnknownPlugins { label_span: span },
607 span,
608 );
609 }
610 Some(name) => {
611 cx.emit_lint(
612 rustc_session::lint::builtin::INVALID_DOC_ATTRIBUTES,
613 DocUnknownAny { name },
614 path.span(),
615 );
616 }
617 None => {
618 let full_name =
619 path.segments().map(|s| s.as_str()).intersperse("::").collect::<String>();
620 let name = Symbol::intern(&full_name);
621 cx.emit_lint(
622 rustc_session::lint::builtin::INVALID_DOC_ATTRIBUTES,
623 DocUnknownAny { name },
624 path.span(),
625 );
626 }
627 }
628 }
629
630 fn accept_single_doc_attr(&mut self, cx: &mut AcceptContext<'_, '_>, args: &ArgParser) {
631 match args {
632 ArgParser::NoArgs => {
633 let suggestions = cx.adcx().suggestions();
634 let span = cx.attr_span;
635 cx.emit_lint(
636 rustc_session::lint::builtin::INVALID_DOC_ATTRIBUTES,
637 IllFormedAttributeInput::new(&suggestions, None, None),
638 span,
639 );
640 }
641 ArgParser::List(items) => {
642 for i in items.mixed() {
643 match i {
644 MetaItemOrLitParser::MetaItemParser(mip) => {
645 if self.nb_doc_attrs == 0 {
646 self.attribute.first_span = cx.attr_span;
647 }
648 self.nb_doc_attrs += 1;
649 self.parse_single_doc_attr_item(cx, mip);
650 }
651 MetaItemOrLitParser::Lit(lit) => {
652 expected_name_value(cx, lit.span, None);
653 }
654 }
655 }
656 }
657 ArgParser::NameValue(nv) => {
658 if nv.value_as_str().is_none() {
659 expected_string_literal(cx, nv.value_span, Some(nv.value_as_lit()));
660 } else {
661 {
::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!(
662 "Should have been handled at the same time as sugar-syntaxed doc comments"
663 );
664 }
665 }
666 }
667 }
668}
669
670impl AttributeParser for DocParser {
671 const ATTRIBUTES: AcceptMapping<Self> = &[(
672 &[sym::doc],
673 crate::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!(
674 List: &[
675 "alias",
676 "attribute",
677 "hidden",
678 "html_favicon_url",
679 "html_logo_url",
680 "html_no_source",
681 "html_playground_url",
682 "html_root_url",
683 "issue_tracker_base_url",
684 "inline",
685 "no_inline",
686 "masked",
687 "cfg",
688 "notable_trait",
689 "keyword",
690 "fake_variadic",
691 "search_unbox",
692 "rust_logo",
693 "auto_cfg",
694 "test",
695 "spotlight",
696 "include",
697 "no_default_passes",
698 "passes",
699 "plugins",
700 ],
701 NameValueStr: "string"
702 ),
703 AttributeStability::Stable, |this, cx, args| {
705 this.accept_single_doc_attr(cx, args);
706 },
707 )];
708 const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(ALL_TARGETS);
710 fn finalize(self, _cx: &FinalizeContext<'_, '_>) -> Option<AttributeKind> {
743 if self.nb_doc_attrs != 0 {
744 Some(AttributeKind::Doc(Box::new(self.attribute)))
745 } else {
746 None
747 }
748 }
749}