1
Fork 0

Auto merge of #85457 - jyn514:remove-doc-include, r=GuillaumeGomez

Remove `doc(include)`

This nightly feature is redundant now that `extended_key_value_attributes` is stable (https://github.com/rust-lang/rust/pull/83366). `@rust-lang/rustdoc` not sure if you think this needs FCP; there was already an FCP in #82539, but technically it was for deprecating, not removing the feature altogether.

This should not be merged before #83366.

cc `@petrochenkov`
This commit is contained in:
bors 2021-06-05 03:36:26 +00:00
commit 2c106885d5
28 changed files with 79 additions and 449 deletions

View file

@ -318,7 +318,6 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> {
}} }}
gate_doc!( gate_doc!(
include => external_doc
cfg => doc_cfg cfg => doc_cfg
masked => doc_masked masked => doc_masked
notable_trait => doc_notable_trait notable_trait => doc_notable_trait

View file

@ -1068,11 +1068,11 @@ impl<'a> ExtCtxt<'a> {
self.resolver.check_unused_macros(); 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( pub fn resolve_path(
&self, &self,
path: impl Into<PathBuf>, path: impl Into<PathBuf>,

View file

@ -12,11 +12,11 @@ use rustc_ast::ptr::P;
use rustc_ast::token; use rustc_ast::token;
use rustc_ast::tokenstream::TokenStream; use rustc_ast::tokenstream::TokenStream;
use rustc_ast::visit::{self, AssocCtxt, Visitor}; 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::{MacCallStmt, MacStmtStyle, MetaItemKind, ModKind, NestedMetaItem};
use rustc_ast::{NodeId, PatKind, Path, StmtKind, Unsafe}; use rustc_ast::{NodeId, PatKind, Path, StmtKind, Unsafe};
use rustc_ast_pretty::pprust; 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::map_in_place::MapInPlace;
use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_data_structures::stack::ensure_sufficient_stack;
use rustc_data_structures::sync::Lrc; 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::lint::BuiltinLintDiagnostics;
use rustc_session::parse::{feature_err, ParseSess}; use rustc_session::parse::{feature_err, ParseSess};
use rustc_session::Limit; use rustc_session::Limit;
use rustc_span::symbol::{sym, Ident, Symbol}; use rustc_span::symbol::{sym, Ident};
use rustc_span::{ExpnId, FileName, Span, DUMMY_SP}; use rustc_span::{ExpnId, FileName, Span};
use smallvec::{smallvec, SmallVec}; use smallvec::{smallvec, SmallVec};
use std::io::ErrorKind;
use std::ops::DerefMut; use std::ops::DerefMut;
use std::path::PathBuf; use std::path::PathBuf;
use std::rc::Rc; use std::rc::Rc;
use std::{iter, mem, slice}; use std::{iter, mem};
macro_rules! ast_fragments { macro_rules! ast_fragments {
( (
@ -1524,139 +1523,6 @@ impl<'a, 'b> MutVisitor for InvocationCollector<'a, 'b> {
noop_flat_map_generic_param(param, self) 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) { fn visit_id(&mut self, id: &mut ast::NodeId) {
if self.monotonic { if self.monotonic {
debug_assert_eq!(*id, ast::DUMMY_NODE_ID); debug_assert_eq!(*id, ast::DUMMY_NODE_ID);

View file

@ -370,9 +370,6 @@ declare_features! (
/// Allows `#[doc(masked)]`. /// Allows `#[doc(masked)]`.
(active, doc_masked, "1.21.0", Some(44027), None), (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)`. /// Allows using `crate` as visibility modifier, synonymous with `pub(crate)`.
(active, crate_visibility_modifier, "1.23.0", Some(53120), None), (active, crate_visibility_modifier, "1.23.0", Some(53120), None),

View file

@ -140,6 +140,10 @@ declare_features! (
(removed, const_fn, "1.54.0", Some(57563), None, (removed, const_fn, "1.54.0", Some(57563), None,
Some("split into finer-grained feature gates")), 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 // feature-group-end: removed features
// ------------------------------------------------------------------------- // -------------------------------------------------------------------------

View file

@ -489,7 +489,7 @@ fn has_doc(sess: &Session, attr: &ast::Attribute) -> bool {
if let Some(list) = attr.meta_item_list() { if let Some(list) = attr.meta_item_list() {
for meta in list { for meta in list {
if meta.has_name(sym::include) || meta.has_name(sym::hidden) { if meta.has_name(sym::hidden) {
return true; return true;
} }
} }

View file

@ -705,7 +705,7 @@ impl CheckAttrVisitor<'tcx> {
let mut is_valid = true; let mut is_valid = true;
if let Some(list) = attr.meta().and_then(|mi| mi.meta_item_list().map(|l| l.to_vec())) { 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() { if let Some(i_meta) = meta.meta_item() {
match i_meta.name_or_empty() { match i_meta.name_or_empty() {
sym::alias sym::alias
@ -757,7 +757,6 @@ impl CheckAttrVisitor<'tcx> {
| sym::html_no_source | sym::html_no_source
| sym::html_playground_url | sym::html_playground_url
| sym::html_root_url | sym::html_root_url
| sym::include
| sym::inline | sym::inline
| sym::issue_tracker_base_url | sym::issue_tracker_base_url
| sym::keyword | sym::keyword
@ -792,6 +791,30 @@ impl CheckAttrVisitor<'tcx> {
); );
diag.note("`doc(spotlight)` is now a no-op"); 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(); diag.emit();
}, },
); );

View file

@ -826,20 +826,6 @@ impl<'tcx> SaveContext<'tcx> {
// FIXME: Should save-analysis beautify doc strings itself or leave it to users? // FIXME: Should save-analysis beautify doc strings itself or leave it to users?
result.push_str(&beautify_doc_string(val).as_str()); result.push_str(&beautify_doc_string(val).as_str());
result.push('\n'); 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');
});
}
} }
} }

View file

@ -159,7 +159,6 @@
#![feature(const_fn_transmute)] #![feature(const_fn_transmute)]
#![feature(abi_unadjusted)] #![feature(abi_unadjusted)]
#![feature(adx_target_feature)] #![feature(adx_target_feature)]
#![feature(external_doc)]
#![feature(associated_type_bounds)] #![feature(associated_type_bounds)]
#![feature(const_caller_location)] #![feature(const_caller_location)]
#![feature(slice_ptr_get)] #![feature(slice_ptr_get)]

View file

@ -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: `lib.rs` to test your README as part of your doctests:
```rust,no_run ```rust,no_run
#![feature(external_doc)] #[doc = include_str!("../README.md")]
#[doc(include = "../README.md")]
#[cfg(doctest)] #[cfg(doctest)]
pub struct ReadmeDoctests; pub struct ReadmeDoctests;
``` ```

View file

@ -131,22 +131,6 @@ Book][unstable-masked] and [its tracking issue][issue-masked].
[unstable-masked]: ../unstable-book/language-features/doc-masked.html [unstable-masked]: ../unstable-book/language-features/doc-masked.html
[issue-masked]: https://github.com/rust-lang/rust/issues/44027 [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 ## Unstable command-line arguments
These features are enabled by passing a command-line flag to Rustdoc, but the flags in question are These features are enabled by passing a command-line flag to Rustdoc, but the flags in question are

View file

@ -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

View file

@ -809,7 +809,7 @@ impl AttributesExt for [ast::Attribute] {
// #[doc(...)] // #[doc(...)]
if let Some(list) = attr.meta().as_ref().and_then(|mi| mi.meta_item_list()) { if let Some(list) = attr.meta().as_ref().and_then(|mi| mi.meta_item_list()) {
for item in list { for item in list {
// #[doc(include)] // #[doc(hidden)]
if !item.has_name(sym::cfg) { if !item.has_name(sym::cfg) {
continue; continue;
} }
@ -894,9 +894,6 @@ crate enum DocFragmentKind {
SugaredDoc, SugaredDoc,
/// A doc fragment created from a "raw" `#[doc=""]` attribute. /// A doc fragment created from a "raw" `#[doc=""]` attribute.
RawDoc, 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 // 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 where
T: IntoIterator<Item = &'a DocFragment>, T: IntoIterator<Item = &'a DocFragment>,
{ {
let mut prev_kind: Option<DocFragmentKind> = None;
iter.into_iter().fold(String::new(), |mut acc, frag| { 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); add_doc_fragment(&mut acc, &frag);
prev_kind = Some(frag.kind);
acc acc
}) })
} }
@ -988,45 +975,6 @@ impl Attributes {
self.other_attrs.lists(name) 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 { crate fn has_doc_flag(&self, flag: Symbol) -> bool {
for attr in &self.other_attrs { for attr in &self.other_attrs {
if !attr.has_name(sym::doc) { if !attr.has_name(sym::doc) {
@ -1050,18 +998,9 @@ impl Attributes {
let mut doc_strings: Vec<DocFragment> = vec![]; let mut doc_strings: Vec<DocFragment> = vec![];
let mut doc_line = 0; 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 let Some(prev) = doc_strings.last_mut() {
if matches!(prev.kind, DocFragmentKind::Include { .. }) prev.need_backline = true;
|| 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;
}
} }
} }
@ -1087,31 +1026,12 @@ impl Attributes {
indent: 0, indent: 0,
}; };
update_need_backline(&mut doc_strings, &frag); update_need_backline(&mut doc_strings);
doc_strings.push(frag); doc_strings.push(frag);
None None
} else { } 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()) Some(attr.clone())
} }
}; };
@ -1137,10 +1057,7 @@ impl Attributes {
let mut out = String::new(); let mut out = String::new();
add_doc_fragment(&mut out, &ori); add_doc_fragment(&mut out, &ori);
while let Some(new_frag) = iter.next() { while let Some(new_frag) = iter.next() {
if matches!(ori.kind, DocFragmentKind::Include { .. }) if new_frag.kind != ori.kind || new_frag.parent_module != ori.parent_module {
|| new_frag.kind != ori.kind
|| new_frag.parent_module != ori.parent_module
{
break; break;
} }
add_doc_fragment(&mut out, &new_frag); add_doc_fragment(&mut out, &new_frag);

View file

@ -1,6 +1,4 @@
#![feature(external_doc)] #[doc = include_str!("input.md")]
#[doc(include="input.md")]
pub struct SomeStruct; pub struct SomeStruct;
pub fn main() { pub fn main() {

View file

@ -2,7 +2,6 @@
#![feature(box_syntax)] #![feature(box_syntax)]
#![feature(rustc_private)] #![feature(rustc_private)]
#![feature(associated_type_defaults)] #![feature(associated_type_defaults)]
#![feature(external_doc)]
extern crate rustc_graphviz; extern crate rustc_graphviz;
// A simple rust project // A simple rust project
@ -454,9 +453,9 @@ impl Iterator for SilenceGenerator {
} }
} }
#[doc = include_str!("extra-docs.md")]
struct StructWithDocs;
trait Foo { trait Foo {
type Bar = FrameBuffer; type Bar = FrameBuffer;
} }
#[doc(include = "extra-docs.md")]
struct StructWithDocs;

View file

@ -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;

View file

@ -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

View file

@ -1,4 +1,4 @@
# Cross-crate imported docs # 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. docs.

View file

@ -1,5 +1,3 @@
#![feature(external_doc)] #[deny(missing_docs)]
#![deny(missing_doc)] #[doc = include_str!("external-cross-doc.md")]
#[doc(include="external-cross-doc.md")]
pub struct NeedMoreDocs; pub struct NeedMoreDocs;

View file

@ -1,3 +1,3 @@
# External Docs # 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.

View file

@ -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 external_doc/struct.IncludeStrDocs.html
// @has - '//h1' 'External Docs' // @has - '//h1' 'External Docs'
// @has - '//h2' 'Inline Docs' // @has - '//h2' 'Inline Docs'

View file

@ -1,5 +1,3 @@
#![feature(external_doc)]
#![crate_name = "foo"] #![crate_name = "foo"]
// @has foo/struct.Example.html // @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' // @matches - '//div[@class="docblock"]/p' '(?m)a\nno whitespace\nJust some text.\Z'
///a ///a
///no whitespace ///no whitespace
#[doc(include = "unindent.md")] #[doc = include_str!("unindent.md")]
pub struct J; pub struct J;
// @has foo/struct.K.html // @has foo/struct.K.html
@ -60,5 +58,5 @@ pub struct J;
/// ///
/// 4 whitespaces! /// 4 whitespaces!
/// ///
#[doc(include = "unindent.md")] #[doc = include_str!("unindent.md")]
pub struct K; pub struct K;

View file

@ -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() {}

View file

@ -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

View file

@ -1,3 +0,0 @@
#[doc(include="asdf.md")] //~ ERROR: `#[doc(include)]` is experimental
//~| ERROR: `#[doc(include)]` is experimental
fn main() {}

View file

@ -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`.

View file

@ -7,8 +7,7 @@
use clippy_utils::attrs::is_doc_hidden; use clippy_utils::attrs::is_doc_hidden;
use clippy_utils::diagnostics::span_lint; use clippy_utils::diagnostics::span_lint;
use if_chain::if_chain; use rustc_ast::ast;
use rustc_ast::ast::{self, MetaItem, MetaItemKind};
use rustc_hir as hir; use rustc_hir as hir;
use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_lint::{LateContext, LateLintPass, LintContext};
use rustc_middle::ty; use rustc_middle::ty;
@ -56,20 +55,6 @@ impl MissingDoc {
*self.doc_hidden_stack.last().expect("empty doc_hidden_stack") *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( fn check_missing_docs_attrs(
&self, &self,
cx: &LateContext<'_>, cx: &LateContext<'_>,
@ -95,7 +80,7 @@ impl MissingDoc {
let has_doc = attrs let has_doc = attrs
.iter() .iter()
.any(|a| a.doc_str().is_some() || Self::has_include(a.meta())); .any(|a| a.doc_str().is_some());
if !has_doc { if !has_doc {
span_lint( span_lint(
cx, cx,

View file

@ -1,5 +1,4 @@
#![warn(clippy::missing_docs_in_private_items)] #![warn(clippy::missing_docs_in_private_items)]
#![feature(external_doc)] #![doc = include_str!("../../README.md")]
#![doc(include = "../../README.md")]
fn main() {} fn main() {}