diff --git a/compiler/rustc_lint_defs/src/lib.rs b/compiler/rustc_lint_defs/src/lib.rs index 70475563a4abe..ac0fbbe1ae073 100644 --- a/compiler/rustc_lint_defs/src/lib.rs +++ b/compiler/rustc_lint_defs/src/lib.rs @@ -446,18 +446,26 @@ macro_rules! declare_lint { macro_rules! declare_tool_lint { ( $(#[$attr:meta])* $vis:vis $tool:ident ::$NAME:ident, $Level: ident, $desc: expr + $(, @future_incompatible = FutureIncompatibleInfo { $($field:ident : $val:expr),* $(,)* }; )? ) => ( - $crate::declare_tool_lint!{$(#[$attr])* $vis $tool::$NAME, $Level, $desc, false} + $crate::declare_tool_lint!{ + $(#[$attr])* $vis $tool::$NAME, $Level, $desc, false + $(, @future_incompatible = FutureIncompatibleInfo { $($field : $val),* };)? + } ); ( $(#[$attr:meta])* $vis:vis $tool:ident ::$NAME:ident, $Level:ident, $desc:expr, report_in_external_macro: $rep:expr + $(, @future_incompatible = FutureIncompatibleInfo { $($field:ident : $val:expr),* $(,)* }; )? ) => ( - $crate::declare_tool_lint!{$(#[$attr])* $vis $tool::$NAME, $Level, $desc, $rep} + $crate::declare_tool_lint!{$(#[$attr])* $vis $tool::$NAME, $Level, $desc, $rep + $(, @future_incompatible = FutureIncompatibleInfo { $($field:ident : $val:expr),* $(,)* }; )? + } ); ( $(#[$attr:meta])* $vis:vis $tool:ident ::$NAME:ident, $Level:ident, $desc:expr, $external:expr + $(, @future_incompatible = FutureIncompatibleInfo { $($field:ident : $val:expr),* $(,)* }; )? ) => ( $(#[$attr])* $vis static $NAME: &$crate::Lint = &$crate::Lint { @@ -466,10 +474,14 @@ macro_rules! declare_tool_lint { desc: $desc, edition_lint_opts: None, report_in_external_macro: $external, - future_incompatible: None, + $(future_incompatible: Some($crate::FutureIncompatibleInfo { + $($field: $val,)* + ..$crate::FutureIncompatibleInfo::default_fields_for_macro() + }),)? is_plugin: true, feature_gate: None, crate_level_only: false, + ..$crate::Lint::default_fields_for_macro() }; ); } diff --git a/src/doc/rustdoc/src/lints.md b/src/doc/rustdoc/src/lints.md index 174db711bcee7..62f4637974892 100644 --- a/src/doc/rustdoc/src/lints.md +++ b/src/doc/rustdoc/src/lints.md @@ -331,3 +331,31 @@ warning: this URL is not a hyperlink 3 | /// [http://example.net] | ^^^^^^^^^^^^^^^^^^ help: use an automatic link instead: `` ``` + +## external_doc + +This lint is **nightly-only** and **warns by default**. It detects when +`#![feature(external_doc)]` is used. This feature is scheduled for removal and will give a hard +error in a future release. + +```rust +#![feature(external_doc)] +#![doc(include = "./external-doc.rs")] +``` + +Which will give: + +```text +warning: `#[doc(include)]` is deprecated and will be removed in a future release + --> external-doc.rs:2:1 + | +2 | #![doc(include = "./external-doc.rs")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(rustdoc::doc_include)]` on by default + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #44732 +help: use `#![feature(extended_key_value_attributes)]` instead + | +2 | #![doc = include_str!("./external-doc.rs")] +``` diff --git a/src/librustdoc/clean/inline.rs b/src/librustdoc/clean/inline.rs index 277ec91f15ed7..64abc803d53ac 100644 --- a/src/librustdoc/clean/inline.rs +++ b/src/librustdoc/clean/inline.rs @@ -7,7 +7,7 @@ use rustc_data_structures::fx::FxHashSet; use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::{DefId, CRATE_DEF_INDEX}; -use rustc_hir::Mutability; +use rustc_hir::{HirId, Mutability}; use rustc_metadata::creader::LoadedMacro; use rustc_middle::ty::{self, TyCtxt}; use rustc_mir::const_eval::is_min_const_fn; @@ -19,6 +19,7 @@ use crate::clean::{self, Attributes, GetDefId, ToSource, TypeKind}; use crate::core::DocContext; use crate::formats::item_type::ItemType; +use super::clean_attrs; use super::Clean; type Attrs<'hir> = rustc_middle::ty::Attributes<'hir>; @@ -121,7 +122,8 @@ crate fn try_inline( }; let target_attrs = load_attrs(cx, did); - let attrs = box merge_attrs(cx, Some(parent_module), target_attrs, attrs_clone); + let local_item = DocContext::as_local_hir_id(cx.tcx, did); + let attrs = box merge_attrs(cx, Some(parent_module), target_attrs, attrs_clone, local_item); cx.inlined.insert(did); let what_rustc_thinks = clean::Item::from_def_id_and_parts(did, Some(name), kind, cx); @@ -289,6 +291,7 @@ fn merge_attrs( parent_module: Option, old_attrs: Attrs<'_>, new_attrs: Option>, + item: Option, ) -> clean::Attributes { // NOTE: If we have additional attributes (from a re-export), // always insert them first. This ensure that re-export @@ -296,15 +299,14 @@ fn merge_attrs( // when we render them. if let Some(inner) = new_attrs { if let Some(new_id) = parent_module { - let diag = cx.sess().diagnostic(); - Attributes::from_ast(diag, old_attrs, Some((inner, new_id))) + Attributes::from_ast(cx.tcx, old_attrs, Some((inner, new_id)), item) } else { let mut both = inner.to_vec(); both.extend_from_slice(old_attrs); - both.clean(cx) + clean_attrs(&both, item, cx) } } else { - old_attrs.clean(cx) + clean_attrs(old_attrs, item, cx) } } @@ -415,7 +417,8 @@ crate fn build_impl( debug!("build_impl: impl {:?} for {:?}", trait_.def_id(), for_.def_id()); - let attrs = box merge_attrs(cx, parent_module.into(), load_attrs(cx, did), attrs); + let local_item = DocContext::as_local_hir_id(tcx, did); + let attrs = box merge_attrs(cx, parent_module.into(), load_attrs(cx, did), attrs, local_item); debug!("merged_attrs={:?}", attrs); ret.push(clean::Item::from_def_id_and_attrs_and_parts( diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index 217e899001ef9..133bf1075d7dd 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -15,6 +15,7 @@ use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_hir as hir; use rustc_hir::def::{CtorKind, DefKind, Res}; use rustc_hir::def_id::{CrateNum, DefId, CRATE_DEF_INDEX, LOCAL_CRATE}; +use rustc_hir::HirId; use rustc_index::vec::{Idx, IndexVec}; use rustc_infer::infer::region_constraints::{Constraint, RegionConstraintData}; use rustc_middle::bug; @@ -108,7 +109,9 @@ impl Clean for CrateNum { // rendering by delegating everything to a hash map. let mut as_primitive = |res: Res| { if let Res::Def(DefKind::Mod, def_id) = res { - let attrs = cx.tcx.get_attrs(def_id).clean(cx); + // We already warned about any attributes on the module when cleaning it. + // Don't warn a second time. + let attrs = clean_attrs(cx.tcx.get_attrs(def_id), None, cx); let mut prim = None; for attr in attrs.lists(sym::doc) { if let Some(v) = attr.value_str() { @@ -155,7 +158,7 @@ impl Clean for CrateNum { let mut as_keyword = |res: Res| { if let Res::Def(DefKind::Mod, def_id) = res { - let attrs = tcx.get_attrs(def_id).clean(cx); + let attrs = clean_attrs(tcx.get_attrs(def_id), None, cx); let mut keyword = None; for attr in attrs.lists(sym::doc) { if attr.has_name(sym::keyword) { @@ -197,7 +200,8 @@ impl Clean for CrateNum { ExternalCrate { name: tcx.crate_name(*self), src: krate_src, - attrs: tcx.get_attrs(root).clean(cx), + // The local crate was already cleaned, and all other crates are non-local. + attrs: clean_attrs(tcx.get_attrs(root), None, cx), primitives, keywords, } @@ -237,10 +241,12 @@ impl Clean for doctree::Module<'_> { } } -impl Clean for [ast::Attribute] { - fn clean(&self, cx: &mut DocContext<'_>) -> Attributes { - Attributes::from_ast(cx.sess().diagnostic(), self, None) - } +fn clean_attrs( + attrs: &[ast::Attribute], + local: Option, + cx: &mut DocContext<'_>, +) -> Attributes { + Attributes::from_ast(cx.tcx, attrs, None, local) } impl Clean for hir::GenericBound<'_> { @@ -2124,7 +2130,7 @@ fn clean_extern_crate( // FIXME: using `from_def_id_and_kind` breaks `rustdoc/masked` for some reason vec![Item { name: Some(name), - attrs: box attrs.clean(cx), + attrs: box clean_attrs(attrs, Some(krate.hir_id()), cx), span: krate.span.clean(cx), def_id: crate_def_id, visibility: krate.vis.clean(cx), diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs index f3c9b987eb02a..55c4197a73086 100644 --- a/src/librustdoc/clean/types.rs +++ b/src/librustdoc/clean/types.rs @@ -16,10 +16,12 @@ use rustc_ast::{self as ast, AttrStyle}; use rustc_attr::{ConstStability, Deprecation, Stability, StabilityLevel}; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_data_structures::thin_vec::ThinVec; +use rustc_errors::Applicability; use rustc_hir as hir; use rustc_hir::def::{CtorKind, Res}; use rustc_hir::def_id::{CrateNum, DefId, DefIndex}; use rustc_hir::lang_items::LangItem; +use rustc_hir::HirId; use rustc_hir::{BodyId, Mutability}; use rustc_index::vec::IndexVec; use rustc_middle::ty::{self, TyCtxt}; @@ -32,11 +34,10 @@ use rustc_target::abi::VariantIdx; use rustc_target::spec::abi::Abi; use crate::clean::cfg::Cfg; -use crate::clean::external_path; -use crate::clean::inline::{self, print_inlined_const}; use crate::clean::types::Type::{QPath, ResolvedPath}; use crate::clean::utils::{is_literal_expr, print_const_expr, print_evaluated_const}; use crate::clean::Clean; +use crate::clean::{clean_attrs, external_path, inline}; use crate::core::DocContext; use crate::formats::cache::Cache; use crate::formats::item_type::ItemType; @@ -150,11 +151,12 @@ impl Item { kind: ItemKind, cx: &mut DocContext<'_>, ) -> Item { + let local_item = DocContext::as_local_hir_id(cx.tcx, def_id); Self::from_def_id_and_attrs_and_parts( def_id, name, kind, - box cx.tcx.get_attrs(def_id).clean(cx), + box clean_attrs(cx.tcx.get_attrs(def_id), local_item, cx), cx, ) } @@ -739,9 +741,10 @@ impl Attributes { } crate fn from_ast( - diagnostic: &::rustc_errors::Handler, + tcx: TyCtxt<'_>, attrs: &[ast::Attribute], additional_attrs: Option<(&[ast::Attribute], DefId)>, + item: Option, ) -> Attributes { let mut doc_strings: Vec = vec![]; let mut sp = None; @@ -800,10 +803,27 @@ impl Attributes { // Extracted #[doc(cfg(...))] match Cfg::parse(cfg_mi) { Ok(new_cfg) => cfg &= new_cfg, - Err(e) => diagnostic.span_err(e.span, e.msg), + Err(e) => tcx.sess.span_err(e.span, e.msg), } } else if let Some((filename, contents)) = Attributes::extract_include(&mi) { + if let Some(hir_id) = item { + tcx.struct_span_lint_hir(crate::lint::DOC_INCLUDE, hir_id, attr.span, |lint_builder| { + let mut diag = lint_builder.build("`#[doc(include)]` is deprecated and will be removed in a future release"); + diag.span_suggestion_verbose( + attr.span, + "use `#![feature(extended_key_value_attributes)]` instead", + format!( + "#{}[doc = include_str!(\"{}\")]", + if attr.style == AttrStyle::Inner { "!" } else { "" }, + filename, + ), + Applicability::MaybeIncorrect, + ); + diag.emit(); + }); + } + let line = doc_line; doc_line += contents.as_str().lines().count(); let frag = DocFragment { @@ -2001,7 +2021,7 @@ impl Constant { crate fn expr(&self, tcx: TyCtxt<'_>) -> String { match self.kind { ConstantKind::TyConst { ref expr } => expr.clone(), - ConstantKind::Extern { def_id } => print_inlined_const(tcx, def_id), + ConstantKind::Extern { def_id } => inline::print_inlined_const(tcx, def_id), ConstantKind::Local { body, .. } | ConstantKind::Anonymous { body } => { print_const_expr(tcx, body) } diff --git a/src/librustdoc/doctest.rs b/src/librustdoc/doctest.rs index 6f6ed0eb68413..c25534bcdbcca 100644 --- a/src/librustdoc/doctest.rs +++ b/src/librustdoc/doctest.rs @@ -1093,7 +1093,7 @@ impl<'a, 'hir, 'tcx> HirCollector<'a, 'hir, 'tcx> { nested: F, ) { let attrs = self.tcx.hir().attrs(hir_id); - let mut attrs = Attributes::from_ast(self.sess.diagnostic(), attrs, None); + let mut attrs = Attributes::from_ast(self.tcx, attrs, None, Some(hir_id)); if let Some(ref cfg) = attrs.cfg { if !cfg.matches(&self.sess.parse_sess, Some(&self.sess.features_untracked())) { return; diff --git a/src/librustdoc/lint.rs b/src/librustdoc/lint.rs index ffa2f7a47fdd8..d7ae2c3eda78e 100644 --- a/src/librustdoc/lint.rs +++ b/src/librustdoc/lint.rs @@ -64,9 +64,12 @@ where } macro_rules! declare_rustdoc_lint { - ($(#[$attr:meta])* $name: ident, $level: ident, $descr: literal $(,)?) => { + ($(#[$attr:meta])* $name: ident, $level: ident, $descr: literal + $(, @future_incompatible = FutureIncompatibleInfo { $($field:ident : $val:expr),* $(,)* }; )? + $(,)?) => { declare_tool_lint! { $(#[$attr])* pub rustdoc::$name, $level, $descr + $(, @future_incompatible = FutureIncompatibleInfo { $($field : $val),* };)? } } } @@ -158,6 +161,22 @@ declare_rustdoc_lint! { "detects URLs that could be written using only angle brackets" } +declare_rustdoc_lint! { + /// The `doc_include` lint detects when `#[doc(include = ...)]` is used. + /// This feature is scheduled for removal and will give a hard error in a future release. + /// + /// This is a `rustdoc` only lint, see the documentation in the [rustdoc book]. + /// + /// [rustdoc book]: ../../../rustdoc/lints.html#non_autolinks + DOC_INCLUDE, + Warn, + "detects using `#[doc(include = ...)]`", + @future_incompatible = FutureIncompatibleInfo { + reference: "issue #44732 ", + edition: None, + }; +} + crate static RUSTDOC_LINTS: Lazy> = Lazy::new(|| { vec![ BROKEN_INTRA_DOC_LINKS, diff --git a/src/test/rustdoc-ui/auxiliary/doc-include-deprecated.rs b/src/test/rustdoc-ui/auxiliary/doc-include-deprecated.rs new file mode 100644 index 0000000000000..3fb2a7d5fa1fc --- /dev/null +++ b/src/test/rustdoc-ui/auxiliary/doc-include-deprecated.rs @@ -0,0 +1,5 @@ +#![crate_name = "inner"] +#![feature(external_doc)] +#[doc(include = "docs.md")] +pub struct HasDocInclude {} +pub struct NoDocs {} diff --git a/src/test/rustdoc-ui/auxiliary/docs.md b/src/test/rustdoc-ui/auxiliary/docs.md new file mode 100644 index 0000000000000..a32e8ca85fdf5 --- /dev/null +++ b/src/test/rustdoc-ui/auxiliary/docs.md @@ -0,0 +1 @@ +Here have some docs diff --git a/src/test/rustdoc-ui/doc-include-deprecated.rs b/src/test/rustdoc-ui/doc-include-deprecated.rs new file mode 100644 index 0000000000000..1a0fa705d6fba --- /dev/null +++ b/src/test/rustdoc-ui/doc-include-deprecated.rs @@ -0,0 +1,20 @@ +// aux-build:doc-include-deprecated.rs +// check-pass +#![feature(external_doc)] +#![doc(include = "auxiliary/docs.md")] +//~^ WARNING deprecated +//~| WARNING hard error in a future release + +extern crate inner; + +pub use inner::HasDocInclude; + +#[doc(include = "auxiliary/docs.md")] +//~^ WARNING deprecated +//~| WARNING hard error in a future release +pub use inner::HasDocInclude as _; + +#[doc(include = "auxiliary/docs.md")] +//~^ WARNING deprecated +//~| WARNING hard error in a future release +pub use inner::NoDocs; diff --git a/src/test/rustdoc-ui/doc-include-deprecated.stderr b/src/test/rustdoc-ui/doc-include-deprecated.stderr new file mode 100644 index 0000000000000..326d94630db78 --- /dev/null +++ b/src/test/rustdoc-ui/doc-include-deprecated.stderr @@ -0,0 +1,42 @@ +warning: `#[doc(include)]` is deprecated and will be removed in a future release + --> $DIR/doc-include-deprecated.rs:12:1 + | +LL | #[doc(include = "auxiliary/docs.md")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(rustdoc::doc_include)]` on by default + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #44732 +help: use `#![feature(extended_key_value_attributes)]` instead + | +LL | #[doc = include_str!("auxiliary/docs.md")] + | + +warning: `#[doc(include)]` is deprecated and will be removed in a future release + --> $DIR/doc-include-deprecated.rs:17:1 + | +LL | #[doc(include = "auxiliary/docs.md")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #44732 +help: use `#![feature(extended_key_value_attributes)]` instead + | +LL | #[doc = include_str!("auxiliary/docs.md")] + | + +warning: `#[doc(include)]` is deprecated and will be removed in a future release + --> $DIR/doc-include-deprecated.rs:4:1 + | +LL | #![doc(include = "auxiliary/docs.md")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #44732 +help: use `#![feature(extended_key_value_attributes)]` instead + | +LL | #![doc = include_str!("auxiliary/docs.md")] + | + +warning: 3 warnings emitted +