about summary refs log tree commit diff
diff options
context:
space:
mode:
authorJoshua Nelson <jyn514@gmail.com>2021-05-18 21:46:41 -0400
committerJoshua Nelson <jyn514@gmail.com>2021-06-04 08:05:54 -0400
commit15fec1fb80b5264d7e2d3382478424abb9afb3d1 (patch)
tree791ffbf60606253828e89a8176ef8d0ef0ad35bd
parentc4c2ab57a43737867982fafc8cfacd9b069fee96 (diff)
downloadrust-15fec1fb80b5264d7e2d3382478424abb9afb3d1.tar.gz
rust-15fec1fb80b5264d7e2d3382478424abb9afb3d1.zip
Remove `doc(include)`
-rw-r--r--compiler/rustc_ast_passes/src/feature_gate.rs1
-rw-r--r--compiler/rustc_expand/src/base.rs6
-rw-r--r--compiler/rustc_expand/src/expand.rs144
-rw-r--r--compiler/rustc_feature/src/active.rs3
-rw-r--r--compiler/rustc_feature/src/removed.rs4
-rw-r--r--compiler/rustc_lint/src/builtin.rs2
-rw-r--r--compiler/rustc_passes/src/check_attr.rs27
-rw-r--r--compiler/rustc_save_analysis/src/lib.rs14
-rw-r--r--library/core/src/lib.rs1
-rw-r--r--src/doc/rustdoc/src/documentation-tests.md4
-rw-r--r--src/doc/rustdoc/src/unstable-features.md16
-rw-r--r--src/doc/unstable-book/src/language-features/external-doc.md40
-rw-r--r--src/librustdoc/clean/types.rs93
-rw-r--r--src/test/run-make-fulldeps/include_bytes_deps/main.rs4
-rw-r--r--src/test/run-make-fulldeps/save-analysis/foo.rs7
-rw-r--r--src/test/rustdoc-ui/doc-include-suggestion.rs10
-rw-r--r--src/test/rustdoc-ui/doc-include-suggestion.stderr12
-rw-r--r--src/test/rustdoc/auxiliary/external-cross-doc.md2
-rw-r--r--src/test/rustdoc/auxiliary/external-cross.rs6
-rw-r--r--src/test/rustdoc/auxiliary/external-doc.md2
-rw-r--r--src/test/rustdoc/external-doc.rs9
-rw-r--r--src/test/rustdoc/unindent.rs6
-rw-r--r--src/test/ui/extern/external-doc-error.rs31
-rw-r--r--src/test/ui/extern/external-doc-error.stderr38
-rw-r--r--src/test/ui/feature-gates/feature-gate-external_doc.rs3
-rw-r--r--src/test/ui/feature-gates/feature-gate-external_doc.stderr21
-rw-r--r--src/tools/clippy/clippy_lints/src/missing_doc.rs19
-rw-r--r--src/tools/clippy/tests/ui/missing-doc-crate.rs3
28 files changed, 79 insertions, 449 deletions
diff --git a/compiler/rustc_ast_passes/src/feature_gate.rs b/compiler/rustc_ast_passes/src/feature_gate.rs
index 4996c2195ef..664e138b39d 100644
--- a/compiler/rustc_ast_passes/src/feature_gate.rs
+++ b/compiler/rustc_ast_passes/src/feature_gate.rs
@@ -318,7 +318,6 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> {
                 }}
 
                 gate_doc!(
-                    include => external_doc
                     cfg => doc_cfg
                     masked => doc_masked
                     notable_trait => doc_notable_trait
diff --git a/compiler/rustc_expand/src/base.rs b/compiler/rustc_expand/src/base.rs
index e1c218c6408..aab2741c852 100644
--- a/compiler/rustc_expand/src/base.rs
+++ b/compiler/rustc_expand/src/base.rs
@@ -1068,11 +1068,11 @@ impl<'a> ExtCtxt<'a> {
         self.resolver.check_unused_macros();
     }
 
-    /// Resolves a path mentioned inside Rust code.
+    /// Resolves a `path` mentioned inside Rust code, returning an absolute path.
     ///
-    /// This unifies the logic used for resolving `include_X!`, and `#[doc(include)]` file paths.
+    /// This unifies the logic used for resolving `include_X!`.
     ///
-    /// Returns an absolute path to the file that `path` refers to.
+    /// FIXME: move this to `rustc_builtin_macros` and make it private.
     pub fn resolve_path(
         &self,
         path: impl Into<PathBuf>,
diff --git a/compiler/rustc_expand/src/expand.rs b/compiler/rustc_expand/src/expand.rs
index f5c6bb3db65..39c0447bd09 100644
--- a/compiler/rustc_expand/src/expand.rs
+++ b/compiler/rustc_expand/src/expand.rs
@@ -12,11 +12,11 @@ use rustc_ast::ptr::P;
 use rustc_ast::token;
 use rustc_ast::tokenstream::TokenStream;
 use rustc_ast::visit::{self, AssocCtxt, Visitor};
-use rustc_ast::{AstLike, AttrItem, Block, Inline, ItemKind, LitKind, MacArgs};
+use rustc_ast::{AstLike, Block, Inline, ItemKind, MacArgs};
 use rustc_ast::{MacCallStmt, MacStmtStyle, MetaItemKind, ModKind, NestedMetaItem};
 use rustc_ast::{NodeId, PatKind, Path, StmtKind, Unsafe};
 use rustc_ast_pretty::pprust;
-use rustc_attr::{self as attr, is_builtin_attr};
+use rustc_attr::is_builtin_attr;
 use rustc_data_structures::map_in_place::MapInPlace;
 use rustc_data_structures::stack::ensure_sufficient_stack;
 use rustc_data_structures::sync::Lrc;
@@ -28,15 +28,14 @@ use rustc_session::lint::builtin::UNUSED_DOC_COMMENTS;
 use rustc_session::lint::BuiltinLintDiagnostics;
 use rustc_session::parse::{feature_err, ParseSess};
 use rustc_session::Limit;
-use rustc_span::symbol::{sym, Ident, Symbol};
-use rustc_span::{ExpnId, FileName, Span, DUMMY_SP};
+use rustc_span::symbol::{sym, Ident};
+use rustc_span::{ExpnId, FileName, Span};
 
 use smallvec::{smallvec, SmallVec};
-use std::io::ErrorKind;
 use std::ops::DerefMut;
 use std::path::PathBuf;
 use std::rc::Rc;
-use std::{iter, mem, slice};
+use std::{iter, mem};
 
 macro_rules! ast_fragments {
     (
@@ -1524,139 +1523,6 @@ impl<'a, 'b> MutVisitor for InvocationCollector<'a, 'b> {
         noop_flat_map_generic_param(param, self)
     }
 
-    fn visit_attribute(&mut self, at: &mut ast::Attribute) {
-        // turn `#[doc(include="filename")]` attributes into `#[doc(include(file="filename",
-        // contents="file contents")]` attributes
-        if !self.cx.sess.check_name(at, sym::doc) {
-            return noop_visit_attribute(at, self);
-        }
-
-        if let Some(list) = at.meta_item_list() {
-            if !list.iter().any(|it| it.has_name(sym::include)) {
-                return noop_visit_attribute(at, self);
-            }
-
-            let mut items = vec![];
-
-            for mut it in list {
-                if !it.has_name(sym::include) {
-                    items.push({
-                        noop_visit_meta_list_item(&mut it, self);
-                        it
-                    });
-                    continue;
-                }
-
-                if let Some(file) = it.value_str() {
-                    let err_count = self.cx.sess.parse_sess.span_diagnostic.err_count();
-                    self.check_attributes(slice::from_ref(at));
-                    if self.cx.sess.parse_sess.span_diagnostic.err_count() > err_count {
-                        // avoid loading the file if they haven't enabled the feature
-                        return noop_visit_attribute(at, self);
-                    }
-
-                    let filename = match self.cx.resolve_path(&*file.as_str(), it.span()) {
-                        Ok(filename) => filename,
-                        Err(mut err) => {
-                            err.emit();
-                            continue;
-                        }
-                    };
-
-                    match self.cx.source_map().load_file(&filename) {
-                        Ok(source_file) => {
-                            let src = source_file
-                                .src
-                                .as_ref()
-                                .expect("freshly loaded file should have a source");
-                            let src_interned = Symbol::intern(src.as_str());
-
-                            let include_info = vec![
-                                ast::NestedMetaItem::MetaItem(attr::mk_name_value_item_str(
-                                    Ident::with_dummy_span(sym::file),
-                                    file,
-                                    DUMMY_SP,
-                                )),
-                                ast::NestedMetaItem::MetaItem(attr::mk_name_value_item_str(
-                                    Ident::with_dummy_span(sym::contents),
-                                    src_interned,
-                                    DUMMY_SP,
-                                )),
-                            ];
-
-                            let include_ident = Ident::with_dummy_span(sym::include);
-                            let item = attr::mk_list_item(include_ident, include_info);
-                            items.push(ast::NestedMetaItem::MetaItem(item));
-                        }
-                        Err(e) => {
-                            let lit_span = it.name_value_literal_span().unwrap();
-
-                            if e.kind() == ErrorKind::InvalidData {
-                                self.cx
-                                    .struct_span_err(
-                                        lit_span,
-                                        &format!("{} wasn't a utf-8 file", filename.display()),
-                                    )
-                                    .span_label(lit_span, "contains invalid utf-8")
-                                    .emit();
-                            } else {
-                                let mut err = self.cx.struct_span_err(
-                                    lit_span,
-                                    &format!("couldn't read {}: {}", filename.display(), e),
-                                );
-                                err.span_label(lit_span, "couldn't read file");
-
-                                err.emit();
-                            }
-                        }
-                    }
-                } else {
-                    let mut err = self
-                        .cx
-                        .struct_span_err(it.span(), "expected path to external documentation");
-
-                    // Check if the user erroneously used `doc(include(...))` syntax.
-                    let literal = it.meta_item_list().and_then(|list| {
-                        if list.len() == 1 {
-                            list[0].literal().map(|literal| &literal.kind)
-                        } else {
-                            None
-                        }
-                    });
-
-                    let (path, applicability) = match &literal {
-                        Some(LitKind::Str(path, ..)) => {
-                            (path.to_string(), Applicability::MachineApplicable)
-                        }
-                        _ => (String::from("<path>"), Applicability::HasPlaceholders),
-                    };
-
-                    err.span_suggestion(
-                        it.span(),
-                        "provide a file path with `=`",
-                        format!("include = \"{}\"", path),
-                        applicability,
-                    );
-
-                    err.emit();
-                }
-            }
-
-            let meta = attr::mk_list_item(Ident::with_dummy_span(sym::doc), items);
-            *at = ast::Attribute {
-                kind: ast::AttrKind::Normal(
-                    AttrItem { path: meta.path, args: meta.kind.mac_args(meta.span), tokens: None },
-                    None,
-                ),
-                span: at.span,
-                id: at.id,
-                style: at.style,
-            };
-        } else {
-            noop_visit_attribute(at, self)
-        }
-    }
-
     fn visit_id(&mut self, id: &mut ast::NodeId) {
         if self.monotonic {
             debug_assert_eq!(*id, ast::DUMMY_NODE_ID);
diff --git a/compiler/rustc_feature/src/active.rs b/compiler/rustc_feature/src/active.rs
index a84737e80a0..2054cd3e4c2 100644
--- a/compiler/rustc_feature/src/active.rs
+++ b/compiler/rustc_feature/src/active.rs
@@ -370,9 +370,6 @@ declare_features! (
     /// Allows `#[doc(masked)]`.
     (active, doc_masked, "1.21.0", Some(44027), None),
 
-    /// Allows `#[doc(include = "some-file")]`.
-    (active, external_doc, "1.22.0", Some(44732), None),
-
     /// Allows using `crate` as visibility modifier, synonymous with `pub(crate)`.
     (active, crate_visibility_modifier, "1.23.0", Some(53120), None),
 
diff --git a/compiler/rustc_feature/src/removed.rs b/compiler/rustc_feature/src/removed.rs
index 138398825af..71c10eb6507 100644
--- a/compiler/rustc_feature/src/removed.rs
+++ b/compiler/rustc_feature/src/removed.rs
@@ -140,6 +140,10 @@ declare_features! (
     (removed, const_fn, "1.54.0", Some(57563), None,
      Some("split into finer-grained feature gates")),
 
+    /// Allows `#[doc(include = "some-file")]`.
+    (removed, external_doc, "1.54.0", Some(44732), None,
+     Some("use #[doc = include_str!(\"filename\")] instead, which handles macro invocations")),
+
     // -------------------------------------------------------------------------
     // feature-group-end: removed features
     // -------------------------------------------------------------------------
diff --git a/compiler/rustc_lint/src/builtin.rs b/compiler/rustc_lint/src/builtin.rs
index e7275374b89..f6a84966f7a 100644
--- a/compiler/rustc_lint/src/builtin.rs
+++ b/compiler/rustc_lint/src/builtin.rs
@@ -489,7 +489,7 @@ fn has_doc(sess: &Session, attr: &ast::Attribute) -> bool {
 
     if let Some(list) = attr.meta_item_list() {
         for meta in list {
-            if meta.has_name(sym::include) || meta.has_name(sym::hidden) {
+            if meta.has_name(sym::hidden) {
                 return true;
             }
         }
diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs
index bf574bbfbb5..91b64611511 100644
--- a/compiler/rustc_passes/src/check_attr.rs
+++ b/compiler/rustc_passes/src/check_attr.rs
@@ -705,7 +705,7 @@ impl CheckAttrVisitor<'tcx> {
         let mut is_valid = true;
 
         if let Some(list) = attr.meta().and_then(|mi| mi.meta_item_list().map(|l| l.to_vec())) {
-            for meta in list {
+            for meta in &list {
                 if let Some(i_meta) = meta.meta_item() {
                     match i_meta.name_or_empty() {
                         sym::alias
@@ -757,7 +757,6 @@ impl CheckAttrVisitor<'tcx> {
                         | sym::html_no_source
                         | sym::html_playground_url
                         | sym::html_root_url
-                        | sym::include
                         | sym::inline
                         | sym::issue_tracker_base_url
                         | sym::keyword
@@ -792,6 +791,30 @@ impl CheckAttrVisitor<'tcx> {
                                         );
                                         diag.note("`doc(spotlight)` is now a no-op");
                                     }
+                                    if i_meta.has_name(sym::include) {
+                                        if let Some(value) = i_meta.value_str() {
+                                            // if there are multiple attributes, the suggestion would suggest deleting all of them, which is incorrect
+                                            let applicability = if list.len() == 1 {
+                                                Applicability::MachineApplicable
+                                            } else {
+                                                Applicability::MaybeIncorrect
+                                            };
+                                            let inner = if attr.style == AttrStyle::Inner {
+                                                "!"
+                                            } else {
+                                                ""
+                                            };
+                                            diag.span_suggestion(
+                                                attr.meta().unwrap().span,
+                                                "use `doc = include_str!` instead",
+                                                format!(
+                                                    "#{}[doc = include_str!(\"{}\")]",
+                                                    inner, value
+                                                ),
+                                                applicability,
+                                            );
+                                        }
+                                    }
                                     diag.emit();
                                 },
                             );
diff --git a/compiler/rustc_save_analysis/src/lib.rs b/compiler/rustc_save_analysis/src/lib.rs
index bdffdd5311e..524c352fef5 100644
--- a/compiler/rustc_save_analysis/src/lib.rs
+++ b/compiler/rustc_save_analysis/src/lib.rs
@@ -826,20 +826,6 @@ impl<'tcx> SaveContext<'tcx> {
                 // FIXME: Should save-analysis beautify doc strings itself or leave it to users?
                 result.push_str(&beautify_doc_string(val).as_str());
                 result.push('\n');
-            } else if self.tcx.sess.check_name(attr, sym::doc) {
-                if let Some(meta_list) = attr.meta_item_list() {
-                    meta_list
-                        .into_iter()
-                        .filter(|it| it.has_name(sym::include))
-                        .filter_map(|it| it.meta_item_list().map(|l| l.to_owned()))
-                        .flat_map(|it| it)
-                        .filter(|meta| meta.has_name(sym::contents))
-                        .filter_map(|meta| meta.value_str())
-                        .for_each(|val| {
-                            result.push_str(&val.as_str());
-                            result.push('\n');
-                        });
-                }
             }
         }
 
diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs
index a023edaca9e..a76d2d0a678 100644
--- a/library/core/src/lib.rs
+++ b/library/core/src/lib.rs
@@ -160,7 +160,6 @@
 #![feature(const_fn_transmute)]
 #![feature(abi_unadjusted)]
 #![feature(adx_target_feature)]
-#![feature(external_doc)]
 #![feature(associated_type_bounds)]
 #![feature(const_caller_location)]
 #![feature(slice_ptr_get)]
diff --git a/src/doc/rustdoc/src/documentation-tests.md b/src/doc/rustdoc/src/documentation-tests.md
index 8e3c6228189..cb7b85655b5 100644
--- a/src/doc/rustdoc/src/documentation-tests.md
+++ b/src/doc/rustdoc/src/documentation-tests.md
@@ -424,9 +424,7 @@ without including it in your main documentation. For example, you could write th
 `lib.rs` to test your README as part of your doctests:
 
 ```rust,no_run
-#![feature(external_doc)]
-
-#[doc(include = "../README.md")]
+#[doc = include_str!("../README.md")]
 #[cfg(doctest)]
 pub struct ReadmeDoctests;
 ```
diff --git a/src/doc/rustdoc/src/unstable-features.md b/src/doc/rustdoc/src/unstable-features.md
index 28b81a40265..e9b15666bb3 100644
--- a/src/doc/rustdoc/src/unstable-features.md
+++ b/src/doc/rustdoc/src/unstable-features.md
@@ -131,22 +131,6 @@ Book][unstable-masked] and [its tracking issue][issue-masked].
 [unstable-masked]: ../unstable-book/language-features/doc-masked.html
 [issue-masked]: https://github.com/rust-lang/rust/issues/44027
 
-### Include external files as API documentation
-
-As designed in [RFC 1990], Rustdoc can read an external file to use as a type's documentation. This
-is useful if certain documentation is so long that it would break the flow of reading the source.
-Instead of writing it all inline, writing `#[doc(include = "sometype.md")]` will ask Rustdoc to
-instead read that file and use it as if it were written inline.
-
-[RFC 1990]: https://github.com/rust-lang/rfcs/pull/1990
-
-`#[doc(include = "...")]` currently requires the `#![feature(external_doc)]` feature gate. For more
-information, see [its chapter in the Unstable Book][unstable-include] and [its tracking
-issue][issue-include].
-
-[unstable-include]: ../unstable-book/language-features/external-doc.html
-[issue-include]: https://github.com/rust-lang/rust/issues/44732
-
 ## Unstable command-line arguments
 
 These features are enabled by passing a command-line flag to Rustdoc, but the flags in question are
diff --git a/src/doc/unstable-book/src/language-features/external-doc.md b/src/doc/unstable-book/src/language-features/external-doc.md
deleted file mode 100644
index effae5d2999..00000000000
--- a/src/doc/unstable-book/src/language-features/external-doc.md
+++ /dev/null
@@ -1,40 +0,0 @@
-# `external_doc`
-
-The tracking issue for this feature is: [#44732]
-
-The `external_doc` feature allows the use of the `include` parameter to the `#[doc]` attribute, to
-include external files in documentation. Use the attribute in place of, or in addition to, regular
-doc comments and `#[doc]` attributes, and `rustdoc` will load the given file when it renders
-documentation for your crate.
-
-With the following files in the same directory:
-
-`external-doc.md`:
-
-```markdown
-# My Awesome Type
-
-This is the documentation for this spectacular type.
-```
-
-`lib.rs`:
-
-```no_run (needs-external-files)
-#![feature(external_doc)]
-
-#[doc(include = "external-doc.md")]
-pub struct MyAwesomeType;
-```
-
-`rustdoc` will load the file `external-doc.md` and use it as the documentation for the `MyAwesomeType`
-struct.
-
-When locating files, `rustdoc` will base paths in the `src/` directory, as if they were alongside the
-`lib.rs` for your crate. So if you want a `docs/` folder to live alongside the `src/` directory,
-start your paths with `../docs/` for `rustdoc` to properly find the file.
-
-This feature was proposed in [RFC #1990] and initially implemented in PR [#44781].
-
-[#44732]: https://github.com/rust-lang/rust/issues/44732
-[RFC #1990]: https://github.com/rust-lang/rfcs/pull/1990
-[#44781]: https://github.com/rust-lang/rust/pull/44781
diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs
index 3ae516bd6df..67397036cd3 100644
--- a/src/librustdoc/clean/types.rs
+++ b/src/librustdoc/clean/types.rs
@@ -809,7 +809,7 @@ impl AttributesExt for [ast::Attribute] {
                 // #[doc(...)]
                 if let Some(list) = attr.meta().as_ref().and_then(|mi| mi.meta_item_list()) {
                     for item in list {
-                        // #[doc(include)]
+                        // #[doc(hidden)]
                         if !item.has_name(sym::cfg) {
                             continue;
                         }
@@ -894,9 +894,6 @@ crate enum DocFragmentKind {
     SugaredDoc,
     /// A doc fragment created from a "raw" `#[doc=""]` attribute.
     RawDoc,
-    /// A doc fragment created from a `#[doc(include="filename")]` attribute. Contains both the
-    /// given filename and the file contents.
-    Include { filename: Symbol },
 }
 
 // The goal of this function is to apply the `DocFragment` transformations that are required when
@@ -930,18 +927,8 @@ impl<'a> FromIterator<&'a DocFragment> for String {
     where
         T: IntoIterator<Item = &'a DocFragment>,
     {
-        let mut prev_kind: Option<DocFragmentKind> = None;
         iter.into_iter().fold(String::new(), |mut acc, frag| {
-            if !acc.is_empty()
-                && prev_kind
-                    .take()
-                    .map(|p| matches!(p, DocFragmentKind::Include { .. }) && p != frag.kind)
-                    .unwrap_or(false)
-            {
-                acc.push('\n');
-            }
             add_doc_fragment(&mut acc, &frag);
-            prev_kind = Some(frag.kind);
             acc
         })
     }
@@ -988,45 +975,6 @@ impl Attributes {
         self.other_attrs.lists(name)
     }
 
-    /// Reads a `MetaItem` from within an attribute, looks for whether it is a
-    /// `#[doc(include="file")]`, and returns the filename and contents of the file as loaded from
-    /// its expansion.
-    crate fn extract_include(mi: &ast::MetaItem) -> Option<(Symbol, Symbol)> {
-        mi.meta_item_list().and_then(|list| {
-            for meta in list {
-                if meta.has_name(sym::include) {
-                    // the actual compiled `#[doc(include="filename")]` gets expanded to
-                    // `#[doc(include(file="filename", contents="file contents")]` so we need to
-                    // look for that instead
-                    return meta.meta_item_list().and_then(|list| {
-                        let mut filename: Option<Symbol> = None;
-                        let mut contents: Option<Symbol> = None;
-
-                        for it in list {
-                            if it.has_name(sym::file) {
-                                if let Some(name) = it.value_str() {
-                                    filename = Some(name);
-                                }
-                            } else if it.has_name(sym::contents) {
-                                if let Some(docs) = it.value_str() {
-                                    contents = Some(docs);
-                                }
-                            }
-                        }
-
-                        if let (Some(filename), Some(contents)) = (filename, contents) {
-                            Some((filename, contents))
-                        } else {
-                            None
-                        }
-                    });
-                }
-            }
-
-            None
-        })
-    }
-
     crate fn has_doc_flag(&self, flag: Symbol) -> bool {
         for attr in &self.other_attrs {
             if !attr.has_name(sym::doc) {
@@ -1050,18 +998,9 @@ impl Attributes {
         let mut doc_strings: Vec<DocFragment> = vec![];
         let mut doc_line = 0;
 
-        fn update_need_backline(doc_strings: &mut Vec<DocFragment>, frag: &DocFragment) {
+        fn update_need_backline(doc_strings: &mut Vec<DocFragment>) {
             if let Some(prev) = doc_strings.last_mut() {
-                if matches!(prev.kind, DocFragmentKind::Include { .. })
-                    || prev.kind != frag.kind
-                    || prev.parent_module != frag.parent_module
-                {
-                    // add a newline for extra padding between segments
-                    prev.need_backline = prev.kind == DocFragmentKind::SugaredDoc
-                        || prev.kind == DocFragmentKind::RawDoc
-                } else {
-                    prev.need_backline = true;
-                }
+                prev.need_backline = true;
             }
         }
 
@@ -1087,31 +1026,12 @@ impl Attributes {
                     indent: 0,
                 };
 
-                update_need_backline(&mut doc_strings, &frag);
+                update_need_backline(&mut doc_strings);
 
                 doc_strings.push(frag);
 
                 None
             } else {
-                if attr.has_name(sym::doc) {
-                    if let Some(mi) = attr.meta() {
-                        if let Some((filename, contents)) = Attributes::extract_include(&mi) {
-                            let line = doc_line;
-                            doc_line += contents.as_str().lines().count();
-                            let frag = DocFragment {
-                                line,
-                                span: attr.span,
-                                doc: contents,
-                                kind: DocFragmentKind::Include { filename },
-                                parent_module,
-                                need_backline: false,
-                                indent: 0,
-                            };
-                            update_need_backline(&mut doc_strings, &frag);
-                            doc_strings.push(frag);
-                        }
-                    }
-                }
                 Some(attr.clone())
             }
         };
@@ -1137,10 +1057,7 @@ impl Attributes {
         let mut out = String::new();
         add_doc_fragment(&mut out, &ori);
         while let Some(new_frag) = iter.next() {
-            if matches!(ori.kind, DocFragmentKind::Include { .. })
-                || new_frag.kind != ori.kind
-                || new_frag.parent_module != ori.parent_module
-            {
+            if new_frag.kind != ori.kind || new_frag.parent_module != ori.parent_module {
                 break;
             }
             add_doc_fragment(&mut out, &new_frag);
diff --git a/src/test/run-make-fulldeps/include_bytes_deps/main.rs b/src/test/run-make-fulldeps/include_bytes_deps/main.rs
index 00ad0eb8d50..2fd55699d44 100644
--- a/src/test/run-make-fulldeps/include_bytes_deps/main.rs
+++ b/src/test/run-make-fulldeps/include_bytes_deps/main.rs
@@ -1,6 +1,4 @@
-#![feature(external_doc)]
-
-#[doc(include="input.md")]
+#[doc = include_str!("input.md")]
 pub struct SomeStruct;
 
 pub fn main() {
diff --git a/src/test/run-make-fulldeps/save-analysis/foo.rs b/src/test/run-make-fulldeps/save-analysis/foo.rs
index 483eeed0b39..dd70675032f 100644
--- a/src/test/run-make-fulldeps/save-analysis/foo.rs
+++ b/src/test/run-make-fulldeps/save-analysis/foo.rs
@@ -2,7 +2,6 @@
 #![feature(box_syntax)]
 #![feature(rustc_private)]
 #![feature(associated_type_defaults)]
-#![feature(external_doc)]
 
 extern crate rustc_graphviz;
 // A simple rust project
@@ -454,9 +453,9 @@ impl Iterator for SilenceGenerator {
     }
 }
 
+#[doc = include_str!("extra-docs.md")]
+struct StructWithDocs;
+
 trait Foo {
     type Bar = FrameBuffer;
 }
-
-#[doc(include = "extra-docs.md")]
-struct StructWithDocs;
diff --git a/src/test/rustdoc-ui/doc-include-suggestion.rs b/src/test/rustdoc-ui/doc-include-suggestion.rs
new file mode 100644
index 00000000000..0c010073551
--- /dev/null
+++ b/src/test/rustdoc-ui/doc-include-suggestion.rs
@@ -0,0 +1,10 @@
+// check-pass
+
+#[doc(include = "external-cross-doc.md")]
+//~^ WARNING unknown `doc` attribute `include`
+//~| HELP use `doc = include_str!` instead
+// FIXME(#85497): make this a deny instead so it's more clear what's happening
+//~| NOTE on by default
+//~| WARNING previously accepted
+//~| NOTE see issue #82730
+pub struct NeedMoreDocs;
diff --git a/src/test/rustdoc-ui/doc-include-suggestion.stderr b/src/test/rustdoc-ui/doc-include-suggestion.stderr
new file mode 100644
index 00000000000..870b7efa2ac
--- /dev/null
+++ b/src/test/rustdoc-ui/doc-include-suggestion.stderr
@@ -0,0 +1,12 @@
+warning: unknown `doc` attribute `include`
+  --> $DIR/doc-include-suggestion.rs:3:7
+   |
+LL | #[doc(include = "external-cross-doc.md")]
+   | ------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-- help: use `doc = include_str!` instead: `#[doc = include_str!("external-cross-doc.md")]`
+   |
+   = note: `#[warn(invalid_doc_attributes)]` 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 #82730 <https://github.com/rust-lang/rust/issues/82730>
+
+warning: 1 warning emitted
+
diff --git a/src/test/rustdoc/auxiliary/external-cross-doc.md b/src/test/rustdoc/auxiliary/external-cross-doc.md
index 8b4e6edc699..d3c85326559 100644
--- a/src/test/rustdoc/auxiliary/external-cross-doc.md
+++ b/src/test/rustdoc/auxiliary/external-cross-doc.md
@@ -1,4 +1,4 @@
 # Cross-crate imported docs
 
-This file is to make sure `#[doc(include="file.md")]` works when you re-export an item with included
+This file is to make sure `#[doc = include_str!("file.md")]` works when you re-export an item with included
 docs.
diff --git a/src/test/rustdoc/auxiliary/external-cross.rs b/src/test/rustdoc/auxiliary/external-cross.rs
index 473d4ec99f0..5de63cdabc6 100644
--- a/src/test/rustdoc/auxiliary/external-cross.rs
+++ b/src/test/rustdoc/auxiliary/external-cross.rs
@@ -1,5 +1,3 @@
-#![feature(external_doc)]
-#![deny(missing_doc)]
-
-#[doc(include="external-cross-doc.md")]
+#[deny(missing_docs)]
+#[doc = include_str!("external-cross-doc.md")]
 pub struct NeedMoreDocs;
diff --git a/src/test/rustdoc/auxiliary/external-doc.md b/src/test/rustdoc/auxiliary/external-doc.md
index 38478c1635a..babde0a05ad 100644
--- a/src/test/rustdoc/auxiliary/external-doc.md
+++ b/src/test/rustdoc/auxiliary/external-doc.md
@@ -1,3 +1,3 @@
 # External Docs
 
-This file is here to test the `#[doc(include="file")]` attribute.
+This file is here to test the `#[doc = include_str!("file")]` attribute.
diff --git a/src/test/rustdoc/external-doc.rs b/src/test/rustdoc/external-doc.rs
index 0dadca551a9..fc29cb252e2 100644
--- a/src/test/rustdoc/external-doc.rs
+++ b/src/test/rustdoc/external-doc.rs
@@ -1,12 +1,3 @@
-#![feature(external_doc)]
-
-// @has external_doc/struct.CanHasDocs.html
-// @has - '//h1' 'External Docs'
-// @has - '//h2' 'Inline Docs'
-#[doc(include = "auxiliary/external-doc.md")]
-/// ## Inline Docs
-pub struct CanHasDocs;
-
 // @has external_doc/struct.IncludeStrDocs.html
 // @has - '//h1' 'External Docs'
 // @has - '//h2' 'Inline Docs'
diff --git a/src/test/rustdoc/unindent.rs b/src/test/rustdoc/unindent.rs
index d10e1ec89c5..372af5f4672 100644
--- a/src/test/rustdoc/unindent.rs
+++ b/src/test/rustdoc/unindent.rs
@@ -1,5 +1,3 @@
-#![feature(external_doc)]
-
 #![crate_name = "foo"]
 
 // @has foo/struct.Example.html
@@ -51,7 +49,7 @@ pub struct I;
 // @matches - '//div[@class="docblock"]/p' '(?m)a\nno whitespace\nJust some text.\Z'
 ///a
 ///no whitespace
-#[doc(include = "unindent.md")]
+#[doc = include_str!("unindent.md")]
 pub struct J;
 
 // @has foo/struct.K.html
@@ -60,5 +58,5 @@ pub struct J;
 ///
 ///    4 whitespaces!
 ///
-#[doc(include = "unindent.md")]
+#[doc = include_str!("unindent.md")]
 pub struct K;
diff --git a/src/test/ui/extern/external-doc-error.rs b/src/test/ui/extern/external-doc-error.rs
deleted file mode 100644
index 4e89f7464da..00000000000
--- a/src/test/ui/extern/external-doc-error.rs
+++ /dev/null
@@ -1,31 +0,0 @@
-// normalize-stderr-test: "not-a-file.md:.*\(" -> "not-a-file.md: $$FILE_NOT_FOUND_MSG ("
-
-#![feature(external_doc)]
-
-#[doc(include = "not-a-file.md")]
-pub struct SomeStruct; //~^ ERROR couldn't read
-
-#[doc(include = "auxiliary/invalid-utf8.txt")]
-pub struct InvalidUtf8; //~^ ERROR wasn't a utf-8 file
-
-#[doc(include)]
-pub struct MissingPath; //~^ ERROR expected path
-                        //~| HELP provide a file path with `=`
-                        //~| SUGGESTION include = "<path>"
-
-#[doc(include("../README.md"))]
-pub struct InvalidPathSyntax; //~^ ERROR expected path
-                              //~| HELP provide a file path with `=`
-                              //~| SUGGESTION include = "../README.md"
-
-#[doc(include = 123)]
-pub struct InvalidPathType; //~^ ERROR expected path
-                            //~| HELP provide a file path with `=`
-                            //~| SUGGESTION include = "<path>"
-
-#[doc(include(123))]
-pub struct InvalidPathSyntaxAndType; //~^ ERROR expected path
-                                     //~| HELP provide a file path with `=`
-                                     //~| SUGGESTION include = "<path>"
-
-fn main() {}
diff --git a/src/test/ui/extern/external-doc-error.stderr b/src/test/ui/extern/external-doc-error.stderr
deleted file mode 100644
index b180cd66c52..00000000000
--- a/src/test/ui/extern/external-doc-error.stderr
+++ /dev/null
@@ -1,38 +0,0 @@
-error: couldn't read $DIR/not-a-file.md: $FILE_NOT_FOUND_MSG (os error 2)
-  --> $DIR/external-doc-error.rs:5:17
-   |
-LL | #[doc(include = "not-a-file.md")]
-   |                 ^^^^^^^^^^^^^^^ couldn't read file
-
-error: $DIR/auxiliary/invalid-utf8.txt wasn't a utf-8 file
-  --> $DIR/external-doc-error.rs:8:17
-   |
-LL | #[doc(include = "auxiliary/invalid-utf8.txt")]
-   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ contains invalid utf-8
-
-error: expected path to external documentation
-  --> $DIR/external-doc-error.rs:11:7
-   |
-LL | #[doc(include)]
-   |       ^^^^^^^ help: provide a file path with `=`: `include = "<path>"`
-
-error: expected path to external documentation
-  --> $DIR/external-doc-error.rs:16:7
-   |
-LL | #[doc(include("../README.md"))]
-   |       ^^^^^^^^^^^^^^^^^^^^^^^ help: provide a file path with `=`: `include = "../README.md"`
-
-error: expected path to external documentation
-  --> $DIR/external-doc-error.rs:21:7
-   |
-LL | #[doc(include = 123)]
-   |       ^^^^^^^^^^^^^ help: provide a file path with `=`: `include = "<path>"`
-
-error: expected path to external documentation
-  --> $DIR/external-doc-error.rs:26:7
-   |
-LL | #[doc(include(123))]
-   |       ^^^^^^^^^^^^ help: provide a file path with `=`: `include = "<path>"`
-
-error: aborting due to 6 previous errors
-
diff --git a/src/test/ui/feature-gates/feature-gate-external_doc.rs b/src/test/ui/feature-gates/feature-gate-external_doc.rs
deleted file mode 100644
index 4e6e293846c..00000000000
--- a/src/test/ui/feature-gates/feature-gate-external_doc.rs
+++ /dev/null
@@ -1,3 +0,0 @@
-#[doc(include="asdf.md")] //~ ERROR: `#[doc(include)]` is experimental
-                          //~| ERROR: `#[doc(include)]` is experimental
-fn main() {}
diff --git a/src/test/ui/feature-gates/feature-gate-external_doc.stderr b/src/test/ui/feature-gates/feature-gate-external_doc.stderr
deleted file mode 100644
index bd2aefe90c1..00000000000
--- a/src/test/ui/feature-gates/feature-gate-external_doc.stderr
+++ /dev/null
@@ -1,21 +0,0 @@
-error[E0658]: `#[doc(include)]` is experimental
-  --> $DIR/feature-gate-external_doc.rs:1:1
-   |
-LL | #[doc(include="asdf.md")]
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^
-   |
-   = note: see issue #44732 <https://github.com/rust-lang/rust/issues/44732> for more information
-   = help: add `#![feature(external_doc)]` to the crate attributes to enable
-
-error[E0658]: `#[doc(include)]` is experimental
-  --> $DIR/feature-gate-external_doc.rs:1:1
-   |
-LL | #[doc(include="asdf.md")]
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^
-   |
-   = note: see issue #44732 <https://github.com/rust-lang/rust/issues/44732> for more information
-   = help: add `#![feature(external_doc)]` to the crate attributes to enable
-
-error: aborting due to 2 previous errors
-
-For more information about this error, try `rustc --explain E0658`.
diff --git a/src/tools/clippy/clippy_lints/src/missing_doc.rs b/src/tools/clippy/clippy_lints/src/missing_doc.rs
index ec1572c26c2..a46a7407df0 100644
--- a/src/tools/clippy/clippy_lints/src/missing_doc.rs
+++ b/src/tools/clippy/clippy_lints/src/missing_doc.rs
@@ -7,8 +7,7 @@
 
 use clippy_utils::attrs::is_doc_hidden;
 use clippy_utils::diagnostics::span_lint;
-use if_chain::if_chain;
-use rustc_ast::ast::{self, MetaItem, MetaItemKind};
+use rustc_ast::ast;
 use rustc_hir as hir;
 use rustc_lint::{LateContext, LateLintPass, LintContext};
 use rustc_middle::ty;
@@ -56,20 +55,6 @@ impl MissingDoc {
         *self.doc_hidden_stack.last().expect("empty doc_hidden_stack")
     }
 
-    fn has_include(meta: Option<MetaItem>) -> bool {
-        if_chain! {
-            if let Some(meta) = meta;
-            if let MetaItemKind::List(list) = meta.kind;
-            if let Some(meta) = list.get(0);
-            if let Some(name) = meta.ident();
-            then {
-                name.name == sym::include
-            } else {
-                false
-            }
-        }
-    }
-
     fn check_missing_docs_attrs(
         &self,
         cx: &LateContext<'_>,
@@ -95,7 +80,7 @@ impl MissingDoc {
 
         let has_doc = attrs
             .iter()
-            .any(|a| a.doc_str().is_some() || Self::has_include(a.meta()));
+            .any(|a| a.doc_str().is_some());
         if !has_doc {
             span_lint(
                 cx,
diff --git a/src/tools/clippy/tests/ui/missing-doc-crate.rs b/src/tools/clippy/tests/ui/missing-doc-crate.rs
index 04711f86488..e00c7fbfed1 100644
--- a/src/tools/clippy/tests/ui/missing-doc-crate.rs
+++ b/src/tools/clippy/tests/ui/missing-doc-crate.rs
@@ -1,5 +1,4 @@
 #![warn(clippy::missing_docs_in_private_items)]
-#![feature(external_doc)]
-#![doc(include = "../../README.md")]
+#![doc = include_str!("../../README.md")]
 
 fn main() {}