Continue migration to new Attribute API for doc attribute
This commit is contained in:
parent
3f08e4dcd9
commit
acb32df7b1
26 changed files with 597 additions and 608 deletions
|
|
@ -4011,7 +4011,6 @@ dependencies = [
|
|||
"itertools",
|
||||
"rustc_abi",
|
||||
"rustc_ast",
|
||||
"rustc_attr_parsing",
|
||||
"rustc_data_structures",
|
||||
"rustc_errors",
|
||||
"rustc_fluent_macro",
|
||||
|
|
@ -4433,7 +4432,6 @@ dependencies = [
|
|||
"rustc_abi",
|
||||
"rustc_ast",
|
||||
"rustc_ast_lowering",
|
||||
"rustc_ast_pretty",
|
||||
"rustc_attr_parsing",
|
||||
"rustc_data_structures",
|
||||
"rustc_errors",
|
||||
|
|
|
|||
|
|
@ -220,6 +220,11 @@ impl AttributeExt for Attribute {
|
|||
fn is_automatically_derived_attr(&self) -> bool {
|
||||
self.has_name(sym::automatically_derived)
|
||||
}
|
||||
|
||||
fn is_doc_hidden(&self) -> bool {
|
||||
self.has_name(sym::doc)
|
||||
&& self.meta_item_list().is_some_and(|l| list_contains_name(&l, sym::hidden))
|
||||
}
|
||||
}
|
||||
|
||||
impl Attribute {
|
||||
|
|
@ -830,6 +835,9 @@ pub trait AttributeExt: Debug {
|
|||
/// commented module (for inner doc) vs within its parent module (for outer
|
||||
/// doc).
|
||||
fn doc_resolution_scope(&self) -> Option<AttrStyle>;
|
||||
|
||||
/// Returns `true` if this attribute contains `doc(hidden)`.
|
||||
fn is_doc_hidden(&self) -> bool;
|
||||
}
|
||||
|
||||
// FIXME(fn_delegation): use function delegation instead of manually forwarding
|
||||
|
|
|
|||
|
|
@ -256,6 +256,10 @@ attr_parsing_doc_keyword_not_keyword =
|
|||
nonexistent keyword `{$keyword}` used in `#[doc(keyword = "...")]`
|
||||
.help = only existing keywords are allowed in core/std
|
||||
|
||||
attr_parsing_doc_attribute_not_attribute =
|
||||
nonexistent builtin attribute `{$attribute}` used in `#[doc(attribute = "...")]`
|
||||
.help = only existing builtin attributes are allowed in core/std
|
||||
|
||||
attr_parsing_doc_inline_conflict =
|
||||
conflicting doc inlining attributes
|
||||
.help = remove one of the conflicting attributes
|
||||
|
|
@ -265,3 +269,54 @@ attr_parsing_doc_inline_conflict_first =
|
|||
|
||||
attr_parsing_doc_inline_conflict_second =
|
||||
{"."}..conflicts with this attribute
|
||||
|
||||
attr_parsing_doc_auto_cfg_expects_hide_or_show =
|
||||
only `hide` or `show` are allowed in `#[doc(auto_cfg(...))]`
|
||||
|
||||
attr_parsing_doc_auto_cfg_hide_show_unexpected_item =
|
||||
`#![doc(auto_cfg({$attr_name}(...)))]` only accepts identifiers or key/value items
|
||||
|
||||
attr_parsing_doc_auto_cfg_hide_show_expects_list =
|
||||
`#![doc(auto_cfg({$attr_name}(...)))]` expects a list of items
|
||||
|
||||
attr_parsing_doc_invalid =
|
||||
invalid `doc` attribute
|
||||
|
||||
attr_parsing_doc_unknown_include =
|
||||
unknown `doc` attribute `include`
|
||||
.suggestion = use `doc = include_str!` instead
|
||||
|
||||
attr_parsing_doc_unknown_spotlight =
|
||||
unknown `doc` attribute `spotlight`
|
||||
.note = `doc(spotlight)` was renamed to `doc(notable_trait)`
|
||||
.suggestion = use `notable_trait` instead
|
||||
.no_op_note = `doc(spotlight)` is now a no-op
|
||||
|
||||
attr_parsing_doc_unknown_passes =
|
||||
unknown `doc` attribute `{$name}`
|
||||
.note = `doc` attribute `{$name}` no longer functions; see issue #44136 <https://github.com/rust-lang/rust/issues/44136>
|
||||
.label = no longer functions
|
||||
.no_op_note = `doc({$name})` is now a no-op
|
||||
|
||||
attr_parsing_doc_unknown_plugins =
|
||||
unknown `doc` attribute `plugins`
|
||||
.note = `doc` attribute `plugins` no longer functions; see issue #44136 <https://github.com/rust-lang/rust/issues/44136> and CVE-2018-1000622 <https://nvd.nist.gov/vuln/detail/CVE-2018-1000622>
|
||||
.label = no longer functions
|
||||
.no_op_note = `doc(plugins)` is now a no-op
|
||||
|
||||
attr_parsing_doc_unknown_any =
|
||||
unknown `doc` attribute `{$name}`
|
||||
|
||||
attr_parsing_doc_auto_cfg_wrong_literal =
|
||||
expected boolean for `#[doc(auto_cfg = ...)]`
|
||||
|
||||
attr_parsing_doc_test_takes_list =
|
||||
`#[doc(test(...)]` takes a list of attributes
|
||||
|
||||
attr_parsing_doc_test_unknown =
|
||||
unknown `doc(test)` attribute `{$name}`
|
||||
|
||||
attr_parsing_doc_test_literal = `#![doc(test(...)]` does not take a literal
|
||||
|
||||
attr_parsing_doc_alias_malformed =
|
||||
doc alias attribute expects a string `#[doc(alias = "a")]` or a list of strings `#[doc(alias("a", "b"))]`
|
||||
|
|
|
|||
|
|
@ -171,7 +171,7 @@ fn parse_cfg_entry_target<S: Stage>(
|
|||
Ok(CfgEntry::All(result, list.span))
|
||||
}
|
||||
|
||||
fn parse_name_value<S: Stage>(
|
||||
pub(crate) fn parse_name_value<S: Stage>(
|
||||
name: Symbol,
|
||||
name_span: Span,
|
||||
value: Option<&NameValueParser>,
|
||||
|
|
|
|||
|
|
@ -1,20 +1,99 @@
|
|||
use rustc_attr_data_structures::lints::AttributeLintKind;
|
||||
use rustc_attr_data_structures::{AttributeKind, DocAttribute, DocInline};
|
||||
// FIXME: to be removed
|
||||
#![allow(unused_imports)]
|
||||
|
||||
use rustc_ast::ast::{AttrStyle, LitKind, MetaItemLit};
|
||||
use rustc_ast::token::CommentKind;
|
||||
use rustc_errors::MultiSpan;
|
||||
use rustc_feature::template;
|
||||
use rustc_hir::attrs::{
|
||||
AttributeKind, CfgEntry, CfgHideShow, CfgInfo, DocAttribute, DocInline, HideOrShow,
|
||||
};
|
||||
use rustc_hir::lints::AttributeLintKind;
|
||||
use rustc_span::{Span, Symbol, edition, sym};
|
||||
use thin_vec::ThinVec;
|
||||
|
||||
use super::prelude::{Allow, AllowedTargets, MethodKind, Target};
|
||||
use super::{AcceptMapping, AttributeParser};
|
||||
use crate::context::{AcceptContext, FinalizeContext, Stage};
|
||||
use crate::fluent_generated as fluent;
|
||||
use crate::parser::{ArgParser, MetaItemOrLitParser, MetaItemParser, PathParser};
|
||||
use crate::session_diagnostics::{
|
||||
DocAliasBadChar, DocAliasEmpty, DocAliasStartEnd, DocKeywordConflict, DocKeywordNotKeyword,
|
||||
DocAliasBadChar, DocAliasEmpty, DocAliasMalformed, DocAliasStartEnd, DocAttributeNotAttribute,
|
||||
DocKeywordConflict, DocKeywordNotKeyword,
|
||||
};
|
||||
|
||||
#[derive(Default)]
|
||||
fn check_keyword<S: Stage>(cx: &mut AcceptContext<'_, '_, S>, keyword: Symbol, span: Span) -> bool {
|
||||
// FIXME: Once rustdoc can handle URL conflicts on case insensitive file systems, we
|
||||
// can remove the `SelfTy` case here, remove `sym::SelfTy`, and update the
|
||||
// `#[doc(keyword = "SelfTy")` attribute in `library/std/src/keyword_docs.rs`.
|
||||
if keyword.is_reserved(|| edition::LATEST_STABLE_EDITION)
|
||||
|| keyword.is_weak()
|
||||
|| keyword == sym::SelfTy
|
||||
{
|
||||
return true;
|
||||
}
|
||||
cx.emit_err(DocKeywordNotKeyword { span, keyword });
|
||||
false
|
||||
}
|
||||
|
||||
fn check_attribute<S: Stage>(
|
||||
cx: &mut AcceptContext<'_, '_, S>,
|
||||
attribute: Symbol,
|
||||
span: Span,
|
||||
) -> bool {
|
||||
// FIXME: This should support attributes with namespace like `diagnostic::do_not_recommend`.
|
||||
if rustc_feature::BUILTIN_ATTRIBUTE_MAP.contains_key(&attribute) {
|
||||
return true;
|
||||
}
|
||||
cx.emit_err(DocAttributeNotAttribute { span, attribute });
|
||||
false
|
||||
}
|
||||
|
||||
fn parse_keyword_and_attribute<'c, S, F>(
|
||||
cx: &'c mut AcceptContext<'_, '_, S>,
|
||||
path: &PathParser<'_>,
|
||||
args: &ArgParser<'_>,
|
||||
attr_value: &mut Option<(Symbol, Span)>,
|
||||
callback: F,
|
||||
) where
|
||||
S: Stage,
|
||||
F: FnOnce(&mut AcceptContext<'_, '_, S>, Symbol, Span) -> bool,
|
||||
{
|
||||
let Some(nv) = args.name_value() else {
|
||||
cx.expected_name_value(args.span().unwrap_or(path.span()), path.word_sym());
|
||||
return;
|
||||
};
|
||||
|
||||
let Some(value) = nv.value_as_str() else {
|
||||
cx.expected_string_literal(nv.value_span, Some(nv.value_as_lit()));
|
||||
return;
|
||||
};
|
||||
|
||||
if !callback(cx, value, nv.value_span) {
|
||||
return;
|
||||
}
|
||||
|
||||
if attr_value.is_some() {
|
||||
cx.duplicate_key(path.span(), path.word_sym().unwrap());
|
||||
return;
|
||||
}
|
||||
|
||||
*attr_value = Some((value, path.span()));
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct DocComment {
|
||||
style: AttrStyle,
|
||||
kind: CommentKind,
|
||||
span: Span,
|
||||
comment: Symbol,
|
||||
}
|
||||
|
||||
#[derive(Default, Debug)]
|
||||
pub(crate) struct DocParser {
|
||||
attribute: DocAttribute,
|
||||
nb_doc_attrs: usize,
|
||||
doc_comment: Option<DocComment>,
|
||||
}
|
||||
|
||||
impl DocParser {
|
||||
|
|
@ -28,8 +107,8 @@ impl DocParser {
|
|||
|
||||
match path.word_sym() {
|
||||
Some(sym::no_crate_inject) => {
|
||||
if !args.no_args() {
|
||||
cx.expected_no_args(args.span().unwrap());
|
||||
if let Err(span) = args.no_args() {
|
||||
cx.expected_no_args(span);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -42,17 +121,20 @@ impl DocParser {
|
|||
}
|
||||
Some(sym::attr) => {
|
||||
let Some(list) = args.list() else {
|
||||
cx.expected_list(args.span().unwrap_or(path.span()));
|
||||
cx.expected_list(cx.attr_span);
|
||||
return;
|
||||
};
|
||||
|
||||
self.attribute.test_attrs.push(todo!());
|
||||
// FIXME: convert list into a Vec of `AttributeKind`.
|
||||
for _ in list.mixed() {
|
||||
// self.attribute.test_attrs.push(AttributeKind::parse());
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
cx.expected_specific_argument(
|
||||
mip.span(),
|
||||
[sym::no_crate_inject.as_str(), sym::attr.as_str()].to_vec(),
|
||||
);
|
||||
Some(name) => {
|
||||
cx.emit_lint(AttributeLintKind::DocTestUnknown { name }, path.span());
|
||||
}
|
||||
None => {
|
||||
cx.emit_lint(AttributeLintKind::DocTestLiteral, path.span());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -98,7 +180,7 @@ impl DocParser {
|
|||
) {
|
||||
match args {
|
||||
ArgParser::NoArgs => {
|
||||
cx.expected_name_value(args.span().unwrap_or(path.span()), path.word_sym());
|
||||
cx.emit_err(DocAliasMalformed { span: args.span().unwrap_or(path.span()) });
|
||||
}
|
||||
ArgParser::List(list) => {
|
||||
for i in list.mixed() {
|
||||
|
|
@ -120,41 +202,6 @@ impl DocParser {
|
|||
}
|
||||
}
|
||||
|
||||
fn parse_keyword<'c, S: Stage>(
|
||||
&mut self,
|
||||
cx: &'c mut AcceptContext<'_, '_, S>,
|
||||
path: &PathParser<'_>,
|
||||
args: &ArgParser<'_>,
|
||||
) {
|
||||
let Some(nv) = args.name_value() else {
|
||||
cx.expected_name_value(args.span().unwrap_or(path.span()), path.word_sym());
|
||||
return;
|
||||
};
|
||||
|
||||
let Some(keyword) = nv.value_as_str() else {
|
||||
cx.expected_string_literal(nv.value_span, Some(nv.value_as_lit()));
|
||||
return;
|
||||
};
|
||||
|
||||
fn is_doc_keyword(s: Symbol) -> bool {
|
||||
// FIXME: Once rustdoc can handle URL conflicts on case insensitive file systems, we
|
||||
// can remove the `SelfTy` case here, remove `sym::SelfTy`, and update the
|
||||
// `#[doc(keyword = "SelfTy")` attribute in `library/std/src/keyword_docs.rs`.
|
||||
s.is_reserved(|| edition::LATEST_STABLE_EDITION) || s.is_weak() || s == sym::SelfTy
|
||||
}
|
||||
|
||||
if !is_doc_keyword(keyword) {
|
||||
cx.emit_err(DocKeywordNotKeyword { span: nv.value_span, keyword });
|
||||
}
|
||||
|
||||
if self.attribute.keyword.is_some() {
|
||||
cx.duplicate_key(path.span(), path.word_sym().unwrap());
|
||||
return;
|
||||
}
|
||||
|
||||
self.attribute.keyword = Some((keyword, path.span()));
|
||||
}
|
||||
|
||||
fn parse_inline<'c, S: Stage>(
|
||||
&mut self,
|
||||
cx: &'c mut AcceptContext<'_, '_, S>,
|
||||
|
|
@ -162,8 +209,8 @@ impl DocParser {
|
|||
args: &ArgParser<'_>,
|
||||
inline: DocInline,
|
||||
) {
|
||||
if !args.no_args() {
|
||||
cx.expected_no_args(args.span().unwrap());
|
||||
if let Err(span) = args.no_args() {
|
||||
cx.expected_no_args(span);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -182,6 +229,103 @@ impl DocParser {
|
|||
self.attribute.inline = Some((inline, span));
|
||||
}
|
||||
|
||||
fn parse_cfg<'c, S: Stage>(
|
||||
&mut self,
|
||||
cx: &'c mut AcceptContext<'_, '_, S>,
|
||||
args: &ArgParser<'_>,
|
||||
) {
|
||||
if let Some(cfg_entry) = super::cfg::parse_cfg(cx, args) {
|
||||
self.attribute.cfg = Some(cfg_entry);
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_auto_cfg<'c, S: Stage>(
|
||||
&mut self,
|
||||
cx: &'c mut AcceptContext<'_, '_, S>,
|
||||
path: &PathParser<'_>,
|
||||
args: &ArgParser<'_>,
|
||||
) {
|
||||
match args {
|
||||
ArgParser::NoArgs => {
|
||||
cx.expected_list(args.span().unwrap_or(path.span()));
|
||||
}
|
||||
ArgParser::List(list) => {
|
||||
for meta in list.mixed() {
|
||||
let MetaItemOrLitParser::MetaItemParser(item) = meta else {
|
||||
cx.emit_lint(AttributeLintKind::DocAutoCfgExpectsHideOrShow, meta.span());
|
||||
continue;
|
||||
};
|
||||
let (kind, attr_name) = match item.path().word_sym() {
|
||||
Some(sym::hide) => (HideOrShow::Hide, sym::hide),
|
||||
Some(sym::show) => (HideOrShow::Show, sym::show),
|
||||
_ => {
|
||||
cx.emit_lint(
|
||||
AttributeLintKind::DocAutoCfgExpectsHideOrShow,
|
||||
item.span(),
|
||||
);
|
||||
continue;
|
||||
}
|
||||
};
|
||||
let ArgParser::List(list) = item.args() else {
|
||||
cx.emit_lint(
|
||||
AttributeLintKind::DocAutoCfgHideShowExpectsList { attr_name },
|
||||
item.span(),
|
||||
);
|
||||
continue;
|
||||
};
|
||||
|
||||
let mut cfg_hide_show = CfgHideShow { kind, values: ThinVec::new() };
|
||||
|
||||
for item in list.mixed() {
|
||||
let MetaItemOrLitParser::MetaItemParser(sub_item) = item else {
|
||||
cx.emit_lint(
|
||||
AttributeLintKind::DocAutoCfgHideShowUnexpectedItem { attr_name },
|
||||
item.span(),
|
||||
);
|
||||
continue;
|
||||
};
|
||||
match sub_item.args() {
|
||||
a @ (ArgParser::NoArgs | ArgParser::NameValue(_)) => {
|
||||
let Some(name) = sub_item.path().word_sym() else {
|
||||
cx.expected_identifier(sub_item.path().span());
|
||||
continue;
|
||||
};
|
||||
if let Ok(CfgEntry::NameValue { name, name_span, value, .. }) =
|
||||
super::cfg::parse_name_value(
|
||||
name,
|
||||
sub_item.path().span(),
|
||||
a.name_value(),
|
||||
sub_item.span(),
|
||||
cx,
|
||||
)
|
||||
{
|
||||
cfg_hide_show.values.push(CfgInfo { name, name_span, value })
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
cx.emit_lint(
|
||||
AttributeLintKind::DocAutoCfgHideShowUnexpectedItem {
|
||||
attr_name,
|
||||
},
|
||||
sub_item.span(),
|
||||
);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
ArgParser::NameValue(nv) => {
|
||||
let MetaItemLit { kind: LitKind::Bool(bool_value), span, .. } = nv.value_as_lit()
|
||||
else {
|
||||
cx.emit_lint(AttributeLintKind::DocAutoCfgWrongLiteral, nv.value_span);
|
||||
return;
|
||||
};
|
||||
self.attribute.auto_cfg_change = Some((*bool_value, *span));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_single_doc_attr_item<'c, S: Stage>(
|
||||
&mut self,
|
||||
cx: &'c mut AcceptContext<'_, '_, S>,
|
||||
|
|
@ -192,8 +336,8 @@ impl DocParser {
|
|||
|
||||
macro_rules! no_args {
|
||||
($ident: ident) => {{
|
||||
if !args.no_args() {
|
||||
cx.expected_no_args(args.span().unwrap());
|
||||
if let Err(span) = args.no_args() {
|
||||
cx.expected_no_args(span);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -217,10 +361,12 @@ impl DocParser {
|
|||
return;
|
||||
};
|
||||
|
||||
if self.attribute.$ident.is_some() {
|
||||
cx.duplicate_key(path.span(), path.word_sym().unwrap());
|
||||
return;
|
||||
}
|
||||
// FIXME: It's errorring when the attribute is passed multiple times on the command
|
||||
// line.
|
||||
// if self.attribute.$ident.is_some() {
|
||||
// cx.duplicate_key(path.span(), path.word_sym().unwrap());
|
||||
// return;
|
||||
// }
|
||||
|
||||
self.attribute.$ident = Some((s, path.span()));
|
||||
}};
|
||||
|
|
@ -238,16 +384,32 @@ impl DocParser {
|
|||
Some(sym::inline) => self.parse_inline(cx, path, args, DocInline::Inline),
|
||||
Some(sym::no_inline) => self.parse_inline(cx, path, args, DocInline::NoInline),
|
||||
Some(sym::masked) => no_args!(masked),
|
||||
Some(sym::cfg) => no_args!(cfg),
|
||||
Some(sym::cfg_hide) => no_args!(cfg_hide),
|
||||
Some(sym::cfg) => self.parse_cfg(cx, args),
|
||||
Some(sym::notable_trait) => no_args!(notable_trait),
|
||||
Some(sym::keyword) => self.parse_keyword(cx, path, args),
|
||||
Some(sym::keyword) => parse_keyword_and_attribute(
|
||||
cx,
|
||||
path,
|
||||
args,
|
||||
&mut self.attribute.keyword,
|
||||
check_keyword,
|
||||
),
|
||||
Some(sym::attribute) => parse_keyword_and_attribute(
|
||||
cx,
|
||||
path,
|
||||
args,
|
||||
&mut self.attribute.attribute,
|
||||
check_attribute,
|
||||
),
|
||||
Some(sym::fake_variadic) => no_args!(fake_variadic),
|
||||
Some(sym::search_unbox) => no_args!(search_unbox),
|
||||
Some(sym::rust_logo) => no_args!(rust_logo),
|
||||
Some(sym::auto_cfg) => self.parse_auto_cfg(cx, path, args),
|
||||
Some(sym::test) => {
|
||||
let Some(list) = args.list() else {
|
||||
cx.expected_list(args.span().unwrap_or(path.span()));
|
||||
cx.emit_lint(
|
||||
AttributeLintKind::DocTestTakesList,
|
||||
args.span().unwrap_or(path.span()),
|
||||
);
|
||||
return;
|
||||
};
|
||||
|
||||
|
|
@ -264,80 +426,32 @@ impl DocParser {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
// let path = rustc_ast_pretty::pprust::path_to_string(&i_meta.path);
|
||||
// if i_meta.has_name(sym::spotlight) {
|
||||
// self.tcx.emit_node_span_lint(
|
||||
// INVALID_DOC_ATTRIBUTES,
|
||||
// hir_id,
|
||||
// i_meta.span,
|
||||
// errors::DocTestUnknownSpotlight { path, span: i_meta.span },
|
||||
// );
|
||||
// } else if i_meta.has_name(sym::include)
|
||||
// && let Some(value) = i_meta.value_str()
|
||||
// {
|
||||
// let applicability = if list.len() == 1 {
|
||||
// Applicability::MachineApplicable
|
||||
// } else {
|
||||
// Applicability::MaybeIncorrect
|
||||
// };
|
||||
// // If there are multiple attributes, the suggestion would suggest
|
||||
// // deleting all of them, which is incorrect.
|
||||
// self.tcx.emit_node_span_lint(
|
||||
// INVALID_DOC_ATTRIBUTES,
|
||||
// hir_id,
|
||||
// i_meta.span,
|
||||
// errors::DocTestUnknownInclude {
|
||||
// path,
|
||||
// value: value.to_string(),
|
||||
// inner: match attr.style() {
|
||||
// AttrStyle::Inner => "!",
|
||||
// AttrStyle::Outer => "",
|
||||
// },
|
||||
// sugg: (attr.span(), applicability),
|
||||
// },
|
||||
// );
|
||||
// } else if i_meta.has_name(sym::passes) || i_meta.has_name(sym::no_default_passes) {
|
||||
// self.tcx.emit_node_span_lint(
|
||||
// INVALID_DOC_ATTRIBUTES,
|
||||
// hir_id,
|
||||
// i_meta.span,
|
||||
// errors::DocTestUnknownPasses { path, span: i_meta.span },
|
||||
// );
|
||||
// } else if i_meta.has_name(sym::plugins) {
|
||||
// self.tcx.emit_node_span_lint(
|
||||
// INVALID_DOC_ATTRIBUTES,
|
||||
// hir_id,
|
||||
// i_meta.span,
|
||||
// errors::DocTestUnknownPlugins { path, span: i_meta.span },
|
||||
// );
|
||||
// } else {
|
||||
// self.tcx.emit_node_span_lint(
|
||||
// INVALID_DOC_ATTRIBUTES,
|
||||
// hir_id,
|
||||
// i_meta.span,
|
||||
// errors::DocTestUnknownAny { path },
|
||||
// );
|
||||
// }
|
||||
}
|
||||
_ => {
|
||||
cx.expected_specific_argument(
|
||||
mip.span(),
|
||||
[
|
||||
sym::alias.as_str(),
|
||||
sym::hidden.as_str(),
|
||||
sym::html_favicon_url.as_str(),
|
||||
sym::html_logo_url.as_str(),
|
||||
sym::html_no_source.as_str(),
|
||||
sym::html_playground_url.as_str(),
|
||||
sym::html_root_url.as_str(),
|
||||
sym::inline.as_str(),
|
||||
sym::no_inline.as_str(),
|
||||
sym::test.as_str(),
|
||||
]
|
||||
.to_vec(),
|
||||
Some(sym::spotlight) => {
|
||||
cx.emit_lint(AttributeLintKind::DocUnknownSpotlight, path.span());
|
||||
}
|
||||
Some(sym::include) if let Some(nv) = args.name_value() => {
|
||||
let inner = match cx.attr_style {
|
||||
AttrStyle::Outer => "",
|
||||
AttrStyle::Inner => "!",
|
||||
};
|
||||
cx.emit_lint(
|
||||
AttributeLintKind::DocUnknownInclude { inner, value: nv.value_as_lit().symbol },
|
||||
path.span(),
|
||||
);
|
||||
}
|
||||
Some(name @ (sym::passes | sym::no_default_passes)) => {
|
||||
cx.emit_lint(AttributeLintKind::DocUnknownPasses { name }, path.span());
|
||||
}
|
||||
Some(sym::plugins) => {
|
||||
cx.emit_lint(AttributeLintKind::DocUnknownPlugins, path.span());
|
||||
}
|
||||
Some(name) => {
|
||||
cx.emit_lint(AttributeLintKind::DocUnknownAny { name }, path.span());
|
||||
}
|
||||
None => {
|
||||
// FIXME: is there anything to do in this case?
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -354,17 +468,30 @@ impl DocParser {
|
|||
for i in items.mixed() {
|
||||
match i {
|
||||
MetaItemOrLitParser::MetaItemParser(mip) => {
|
||||
self.nb_doc_attrs += 1;
|
||||
self.parse_single_doc_attr_item(cx, mip);
|
||||
}
|
||||
MetaItemOrLitParser::Lit(lit) => todo!("error should've used equals"),
|
||||
MetaItemOrLitParser::Lit(lit) => {
|
||||
cx.expected_name_value(lit.span, None);
|
||||
}
|
||||
MetaItemOrLitParser::Err(..) => {
|
||||
// already had an error here, move on.
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
ArgParser::NameValue(v) => {
|
||||
panic!("this should be rare if at all possible");
|
||||
ArgParser::NameValue(nv) => {
|
||||
let Some(comment) = nv.value_as_str() else {
|
||||
cx.expected_string_literal(nv.value_span, Some(nv.value_as_lit()));
|
||||
return;
|
||||
};
|
||||
|
||||
self.doc_comment = Some(DocComment {
|
||||
style: cx.attr_style,
|
||||
kind: CommentKind::Block,
|
||||
span: nv.value_span,
|
||||
comment,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -373,13 +500,49 @@ impl DocParser {
|
|||
impl<S: Stage> AttributeParser<S> for DocParser {
|
||||
const ATTRIBUTES: AcceptMapping<Self, S> = &[(
|
||||
&[sym::doc],
|
||||
template!(List: "hidden|inline|...", NameValueStr: "string"),
|
||||
template!(List: &["hidden", "inline", "test"], NameValueStr: "string"),
|
||||
|this, cx, args| {
|
||||
this.accept_single_doc_attr(cx, args);
|
||||
},
|
||||
)];
|
||||
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[
|
||||
Allow(Target::ExternCrate),
|
||||
Allow(Target::Use),
|
||||
Allow(Target::Static),
|
||||
Allow(Target::Const),
|
||||
Allow(Target::Fn),
|
||||
Allow(Target::Mod),
|
||||
Allow(Target::ForeignMod),
|
||||
Allow(Target::TyAlias),
|
||||
Allow(Target::Enum),
|
||||
Allow(Target::Variant),
|
||||
Allow(Target::Struct),
|
||||
Allow(Target::Field),
|
||||
Allow(Target::Union),
|
||||
Allow(Target::Trait),
|
||||
Allow(Target::TraitAlias),
|
||||
Allow(Target::Impl { of_trait: true }),
|
||||
Allow(Target::Impl { of_trait: false }),
|
||||
Allow(Target::AssocConst),
|
||||
Allow(Target::Method(MethodKind::Inherent)),
|
||||
Allow(Target::Method(MethodKind::Trait { body: true })),
|
||||
Allow(Target::Method(MethodKind::Trait { body: false })),
|
||||
Allow(Target::Method(MethodKind::TraitImpl)),
|
||||
Allow(Target::AssocTy),
|
||||
Allow(Target::ForeignFn),
|
||||
Allow(Target::ForeignStatic),
|
||||
Allow(Target::ForeignTy),
|
||||
Allow(Target::MacroDef),
|
||||
Allow(Target::Crate),
|
||||
]);
|
||||
|
||||
fn finalize(self, cx: &FinalizeContext<'_, '_, S>) -> Option<AttributeKind> {
|
||||
todo!()
|
||||
fn finalize(self, _cx: &FinalizeContext<'_, '_, S>) -> Option<AttributeKind> {
|
||||
if let Some(DocComment { style, kind, span, comment }) = self.doc_comment {
|
||||
Some(AttributeKind::DocComment { style, kind, span, comment })
|
||||
} else if self.nb_doc_attrs != 0 {
|
||||
Some(AttributeKind::Doc(Box::new(self.attribute)))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -39,8 +39,8 @@ pub(crate) mod confusables;
|
|||
pub(crate) mod crate_level;
|
||||
pub(crate) mod debugger;
|
||||
pub(crate) mod deprecation;
|
||||
pub(crate) mod dummy;
|
||||
pub(crate) mod doc;
|
||||
pub(crate) mod dummy;
|
||||
pub(crate) mod inline;
|
||||
pub(crate) mod link_attrs;
|
||||
pub(crate) mod lint_helpers;
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ use rustc_ast::attr::AttributeExt;
|
|||
use rustc_feature::is_builtin_attr_name;
|
||||
use rustc_hir::RustcVersion;
|
||||
use rustc_hir::limit::Limit;
|
||||
use rustc_span::{Symbol, sym};
|
||||
use rustc_span::Symbol;
|
||||
|
||||
use crate::context::{AcceptContext, Stage};
|
||||
use crate::parser::{ArgParser, NameValueParser};
|
||||
|
|
@ -32,7 +32,6 @@ pub fn is_builtin_attr(attr: &impl AttributeExt) -> bool {
|
|||
|| attr.ident().is_some_and(|ident| is_builtin_attr_name(ident.name))
|
||||
}
|
||||
|
||||
|
||||
/// Parse a single integer.
|
||||
///
|
||||
/// Used by attributes that take a single integer as argument, such as
|
||||
|
|
@ -92,7 +91,3 @@ impl<S: Stage> AcceptContext<'_, '_, S> {
|
|||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn find_crate_name(attrs: &[impl AttributeExt]) -> Option<Symbol> {
|
||||
first_attr_value_str_by_name(attrs, sym::crate_name)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -163,6 +163,8 @@ attribute_parsers!(
|
|||
BodyStabilityParser,
|
||||
ConfusablesParser,
|
||||
ConstStabilityParser,
|
||||
DocParser,
|
||||
MacroUseParser,
|
||||
|
||||
NakedParser,
|
||||
StabilityParser,
|
||||
|
|
|
|||
|
|
@ -298,17 +298,6 @@ impl<'sess, S: Stage> AttributeParser<'sess, S> {
|
|||
comment: *symbol,
|
||||
}))
|
||||
}
|
||||
// // FIXME: make doc attributes go through a proper attribute parser
|
||||
// ast::AttrKind::Normal(n) if n.has_name(sym::doc) => {
|
||||
// let p = GenericMetaItemParser::from_attr(&n, self.dcx());
|
||||
//
|
||||
// attributes.push(Attribute::Parsed(AttributeKind::DocComment {
|
||||
// style: attr.style,
|
||||
// kind: CommentKind::Line,
|
||||
// span: attr.span,
|
||||
// comment: p.args().name_value(),
|
||||
// }))
|
||||
// }
|
||||
ast::AttrKind::Normal(n) => {
|
||||
attr_paths.push(PathParser(Cow::Borrowed(&n.item.path)));
|
||||
let attr_path = AttrPath::from_ast(&n.item.path, lower_span);
|
||||
|
|
|
|||
|
|
@ -80,6 +80,7 @@
|
|||
#![feature(decl_macro)]
|
||||
#![recursion_limit = "256"]
|
||||
// tidy-alphabetical-end
|
||||
#![feature(if_let_guard)]
|
||||
|
||||
#[macro_use]
|
||||
/// All the individual attribute parsers for each of rustc's built-in attributes.
|
||||
|
|
@ -107,7 +108,7 @@ pub use attributes::cfg::{
|
|||
};
|
||||
pub use attributes::cfg_old::*;
|
||||
pub use attributes::cfg_select::*;
|
||||
pub use attributes::util::{is_builtin_attr, is_doc_alias_attrs_contain_symbol, parse_version};
|
||||
pub use attributes::util::{is_builtin_attr, parse_version};
|
||||
pub use context::{Early, Late, OmitDoc, ShouldEmit};
|
||||
pub use interface::AttributeParser;
|
||||
pub use session_diagnostics::ParsedDescription;
|
||||
|
|
|
|||
|
|
@ -69,6 +69,15 @@ pub(crate) struct DocKeywordNotKeyword {
|
|||
pub keyword: Symbol,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(attr_parsing_doc_attribute_not_attribute)]
|
||||
#[help]
|
||||
pub(crate) struct DocAttributeNotAttribute {
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
pub attribute: Symbol,
|
||||
}
|
||||
|
||||
/// Error code: E0541
|
||||
pub(crate) struct UnknownMetaItem<'a> {
|
||||
pub span: Span,
|
||||
|
|
@ -588,23 +597,6 @@ pub(crate) struct DocKeywordConflict {
|
|||
pub spans: MultiSpan,
|
||||
}
|
||||
|
||||
#[derive(Subdiagnostic)]
|
||||
pub(crate) enum UnusedNote {
|
||||
#[note(attr_parsing_unused_empty_lints_note)]
|
||||
EmptyList { name: Symbol },
|
||||
#[note(attr_parsing_unused_no_lints_note)]
|
||||
NoLints { name: Symbol },
|
||||
}
|
||||
|
||||
#[derive(LintDiagnostic)]
|
||||
#[diag(attr_parsing_unused)]
|
||||
pub(crate) struct Unused {
|
||||
#[suggestion(code = "", applicability = "machine-applicable")]
|
||||
pub attr_span: Span,
|
||||
#[subdiagnostic]
|
||||
pub note: UnusedNote,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(attr_parsing_link_ordinal_out_of_range)]
|
||||
#[note]
|
||||
|
|
@ -1011,3 +1003,91 @@ pub(crate) struct CfgAttrBadDelim {
|
|||
#[subdiagnostic]
|
||||
pub sugg: MetaBadDelimSugg,
|
||||
}
|
||||
|
||||
#[derive(LintDiagnostic)]
|
||||
#[diag(attr_parsing_doc_auto_cfg_expects_hide_or_show)]
|
||||
pub(crate) struct DocAutoCfgExpectsHideOrShow;
|
||||
|
||||
#[derive(LintDiagnostic)]
|
||||
#[diag(attr_parsing_doc_auto_cfg_hide_show_unexpected_item)]
|
||||
pub(crate) struct DocAutoCfgHideShowUnexpectedItem {
|
||||
pub attr_name: Symbol,
|
||||
}
|
||||
|
||||
#[derive(LintDiagnostic)]
|
||||
#[diag(attr_parsing_doc_auto_cfg_hide_show_expects_list)]
|
||||
pub(crate) struct DocAutoCfgHideShowExpectsList {
|
||||
pub attr_name: Symbol,
|
||||
}
|
||||
|
||||
#[derive(LintDiagnostic)]
|
||||
#[diag(attr_parsing_doc_invalid)]
|
||||
pub(crate) struct DocInvalid;
|
||||
|
||||
#[derive(LintDiagnostic)]
|
||||
#[diag(attr_parsing_doc_unknown_include)]
|
||||
pub(crate) struct DocUnknownInclude {
|
||||
pub inner: &'static str,
|
||||
pub value: Symbol,
|
||||
#[suggestion(code = "#{inner}[doc = include_str!(\"{value}\")]")]
|
||||
pub sugg: (Span, Applicability),
|
||||
}
|
||||
|
||||
#[derive(LintDiagnostic)]
|
||||
#[diag(attr_parsing_doc_unknown_spotlight)]
|
||||
#[note]
|
||||
#[note(attr_parsing_no_op_note)]
|
||||
pub(crate) struct DocUnknownSpotlight {
|
||||
#[suggestion(style = "short", applicability = "machine-applicable", code = "notable_trait")]
|
||||
pub sugg_span: Span,
|
||||
}
|
||||
|
||||
#[derive(LintDiagnostic)]
|
||||
#[diag(attr_parsing_doc_unknown_passes)]
|
||||
#[note]
|
||||
#[note(attr_parsing_no_op_note)]
|
||||
pub(crate) struct DocUnknownPasses {
|
||||
pub name: Symbol,
|
||||
#[label]
|
||||
pub note_span: Span,
|
||||
}
|
||||
|
||||
#[derive(LintDiagnostic)]
|
||||
#[diag(attr_parsing_doc_unknown_plugins)]
|
||||
#[note]
|
||||
#[note(attr_parsing_no_op_note)]
|
||||
pub(crate) struct DocUnknownPlugins {
|
||||
#[label]
|
||||
pub label_span: Span,
|
||||
}
|
||||
|
||||
#[derive(LintDiagnostic)]
|
||||
#[diag(attr_parsing_doc_unknown_any)]
|
||||
pub(crate) struct DocUnknownAny {
|
||||
pub name: Symbol,
|
||||
}
|
||||
|
||||
#[derive(LintDiagnostic)]
|
||||
#[diag(attr_parsing_doc_auto_cfg_wrong_literal)]
|
||||
pub(crate) struct DocAutoCfgWrongLiteral;
|
||||
|
||||
#[derive(LintDiagnostic)]
|
||||
#[diag(attr_parsing_doc_test_takes_list)]
|
||||
pub(crate) struct DocTestTakesList;
|
||||
|
||||
#[derive(LintDiagnostic)]
|
||||
#[diag(attr_parsing_doc_test_unknown)]
|
||||
pub(crate) struct DocTestUnknown {
|
||||
pub name: Symbol,
|
||||
}
|
||||
|
||||
#[derive(LintDiagnostic)]
|
||||
#[diag(attr_parsing_doc_test_literal)]
|
||||
pub(crate) struct DocTestLiteral;
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(attr_parsing_doc_alias_malformed)]
|
||||
pub(crate) struct DocAliasMalformed {
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -428,6 +428,26 @@ pub enum DocInline {
|
|||
NoInline,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq)]
|
||||
#[derive(HashStable_Generic, Encodable, Decodable, PrintAttribute)]
|
||||
pub enum HideOrShow {
|
||||
Hide,
|
||||
Show,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, HashStable_Generic, Encodable, Decodable, PrintAttribute)]
|
||||
pub struct CfgInfo {
|
||||
pub name: Symbol,
|
||||
pub name_span: Span,
|
||||
pub value: Option<(Symbol, Span)>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, HashStable_Generic, Encodable, Decodable, PrintAttribute)]
|
||||
pub struct CfgHideShow {
|
||||
pub kind: HideOrShow,
|
||||
pub values: ThinVec<CfgInfo>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, HashStable_Generic, Encodable, Decodable, PrintAttribute)]
|
||||
pub struct DocAttribute {
|
||||
pub aliases: FxIndexMap<Symbol, Span>,
|
||||
|
|
@ -435,12 +455,15 @@ pub struct DocAttribute {
|
|||
pub inline: Option<(DocInline, Span)>,
|
||||
|
||||
// unstable
|
||||
pub cfg: Option<Span>,
|
||||
pub cfg_hide: Option<Span>,
|
||||
pub cfg: Option<CfgEntry>,
|
||||
pub auto_cfg: ThinVec<CfgHideShow>,
|
||||
/// This is for `#[doc(auto_cfg = false|true)]`.
|
||||
pub auto_cfg_change: Option<(bool, Span)>,
|
||||
|
||||
// builtin
|
||||
pub fake_variadic: Option<Span>,
|
||||
pub keyword: Option<(Symbol, Span)>,
|
||||
pub attribute: Option<(Symbol, Span)>,
|
||||
pub masked: Option<Span>,
|
||||
pub notable_trait: Option<Span>,
|
||||
pub search_unbox: Option<Span>,
|
||||
|
|
@ -455,7 +478,7 @@ pub struct DocAttribute {
|
|||
pub rust_logo: Option<Span>,
|
||||
|
||||
// #[doc(test(...))]
|
||||
pub test_attrs: ThinVec<()>,
|
||||
pub test_attrs: ThinVec<Span>,
|
||||
pub no_crate_inject: Option<Span>,
|
||||
}
|
||||
|
||||
|
|
@ -466,9 +489,11 @@ impl Default for DocAttribute {
|
|||
hidden: None,
|
||||
inline: None,
|
||||
cfg: None,
|
||||
cfg_hide: None,
|
||||
auto_cfg: ThinVec::new(),
|
||||
auto_cfg_change: None,
|
||||
fake_variadic: None,
|
||||
keyword: None,
|
||||
attribute: None,
|
||||
masked: None,
|
||||
notable_trait: None,
|
||||
search_unbox: None,
|
||||
|
|
|
|||
|
|
@ -1414,6 +1414,10 @@ impl AttributeExt for Attribute {
|
|||
)
|
||||
)
|
||||
}
|
||||
|
||||
fn is_doc_hidden(&self) -> bool {
|
||||
matches!(self, Attribute::Parsed(AttributeKind::Doc(d)) if d.hidden.is_some())
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME(fn_delegation): use function delegation instead of manually forwarding
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ use rustc_data_structures::fingerprint::Fingerprint;
|
|||
pub use rustc_lint_defs::AttributeLintKind;
|
||||
use rustc_lint_defs::LintId;
|
||||
use rustc_macros::HashStable_Generic;
|
||||
use rustc_span::Span;
|
||||
use rustc_span::{Span, Symbol};
|
||||
|
||||
use crate::HirId;
|
||||
|
||||
|
|
@ -72,4 +72,30 @@ pub enum AttributeLintKind {
|
|||
DuplicateDocAlias {
|
||||
first_definition: Span,
|
||||
},
|
||||
DocAutoCfgExpectsHideOrShow,
|
||||
DocAutoCfgHideShowUnexpectedItem {
|
||||
attr_name: Symbol,
|
||||
},
|
||||
DocAutoCfgHideShowExpectsList {
|
||||
attr_name: Symbol,
|
||||
},
|
||||
DocInvalid,
|
||||
DocUnknownInclude {
|
||||
inner: &'static str,
|
||||
value: Symbol,
|
||||
},
|
||||
DocUnknownSpotlight,
|
||||
DocUnknownPasses {
|
||||
name: Symbol,
|
||||
},
|
||||
DocUnknownPlugins,
|
||||
DocUnknownAny {
|
||||
name: Symbol,
|
||||
},
|
||||
DocAutoCfgWrongLiteral,
|
||||
DocTestTakesList,
|
||||
DocTestUnknown {
|
||||
name: Symbol,
|
||||
},
|
||||
DocTestLiteral,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,7 +8,6 @@ edition = "2024"
|
|||
itertools = "0.12"
|
||||
rustc_abi = { path = "../rustc_abi" }
|
||||
rustc_ast = { path = "../rustc_ast" }
|
||||
rustc_attr_parsing = { path = "../rustc_attr_parsing" }
|
||||
rustc_data_structures = { path = "../rustc_data_structures" }
|
||||
rustc_errors = { path = "../rustc_errors" }
|
||||
rustc_fluent_macro = { path = "../rustc_fluent_macro" }
|
||||
|
|
|
|||
|
|
@ -1,6 +1,4 @@
|
|||
// tidy-alphabetical-start
|
||||
#![allow(rustc::diagnostic_outside_of_impl)]
|
||||
#![allow(rustc::untranslatable_diagnostic)]
|
||||
#![feature(assert_matches)]
|
||||
#![feature(box_patterns)]
|
||||
#![feature(if_let_guard)]
|
||||
|
|
|
|||
|
|
@ -3,12 +3,12 @@ use std::cell::{Cell, RefCell};
|
|||
use std::cmp::max;
|
||||
use std::ops::Deref;
|
||||
|
||||
use rustc_attr_data_structures::{AttributeKind, find_attr};
|
||||
use rustc_data_structures::fx::FxHashSet;
|
||||
use rustc_data_structures::sso::SsoHashSet;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::attrs::AttributeKind;
|
||||
use rustc_hir::def::DefKind;
|
||||
use rustc_hir::{self as hir, ExprKind, HirId, Node};
|
||||
use rustc_hir::{self as hir, ExprKind, HirId, Node, find_attr};
|
||||
use rustc_hir_analysis::autoderef::{self, Autoderef};
|
||||
use rustc_infer::infer::canonical::{Canonical, OriginalQueryValues, QueryResponse};
|
||||
use rustc_infer::infer::{BoundRegionConversionTime, DefineOpaqueTypes, InferOk, TyCtxtInferExt};
|
||||
|
|
|
|||
|
|
@ -825,7 +825,7 @@ fn warn_if_doc(cx: &EarlyContext<'_>, node_span: Span, node_kind: &str, attrs: &
|
|||
|
||||
let span = sugared_span.take().unwrap_or(attr.span);
|
||||
|
||||
if is_doc_comment || attr.has_name(sym::doc) {
|
||||
if is_doc_comment {
|
||||
let sub = match attr.kind {
|
||||
AttrKind::DocComment(CommentKind::Line, _) | AttrKind::Normal(..) => {
|
||||
BuiltinUnusedDocCommentSub::PlainHelp
|
||||
|
|
|
|||
|
|
@ -657,11 +657,7 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> {
|
|||
}
|
||||
|
||||
// `#[doc(hidden)]` disables missing_docs check.
|
||||
if attr.has_name(sym::doc)
|
||||
&& attr
|
||||
.meta_item_list()
|
||||
.is_some_and(|l| ast::attr::list_contains_name(&l, sym::hidden))
|
||||
{
|
||||
if attr.is_doc_hidden() {
|
||||
self.insert(
|
||||
LintId::of(MISSING_DOCS),
|
||||
LevelAndSource {
|
||||
|
|
|
|||
|
|
@ -870,25 +870,20 @@ fn analyze_attr(attr: &hir::Attribute, state: &mut AnalyzeAttrState<'_>) -> bool
|
|||
&& !rustc_feature::encode_cross_crate(name)
|
||||
{
|
||||
// Attributes not marked encode-cross-crate don't need to be encoded for downstream crates.
|
||||
} else if attr.doc_str().is_some() {
|
||||
} else if let hir::Attribute::Parsed(AttributeKind::DocComment { .. }) = attr {
|
||||
// We keep all doc comments reachable to rustdoc because they might be "imported" into
|
||||
// downstream crates if they use `#[doc(inline)]` to copy an item's documentation into
|
||||
// their own.
|
||||
if state.is_exported {
|
||||
should_encode = true;
|
||||
}
|
||||
} else if attr.has_name(sym::doc) {
|
||||
} else if let hir::Attribute::Parsed(AttributeKind::Doc(d)) = attr {
|
||||
// If this is a `doc` attribute that doesn't have anything except maybe `inline` (as in
|
||||
// `#[doc(inline)]`), then we can remove it. It won't be inlinable in downstream crates.
|
||||
if let Some(item_list) = attr.meta_item_list() {
|
||||
for item in item_list {
|
||||
if !item.has_name(sym::inline) {
|
||||
should_encode = true;
|
||||
if item.has_name(sym::hidden) {
|
||||
state.is_doc_hidden = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if d.inline.is_none() {
|
||||
should_encode = true;
|
||||
if d.hidden.is_some() {
|
||||
state.is_doc_hidden = true;
|
||||
}
|
||||
}
|
||||
} else if let &[sym::diagnostic, seg] = &*attr.path() {
|
||||
|
|
|
|||
|
|
@ -4,12 +4,14 @@ use std::{fmt, iter};
|
|||
|
||||
use rustc_abi::{Float, Integer, IntegerType, Size};
|
||||
use rustc_apfloat::Float as _;
|
||||
use rustc_ast::attr::AttributeExt;
|
||||
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
|
||||
use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
|
||||
use rustc_data_structures::stack::ensure_sufficient_stack;
|
||||
use rustc_errors::ErrorGuaranteed;
|
||||
use rustc_hashes::Hash128;
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::attrs::AttributeKind;
|
||||
use rustc_hir::def::{CtorOf, DefKind, Res};
|
||||
use rustc_hir::def_id::{CrateNum, DefId, LocalDefId};
|
||||
use rustc_hir::limit::Limit;
|
||||
|
|
@ -1664,16 +1666,14 @@ pub fn reveal_opaque_types_in_bounds<'tcx>(
|
|||
|
||||
/// Determines whether an item is directly annotated with `doc(hidden)`.
|
||||
fn is_doc_hidden(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool {
|
||||
tcx.get_attrs(def_id, sym::doc)
|
||||
.filter_map(|attr| attr.meta_item_list())
|
||||
.any(|items| items.iter().any(|item| item.has_name(sym::hidden)))
|
||||
let attrs = tcx.hir_attrs(tcx.local_def_id_to_hir_id(def_id));
|
||||
attrs.iter().any(|attr| attr.is_doc_hidden())
|
||||
}
|
||||
|
||||
/// Determines whether an item is annotated with `doc(notable_trait)`.
|
||||
pub fn is_doc_notable_trait(tcx: TyCtxt<'_>, def_id: DefId) -> bool {
|
||||
tcx.get_attrs(def_id, sym::doc)
|
||||
.filter_map(|attr| attr.meta_item_list())
|
||||
.any(|items| items.iter().any(|item| item.has_name(sym::notable_trait)))
|
||||
let attrs = tcx.get_all_attrs(def_id);
|
||||
attrs.iter().any(|attr| matches!(attr, hir::Attribute::Parsed(AttributeKind::Doc(doc)) if doc.notable_trait.is_some()))
|
||||
}
|
||||
|
||||
/// Determines whether an item is an intrinsic (which may be via Abi or via the `rustc_intrinsic` attribute).
|
||||
|
|
|
|||
|
|
@ -8,7 +8,6 @@ edition = "2024"
|
|||
rustc_abi = { path = "../rustc_abi" }
|
||||
rustc_ast = { path = "../rustc_ast" }
|
||||
rustc_ast_lowering = { path = "../rustc_ast_lowering" }
|
||||
rustc_ast_pretty = { path = "../rustc_ast_pretty" }
|
||||
rustc_attr_parsing = { path = "../rustc_attr_parsing" }
|
||||
rustc_data_structures = { path = "../rustc_data_structures" }
|
||||
rustc_errors = { path = "../rustc_errors" }
|
||||
|
|
|
|||
|
|
@ -107,39 +107,14 @@ passes_diagnostic_item_first_defined =
|
|||
the diagnostic item is first defined here
|
||||
|
||||
passes_doc_alias_bad_location =
|
||||
{$attr_str} isn't allowed on {$location}
|
||||
|
||||
passes_doc_alias_malformed =
|
||||
doc alias attribute expects a string `#[doc(alias = "a")]` or a list of strings `#[doc(alias("a", "b"))]`
|
||||
doc alias attribute isn't allowed on {$location}
|
||||
|
||||
passes_doc_alias_not_an_alias =
|
||||
{$attr_str} is the same as the item's name
|
||||
|
||||
passes_doc_alias_not_string_literal =
|
||||
`#[doc(alias("a"))]` expects string literals
|
||||
|
||||
passes_doc_attr_not_crate_level =
|
||||
`#![doc({$attr_name} = "...")]` isn't allowed as a crate-level attribute
|
||||
|
||||
passes_doc_attribute_not_attribute =
|
||||
nonexistent builtin attribute `{$attribute}` used in `#[doc(attribute = "...")]`
|
||||
.help = only existing builtin attributes are allowed in core/std
|
||||
|
||||
passes_doc_auto_cfg_expects_hide_or_show =
|
||||
only `hide` or `show` are allowed in `#[doc(auto_cfg(...))]`
|
||||
|
||||
passes_doc_auto_cfg_hide_show_expects_list =
|
||||
`#![doc(auto_cfg({$attr_name}(...)))]` expects a list of items
|
||||
|
||||
passes_doc_auto_cfg_hide_show_unexpected_item =
|
||||
`#![doc(auto_cfg({$attr_name}(...)))]` only accepts identifiers or key/value items
|
||||
|
||||
passes_doc_auto_cfg_wrong_literal =
|
||||
expected boolean for `#[doc(auto_cfg = ...)]`
|
||||
|
||||
passes_doc_expect_str =
|
||||
doc {$attr_name} attribute expects a string: #[doc({$attr_name} = "a")]
|
||||
|
||||
passes_doc_fake_variadic_not_valid =
|
||||
`#[doc(fake_variadic)]` must be used on the first of a set of tuple or fn pointer trait impls with varying arity
|
||||
|
||||
|
|
@ -151,9 +126,6 @@ passes_doc_inline_only_use =
|
|||
.not_a_use_item_label = not a `use` item
|
||||
.note = read <https://doc.rust-lang.org/nightly/rustdoc/the-doc-attribute.html#inline-and-no_inline> for more information
|
||||
|
||||
passes_doc_invalid =
|
||||
invalid `doc` attribute
|
||||
|
||||
passes_doc_keyword_attribute_empty_mod =
|
||||
`#[doc({$attr_name} = "...")]` should be used on empty modules
|
||||
|
||||
|
|
@ -180,39 +152,6 @@ passes_doc_rust_logo =
|
|||
passes_doc_search_unbox_invalid =
|
||||
`#[doc(search_unbox)]` should be used on generic structs and enums
|
||||
|
||||
passes_doc_test_literal = `#![doc(test(...)]` does not take a literal
|
||||
|
||||
passes_doc_test_takes_list =
|
||||
`#[doc(test(...)]` takes a list of attributes
|
||||
|
||||
passes_doc_test_unknown =
|
||||
unknown `doc(test)` attribute `{$path}`
|
||||
|
||||
passes_doc_test_unknown_any =
|
||||
unknown `doc` attribute `{$path}`
|
||||
|
||||
passes_doc_test_unknown_include =
|
||||
unknown `doc` attribute `{$path}`
|
||||
.suggestion = use `doc = include_str!` instead
|
||||
|
||||
passes_doc_test_unknown_passes =
|
||||
unknown `doc` attribute `{$path}`
|
||||
.note = `doc` attribute `{$path}` no longer functions; see issue #44136 <https://github.com/rust-lang/rust/issues/44136>
|
||||
.label = no longer functions
|
||||
.no_op_note = `doc({$path})` is now a no-op
|
||||
|
||||
passes_doc_test_unknown_plugins =
|
||||
unknown `doc` attribute `{$path}`
|
||||
.note = `doc` attribute `{$path}` no longer functions; see issue #44136 <https://github.com/rust-lang/rust/issues/44136> and CVE-2018-1000622 <https://nvd.nist.gov/vuln/detail/CVE-2018-1000622>
|
||||
.label = no longer functions
|
||||
.no_op_note = `doc({$path})` is now a no-op
|
||||
|
||||
passes_doc_test_unknown_spotlight =
|
||||
unknown `doc` attribute `{$path}`
|
||||
.note = `doc(spotlight)` was renamed to `doc(notable_trait)`
|
||||
.suggestion = use `notable_trait` instead
|
||||
.no_op_note = `doc(spotlight)` is now a no-op
|
||||
|
||||
passes_duplicate_diagnostic_item_in_crate =
|
||||
duplicate diagnostic item in crate `{$crate_name}`: `{$name}`
|
||||
.note = the diagnostic item is first defined in crate `{$orig_crate_name}`
|
||||
|
|
|
|||
|
|
@ -10,22 +10,24 @@ use std::collections::hash_map::Entry;
|
|||
use std::slice;
|
||||
|
||||
use rustc_abi::{Align, ExternAbi, Size};
|
||||
use rustc_ast::{AttrStyle, LitKind, MetaItem, MetaItemInner, MetaItemKind, ast};
|
||||
use rustc_ast::{AttrStyle, LitKind, MetaItemKind, ast};
|
||||
use rustc_attr_parsing::{AttributeParser, Late};
|
||||
use rustc_data_structures::fx::FxHashMap;
|
||||
use rustc_errors::{Applicability, DiagCtxtHandle, IntoDiagArg, MultiSpan, StashKey};
|
||||
use rustc_errors::{DiagCtxtHandle, IntoDiagArg, StashKey};
|
||||
use rustc_feature::{
|
||||
ACCEPTED_LANG_FEATURES, AttributeDuplicates, AttributeType, BUILTIN_ATTRIBUTE_MAP,
|
||||
BuiltinAttribute,
|
||||
};
|
||||
use rustc_hir::attrs::{AttributeKind, InlineAttr, MirDialect, MirPhase, ReprAttr, SanitizerSet};
|
||||
use rustc_hir::attrs::{
|
||||
AttributeKind, DocAttribute, InlineAttr, MirDialect, MirPhase, ReprAttr, SanitizerSet,
|
||||
};
|
||||
use rustc_hir::def::DefKind;
|
||||
use rustc_hir::def_id::LocalModDefId;
|
||||
use rustc_hir::intravisit::{self, Visitor};
|
||||
use rustc_hir::{
|
||||
self as hir, Attribute, CRATE_HIR_ID, CRATE_OWNER_ID, Constness, FnSig, ForeignItem, HirId,
|
||||
Item, ItemKind, MethodKind, PartialConstStability, Safety, Stability, StabilityLevel, Target,
|
||||
TraitItem, find_attr,
|
||||
self as hir, Attribute, CRATE_HIR_ID, Constness, FnSig, ForeignItem, HirId, Item, ItemKind,
|
||||
MethodKind, PartialConstStability, Safety, Stability, StabilityLevel, Target, TraitItem,
|
||||
find_attr,
|
||||
};
|
||||
use rustc_macros::LintDiagnostic;
|
||||
use rustc_middle::hir::nested_filter;
|
||||
|
|
@ -43,7 +45,7 @@ use rustc_session::lint::builtin::{
|
|||
};
|
||||
use rustc_session::parse::feature_err;
|
||||
use rustc_span::edition::Edition;
|
||||
use rustc_span::{BytePos, DUMMY_SP, Span, Symbol, edition, sym};
|
||||
use rustc_span::{BytePos, DUMMY_SP, Span, Symbol, sym};
|
||||
use rustc_trait_selection::error_reporting::InferCtxtErrorExt;
|
||||
use rustc_trait_selection::infer::{TyCtxtInferExt, ValuePairs};
|
||||
use rustc_trait_selection::traits::ObligationCtxt;
|
||||
|
|
@ -106,21 +108,6 @@ impl IntoDiagArg for ProcMacroKind {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
enum DocFakeItemKind {
|
||||
Attribute,
|
||||
Keyword,
|
||||
}
|
||||
|
||||
impl DocFakeItemKind {
|
||||
fn name(self) -> &'static str {
|
||||
match self {
|
||||
Self::Attribute => "attribute",
|
||||
Self::Keyword => "keyword",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct CheckAttrVisitor<'tcx> {
|
||||
tcx: TyCtxt<'tcx>,
|
||||
|
||||
|
|
@ -223,6 +210,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
|
|||
Attribute::Parsed(AttributeKind::MacroExport { span, .. }) => {
|
||||
self.check_macro_export(hir_id, *span, target)
|
||||
},
|
||||
Attribute::Parsed(AttributeKind::Doc(attr)) => self.check_doc_attrs(attr, hir_id, target),
|
||||
Attribute::Parsed(
|
||||
AttributeKind::BodyStability { .. }
|
||||
| AttributeKind::ConstStabilityIndirect
|
||||
|
|
@ -771,13 +759,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
fn doc_attr_str_error(&self, meta: &MetaItemInner, attr_name: &str) {
|
||||
self.dcx().emit_err(errors::DocExpectStr { attr_span: meta.span(), attr_name });
|
||||
}
|
||||
|
||||
fn check_doc_alias_value(&self, span: Span, alias: Symbol, hir_id: HirId, target: Target) {
|
||||
let tcx = self.tcx;
|
||||
|
||||
fn check_doc_alias_value(&self, span: Span, hir_id: HirId, target: Target, alias: Symbol) {
|
||||
if let Some(location) = match target {
|
||||
Target::AssocTy => {
|
||||
if let DefKind::Impl { .. } =
|
||||
|
|
@ -834,96 +816,12 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
|
|||
| Target::MacroCall
|
||||
| Target::Delegation { .. } => None,
|
||||
} {
|
||||
// FIXME: emit proper error
|
||||
// tcx.dcx().emit_err(errors::DocAliasBadLocation {
|
||||
// span,
|
||||
// errors::DocAliasDuplicated { first_defn: *entry.entry.get() },
|
||||
// );
|
||||
self.tcx.dcx().emit_err(errors::DocAliasBadLocation { span, location });
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
fn check_doc_alias(
|
||||
&self,
|
||||
meta: &MetaItemInner,
|
||||
hir_id: HirId,
|
||||
target: Target,
|
||||
aliases: &mut FxHashMap<String, Span>,
|
||||
) {
|
||||
if let Some(values) = meta.meta_item_list() {
|
||||
for v in values {
|
||||
match v.lit() {
|
||||
Some(l) => match l.kind {
|
||||
LitKind::Str(s, _) => {
|
||||
self.check_doc_alias_value(v, s, hir_id, target, true, aliases);
|
||||
}
|
||||
_ => {
|
||||
self.tcx
|
||||
.dcx()
|
||||
.emit_err(errors::DocAliasNotStringLiteral { span: v.span() });
|
||||
}
|
||||
},
|
||||
None => {
|
||||
self.tcx
|
||||
.dcx()
|
||||
.emit_err(errors::DocAliasNotStringLiteral { span: v.span() });
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if let Some(doc_alias) = meta.value_str() {
|
||||
self.check_doc_alias_value(meta, doc_alias, hir_id, target, false, aliases)
|
||||
} else {
|
||||
self.dcx().emit_err(errors::DocAliasMalformed { span: meta.span() });
|
||||
}
|
||||
}
|
||||
|
||||
fn check_doc_keyword_and_attribute(
|
||||
&self,
|
||||
span: Span,
|
||||
hir_id: HirId,
|
||||
attr_kind: DocFakeItemKind,
|
||||
) {
|
||||
fn is_doc_keyword(s: Symbol) -> bool {
|
||||
// FIXME: Once rustdoc can handle URL conflicts on case insensitive file systems, we
|
||||
// can remove the `SelfTy` case here, remove `sym::SelfTy`, and update the
|
||||
// `#[doc(keyword = "SelfTy")` attribute in `library/std/src/keyword_docs.rs`.
|
||||
s.is_reserved(|| edition::LATEST_STABLE_EDITION) || s.is_weak() || s == sym::SelfTy
|
||||
}
|
||||
|
||||
let item_kind = match self.tcx.hir_node(hir_id) {
|
||||
hir::Node::Item(item) => Some(&item.kind),
|
||||
_ => None,
|
||||
};
|
||||
match item_kind {
|
||||
Some(ItemKind::Mod(_, module)) => {
|
||||
if !module.item_ids.is_empty() {
|
||||
self.dcx()
|
||||
.emit_err(errors::DocKeywordEmptyMod { span, attr_name: attr_kind.name() });
|
||||
return;
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
self.dcx().emit_err(errors::DocKeywordNotMod { span, attr_name: attr_kind.name() });
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
match attr_kind {
|
||||
DocFakeItemKind::Keyword => {
|
||||
if !is_doc_keyword(value) {
|
||||
self.dcx().emit_err(errors::DocKeywordNotKeyword {
|
||||
span: meta.name_value_literal_span().unwrap_or_else(|| meta.span()),
|
||||
keyword: value,
|
||||
});
|
||||
}
|
||||
}
|
||||
DocFakeItemKind::Attribute => {
|
||||
if !is_builtin_attr(value) {
|
||||
self.dcx().emit_err(errors::DocAttributeNotAttribute {
|
||||
span: meta.name_value_literal_span().unwrap_or_else(|| meta.span()),
|
||||
attribute: value,
|
||||
});
|
||||
}
|
||||
}
|
||||
if self.tcx.hir_opt_name(hir_id) == Some(alias) {
|
||||
self.tcx.dcx().emit_err(errors::DocAliasNotAnAlias { span, attr_str: alias });
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1027,6 +925,25 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
fn check_doc_keyword_and_attribute(&self, span: Span, hir_id: HirId, attr_name: &'static str) {
|
||||
let item_kind = match self.tcx.hir_node(hir_id) {
|
||||
hir::Node::Item(item) => Some(&item.kind),
|
||||
_ => None,
|
||||
};
|
||||
match item_kind {
|
||||
Some(ItemKind::Mod(_, module)) => {
|
||||
if !module.item_ids.is_empty() {
|
||||
self.dcx().emit_err(errors::DocKeywordAttributeEmptyMod { span, attr_name });
|
||||
return;
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
self.dcx().emit_err(errors::DocKeywordAttributeNotMod { span, attr_name });
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Checks that an attribute is *not* used at the crate level. Returns `true` if valid.
|
||||
fn check_attr_not_crate_level(&self, span: Span, hir_id: HirId, attr_name: &str) -> bool {
|
||||
if CRATE_HIR_ID == hir_id {
|
||||
|
|
@ -1050,62 +967,6 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
|
|||
true
|
||||
}
|
||||
|
||||
/// Check that the `#![doc(auto_cfg)]` attribute has the expected input.
|
||||
fn check_doc_auto_cfg(&self, meta: &MetaItem, hir_id: HirId) {
|
||||
match &meta.kind {
|
||||
MetaItemKind::Word => {}
|
||||
MetaItemKind::NameValue(lit) => {
|
||||
if !matches!(lit.kind, LitKind::Bool(_)) {
|
||||
self.tcx.emit_node_span_lint(
|
||||
INVALID_DOC_ATTRIBUTES,
|
||||
hir_id,
|
||||
meta.span,
|
||||
errors::DocAutoCfgWrongLiteral,
|
||||
);
|
||||
}
|
||||
}
|
||||
MetaItemKind::List(list) => {
|
||||
for item in list {
|
||||
let Some(attr_name @ (sym::hide | sym::show)) = item.name() else {
|
||||
self.tcx.emit_node_span_lint(
|
||||
INVALID_DOC_ATTRIBUTES,
|
||||
hir_id,
|
||||
meta.span,
|
||||
errors::DocAutoCfgExpectsHideOrShow,
|
||||
);
|
||||
continue;
|
||||
};
|
||||
if let Some(list) = item.meta_item_list() {
|
||||
for item in list {
|
||||
let valid = item.meta_item().is_some_and(|meta| {
|
||||
meta.path.segments.len() == 1
|
||||
&& matches!(
|
||||
&meta.kind,
|
||||
MetaItemKind::Word | MetaItemKind::NameValue(_)
|
||||
)
|
||||
});
|
||||
if !valid {
|
||||
self.tcx.emit_node_span_lint(
|
||||
INVALID_DOC_ATTRIBUTES,
|
||||
hir_id,
|
||||
item.span(),
|
||||
errors::DocAutoCfgHideShowUnexpectedItem { attr_name },
|
||||
);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
self.tcx.emit_node_span_lint(
|
||||
INVALID_DOC_ATTRIBUTES,
|
||||
hir_id,
|
||||
meta.span,
|
||||
errors::DocAutoCfgHideShowExpectsList { attr_name },
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Runs various checks on `#[doc]` attributes.
|
||||
///
|
||||
/// `specified_inline` should be initialized to `None` and kept for the scope
|
||||
|
|
@ -1121,7 +982,10 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
|
|||
inline,
|
||||
// FIXME: currently unchecked
|
||||
cfg: _,
|
||||
cfg_hide,
|
||||
// already check in attr_parsing
|
||||
auto_cfg: _,
|
||||
// already check in attr_parsing
|
||||
auto_cfg_change: _,
|
||||
fake_variadic,
|
||||
keyword,
|
||||
masked,
|
||||
|
|
@ -1138,36 +1002,23 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
|
|||
// allowed anywhere
|
||||
test_attrs: _,
|
||||
no_crate_inject,
|
||||
attribute,
|
||||
} = attr;
|
||||
|
||||
for (alias, span) in aliases {
|
||||
if self.check_attr_not_crate_level(*span, hir_id, "alias") {
|
||||
self.check_doc_alias_value(*span, *alias, hir_id, target);
|
||||
self.check_doc_alias_value(*span, hir_id, target, *alias);
|
||||
}
|
||||
}
|
||||
|
||||
if let Some((_, span)) = keyword {
|
||||
if self.check_attr_not_crate_level(*span, hir_id, "keyword") {
|
||||
self.check_doc_keyword(*span, hir_id);
|
||||
}
|
||||
self.check_attr_not_crate_level(*span, hir_id, "keyword");
|
||||
self.check_doc_keyword_and_attribute(*span, hir_id, "keyword");
|
||||
}
|
||||
if let Some((_, span)) = attribute {
|
||||
self.check_attr_not_crate_level(*span, hir_id, "attribute");
|
||||
self.check_doc_keyword_and_attribute(*span, hir_id, "attribute");
|
||||
}
|
||||
|
||||
// FIXME: check doc attribute
|
||||
// self.check_doc_keyword(meta, hir_id);
|
||||
// self.check_doc_keyword_and_attribute(
|
||||
// meta,
|
||||
// hir_id,
|
||||
// DocFakeItemKind::Keyword,
|
||||
// );
|
||||
// }
|
||||
// }
|
||||
// Some(sym::attribute) => {
|
||||
// if self.check_attr_not_crate_level(meta, hir_id, "attribute") {
|
||||
// self.check_doc_keyword_and_attribute(
|
||||
// meta,
|
||||
// hir_id,
|
||||
// DocFakeItemKind::Attribute,
|
||||
// );
|
||||
|
||||
if let Some(span) = fake_variadic {
|
||||
if self.check_attr_not_crate_level(*span, hir_id, "fake_variadic") {
|
||||
|
|
@ -1220,10 +1071,6 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
|
|||
if let Some(span) = masked {
|
||||
self.check_doc_masked(*span, hir_id, target);
|
||||
}
|
||||
|
||||
if let Some(span) = cfg_hide {
|
||||
self.check_attr_crate_level(*span, hir_id);
|
||||
}
|
||||
}
|
||||
|
||||
fn check_has_incoherent_inherent_impls(&self, attr: &Attribute, span: Span, target: Target) {
|
||||
|
|
|
|||
|
|
@ -24,12 +24,6 @@ pub(crate) struct IncorrectDoNotRecommendLocation;
|
|||
#[diag(passes_incorrect_do_not_recommend_args)]
|
||||
pub(crate) struct DoNotRecommendDoesNotExpectArgs;
|
||||
|
||||
#[derive(LintDiagnostic)]
|
||||
#[diag(passes_doc_attr_expects_string)]
|
||||
pub(crate) struct DocAttrExpectsString {
|
||||
pub(crate) attr_name: Symbol,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(passes_autodiff_attr)]
|
||||
pub(crate) struct AutoDiffAttr {
|
||||
|
|
@ -129,43 +123,20 @@ pub(crate) struct AttrShouldBeAppliedToStatic {
|
|||
pub defn_span: Span,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(passes_doc_expect_str)]
|
||||
pub(crate) struct DocExpectStr<'a> {
|
||||
#[primary_span]
|
||||
pub attr_span: Span,
|
||||
pub attr_name: &'a str,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(passes_doc_alias_bad_location)]
|
||||
pub(crate) struct DocAliasBadLocation<'a> {
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
pub attr_str: &'a str,
|
||||
pub location: &'a str,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(passes_doc_alias_not_an_alias)]
|
||||
pub(crate) struct DocAliasNotAnAlias<'a> {
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
pub attr_str: &'a str,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(passes_doc_alias_not_string_literal)]
|
||||
pub(crate) struct DocAliasNotStringLiteral {
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(passes_doc_alias_malformed)]
|
||||
pub(crate) struct DocAliasMalformed {
|
||||
pub(crate) struct DocAliasNotAnAlias {
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
pub attr_str: Symbol,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
|
|
@ -176,24 +147,6 @@ pub(crate) struct DocKeywordAttributeEmptyMod {
|
|||
pub attr_name: &'static str,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(passes_doc_keyword_not_keyword)]
|
||||
#[help]
|
||||
pub(crate) struct DocKeywordNotKeyword {
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
pub keyword: Symbol,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(passes_doc_attribute_not_attribute)]
|
||||
#[help]
|
||||
pub(crate) struct DocAttributeNotAttribute {
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
pub attribute: Symbol,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(passes_doc_keyword_attribute_not_mod)]
|
||||
pub(crate) struct DocKeywordAttributeNotMod {
|
||||
|
|
@ -260,90 +213,6 @@ pub(crate) struct DocAttrNotCrateLevel<'a> {
|
|||
pub attr_name: &'a str,
|
||||
}
|
||||
|
||||
#[derive(LintDiagnostic)]
|
||||
#[diag(passes_doc_test_unknown)]
|
||||
pub(crate) struct DocTestUnknown {
|
||||
pub path: String,
|
||||
}
|
||||
|
||||
#[derive(LintDiagnostic)]
|
||||
#[diag(passes_doc_test_literal)]
|
||||
pub(crate) struct DocTestLiteral;
|
||||
|
||||
#[derive(LintDiagnostic)]
|
||||
#[diag(passes_doc_test_takes_list)]
|
||||
pub(crate) struct DocTestTakesList;
|
||||
|
||||
#[derive(LintDiagnostic)]
|
||||
#[diag(passes_doc_auto_cfg_wrong_literal)]
|
||||
pub(crate) struct DocAutoCfgWrongLiteral;
|
||||
|
||||
#[derive(LintDiagnostic)]
|
||||
#[diag(passes_doc_auto_cfg_expects_hide_or_show)]
|
||||
pub(crate) struct DocAutoCfgExpectsHideOrShow;
|
||||
|
||||
#[derive(LintDiagnostic)]
|
||||
#[diag(passes_doc_auto_cfg_hide_show_expects_list)]
|
||||
pub(crate) struct DocAutoCfgHideShowExpectsList {
|
||||
pub attr_name: Symbol,
|
||||
}
|
||||
|
||||
#[derive(LintDiagnostic)]
|
||||
#[diag(passes_doc_auto_cfg_hide_show_unexpected_item)]
|
||||
pub(crate) struct DocAutoCfgHideShowUnexpectedItem {
|
||||
pub attr_name: Symbol,
|
||||
}
|
||||
|
||||
#[derive(LintDiagnostic)]
|
||||
#[diag(passes_doc_test_unknown_any)]
|
||||
pub(crate) struct DocTestUnknownAny {
|
||||
pub path: String,
|
||||
}
|
||||
|
||||
#[derive(LintDiagnostic)]
|
||||
#[diag(passes_doc_test_unknown_spotlight)]
|
||||
#[note]
|
||||
#[note(passes_no_op_note)]
|
||||
pub(crate) struct DocTestUnknownSpotlight {
|
||||
pub path: String,
|
||||
#[suggestion(style = "short", applicability = "machine-applicable", code = "notable_trait")]
|
||||
pub span: Span,
|
||||
}
|
||||
|
||||
#[derive(LintDiagnostic)]
|
||||
#[diag(passes_doc_test_unknown_passes)]
|
||||
#[note]
|
||||
#[note(passes_no_op_note)]
|
||||
pub(crate) struct DocTestUnknownPasses {
|
||||
pub path: String,
|
||||
#[label]
|
||||
pub span: Span,
|
||||
}
|
||||
|
||||
#[derive(LintDiagnostic)]
|
||||
#[diag(passes_doc_test_unknown_plugins)]
|
||||
#[note]
|
||||
#[note(passes_no_op_note)]
|
||||
pub(crate) struct DocTestUnknownPlugins {
|
||||
pub path: String,
|
||||
#[label]
|
||||
pub span: Span,
|
||||
}
|
||||
|
||||
#[derive(LintDiagnostic)]
|
||||
#[diag(passes_doc_test_unknown_include)]
|
||||
pub(crate) struct DocTestUnknownInclude {
|
||||
pub path: String,
|
||||
pub value: String,
|
||||
pub inner: &'static str,
|
||||
#[suggestion(code = "#{inner}[doc = include_str!(\"{value}\")]")]
|
||||
pub sugg: (Span, Applicability),
|
||||
}
|
||||
|
||||
#[derive(LintDiagnostic)]
|
||||
#[diag(passes_doc_invalid)]
|
||||
pub(crate) struct DocInvalid;
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(passes_has_incoherent_inherent_impl)]
|
||||
pub(crate) struct HasIncoherentInherentImpl {
|
||||
|
|
|
|||
|
|
@ -10,7 +10,6 @@ use rustc_ast::{
|
|||
Item, ItemKind, MethodCall, NodeId, Path, PathSegment, Ty, TyKind,
|
||||
};
|
||||
use rustc_ast_pretty::pprust::where_bound_predicate_to_string;
|
||||
use rustc_attr_data_structures::{AttributeKind, find_attr};
|
||||
use rustc_data_structures::fx::{FxHashSet, FxIndexSet};
|
||||
use rustc_errors::codes::*;
|
||||
use rustc_errors::{
|
||||
|
|
@ -18,6 +17,7 @@ use rustc_errors::{
|
|||
struct_span_code_err,
|
||||
};
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::attrs::AttributeKind;
|
||||
use rustc_hir::def::Namespace::{self, *};
|
||||
use rustc_hir::def::{self, CtorKind, CtorOf, DefKind, MacroKinds};
|
||||
use rustc_hir::def_id::{CRATE_DEF_ID, DefId};
|
||||
|
|
@ -903,7 +903,8 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> {
|
|||
// confused by them.
|
||||
continue;
|
||||
}
|
||||
if let Some(d) = find_attr!(r.tcx.get_all_attrs(did), AttributeKind::Doc(d) => d)
|
||||
if let Some(d) =
|
||||
hir::find_attr!(r.tcx.get_all_attrs(did), AttributeKind::Doc(d) => d)
|
||||
&& d.aliases.contains_key(&item_name)
|
||||
{
|
||||
return Some(did);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue