From e9ae64cca7fd5f88b8c94dbb464594ca13f26792 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Wed, 20 May 2020 12:02:52 +0200 Subject: [PATCH 1/8] Improve E0599 explanation --- src/librustc_error_codes/error_codes/E0599.md | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/librustc_error_codes/error_codes/E0599.md b/src/librustc_error_codes/error_codes/E0599.md index c44e60571e1e3..5b1590b29998f 100644 --- a/src/librustc_error_codes/error_codes/E0599.md +++ b/src/librustc_error_codes/error_codes/E0599.md @@ -9,3 +9,18 @@ let x = Mouth; x.chocolate(); // error: no method named `chocolate` found for type `Mouth` // in the current scope ``` + +In this case, you need to implement the `chocolate` method to fix the error: + +``` +struct Mouth; + +impl Mouth { + fn chocolate(&self) { // We implement the `chocolate` method here. + println!("Hmmm! I love chocolate!"); + } +} + +let x = Mouth; +x.chocolate(); // ok! +``` From 93abdd75114dd16e1297370603754fd2ea1e0964 Mon Sep 17 00:00:00 2001 From: Santiago Pastorino Date: Wed, 20 May 2020 12:15:43 -0300 Subject: [PATCH 2/8] Add some teams to prioritization exclude_labels --- triagebot.toml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/triagebot.toml b/triagebot.toml index 2210a8ff8e656..f12d516376322 100644 --- a/triagebot.toml +++ b/triagebot.toml @@ -37,5 +37,9 @@ label = "ICEBreaker-Cleanup-Crew" [prioritize] label = "I-prioritize" prioritize_on = ["regression-from-stable-to-stable", "regression-from-stable-to-beta", "regression-from-stable-to-nightly"] -priority_labels = "P-*" +exclude_labels = [ + "P-*", + "T-infra", + "T-release", +] zulip_stream = 227806 From 6a3aae8aea2448432d4fd0f7c3a22778beaab831 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Thu, 21 May 2020 19:48:44 -0700 Subject: [PATCH 3/8] Add flag to open docs: x.py doc --open Tested with: # opens doc/index.html x.py doc --stage 0 --open x.py doc --stage 0 --open src/doc # opens doc/book/index.html x.py doc --stage 0 --open src/doc/book # opens doc/std/index.html x.py doc --stage 0 --open src/libstd # opens doc/proc_macro/index.html x.py doc --stage 0 --open src/libproc_macro # opens both x.py doc --stage 0 --open src/libstd src/libproc_macro --- Cargo.lock | 1 + src/bootstrap/Cargo.toml | 1 + src/bootstrap/builder.rs | 2 +- src/bootstrap/doc.rs | 59 +++++++++++++++++++++++++++++++++++++++- src/bootstrap/flags.rs | 14 +++++++++- 5 files changed, 74 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d81fd6e8d3afd..b0b9e2c27df9c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -213,6 +213,7 @@ dependencies = [ "lazy_static 1.4.0", "libc", "num_cpus", + "opener", "pretty_assertions", "serde", "serde_json", diff --git a/src/bootstrap/Cargo.toml b/src/bootstrap/Cargo.toml index f7856f6a7fcb1..c4918d7f2e714 100644 --- a/src/bootstrap/Cargo.toml +++ b/src/bootstrap/Cargo.toml @@ -48,6 +48,7 @@ toml = "0.5" lazy_static = "1.3.0" time = "0.1" ignore = "0.4.10" +opener = "0.4" [target.'cfg(windows)'.dependencies.winapi] version = "0.3" diff --git a/src/bootstrap/builder.rs b/src/bootstrap/builder.rs index 4bc81a7b42dc0..5489b1bc66b64 100644 --- a/src/bootstrap/builder.rs +++ b/src/bootstrap/builder.rs @@ -503,7 +503,7 @@ impl<'a> Builder<'a> { Subcommand::Check { ref paths } => (Kind::Check, &paths[..]), Subcommand::Clippy { ref paths } => (Kind::Clippy, &paths[..]), Subcommand::Fix { ref paths } => (Kind::Fix, &paths[..]), - Subcommand::Doc { ref paths } => (Kind::Doc, &paths[..]), + Subcommand::Doc { ref paths, .. } => (Kind::Doc, &paths[..]), Subcommand::Test { ref paths, .. } => (Kind::Test, &paths[..]), Subcommand::Bench { ref paths, .. } => (Kind::Bench, &paths[..]), Subcommand::Dist { ref paths } => (Kind::Dist, &paths[..]), diff --git a/src/bootstrap/doc.rs b/src/bootstrap/doc.rs index 7eab92ddc92a9..be5e3bfee303f 100644 --- a/src/bootstrap/doc.rs +++ b/src/bootstrap/doc.rs @@ -70,6 +70,35 @@ book!( RustdocBook, "src/doc/rustdoc", "rustdoc"; ); +fn open(builder: &Builder<'_>, path: impl AsRef) { + if builder.config.dry_run || !builder.config.cmd.open() { + return; + } + + let path = path.as_ref(); + builder.info(&format!("Opening doc {}", path.to_string_lossy())); + + // ignore error + let _ = opener::open(path); +} + +// "src/libstd" -> ["src", "libstd"] +// +// Used for deciding whether a particular step is one requested by the user on +// the `x.py doc` command line, which determines whether `--open` will open that +// page. +fn components_simplified(path: &PathBuf) -> Vec<&str> { + path.iter().map(|component| component.to_str().unwrap_or("???")).collect() +} + +fn is_explicit_request(builder: &Builder<'_>, path: &str) -> bool { + builder + .paths + .iter() + .map(components_simplified) + .any(|requested| requested.iter().copied().eq(path.split("/"))) +} + #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] pub struct UnstableBook { target: Interned, @@ -200,6 +229,12 @@ impl Step for TheBook { invoke_rustdoc(builder, compiler, target, path); } + + if is_explicit_request(builder, "src/doc/book") { + let out = builder.doc_out(target); + let index = out.join("book").join("index.html"); + open(builder, &index); + } } } @@ -338,6 +373,13 @@ impl Step for Standalone { } builder.run(&mut cmd); } + + // We open doc/index.html as the default if invoked as `x.py doc --open` + // with no particular explicit doc requested (e.g. src/libcore). + if builder.paths.is_empty() || is_explicit_request(builder, "src/doc") { + let index = out.join("index.html"); + open(builder, &index); + } } } @@ -418,10 +460,25 @@ impl Step for Std { builder.run(&mut cargo.into()); }; - for krate in &["alloc", "core", "std", "proc_macro", "test"] { + let krates = ["alloc", "core", "std", "proc_macro", "test"]; + for krate in &krates { run_cargo_rustdoc_for(krate); } builder.cp_r(&my_out, &out); + + // Look for src/libstd, src/libcore etc in the `x.py doc` arguments and + // open the corresponding rendered docs. + for path in builder.paths.iter().map(components_simplified) { + if path.get(0) == Some(&"src") + && path.get(1).map_or(false, |dir| dir.starts_with("lib")) + { + let requested_crate = &path[1][3..]; + if krates.contains(&requested_crate) { + let index = out.join(requested_crate).join("index.html"); + open(builder, &index); + } + } + } } } diff --git a/src/bootstrap/flags.rs b/src/bootstrap/flags.rs index 646b9e05d99c3..cfaa43f397095 100644 --- a/src/bootstrap/flags.rs +++ b/src/bootstrap/flags.rs @@ -61,6 +61,7 @@ pub enum Subcommand { }, Doc { paths: Vec, + open: bool, }, Test { paths: Vec, @@ -248,6 +249,9 @@ To learn more about a subcommand, run `./x.py -h`", "bench" => { opts.optmulti("", "test-args", "extra arguments", "ARGS"); } + "doc" => { + opts.optflag("", "open", "open the docs in a browser"); + } "clean" => { opts.optflag("", "all", "clean all build artifacts"); } @@ -404,6 +408,7 @@ Arguments: ./x.py doc src/doc/book ./x.py doc src/doc/nomicon ./x.py doc src/doc/book src/libstd + ./x.py doc src/libstd --open If no arguments are passed then everything is documented: @@ -479,7 +484,7 @@ Arguments: }, }, "bench" => Subcommand::Bench { paths, test_args: matches.opt_strs("test-args") }, - "doc" => Subcommand::Doc { paths }, + "doc" => Subcommand::Doc { paths, open: matches.opt_present("open") }, "clean" => { if !paths.is_empty() { println!("\nclean does not take a path argument\n"); @@ -613,6 +618,13 @@ impl Subcommand { _ => None, } } + + pub fn open(&self) -> bool { + match *self { + Subcommand::Doc { open, .. } => open, + _ => false, + } + } } fn split(s: &[String]) -> Vec { From 9ff502029d379bebc50cf92a97f019fefba09d79 Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Fri, 22 May 2020 10:07:46 +0200 Subject: [PATCH 4/8] Add core::future::IntoFuture This patch adds `core::future::IntoFuture`. However unlike earlier PRs this patch does not integrate it into the `async/.await` lowering. That integration should be done in a follow-up PR. --- src/libcore/future/into_future.rs | 27 +++++++++++++++++++++++++++ src/libcore/future/mod.rs | 4 ++++ src/libstd/future.rs | 14 +++++++++++++- src/libstd/lib.rs | 3 +++ 4 files changed, 47 insertions(+), 1 deletion(-) create mode 100644 src/libcore/future/into_future.rs diff --git a/src/libcore/future/into_future.rs b/src/libcore/future/into_future.rs new file mode 100644 index 0000000000000..4020c254446e3 --- /dev/null +++ b/src/libcore/future/into_future.rs @@ -0,0 +1,27 @@ +use crate::future::Future; + +/// Conversion into a `Future`. +#[unstable(feature = "into_future", issue = "67644")] +pub trait IntoFuture { + /// The output that the future will produce on completion. + #[unstable(feature = "into_future", issue = "67644")] + type Output; + + /// Which kind of future are we turning this into? + #[unstable(feature = "into_future", issue = "67644")] + type Future: Future; + + /// Creates a future from a value. + #[unstable(feature = "into_future", issue = "67644")] + fn into_future(self) -> Self::Future; +} + +#[unstable(feature = "into_future", issue = "67644")] +impl IntoFuture for F { + type Output = F::Output; + type Future = F; + + fn into_future(self) -> Self::Future { + self + } +} diff --git a/src/libcore/future/mod.rs b/src/libcore/future/mod.rs index b5a102916a07e..6f6009b47e672 100644 --- a/src/libcore/future/mod.rs +++ b/src/libcore/future/mod.rs @@ -10,12 +10,16 @@ use crate::{ }; mod future; +mod into_future; mod pending; mod ready; #[stable(feature = "futures_api", since = "1.36.0")] pub use self::future::Future; +#[unstable(feature = "into_future", issue = "67644")] +pub use into_future::IntoFuture; + #[unstable(feature = "future_readiness_fns", issue = "70921")] pub use pending::{pending, Pending}; #[unstable(feature = "future_readiness_fns", issue = "70921")] diff --git a/src/libstd/future.rs b/src/libstd/future.rs index e2092cfefa369..89dd9fb9b2cd5 100644 --- a/src/libstd/future.rs +++ b/src/libstd/future.rs @@ -2,4 +2,16 @@ #[doc(inline)] #[stable(feature = "futures_api", since = "1.36.0")] -pub use core::future::*; +pub use core::future::Future; + +#[doc(inline)] +#[unstable(feature = "gen_future", issue = "50547")] +pub use core::future::{from_generator, get_context, ResumeTy}; + +#[doc(inline)] +#[unstable(feature = "future_readiness_fns", issue = "70921")] +pub use core::future::{pending, ready, Pending, Ready}; + +#[doc(inline)] +#[unstable(feature = "into_future", issue = "67644")] +pub use core::future::IntoFuture; diff --git a/src/libstd/lib.rs b/src/libstd/lib.rs index ac07af5e278fb..cc3e613fa3d60 100644 --- a/src/libstd/lib.rs +++ b/src/libstd/lib.rs @@ -266,12 +266,15 @@ #![feature(external_doc)] #![feature(fn_traits)] #![feature(format_args_nl)] +#![feature(future_readiness_fns)] +#![feature(gen_future)] #![feature(generator_trait)] #![feature(global_asm)] #![feature(hash_raw_entry)] #![feature(hashmap_internals)] #![feature(int_error_internals)] #![feature(int_error_matching)] +#![feature(into_future)] #![feature(integer_atomics)] #![feature(lang_items)] #![feature(libc)] From 6e5cb37b668db4d5ae1a8b01830bd0a88ac6a406 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Fri, 22 May 2020 13:24:34 +0200 Subject: [PATCH 5/8] Clean up E0600 explanation --- src/librustc_error_codes/error_codes/E0600.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/librustc_error_codes/error_codes/E0600.md b/src/librustc_error_codes/error_codes/E0600.md index 172e2a346c5a7..356006c72f3d1 100644 --- a/src/librustc_error_codes/error_codes/E0600.md +++ b/src/librustc_error_codes/error_codes/E0600.md @@ -1,6 +1,6 @@ An unary operator was used on a type which doesn't implement it. -Example of erroneous code: +Erroneous code example: ```compile_fail,E0600 enum Question { From fc0675bf7508e4b2bb48ccf01ad2ecbd924d7b3b Mon Sep 17 00:00:00 2001 From: Tymoteusz Jankowski Date: Fri, 22 May 2020 11:50:22 +0200 Subject: [PATCH 6/8] Allow using `Self::` in doc --- .../passes/collect_intra_doc_links.rs | 54 +++++++++++- src/test/rustdoc/intra-link-self.rs | 88 +++++++++++++++++++ 2 files changed, 138 insertions(+), 4 deletions(-) diff --git a/src/librustdoc/passes/collect_intra_doc_links.rs b/src/librustdoc/passes/collect_intra_doc_links.rs index 05f3b598ecdf4..adb7fc3eb9cff 100644 --- a/src/librustdoc/passes/collect_intra_doc_links.rs +++ b/src/librustdoc/passes/collect_intra_doc_links.rs @@ -431,6 +431,43 @@ impl<'a, 'tcx> DocFolder for LinkCollector<'a, 'tcx> { look_for_tests(&cx, &dox, &item, true); + // find item's parent to resolve `Self` in item's docs below + let parent_name = self.cx.as_local_hir_id(item.def_id).and_then(|item_hir| { + let parent_hir = self.cx.tcx.hir().get_parent_item(item_hir); + let item_parent = self.cx.tcx.hir().find(parent_hir); + match item_parent { + Some(hir::Node::Item(hir::Item { + kind: + hir::ItemKind::Impl { + self_ty: + hir::Ty { + kind: + hir::TyKind::Path(hir::QPath::Resolved( + _, + hir::Path { segments, .. }, + )), + .. + }, + .. + }, + .. + })) => segments.first().and_then(|seg| Some(seg.ident.to_string())), + Some(hir::Node::Item(hir::Item { + ident, kind: hir::ItemKind::Enum(..), .. + })) + | Some(hir::Node::Item(hir::Item { + ident, kind: hir::ItemKind::Struct(..), .. + })) + | Some(hir::Node::Item(hir::Item { + ident, kind: hir::ItemKind::Union(..), .. + })) + | Some(hir::Node::Item(hir::Item { + ident, kind: hir::ItemKind::Trait(..), .. + })) => Some(ident.to_string()), + _ => None, + } + }); + for (ori_link, link_range) in markdown_links(&dox) { // Bail early for real links. if ori_link.contains('/') { @@ -467,7 +504,7 @@ impl<'a, 'tcx> DocFolder for LinkCollector<'a, 'tcx> { }; let (res, fragment) = { let mut kind = None; - let path_str = if let Some(prefix) = + let mut path_str = if let Some(prefix) = ["struct@", "enum@", "type@", "trait@", "union@"] .iter() .find(|p| link.starts_with(**p)) @@ -521,6 +558,15 @@ impl<'a, 'tcx> DocFolder for LinkCollector<'a, 'tcx> { let base_node = if item.is_mod() && item.attrs.inner_docs { None } else { parent_node }; + let resolved_self; + // replace `Self` with suitable item's parent name + if path_str.starts_with("Self::") { + if let Some(ref name) = parent_name { + resolved_self = format!("{}::{}", name, &path_str[6..]); + path_str = &resolved_self; + } + } + match kind { Some(ns @ ValueNS) => { match self.resolve( @@ -529,7 +575,7 @@ impl<'a, 'tcx> DocFolder for LinkCollector<'a, 'tcx> { ¤t_item, base_node, &extra_fragment, - None, + Some(&item), ) { Ok(res) => res, Err(ErrorKind::ResolutionFailure) => { @@ -552,7 +598,7 @@ impl<'a, 'tcx> DocFolder for LinkCollector<'a, 'tcx> { ¤t_item, base_node, &extra_fragment, - None, + Some(&item), ) { Ok(res) => res, Err(ErrorKind::ResolutionFailure) => { @@ -577,7 +623,7 @@ impl<'a, 'tcx> DocFolder for LinkCollector<'a, 'tcx> { ¤t_item, base_node, &extra_fragment, - None, + Some(&item), ) { Err(ErrorKind::AnchorFailure(msg)) => { anchor_failure(cx, &item, &ori_link, &dox, link_range, msg); diff --git a/src/test/rustdoc/intra-link-self.rs b/src/test/rustdoc/intra-link-self.rs index acf975f5c738e..97752d5cfcb5c 100644 --- a/src/test/rustdoc/intra-link-self.rs +++ b/src/test/rustdoc/intra-link-self.rs @@ -1,5 +1,7 @@ #![crate_name = "foo"] +// ignore-tidy-linelength + // @has foo/index.html '//a/@href' '../foo/struct.Foo.html#method.new' // @has foo/struct.Foo.html '//a/@href' '../foo/struct.Foo.html#method.new' @@ -27,3 +29,89 @@ impl Bar { unimplemented!() } } + +pub struct MyStruct { + // @has foo/struct.MyStruct.html '//a/@href' '../foo/struct.MyStruct.html#structfield.struct_field' + + /// [`struct_field`] + /// + /// [`struct_field`]: Self::struct_field + pub struct_field: u8, +} + +pub enum MyEnum { + // @has foo/enum.MyEnum.html '//a/@href' '../foo/enum.MyEnum.html#EnumVariant.v' + + /// [`EnumVariant`] + /// + /// [`EnumVariant`]: Self::EnumVariant + EnumVariant, +} + +pub union MyUnion { + // @has foo/union.MyUnion.html '//a/@href' '../foo/union.MyUnion.html#structfield.union_field' + + /// [`union_field`] + /// + /// [`union_field`]: Self::union_field + pub union_field: f32, +} + +pub trait MyTrait { + // @has foo/trait.MyTrait.html '//a/@href' '../foo/trait.MyTrait.html#associatedtype.AssoType' + + /// [`AssoType`] + /// + /// [`AssoType`]: Self::AssoType + type AssoType; + + // @has foo/trait.MyTrait.html '//a/@href' '../foo/trait.MyTrait.html#associatedconstant.ASSO_CONST' + + /// [`ASSO_CONST`] + /// + /// [`ASSO_CONST`]: Self::ASSO_CONST + const ASSO_CONST: i32 = 1; + + // @has foo/trait.MyTrait.html '//a/@href' '../foo/trait.MyTrait.html#method.asso_fn' + + /// [`asso_fn`] + /// + /// [`asso_fn`]: Self::asso_fn + fn asso_fn() {} +} + +impl MyStruct { + // @has foo/struct.MyStruct.html '//a/@href' '../foo/struct.MyStruct.html#method.for_impl' + + /// [`for_impl`] + /// + /// [`for_impl`]: Self::for_impl + pub fn for_impl() { + unimplemented!() + } +} + +impl MyTrait for MyStruct { + // @has foo/struct.MyStruct.html '//a/@href' '../foo/struct.MyStruct.html#associatedtype.AssoType' + + /// [`AssoType`] + /// + /// [`AssoType`]: Self::AssoType + type AssoType = u32; + + // @has foo/struct.MyStruct.html '//a/@href' '../foo/struct.MyStruct.html#associatedconstant.ASSO_CONST' + + /// [`ASSO_CONST`] + /// + /// [`ASSO_CONST`]: Self::ASSO_CONST + const ASSO_CONST: i32 = 10; + + // @has foo/struct.MyStruct.html '//a/@href' '../foo/struct.MyStruct.html#method.asso_fn' + + /// [`asso_fn`] + /// + /// [`asso_fn`]: Self::asso_fn + fn asso_fn() { + unimplemented!() + } +} From dc4b9fd863091d22396473b5d4e6cd5a048ee22b Mon Sep 17 00:00:00 2001 From: Elinvynia <59487684+Elinvynia@users.noreply.github.com> Date: Wed, 20 May 2020 22:14:47 +0200 Subject: [PATCH 7/8] Allow rust-highfive to label issues it creates. Replace sets with lists. --- src/tools/publish_toolstate.py | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/src/tools/publish_toolstate.py b/src/tools/publish_toolstate.py index 988a226706dc0..72437e070044c 100755 --- a/src/tools/publish_toolstate.py +++ b/src/tools/publish_toolstate.py @@ -39,6 +39,19 @@ 'rustc-dev-guide': {'mark-i-m', 'spastorino', 'amanjeev', 'JohnTitor'}, } +LABELS = { + 'miri': ['A-miri', 'C-bug'], + 'rls': ['A-rls', 'C-bug'], + 'rustfmt': ['C-bug'], + 'book': ['C-bug'], + 'nomicon': ['C-bug'], + 'reference': ['C-bug'], + 'rust-by-example': ['C-bug'], + 'embedded-book': ['C-bug'], + 'edition-guide': ['C-bug'], + 'rustc-dev-guide': ['C-bug'], +} + REPOS = { 'miri': 'https://github.com/rust-lang/miri', 'rls': 'https://github.com/rust-lang/rls', @@ -132,6 +145,7 @@ def issue( assignees, relevant_pr_number, relevant_pr_user, + labels, ): # Open an issue about the toolstate failure. if status == 'test-fail': @@ -155,6 +169,7 @@ def issue( )), 'title': '`{}` no longer builds after {}'.format(tool, relevant_pr_number), 'assignees': list(assignees), + 'labels': labels, }) print("Creating issue:\n{}".format(request)) response = urllib2.urlopen(urllib2.Request( @@ -235,7 +250,7 @@ def update_latest( try: issue( tool, create_issue_for_status, MAINTAINERS.get(tool, ''), - relevant_pr_number, relevant_pr_user, + relevant_pr_number, relevant_pr_user, LABELS.get(tool, ''), ) except urllib2.HTTPError as e: # network errors will simply end up not creating an issue, but that's better From 07b1de4e9a4a223840efd60475b86f815e84acfa Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Fri, 22 May 2020 09:50:41 -0700 Subject: [PATCH 8/8] Report error from opener in bootstrap MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit On my machine, an error looks like: Finished release [optimized] target(s) in 0.29s Opening doc /git/rust/build/x86_64-unknown-linux-gnu/doc/std/index.html command 'xdg-open (internal)' did not execute successfully; exit code: 4 command stderr: gio: file:///git/rust/build/x86_64-unknown-linux-gnu/doc/std/index.html: Error when getting information for file “/git/rust/build/x86_64-unknown-linux-gnu/doc/std/index.html”: No such file or directory Build completed successfully in 0:00:08 --- src/bootstrap/doc.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/bootstrap/doc.rs b/src/bootstrap/doc.rs index be5e3bfee303f..5c01c5e852c48 100644 --- a/src/bootstrap/doc.rs +++ b/src/bootstrap/doc.rs @@ -76,10 +76,10 @@ fn open(builder: &Builder<'_>, path: impl AsRef) { } let path = path.as_ref(); - builder.info(&format!("Opening doc {}", path.to_string_lossy())); - - // ignore error - let _ = opener::open(path); + builder.info(&format!("Opening doc {}", path.display())); + if let Err(err) = opener::open(path) { + builder.info(&format!("{}\n", err)); + } } // "src/libstd" -> ["src", "libstd"]