diff --git a/compiler/rustc_attr_parsing/messages.ftl b/compiler/rustc_attr_parsing/messages.ftl index a2a5f8ab14236..06fc6baf35576 100644 --- a/compiler/rustc_attr_parsing/messages.ftl +++ b/compiler/rustc_attr_parsing/messages.ftl @@ -261,8 +261,38 @@ attr_parsing_unused_multiple = .suggestion = remove this attribute .note = attribute also specified here + +attr_parsing_doc_alias_duplicated = doc alias is duplicated + .label = first defined here + -attr_parsing_previously_accepted = this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! attr_parsing_whole_archive_needs_static = linking modifier `whole-archive` is only compatible with `static` linking kind + +attr_parsing_unused_no_lints_note = + attribute `{$name}` without any lints has no effect + +attr_parsing_doc_alias_empty = + {$attr_str} attribute cannot have empty value + +attr_parsing_doc_alias_bad_char = + {$char_} character isn't allowed in {$attr_str} + +attr_parsing_doc_alias_start_end = + {$attr_str} cannot start or end with ' ' + +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_inline_conflict = + conflicting doc inlining attributes + .help = remove one of the conflicting attributes + +attr_parsing_doc_inline_conflict_first = + this attribute... + +attr_parsing_doc_inline_conflict_second = + {"."}..conflicts with this attribute diff --git a/compiler/rustc_attr_parsing/src/attributes/doc.rs b/compiler/rustc_attr_parsing/src/attributes/doc.rs new file mode 100644 index 0000000000000..c281cbc64e06f --- /dev/null +++ b/compiler/rustc_attr_parsing/src/attributes/doc.rs @@ -0,0 +1,422 @@ +// FIXME: to be removed +#![allow(unused_imports)] + +use rustc_errors::MultiSpan; +use rustc_feature::template; +use rustc_hir::attrs::{AttributeKind, DocAttribute, DocInline}; +use rustc_hir::lints::AttributeLintKind; +use rustc_span::{Span, Symbol, edition, sym}; + +use super::{AcceptMapping, AttributeParser}; +use super::prelude::{Allow, AllowedTargets, MethodKind, Target}; +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, +}; + +#[derive(Default)] +pub(crate) struct DocParser { + attribute: DocAttribute, +} + +impl DocParser { + fn parse_single_test_doc_attr_item<'c, S: Stage>( + &mut self, + cx: &'c mut AcceptContext<'_, '_, S>, + mip: &'c MetaItemParser<'_>, + ) { + let path = mip.path(); + let args = mip.args(); + + match path.word_sym() { + Some(sym::no_crate_inject) => { + if let Err(span) = args.no_args() { + cx.expected_no_args(span); + return; + } + + if self.attribute.no_crate_inject.is_some() { + cx.duplicate_key(path.span(), sym::no_crate_inject); + return; + } + + self.attribute.no_crate_inject = Some(path.span()) + } + Some(sym::attr) => { + let Some(list) = args.list() else { + cx.expected_list(cx.attr_span); + return; + }; + + // FIXME: convert list into a Vec of `AttributeKind`. + for _ in list { + // self.attribute.test_attrs.push(AttributeKind::parse()); + } + } + _ => { + cx.expected_specific_argument( + mip.span(), + &[sym::no_crate_inject, sym::attr], + ); + } + } + } + + fn add_alias<'c, S: Stage>( + &mut self, + cx: &'c mut AcceptContext<'_, '_, S>, + alias: Symbol, + span: Span, + is_list: bool, + ) { + let attr_str = + &format!("`#[doc(alias{})]`", if is_list { "(\"...\")" } else { " = \"...\"" }); + if alias == sym::empty { + cx.emit_err(DocAliasEmpty { span, attr_str }); + return; + } + + let alias_str = alias.as_str(); + if let Some(c) = + alias_str.chars().find(|&c| c == '"' || c == '\'' || (c.is_whitespace() && c != ' ')) + { + cx.emit_err(DocAliasBadChar { span, attr_str, char_: c }); + return; + } + if alias_str.starts_with(' ') || alias_str.ends_with(' ') { + cx.emit_err(DocAliasStartEnd { span, attr_str }); + return; + } + + if let Some(first_definition) = self.attribute.aliases.get(&alias).copied() { + cx.emit_lint(AttributeLintKind::DuplicateDocAlias { first_definition }, span); + } + + self.attribute.aliases.insert(alias, span); + } + + fn parse_alias<'c, S: Stage>( + &mut self, + cx: &'c mut AcceptContext<'_, '_, S>, + path: &PathParser<'_>, + args: &ArgParser<'_>, + ) { + match args { + ArgParser::NoArgs => { + cx.expected_name_value(args.span().unwrap_or(path.span()), path.word_sym()); + } + ArgParser::List(list) => { + for i in list.mixed() { + let Some(alias) = i.lit().and_then(|i| i.value_str()) else { + cx.expected_string_literal(i.span(), i.lit()); + continue; + }; + + self.add_alias(cx, alias, i.span(), false); + } + } + ArgParser::NameValue(nv) => { + let Some(alias) = nv.value_as_str() else { + cx.expected_string_literal(nv.value_span, Some(nv.value_as_lit())); + return; + }; + self.add_alias(cx, alias, nv.value_span, false); + } + } + } + + 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>, + path: &PathParser<'_>, + args: &ArgParser<'_>, + inline: DocInline, + ) { + if let Err(span) = args.no_args() { + cx.expected_no_args(span); + return; + } + + let span = path.span(); + + if let Some((prev_inline, prev_span)) = self.attribute.inline { + if prev_inline == inline { + let mut spans = MultiSpan::from_spans(vec![prev_span, span]); + spans.push_span_label(prev_span, fluent::attr_parsing_doc_inline_conflict_first); + spans.push_span_label(span, fluent::attr_parsing_doc_inline_conflict_second); + cx.emit_err(DocKeywordConflict { spans }); + return; + } + } + + self.attribute.inline = Some((inline, span)); + } + + fn parse_single_doc_attr_item<'c, S: Stage>( + &mut self, + cx: &'c mut AcceptContext<'_, '_, S>, + mip: &MetaItemParser<'_>, + ) { + let path = mip.path(); + let args = mip.args(); + + macro_rules! no_args { + ($ident: ident) => {{ + if let Err(span) = args.no_args() { + cx.expected_no_args(span); + return; + } + + if self.attribute.$ident.is_some() { + cx.duplicate_key(path.span(), path.word_sym().unwrap()); + return; + } + + self.attribute.$ident = Some(path.span()); + }}; + } + macro_rules! string_arg { + ($ident: ident) => {{ + let Some(nv) = args.name_value() else { + cx.expected_name_value(args.span().unwrap_or(path.span()), path.word_sym()); + return; + }; + + let Some(s) = nv.value_as_str() else { + cx.expected_string_literal(nv.value_span, Some(nv.value_as_lit())); + return; + }; + + if self.attribute.$ident.is_some() { + cx.duplicate_key(path.span(), path.word_sym().unwrap()); + return; + } + + self.attribute.$ident = Some((s, path.span())); + }}; + } + + match path.word_sym() { + Some(sym::alias) => self.parse_alias(cx, path, args), + Some(sym::hidden) => no_args!(hidden), + Some(sym::html_favicon_url) => string_arg!(html_favicon_url), + Some(sym::html_logo_url) => string_arg!(html_logo_url), + Some(sym::html_no_source) => no_args!(html_no_source), + Some(sym::html_playground_url) => string_arg!(html_playground_url), + Some(sym::html_root_url) => string_arg!(html_root_url), + Some(sym::issue_tracker_base_url) => string_arg!(issue_tracker_base_url), + 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::notable_trait) => no_args!(notable_trait), + Some(sym::keyword) => self.parse_keyword(cx, path, args), + 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::test) => { + let Some(list) = args.list() else { + cx.expected_list(args.span().unwrap_or(path.span())); + return; + }; + + for i in list.mixed() { + match i { + MetaItemOrLitParser::MetaItemParser(mip) => { + self.parse_single_test_doc_attr_item(cx, mip); + } + MetaItemOrLitParser::Lit(lit) => { + cx.unexpected_literal(lit.span); + } + MetaItemOrLitParser::Err(..) => { + // already had an error here, move on. + } + } + } + + // 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, + sym::hidden, + sym::html_favicon_url, + sym::html_logo_url, + sym::html_no_source, + sym::html_playground_url, + sym::html_root_url, + sym::inline, + sym::no_inline, + sym::test, + ], + ); + } + } + } + + fn accept_single_doc_attr<'c, S: Stage>( + &mut self, + cx: &'c mut AcceptContext<'_, '_, S>, + args: &'c ArgParser<'_>, + ) { + match args { + ArgParser::NoArgs => { + todo!() + } + ArgParser::List(items) => { + for i in items.mixed() { + match i { + MetaItemOrLitParser::MetaItemParser(mip) => { + self.parse_single_doc_attr_item(cx, mip); + } + MetaItemOrLitParser::Lit(lit) => { + cx.expected_name_value(i.span(), None); + } + MetaItemOrLitParser::Err(..) => { + // already had an error here, move on. + } + } + } + } + ArgParser::NameValue(v) => { + self.attribute.value.push((v, args.span())); + } + } + } +} + +impl AttributeParser for DocParser { + const ATTRIBUTES: AcceptMapping = &[( + &[sym::doc], + 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::Field { of_trait: true }), + Allow(Target::AssocConst), + Allow(Target::Method(MethodKind::Inherent)), + Allow(Target::Method(MethodKind::Trait { body: true })), + 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 { + todo!() + } +} diff --git a/compiler/rustc_attr_parsing/src/attributes/mod.rs b/compiler/rustc_attr_parsing/src/attributes/mod.rs index 639c75d7c5e47..f1f570afb12b4 100644 --- a/compiler/rustc_attr_parsing/src/attributes/mod.rs +++ b/compiler/rustc_attr_parsing/src/attributes/mod.rs @@ -39,6 +39,7 @@ pub(crate) mod crate_level; pub(crate) mod debugger; pub(crate) mod deprecation; pub(crate) mod dummy; +pub(crate) mod doc; pub(crate) mod inline; pub(crate) mod link_attrs; pub(crate) mod lint_helpers; diff --git a/compiler/rustc_attr_parsing/src/attributes/util.rs b/compiler/rustc_attr_parsing/src/attributes/util.rs index 520fd9da7c2ab..105f7164bf3b3 100644 --- a/compiler/rustc_attr_parsing/src/attributes/util.rs +++ b/compiler/rustc_attr_parsing/src/attributes/util.rs @@ -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,36 +32,6 @@ pub fn is_builtin_attr(attr: &impl AttributeExt) -> bool { || attr.ident().is_some_and(|ident| is_builtin_attr_name(ident.name)) } -pub fn is_doc_alias_attrs_contain_symbol<'tcx, T: AttributeExt + 'tcx>( - attrs: impl Iterator, - symbol: Symbol, -) -> bool { - let doc_attrs = attrs.filter(|attr| attr.has_name(sym::doc)); - for attr in doc_attrs { - let Some(values) = attr.meta_item_list() else { - continue; - }; - let alias_values = values.iter().filter(|v| v.has_name(sym::alias)); - for v in alias_values { - if let Some(nested) = v.meta_item_list() { - // #[doc(alias("foo", "bar"))] - let mut iter = nested.iter().filter_map(|item| item.lit()).map(|item| item.symbol); - if iter.any(|s| s == symbol) { - return true; - } - } else if let Some(meta) = v.meta_item() - && let Some(lit) = meta.name_value_literal() - { - // #[doc(alias = "foo")] - if lit.symbol == symbol { - return true; - } - } - } - } - false -} - /// Parse a single integer. /// /// Used by attributes that take a single integer as argument, such as diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs index 6749bdfb1a672..0a86bb35de685 100644 --- a/compiler/rustc_attr_parsing/src/context.rs +++ b/compiler/rustc_attr_parsing/src/context.rs @@ -30,6 +30,7 @@ use crate::attributes::crate_level::{ }; use crate::attributes::debugger::DebuggerViualizerParser; use crate::attributes::deprecation::DeprecationParser; +use crate::attributes::doc::DocParser; use crate::attributes::dummy::DummyParser; use crate::attributes::inline::{InlineParser, RustcForceInlineParser}; use crate::attributes::link_attrs::{ @@ -155,7 +156,7 @@ attribute_parsers!( BodyStabilityParser, ConfusablesParser, ConstStabilityParser, - MacroUseParser, + NakedParser, StabilityParser, UsedParser, @@ -403,7 +404,7 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> { &self, span: Span, found: String, - options: &'static [&'static str], + options: &[&'static str], ) -> ErrorGuaranteed { self.emit_err(UnknownMetaItem { span, item: found, expected: options }) } diff --git a/compiler/rustc_attr_parsing/src/lib.rs b/compiler/rustc_attr_parsing/src/lib.rs index bcd0d674c75f1..edbc9ec68ef3b 100644 --- a/compiler/rustc_attr_parsing/src/lib.rs +++ b/compiler/rustc_attr_parsing/src/lib.rs @@ -109,7 +109,7 @@ pub use attributes::cfg::{ CFG_TEMPLATE, EvalConfigResult, eval_config_entry, parse_cfg, parse_cfg_attr, }; pub use attributes::cfg_old::*; -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 lints::emit_attribute_lint; diff --git a/compiler/rustc_attr_parsing/src/lints.rs b/compiler/rustc_attr_parsing/src/lints.rs index 3a2a370466961..de38768446904 100644 --- a/compiler/rustc_attr_parsing/src/lints.rs +++ b/compiler/rustc_attr_parsing/src/lints.rs @@ -31,6 +31,14 @@ pub fn emit_attribute_lint(lint: &AttributeLint, lint_emi }, ); } + AttributeLintKind::DuplicateDocAlias { first_definition } => { + lint_emitter.emit_node_span_lint( + rustc_session::lint::builtin::ILL_FORMED_ATTRIBUTE_INPUT, + *id, + *span, + session_diagnostics::DocAliasDuplicated { first_defn: *first_definition }, + ); + } AttributeLintKind::InvalidMacroExportArguments { suggestions } => lint_emitter .emit_node_span_lint( rustc_session::lint::builtin::INVALID_MACRO_EXPORT_ARGUMENTS, diff --git a/compiler/rustc_attr_parsing/src/session_diagnostics.rs b/compiler/rustc_attr_parsing/src/session_diagnostics.rs index 7b82f3baec093..5a6753204bf46 100644 --- a/compiler/rustc_attr_parsing/src/session_diagnostics.rs +++ b/compiler/rustc_attr_parsing/src/session_diagnostics.rs @@ -4,6 +4,7 @@ use rustc_ast::{self as ast, AttrStyle, Path}; use rustc_errors::codes::*; use rustc_errors::{ Applicability, Diag, DiagArgValue, DiagCtxtHandle, Diagnostic, EmissionGuarantee, Level, + MultiSpan, }; use rustc_feature::AttributeTemplate; use rustc_hir::{AttrPath, Target}; @@ -34,6 +35,40 @@ pub(crate) struct InvalidPredicate { pub predicate: String, } +#[derive(Diagnostic)] +#[diag(attr_parsing_doc_alias_empty)] +pub(crate) struct DocAliasEmpty<'a> { + #[primary_span] + pub span: Span, + pub attr_str: &'a str, +} + +#[derive(Diagnostic)] +#[diag(attr_parsing_doc_alias_bad_char)] +pub(crate) struct DocAliasBadChar<'a> { + #[primary_span] + pub span: Span, + pub attr_str: &'a str, + pub char_: char, +} + +#[derive(Diagnostic)] +#[diag(attr_parsing_doc_alias_start_end)] +pub(crate) struct DocAliasStartEnd<'a> { + #[primary_span] + pub span: Span, + pub attr_str: &'a str, +} + +#[derive(Diagnostic)] +#[diag(attr_parsing_doc_keyword_not_keyword)] +#[help] +pub(crate) struct DocKeywordNotKeyword { + #[primary_span] + pub span: Span, + pub keyword: Symbol, +} + /// Error code: E0541 pub(crate) struct UnknownMetaItem<'a> { pub span: Span, @@ -580,6 +615,21 @@ pub(crate) struct NakedFunctionIncompatibleAttribute { pub attr: String, } +#[derive(LintDiagnostic)] +#[diag(attr_parsing_doc_alias_duplicated)] +pub(crate) struct DocAliasDuplicated { + #[label] + pub first_defn: Span, +} + +#[derive(Diagnostic)] +#[diag(attr_parsing_doc_inline_conflict)] +#[help] +pub(crate) struct DocKeywordConflict { + #[primary_span] + pub spans: MultiSpan, +} + #[derive(Diagnostic)] #[diag(attr_parsing_link_ordinal_out_of_range)] #[note] diff --git a/compiler/rustc_hir/src/attrs/data_structures.rs b/compiler/rustc_hir/src/attrs/data_structures.rs index 828c15cc391dd..43b5f91101fcc 100644 --- a/compiler/rustc_hir/src/attrs/data_structures.rs +++ b/compiler/rustc_hir/src/attrs/data_structures.rs @@ -5,6 +5,7 @@ pub use ReprAttr::*; use rustc_abi::Align; use rustc_ast::token::CommentKind; use rustc_ast::{AttrStyle, ast}; +use rustc_data_structures::fx::FxIndexMap; use rustc_error_messages::{DiagArgValue, IntoDiagArg}; use rustc_macros::{Decodable, Encodable, HashStable_Generic, PrintAttribute}; use rustc_span::def_id::DefId; @@ -382,6 +383,74 @@ pub struct DebugVisualizer { pub path: Symbol, } +#[derive(Copy, Clone, Debug, PartialEq)] +#[derive(HashStable_Generic, Encodable, Decodable, PrintAttribute)] +pub enum DocInline { + Inline, + NoInline, +} + +#[derive(Clone, Debug, HashStable_Generic, Encodable, Decodable, PrintAttribute)] +pub struct DocAttribute { + /// `/// doc` or `#[doc = "doc"]`. + pub value: ThinVec<(Symbol, Span)>, + + pub aliases: FxIndexMap, + pub hidden: Option, + pub inline: Option<(DocInline, Span)>, + + // unstable + pub cfg: Option, + pub cfg_hide: Option, + + // builtin + pub fake_variadic: Option, + pub keyword: Option<(Symbol, Span)>, + pub masked: Option, + pub notable_trait: Option, + pub search_unbox: Option, + + // valid on crate + pub html_favicon_url: Option<(Symbol, Span)>, + pub html_logo_url: Option<(Symbol, Span)>, + pub html_playground_url: Option<(Symbol, Span)>, + pub html_root_url: Option<(Symbol, Span)>, + pub html_no_source: Option, + pub issue_tracker_base_url: Option<(Symbol, Span)>, + pub rust_logo: Option, + + // #[doc(test(...))] + pub test_attrs: ThinVec, + pub no_crate_inject: Option, +} + +impl Default for DocAttribute { + fn default() -> Self { + Self { + value: ThinVec::new(), + aliases: FxIndexMap::default(), + hidden: None, + inline: None, + cfg: None, + cfg_hide: None, + fake_variadic: None, + keyword: None, + masked: None, + notable_trait: None, + search_unbox: None, + html_favicon_url: None, + html_logo_url: None, + html_playground_url: None, + html_root_url: None, + html_no_source: None, + issue_tracker_base_url: None, + rust_logo: None, + test_attrs: ThinVec::new(), + no_crate_inject: None, + } + } +} + /// Represents parsed *built-in* inert attributes. /// /// ## Overview @@ -516,7 +585,13 @@ pub enum AttributeKind { /// Represents `#[rustc_do_not_implement_via_object]`. DoNotImplementViaObject(Span), - /// Represents [`#[doc = "..."]`](https://doc.rust-lang.org/stable/rustdoc/write-documentation/the-doc-attribute.html). + /// Represents [`#[doc]`](https://doc.rust-lang.org/stable/rustdoc/write-documentation/the-doc-attribute.html). + /// Represents all other uses of the [`#[doc]`](https://doc.rust-lang.org/stable/rustdoc/write-documentation/the-doc-attribute.html) + /// attribute. + Doc(Box), + + /// Represents specifically [`#[doc = "..."]`](https://doc.rust-lang.org/stable/rustdoc/write-documentation/the-doc-attribute.html). + /// i.e. doc comments. DocComment { style: AttrStyle, kind: CommentKind, span: Span, comment: Symbol }, /// Represents `#[rustc_dummy]`. diff --git a/compiler/rustc_hir/src/attrs/encode_cross_crate.rs b/compiler/rustc_hir/src/attrs/encode_cross_crate.rs index 362ab407aab35..f00d9b2507cf9 100644 --- a/compiler/rustc_hir/src/attrs/encode_cross_crate.rs +++ b/compiler/rustc_hir/src/attrs/encode_cross_crate.rs @@ -41,6 +41,7 @@ impl AttributeKind { DenyExplicitImpl(..) => No, Deprecation { .. } => Yes, DoNotImplementViaObject(..) => No, + Doc(_) => Yes, DocComment { .. } => Yes, Dummy => No, ExportName { .. } => Yes, diff --git a/compiler/rustc_hir/src/attrs/pretty_printing.rs b/compiler/rustc_hir/src/attrs/pretty_printing.rs index ea86dfbd9c80e..75886fb08a2e0 100644 --- a/compiler/rustc_hir/src/attrs/pretty_printing.rs +++ b/compiler/rustc_hir/src/attrs/pretty_printing.rs @@ -4,6 +4,7 @@ use rustc_abi::Align; use rustc_ast::token::CommentKind; use rustc_ast::{AttrStyle, IntTy, UintTy}; use rustc_ast_pretty::pp::Printer; +use rustc_data_structures::fx::FxIndexMap; use rustc_span::hygiene::Transparency; use rustc_span::{ErrorGuaranteed, Ident, Span, Symbol}; use rustc_target::spec::SanitizerSet; @@ -64,6 +65,25 @@ impl PrintAttribute for ThinVec { p.word("]"); } } +impl PrintAttribute for FxIndexMap { + fn should_render(&self) -> bool { + self.is_empty() || self[0].should_render() + } + + fn print_attribute(&self, p: &mut Printer) { + let mut last_printed = false; + p.word("["); + for (i, _) in self { + if last_printed { + p.word_space(","); + } + i.print_attribute(p); + last_printed = i.should_render(); + } + p.word("]"); + } +} + macro_rules! print_skip { ($($t: ty),* $(,)?) => {$( impl PrintAttribute for $t { diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index 90bf82f15f819..17497b57d0c40 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -1345,7 +1345,6 @@ impl AttributeExt for Attribute { fn doc_str(&self) -> Option { match &self { Attribute::Parsed(AttributeKind::DocComment { comment, .. }) => Some(*comment), - Attribute::Unparsed(_) if self.has_name(sym::doc) => self.value_str(), _ => None, } } @@ -1360,9 +1359,6 @@ impl AttributeExt for Attribute { Attribute::Parsed(AttributeKind::DocComment { kind, comment, .. }) => { Some((*comment, *kind)) } - Attribute::Unparsed(_) if self.has_name(sym::doc) => { - self.value_str().map(|s| (s, CommentKind::Line)) - } _ => None, } } diff --git a/compiler/rustc_hir/src/lints.rs b/compiler/rustc_hir/src/lints.rs index c9de6f6b5d526..a535fbb0e59b3 100644 --- a/compiler/rustc_hir/src/lints.rs +++ b/compiler/rustc_hir/src/lints.rs @@ -62,4 +62,7 @@ pub enum AttributeLintKind { target: Target, target_span: Span, }, + DuplicateDocAlias { + first_definition: Span, + }, } diff --git a/compiler/rustc_hir_typeck/src/method/probe.rs b/compiler/rustc_hir_typeck/src/method/probe.rs index a3f6803c5dc2c..1c6fb5ee2c536 100644 --- a/compiler/rustc_hir_typeck/src/method/probe.rs +++ b/compiler/rustc_hir_typeck/src/method/probe.rs @@ -3,7 +3,7 @@ use std::cell::{Cell, RefCell}; use std::cmp::max; use std::ops::Deref; -use rustc_attr_parsing::is_doc_alias_attrs_contain_symbol; +use rustc_attr_data_structures::{AttributeKind, find_attr}; use rustc_data_structures::fx::FxHashSet; use rustc_data_structures::sso::SsoHashSet; use rustc_errors::Applicability; @@ -2535,7 +2535,9 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { let hir_id = self.fcx.tcx.local_def_id_to_hir_id(local_def_id); let attrs = self.fcx.tcx.hir_attrs(hir_id); - if is_doc_alias_attrs_contain_symbol(attrs.into_iter(), method.name) { + if let Some(d) = find_attr!(attrs, AttributeKind::Doc(d) => d) + && d.aliases.contains_key(&method.name) + { return true; } diff --git a/compiler/rustc_lint/src/builtin.rs b/compiler/rustc_lint/src/builtin.rs index d3468499b4b3a..be8f247b51d78 100644 --- a/compiler/rustc_lint/src/builtin.rs +++ b/compiler/rustc_lint/src/builtin.rs @@ -25,7 +25,7 @@ use rustc_attr_parsing::AttributeParser; use rustc_errors::{Applicability, LintDiagnostic}; use rustc_feature::GateIssue; use rustc_hir as hir; -use rustc_hir::attrs::AttributeKind; +use rustc_hir::attrs::{AttributeKind, DocAttribute}; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::{CRATE_DEF_ID, DefId, LocalDefId}; use rustc_hir::intravisit::FnKind as HirFnKind; @@ -396,26 +396,16 @@ pub struct MissingDoc; impl_lint_pass!(MissingDoc => [MISSING_DOCS]); fn has_doc(attr: &hir::Attribute) -> bool { - if attr.is_doc_comment().is_some() { + if matches!(attr, hir::Attribute::Parsed(AttributeKind::DocComment { .. })) { return true; } - if !attr.has_name(sym::doc) { - return false; - } - - if attr.value_str().is_some() { + if let hir::Attribute::Parsed(AttributeKind::Doc(d)) = attr + && matches!(d.as_ref(), DocAttribute { hidden: Some(..), .. }) + { return true; } - if let Some(list) = attr.meta_item_list() { - for meta in list { - if meta.has_name(sym::hidden) { - return true; - } - } - } - false } diff --git a/compiler/rustc_passes/messages.ftl b/compiler/rustc_passes/messages.ftl index c668bf0733d14..2c62d81e9286e 100644 --- a/compiler/rustc_passes/messages.ftl +++ b/compiler/rustc_passes/messages.ftl @@ -102,18 +102,9 @@ passes_diagnostic_diagnostic_on_unimplemented_only_for_traits = passes_diagnostic_item_first_defined = the diagnostic item is first defined here -passes_doc_alias_bad_char = - {$char_} character isn't allowed in {$attr_str} - passes_doc_alias_bad_location = {$attr_str} isn't allowed on {$location} -passes_doc_alias_duplicated = doc alias is duplicated - .label = first defined here - -passes_doc_alias_empty = - {$attr_str} attribute cannot have empty value - passes_doc_alias_malformed = doc alias attribute expects a string `#[doc(alias = "a")]` or a list of strings `#[doc(alias("a", "b"))]` @@ -123,9 +114,6 @@ passes_doc_alias_not_an_alias = passes_doc_alias_not_string_literal = `#[doc(alias("a"))]` expects string literals -passes_doc_alias_start_end = - {$attr_str} cannot start or end with ' ' - passes_doc_attr_not_crate_level = `#![doc({$attr_name} = "...")]` isn't allowed as a crate-level attribute @@ -151,15 +139,7 @@ passes_doc_expect_str = 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 -passes_doc_inline_conflict = - conflicting doc inlining attributes - .help = remove one of the conflicting attributes - -passes_doc_inline_conflict_first = - this attribute... -passes_doc_inline_conflict_second = - {"."}..conflicts with this attribute passes_doc_inline_only_use = this attribute can only be applied to a `use` item @@ -176,10 +156,6 @@ passes_doc_keyword_attribute_empty_mod = passes_doc_keyword_attribute_not_mod = `#[doc({$attr_name} = "...")]` should be used on modules -passes_doc_keyword_not_keyword = - nonexistent keyword `{$keyword}` used in `#[doc(keyword = "...")]` - .help = only existing keywords are allowed in core/std - passes_doc_keyword_only_impl = `#[doc(keyword = "...")]` should be used on impl blocks diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index ef42c42f68b37..227e97e9e3387 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -134,8 +134,6 @@ impl<'tcx> CheckAttrVisitor<'tcx> { target: Target, item: Option>, ) { - let mut doc_aliases = FxHashMap::default(); - let mut specified_inline = None; let mut seen = FxHashMap::default(); let attrs = self.tcx.hir_attrs(hir_id); for attr in attrs { @@ -295,15 +293,6 @@ impl<'tcx> CheckAttrVisitor<'tcx> { self.check_diagnostic_on_unimplemented(attr.span(), hir_id, target) } [sym::thread_local, ..] => self.check_thread_local(attr, span, target), - [sym::doc, ..] => self.check_doc_attrs( - attr, - attr.span(), - attr_item.style, - hir_id, - target, - &mut specified_inline, - &mut doc_aliases, - ), [sym::no_link, ..] => self.check_no_link(hir_id, attr, span, target), [sym::rustc_no_implicit_autorefs, ..] => { self.check_applied_to_fn_or_method(hir_id, attr.span(), span, target) @@ -750,38 +739,9 @@ impl<'tcx> CheckAttrVisitor<'tcx> { self.dcx().emit_err(errors::DocExpectStr { attr_span: meta.span(), attr_name }); } - fn check_doc_alias_value( - &self, - meta: &MetaItemInner, - doc_alias: Symbol, - hir_id: HirId, - target: Target, - is_list: bool, - aliases: &mut FxHashMap, - ) { + fn check_doc_alias_value(&self, span: Span, alias: Symbol, hir_id: HirId, target: Target) { let tcx = self.tcx; - let span = meta.name_value_literal_span().unwrap_or_else(|| meta.span()); - let attr_str = - &format!("`#[doc(alias{})]`", if is_list { "(\"...\")" } else { " = \"...\"" }); - if doc_alias == sym::empty { - tcx.dcx().emit_err(errors::DocAliasEmpty { span, attr_str }); - return; - } - let doc_alias_str = doc_alias.as_str(); - if let Some(c) = doc_alias_str - .chars() - .find(|&c| c == '"' || c == '\'' || (c.is_whitespace() && c != ' ')) - { - tcx.dcx().emit_err(errors::DocAliasBadChar { span, attr_str, char_: c }); - return; - } - if doc_alias_str.starts_with(' ') || doc_alias_str.ends_with(' ') { - tcx.dcx().emit_err(errors::DocAliasStartEnd { span, attr_str }); - return; - } - - let span = meta.span(); if let Some(location) = match target { Target::AssocTy => { if let DefKind::Impl { .. } = @@ -838,20 +798,11 @@ impl<'tcx> CheckAttrVisitor<'tcx> { | Target::MacroCall | Target::Delegation { .. } => None, } { - tcx.dcx().emit_err(errors::DocAliasBadLocation { span, attr_str, location }); - return; - } - if self.tcx.hir_opt_name(hir_id) == Some(doc_alias) { - tcx.dcx().emit_err(errors::DocAliasNotAnAlias { span, attr_str }); - return; - } - if let Err(entry) = aliases.try_insert(doc_alias_str.to_owned(), span) { - self.tcx.emit_node_span_lint( - UNUSED_ATTRIBUTES, - hir_id, - span, - errors::DocAliasDuplicated { first_defn: *entry.entry.get() }, - ); + // FIXME: emit proper error + // tcx.dcx().emit_err(errors::DocAliasBadLocation { + // span, + // errors::DocAliasDuplicated { first_defn: *entry.entry.get() }, + // ); } } @@ -891,7 +842,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { fn check_doc_keyword_and_attribute( &self, - meta: &MetaItemInner, + span: Span, hir_id: HirId, attr_kind: DocFakeItemKind, ) { @@ -902,16 +853,6 @@ impl<'tcx> CheckAttrVisitor<'tcx> { s.is_reserved(|| edition::LATEST_STABLE_EDITION) || s.is_weak() || s == sym::SelfTy } - // FIXME: This should support attributes with namespace like `diagnostic::do_not_recommend`. - fn is_builtin_attr(s: Symbol) -> bool { - rustc_feature::BUILTIN_ATTRIBUTE_MAP.contains_key(&s) - } - - let value = match meta.value_str() { - Some(value) if value != sym::empty => value, - _ => return self.doc_attr_str_error(meta, attr_kind.name()), - }; - let item_kind = match self.tcx.hir_node(hir_id) { hir::Node::Item(item) => Some(&item.kind), _ => None, @@ -919,21 +860,17 @@ impl<'tcx> CheckAttrVisitor<'tcx> { match item_kind { Some(ItemKind::Mod(_, module)) => { if !module.item_ids.is_empty() { - self.dcx().emit_err(errors::DocKeywordAttributeEmptyMod { - span: meta.span(), - attr_name: attr_kind.name(), - }); + self.dcx() + .emit_err(errors::DocKeywordEmptyMod { span, attr_name: attr_kind.name() }); return; } } _ => { - self.dcx().emit_err(errors::DocKeywordAttributeNotMod { - span: meta.span(), - attr_name: attr_kind.name(), - }); + self.dcx().emit_err(errors::DocKeywordNotMod { span, attr_name: attr_kind.name() }); return; } } + match attr_kind { DocFakeItemKind::Keyword => { if !is_doc_keyword(value) { @@ -954,7 +891,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { } } - fn check_doc_fake_variadic(&self, meta: &MetaItemInner, hir_id: HirId) { + fn check_doc_fake_variadic(&self, span: Span, hir_id: HirId) { let item_kind = match self.tcx.hir_node(hir_id) { hir::Node::Item(item) => Some(&item.kind), _ => None, @@ -972,18 +909,18 @@ impl<'tcx> CheckAttrVisitor<'tcx> { false }; if !is_valid { - self.dcx().emit_err(errors::DocFakeVariadicNotValid { span: meta.span() }); + self.dcx().emit_err(errors::DocFakeVariadicNotValid { span }); } } _ => { - self.dcx().emit_err(errors::DocKeywordOnlyImpl { span: meta.span() }); + self.dcx().emit_err(errors::DocKeywordOnlyImpl { span }); } } } - fn check_doc_search_unbox(&self, meta: &MetaItemInner, hir_id: HirId) { + fn check_doc_search_unbox(&self, span: Span, hir_id: HirId) { let hir::Node::Item(item) = self.tcx.hir_node(hir_id) else { - self.dcx().emit_err(errors::DocSearchUnboxInvalid { span: meta.span() }); + self.dcx().emit_err(errors::DocSearchUnboxInvalid { span }); return; }; match item.kind { @@ -996,7 +933,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { }) => {} ItemKind::TyAlias(_, generics, _) if generics.params.len() != 0 => {} _ => { - self.dcx().emit_err(errors::DocSearchUnboxInvalid { span: meta.span() }); + self.dcx().emit_err(errors::DocSearchUnboxInvalid { span }); } } } @@ -1010,60 +947,32 @@ impl<'tcx> CheckAttrVisitor<'tcx> { /// already seen an inlining attribute for this item. /// If so, `specified_inline` holds the value and the span of /// the first `inline`/`no_inline` attribute. - fn check_doc_inline( - &self, - style: AttrStyle, - meta: &MetaItemInner, - hir_id: HirId, - target: Target, - specified_inline: &mut Option<(bool, Span)>, - ) { + fn check_doc_inline(&self, span: Span, hir_id: HirId, target: Target) { match target { - Target::Use | Target::ExternCrate => { - let do_inline = meta.has_name(sym::inline); - if let Some((prev_inline, prev_span)) = *specified_inline { - if do_inline != prev_inline { - let mut spans = MultiSpan::from_spans(vec![prev_span, meta.span()]); - spans.push_span_label(prev_span, fluent::passes_doc_inline_conflict_first); - spans.push_span_label( - meta.span(), - fluent::passes_doc_inline_conflict_second, - ); - self.dcx().emit_err(errors::DocKeywordConflict { spans }); - } - } else { - *specified_inline = Some((do_inline, meta.span())); - } - } + Target::Use | Target::ExternCrate => {} _ => { self.tcx.emit_node_span_lint( INVALID_DOC_ATTRIBUTES, hir_id, - meta.span(), + span, errors::DocInlineOnlyUse { - attr_span: meta.span(), - item_span: (style == AttrStyle::Outer).then(|| self.tcx.hir_span(hir_id)), + attr_span: span, + item_span: self.tcx.hir_span(hir_id), }, ); } } } - fn check_doc_masked( - &self, - style: AttrStyle, - meta: &MetaItemInner, - hir_id: HirId, - target: Target, - ) { + fn check_doc_masked(&self, span: Span, hir_id: HirId, target: Target) { if target != Target::ExternCrate { self.tcx.emit_node_span_lint( INVALID_DOC_ATTRIBUTES, hir_id, - meta.span(), + span, errors::DocMaskedOnlyExternCrate { - attr_span: meta.span(), - item_span: (style == AttrStyle::Outer).then(|| self.tcx.hir_span(hir_id)), + attr_span: span, + item_span: self.tcx.hir_span(hir_id), }, ); return; @@ -1073,103 +982,38 @@ impl<'tcx> CheckAttrVisitor<'tcx> { self.tcx.emit_node_span_lint( INVALID_DOC_ATTRIBUTES, hir_id, - meta.span(), + span, errors::DocMaskedNotExternCrateSelf { - attr_span: meta.span(), - item_span: (style == AttrStyle::Outer).then(|| self.tcx.hir_span(hir_id)), + attr_span: span, + item_span: self.tcx.hir_span(hir_id), }, ); } } /// Checks that an attribute is *not* used at the crate level. Returns `true` if valid. - fn check_attr_not_crate_level( - &self, - meta: &MetaItemInner, - hir_id: HirId, - attr_name: &str, - ) -> bool { + fn check_attr_not_crate_level(&self, span: Span, hir_id: HirId, attr_name: &str) -> bool { if CRATE_HIR_ID == hir_id { - self.dcx().emit_err(errors::DocAttrNotCrateLevel { span: meta.span(), attr_name }); + self.dcx().emit_err(errors::DocAttrNotCrateLevel { span, attr_name }); return false; } true } /// Checks that an attribute is used at the crate level. Returns `true` if valid. - fn check_attr_crate_level( - &self, - attr_span: Span, - style: AttrStyle, - meta: &MetaItemInner, - hir_id: HirId, - ) -> bool { + fn check_attr_crate_level(&self, span: Span, hir_id: HirId) -> bool { if hir_id != CRATE_HIR_ID { - // insert a bang between `#` and `[...` - let bang_span = attr_span.lo() + BytePos(1); - let sugg = (style == AttrStyle::Outer - && self.tcx.hir_get_parent_item(hir_id) == CRATE_OWNER_ID) - .then_some(errors::AttrCrateLevelOnlySugg { - attr: attr_span.with_lo(bang_span).with_hi(bang_span), - }); self.tcx.emit_node_span_lint( INVALID_DOC_ATTRIBUTES, hir_id, - meta.span(), - errors::AttrCrateLevelOnly { sugg }, + span, + errors::AttrCrateLevelOnly {}, ); return false; } true } - /// Checks that `doc(test(...))` attribute contains only valid attributes and are at the right place. - fn check_test_attr( - &self, - attr_span: Span, - style: AttrStyle, - meta: &MetaItemInner, - hir_id: HirId, - ) { - if let Some(metas) = meta.meta_item_list() { - for i_meta in metas { - match (i_meta.name(), i_meta.meta_item()) { - (Some(sym::attr), _) => { - // Allowed everywhere like `#[doc]` - } - (Some(sym::no_crate_inject), _) => { - self.check_attr_crate_level(attr_span, style, meta, hir_id); - } - (_, Some(m)) => { - self.tcx.emit_node_span_lint( - INVALID_DOC_ATTRIBUTES, - hir_id, - i_meta.span(), - errors::DocTestUnknown { - path: rustc_ast_pretty::pprust::path_to_string(&m.path), - }, - ); - } - (_, None) => { - self.tcx.emit_node_span_lint( - INVALID_DOC_ATTRIBUTES, - hir_id, - i_meta.span(), - errors::DocTestLiteral, - ); - } - } - } - } else { - self.tcx.emit_node_span_lint( - INVALID_DOC_ATTRIBUTES, - hir_id, - meta.span(), - errors::DocTestTakesList, - ); - } - } - /// 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 { @@ -1232,167 +1076,117 @@ impl<'tcx> CheckAttrVisitor<'tcx> { /// of one item. Read the documentation of [`check_doc_inline`] for more information. /// /// [`check_doc_inline`]: Self::check_doc_inline - fn check_doc_attrs( - &self, - attr: &Attribute, - attr_span: Span, - style: AttrStyle, - hir_id: HirId, - target: Target, - specified_inline: &mut Option<(bool, Span)>, - aliases: &mut FxHashMap, - ) { - if let Some(list) = attr.meta_item_list() { - for meta in &list { - if let Some(i_meta) = meta.meta_item() { - match i_meta.name() { - Some(sym::alias) => { - if self.check_attr_not_crate_level(meta, hir_id, "alias") { - self.check_doc_alias(meta, hir_id, target, aliases); - } - } - - Some(sym::keyword) => { - if self.check_attr_not_crate_level(meta, hir_id, "keyword") { - self.check_doc_keyword_and_attribute( - meta, - hir_id, - DocFakeItemKind::Keyword, - ); - } - } + fn check_doc_attrs(&self, attr: &DocAttribute, hir_id: HirId, target: Target) { + let DocAttribute { + aliases, + // valid pretty much anywhere, not checked here? + // FIXME: should we? + hidden: _, + inline, + // FIXME: currently unchecked + cfg: _, + cfg_hide, + fake_variadic, + keyword, + masked, + // FIXME: currently unchecked + notable_trait: _, + search_unbox, + html_favicon_url, + html_logo_url, + html_playground_url, + html_root_url, + html_no_source, + issue_tracker_base_url, + rust_logo, + // allowed anywhere + test_attrs: _, + no_crate_inject, + } = 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); + } + } - 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)) = keyword { + if self.check_attr_not_crate_level(*span, hir_id, "keyword") { + self.check_doc_keyword(*span, hir_id); + } + } - Some(sym::fake_variadic) => { - if self.check_attr_not_crate_level(meta, hir_id, "fake_variadic") { - self.check_doc_fake_variadic(meta, hir_id); - } - } + // 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") { + self.check_doc_fake_variadic(*span, hir_id); + } + } - Some(sym::search_unbox) => { - if self.check_attr_not_crate_level(meta, hir_id, "fake_variadic") { - self.check_doc_search_unbox(meta, hir_id); - } - } + if let Some(span) = search_unbox { + if self.check_attr_not_crate_level(*span, hir_id, "search_unbox") { + self.check_doc_search_unbox(*span, hir_id); + } + } - Some(sym::test) => { - self.check_test_attr(attr_span, style, meta, hir_id); - } + for i in [ + html_favicon_url, + html_logo_url, + html_playground_url, + issue_tracker_base_url, + html_root_url, + ] { + if let Some((_, span)) = i { + self.check_attr_crate_level(*span, hir_id); + } + } - Some( - sym::html_favicon_url - | sym::html_logo_url - | sym::html_playground_url - | sym::issue_tracker_base_url - | sym::html_root_url - | sym::html_no_source, - ) => { - self.check_attr_crate_level(attr_span, style, meta, hir_id); - } + for i in [html_no_source, no_crate_inject] { + if let Some(span) = i { + self.check_attr_crate_level(*span, hir_id); + } + } - Some(sym::auto_cfg) => { - self.check_doc_auto_cfg(i_meta, hir_id); - } + if let Some((_, span)) = inline { + self.check_doc_inline(*span, hir_id, target) + } - Some(sym::inline | sym::no_inline) => { - self.check_doc_inline(style, meta, hir_id, target, specified_inline) - } + if let Some(span) = rust_logo { + if self.check_attr_crate_level(*span, hir_id) + && !self.tcx.features().rustdoc_internals() + { + feature_err( + &self.tcx.sess, + sym::rustdoc_internals, + *span, + fluent::passes_doc_rust_logo, + ) + .emit(); + } + } - Some(sym::masked) => self.check_doc_masked(style, meta, hir_id, target), - - Some(sym::cfg | sym::hidden | sym::notable_trait) => {} - - Some(sym::rust_logo) => { - if self.check_attr_crate_level(attr_span, style, meta, hir_id) - && !self.tcx.features().rustdoc_internals() - { - feature_err( - &self.tcx.sess, - sym::rustdoc_internals, - meta.span(), - fluent::passes_doc_rust_logo, - ) - .emit(); - } - } + if let Some(span) = masked { + self.check_doc_masked(*span, hir_id, target); + } - _ => { - 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 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 }, - ); - } - } - } - } else { - self.tcx.emit_node_span_lint( - INVALID_DOC_ATTRIBUTES, - hir_id, - meta.span(), - errors::DocInvalid, - ); - } - } + if let Some(span) = cfg_hide { + self.check_attr_crate_level(*span, hir_id); } } diff --git a/compiler/rustc_passes/src/errors.rs b/compiler/rustc_passes/src/errors.rs index f5023646b1917..e7ac42f6a9c28 100644 --- a/compiler/rustc_passes/src/errors.rs +++ b/compiler/rustc_passes/src/errors.rs @@ -131,31 +131,6 @@ pub(crate) struct DocExpectStr<'a> { pub attr_name: &'a str, } -#[derive(Diagnostic)] -#[diag(passes_doc_alias_empty)] -pub(crate) struct DocAliasEmpty<'a> { - #[primary_span] - pub span: Span, - pub attr_str: &'a str, -} - -#[derive(Diagnostic)] -#[diag(passes_doc_alias_bad_char)] -pub(crate) struct DocAliasBadChar<'a> { - #[primary_span] - pub span: Span, - pub attr_str: &'a str, - pub char_: char, -} - -#[derive(Diagnostic)] -#[diag(passes_doc_alias_start_end)] -pub(crate) struct DocAliasStartEnd<'a> { - #[primary_span] - pub span: Span, - pub attr_str: &'a str, -} - #[derive(Diagnostic)] #[diag(passes_doc_alias_bad_location)] pub(crate) struct DocAliasBadLocation<'a> { @@ -173,13 +148,6 @@ pub(crate) struct DocAliasNotAnAlias<'a> { pub attr_str: &'a str, } -#[derive(LintDiagnostic)] -#[diag(passes_doc_alias_duplicated)] -pub(crate) struct DocAliasDuplicated { - #[label] - pub first_defn: Span, -} - #[derive(Diagnostic)] #[diag(passes_doc_alias_not_string_literal)] pub(crate) struct DocAliasNotStringLiteral { @@ -249,14 +217,6 @@ pub(crate) struct DocSearchUnboxInvalid { pub span: Span, } -#[derive(Diagnostic)] -#[diag(passes_doc_inline_conflict)] -#[help] -pub(crate) struct DocKeywordConflict { - #[primary_span] - pub spans: MultiSpan, -} - #[derive(LintDiagnostic)] #[diag(passes_doc_inline_only_use)] #[note] @@ -264,7 +224,7 @@ pub(crate) struct DocInlineOnlyUse { #[label] pub attr_span: Span, #[label(passes_not_a_use_item_label)] - pub item_span: Option, + pub item_span: Span, } #[derive(LintDiagnostic)] @@ -274,7 +234,7 @@ pub(crate) struct DocMaskedOnlyExternCrate { #[label] pub attr_span: Span, #[label(passes_not_an_extern_crate_label)] - pub item_span: Option, + pub item_span: Span, } #[derive(LintDiagnostic)] @@ -283,7 +243,7 @@ pub(crate) struct DocMaskedNotExternCrateSelf { #[label] pub attr_span: Span, #[label(passes_extern_crate_self_label)] - pub item_span: Option, + pub item_span: Span, } #[derive(Diagnostic)] @@ -1327,17 +1287,7 @@ pub(crate) struct IneffectiveUnstableImpl; #[derive(LintDiagnostic)] #[diag(passes_attr_crate_level)] #[note] -pub(crate) struct AttrCrateLevelOnly { - #[subdiagnostic] - pub sugg: Option, -} - -#[derive(Subdiagnostic)] -#[suggestion(passes_suggestion, applicability = "maybe-incorrect", code = "!", style = "verbose")] -pub(crate) struct AttrCrateLevelOnlySugg { - #[primary_span] - pub attr: Span, -} +pub(crate) struct AttrCrateLevelOnly {} /// "sanitize attribute not allowed here" #[derive(Diagnostic)] diff --git a/compiler/rustc_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs index 1589e3f4092c4..f2085bcb9c051 100644 --- a/compiler/rustc_resolve/src/late/diagnostics.rs +++ b/compiler/rustc_resolve/src/late/diagnostics.rs @@ -10,7 +10,7 @@ use rustc_ast::{ Item, ItemKind, MethodCall, NodeId, Path, PathSegment, Ty, TyKind, }; use rustc_ast_pretty::pprust::where_bound_predicate_to_string; -use rustc_attr_parsing::is_doc_alias_attrs_contain_symbol; +use rustc_attr_data_structures::{AttributeKind, find_attr}; use rustc_data_structures::fx::{FxHashSet, FxIndexSet}; use rustc_errors::codes::*; use rustc_errors::{ @@ -888,7 +888,9 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { // confused by them. continue; } - if is_doc_alias_attrs_contain_symbol(r.tcx.get_attrs(did, sym::doc), item_name) { + if let Some(d) = find_attr!(r.tcx.get_all_attrs(did), AttributeKind::Doc(d) => d) + && d.aliases.contains_key(&item_name) + { return Some(did); } } diff --git a/server exited unexpectedly b/server exited unexpectedly new file mode 100644 index 0000000000000..19fa52391a207 --- /dev/null +++ b/server exited unexpectedly @@ -0,0 +1 @@ +Ptmux;_Ga=d,d=a,q=2\\ \ No newline at end of file