Auto merge of #96015 - Dylan-DPC:rollup-vhdprid, r=Dylan-DPC
Rollup of 6 pull requests Successful merges: - #93217 (Improve Rustdoc UI for scraped examples with multiline arguments, fix overflow in line numbers) - #95885 (Improve error message in case of missing checksum) - #95962 (Document that DirEntry holds the directory open) - #95991 (fix: wrong trait import suggestion for T:) - #96005 (Add missing article to fix "few" to "a few".) - #96006 (Add a missing article) Failed merges: r? `@ghost` `@rustbot` modify labels: rollup
This commit is contained in:
commit
0d13f6afeb
@ -1880,9 +1880,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||||||
};
|
};
|
||||||
let sp = hir.span(id);
|
let sp = hir.span(id);
|
||||||
let sp = if let Some(first_bound) = has_bounds {
|
let sp = if let Some(first_bound) = has_bounds {
|
||||||
// `sp` only covers `T`, change it so that it covers
|
|
||||||
// `T:` when appropriate
|
|
||||||
sp.until(first_bound.span())
|
sp.until(first_bound.span())
|
||||||
|
} else if let Some(colon_sp) =
|
||||||
|
// If the generic param is declared with a colon but without bounds:
|
||||||
|
// fn foo<T:>(t: T) { ... }
|
||||||
|
param.colon_span_for_suggestions(
|
||||||
|
self.inh.tcx.sess.source_map(),
|
||||||
|
)
|
||||||
|
{
|
||||||
|
sp.to(colon_sp)
|
||||||
} else {
|
} else {
|
||||||
sp
|
sp
|
||||||
};
|
};
|
||||||
|
@ -108,11 +108,11 @@ pub const fn identity<T>(x: T) -> T {
|
|||||||
/// If you need to do a costly conversion it is better to implement [`From`] with type
|
/// If you need to do a costly conversion it is better to implement [`From`] with type
|
||||||
/// `&T` or write a custom function.
|
/// `&T` or write a custom function.
|
||||||
///
|
///
|
||||||
/// `AsRef` has the same signature as [`Borrow`], but [`Borrow`] is different in few aspects:
|
/// `AsRef` has the same signature as [`Borrow`], but [`Borrow`] is different in a few aspects:
|
||||||
///
|
///
|
||||||
/// - Unlike `AsRef`, [`Borrow`] has a blanket impl for any `T`, and can be used to accept either
|
/// - Unlike `AsRef`, [`Borrow`] has a blanket impl for any `T`, and can be used to accept either
|
||||||
/// a reference or a value.
|
/// a reference or a value.
|
||||||
/// - [`Borrow`] also requires that [`Hash`], [`Eq`] and [`Ord`] for borrowed value are
|
/// - [`Borrow`] also requires that [`Hash`], [`Eq`] and [`Ord`] for a borrowed value are
|
||||||
/// equivalent to those of the owned value. For this reason, if you want to
|
/// equivalent to those of the owned value. For this reason, if you want to
|
||||||
/// borrow only a single field of a struct you can implement `AsRef`, but not [`Borrow`].
|
/// borrow only a single field of a struct you can implement `AsRef`, but not [`Borrow`].
|
||||||
///
|
///
|
||||||
|
@ -132,6 +132,16 @@ pub struct ReadDir(fs_imp::ReadDir);
|
|||||||
/// An instance of `DirEntry` represents an entry inside of a directory on the
|
/// An instance of `DirEntry` represents an entry inside of a directory on the
|
||||||
/// filesystem. Each entry can be inspected via methods to learn about the full
|
/// filesystem. Each entry can be inspected via methods to learn about the full
|
||||||
/// path or possibly other metadata through per-platform extension traits.
|
/// path or possibly other metadata through per-platform extension traits.
|
||||||
|
///
|
||||||
|
/// # Platform-specific behavior
|
||||||
|
///
|
||||||
|
/// On Unix, the `DirEntry` struct contains an internal reference to the open
|
||||||
|
/// directory. Holding `DirEntry` objects will consume a file handle even
|
||||||
|
/// after the `ReadDir` iterator is dropped.
|
||||||
|
///
|
||||||
|
/// Note that this [may change in the future][changes].
|
||||||
|
///
|
||||||
|
/// [changes]: io#platform-specific-behavior
|
||||||
#[stable(feature = "rust1", since = "1.0.0")]
|
#[stable(feature = "rust1", since = "1.0.0")]
|
||||||
pub struct DirEntry(fs_imp::DirEntry);
|
pub struct DirEntry(fs_imp::DirEntry);
|
||||||
|
|
||||||
|
@ -70,7 +70,11 @@ def get(base, url, path, checksums, verbose=False, do_verify=True, help_on_error
|
|||||||
try:
|
try:
|
||||||
if do_verify:
|
if do_verify:
|
||||||
if url not in checksums:
|
if url not in checksums:
|
||||||
raise RuntimeError("src/stage0.json doesn't contain a checksum for {}".format(url))
|
raise RuntimeError(("src/stage0.json doesn't contain a checksum for {}. "
|
||||||
|
"Pre-built artifacts might not available for this "
|
||||||
|
"target at this time, see https://doc.rust-lang.org/nightly"
|
||||||
|
"/rustc/platform-support.html for more information.")
|
||||||
|
.format(url))
|
||||||
sha256 = checksums[url]
|
sha256 = checksums[url]
|
||||||
if os.path.exists(path):
|
if os.path.exists(path):
|
||||||
if verify(path, sha256, False):
|
if verify(path, sha256, False):
|
||||||
|
@ -9,6 +9,7 @@
|
|||||||
- [Linking to items by name](write-documentation/linking-to-items-by-name.md)
|
- [Linking to items by name](write-documentation/linking-to-items-by-name.md)
|
||||||
- [Documentation tests](write-documentation/documentation-tests.md)
|
- [Documentation tests](write-documentation/documentation-tests.md)
|
||||||
- [Rustdoc-specific lints](lints.md)
|
- [Rustdoc-specific lints](lints.md)
|
||||||
|
- [Scraped examples](scraped-examples.md)
|
||||||
- [Advanced features](advanced-features.md)
|
- [Advanced features](advanced-features.md)
|
||||||
- [Unstable features](unstable-features.md)
|
- [Unstable features](unstable-features.md)
|
||||||
- [Deprecated features](deprecated-features.md)
|
- [Deprecated features](deprecated-features.md)
|
||||||
|
55
src/doc/rustdoc/src/scraped-examples.md
Normal file
55
src/doc/rustdoc/src/scraped-examples.md
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
# Scraped examples
|
||||||
|
|
||||||
|
Rustdoc has an unstable feature where it can automatically scrape examples of items being documented from the `examples/` directory of a Cargo workspace. These examples will be included within the generated documentation for that item. For example, if your library contains a public function:
|
||||||
|
|
||||||
|
```rust,ignore (needs-other-file)
|
||||||
|
// a_crate/src/lib.rs
|
||||||
|
pub fn a_func() {}
|
||||||
|
```
|
||||||
|
|
||||||
|
And you have an example calling this function:
|
||||||
|
|
||||||
|
```rust,ignore (needs-other-file)
|
||||||
|
// a_crate/examples/ex.rs
|
||||||
|
fn main() {
|
||||||
|
a_crate::a_func();
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Then this code snippet will be included in the documentation for `a_func`. This documentation is inserted by Rustdoc and cannot be manually edited by the crate author.
|
||||||
|
|
||||||
|
|
||||||
|
## How to use this feature
|
||||||
|
|
||||||
|
This feature is unstable, so you can enable it by calling Rustdoc with the unstable `rustdoc-scrape-examples` flag:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cargo doc -Zunstable-options -Zrustdoc-scrape-examples=examples
|
||||||
|
```
|
||||||
|
|
||||||
|
To enable this feature on [docs.rs](https://docs.rs), add this to your Cargo.toml:
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[package.metadata.docs.rs]
|
||||||
|
cargo-args = ["-Zunstable-options", "-Zrustdoc-scrape-examples=examples"]
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
## How it works
|
||||||
|
|
||||||
|
When you run `cargo doc`, Rustdoc will analyze all the crates that match Cargo's `--examples` filter for instances of items being documented. Then Rustdoc will include the source code of these instances in the generated documentation.
|
||||||
|
|
||||||
|
Rustdoc has a few techniques to ensure these examples don't overwhelm documentation readers, and that it doesn't blow up the page size:
|
||||||
|
|
||||||
|
1. For a given item, a maximum of 5 examples are included in the page. The remaining examples are just links to source code.
|
||||||
|
2. Only one example is shown by default, and the remaining examples are hidden behind a toggle.
|
||||||
|
3. For a given file that contains examples, only the item containing the examples will be included in the generated documentation.
|
||||||
|
|
||||||
|
For a given item, Rustdoc sorts its examples based on the size of the example — smaller ones are shown first.
|
||||||
|
|
||||||
|
|
||||||
|
## FAQ
|
||||||
|
|
||||||
|
### My example is not showing up in the documentation
|
||||||
|
|
||||||
|
This feature uses Cargo's convention for finding examples. You should ensure that `cargo check --examples` includes your example file.
|
@ -17,8 +17,8 @@ use super::print_item::{full_path, item_path, print_item};
|
|||||||
use super::search_index::build_index;
|
use super::search_index::build_index;
|
||||||
use super::write_shared::write_shared;
|
use super::write_shared::write_shared;
|
||||||
use super::{
|
use super::{
|
||||||
collect_spans_and_sources, print_sidebar, settings, AllTypes, LinkFromSrc, NameDoc, StylePath,
|
collect_spans_and_sources, print_sidebar, scrape_examples_help, settings, AllTypes,
|
||||||
BASIC_KEYWORDS,
|
LinkFromSrc, NameDoc, StylePath, BASIC_KEYWORDS,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::clean::{self, types::ExternalLocation, ExternalCrate};
|
use crate::clean::{self, types::ExternalLocation, ExternalCrate};
|
||||||
@ -551,6 +551,7 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> {
|
|||||||
let crate_name = self.tcx().crate_name(LOCAL_CRATE);
|
let crate_name = self.tcx().crate_name(LOCAL_CRATE);
|
||||||
let final_file = self.dst.join(crate_name.as_str()).join("all.html");
|
let final_file = self.dst.join(crate_name.as_str()).join("all.html");
|
||||||
let settings_file = self.dst.join("settings.html");
|
let settings_file = self.dst.join("settings.html");
|
||||||
|
let scrape_examples_help_file = self.dst.join("scrape-examples-help.html");
|
||||||
|
|
||||||
let mut root_path = self.dst.to_str().expect("invalid path").to_owned();
|
let mut root_path = self.dst.to_str().expect("invalid path").to_owned();
|
||||||
if !root_path.ends_with('/') {
|
if !root_path.ends_with('/') {
|
||||||
@ -606,6 +607,20 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> {
|
|||||||
&self.shared.style_files,
|
&self.shared.style_files,
|
||||||
);
|
);
|
||||||
self.shared.fs.write(settings_file, v)?;
|
self.shared.fs.write(settings_file, v)?;
|
||||||
|
|
||||||
|
if self.shared.layout.scrape_examples_extension {
|
||||||
|
page.title = "About scraped examples";
|
||||||
|
page.description = "How the scraped examples feature works in Rustdoc";
|
||||||
|
let v = layout::render(
|
||||||
|
&self.shared.layout,
|
||||||
|
&page,
|
||||||
|
"",
|
||||||
|
scrape_examples_help(&*self.shared),
|
||||||
|
&self.shared.style_files,
|
||||||
|
);
|
||||||
|
self.shared.fs.write(scrape_examples_help_file, v)?;
|
||||||
|
}
|
||||||
|
|
||||||
if let Some(ref redirections) = self.shared.redirections {
|
if let Some(ref redirections) = self.shared.redirections {
|
||||||
if !redirections.borrow().is_empty() {
|
if !redirections.borrow().is_empty() {
|
||||||
let redirect_map_path =
|
let redirect_map_path =
|
||||||
|
@ -75,8 +75,10 @@ use crate::html::format::{
|
|||||||
use crate::html::highlight;
|
use crate::html::highlight;
|
||||||
use crate::html::markdown::{HeadingOffset, IdMap, Markdown, MarkdownHtml, MarkdownSummaryLine};
|
use crate::html::markdown::{HeadingOffset, IdMap, Markdown, MarkdownHtml, MarkdownSummaryLine};
|
||||||
use crate::html::sources;
|
use crate::html::sources;
|
||||||
|
use crate::html::static_files::SCRAPE_EXAMPLES_HELP_MD;
|
||||||
use crate::scrape_examples::{CallData, CallLocation};
|
use crate::scrape_examples::{CallData, CallLocation};
|
||||||
use crate::try_none;
|
use crate::try_none;
|
||||||
|
use crate::DOC_RUST_LANG_ORG_CHANNEL;
|
||||||
|
|
||||||
/// A pair of name and its optional document.
|
/// A pair of name and its optional document.
|
||||||
crate type NameDoc = (String, Option<String>);
|
crate type NameDoc = (String, Option<String>);
|
||||||
@ -460,6 +462,34 @@ fn settings(root_path: &str, suffix: &str, theme_names: Vec<String>) -> Result<S
|
|||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn scrape_examples_help(shared: &SharedContext<'_>) -> String {
|
||||||
|
let mut content = SCRAPE_EXAMPLES_HELP_MD.to_owned();
|
||||||
|
content.push_str(&format!(
|
||||||
|
"## More information\n\n\
|
||||||
|
If you want more information about this feature, please read the [corresponding chapter in the Rustdoc book]({}/rustdoc/scraped-examples.html).",
|
||||||
|
DOC_RUST_LANG_ORG_CHANNEL));
|
||||||
|
|
||||||
|
let mut ids = IdMap::default();
|
||||||
|
format!(
|
||||||
|
"<div class=\"main-heading\">\
|
||||||
|
<h1 class=\"fqn\">\
|
||||||
|
<span class=\"in-band\">About scraped examples</span>\
|
||||||
|
</h1>\
|
||||||
|
</div>\
|
||||||
|
<div>{}</div>",
|
||||||
|
Markdown {
|
||||||
|
content: &content,
|
||||||
|
links: &[],
|
||||||
|
ids: &mut ids,
|
||||||
|
error_codes: shared.codes,
|
||||||
|
edition: shared.edition(),
|
||||||
|
playground: &shared.playground,
|
||||||
|
heading_offset: HeadingOffset::H1
|
||||||
|
}
|
||||||
|
.into_string()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
fn document(
|
fn document(
|
||||||
w: &mut Buffer,
|
w: &mut Buffer,
|
||||||
cx: &Context<'_>,
|
cx: &Context<'_>,
|
||||||
@ -2743,7 +2773,9 @@ fn render_call_locations(w: &mut Buffer, cx: &Context<'_>, item: &clean::Item) {
|
|||||||
<span></span>\
|
<span></span>\
|
||||||
<h5 id=\"{id}\">\
|
<h5 id=\"{id}\">\
|
||||||
<a href=\"#{id}\">Examples found in repository</a>\
|
<a href=\"#{id}\">Examples found in repository</a>\
|
||||||
|
<a class=\"scrape-help\" href=\"{root_path}scrape-examples-help.html\">?</a>\
|
||||||
</h5>",
|
</h5>",
|
||||||
|
root_path = cx.root_path(),
|
||||||
id = id
|
id = id
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -2795,9 +2827,10 @@ fn render_call_locations(w: &mut Buffer, cx: &Context<'_>, item: &clean::Item) {
|
|||||||
.locations
|
.locations
|
||||||
.iter()
|
.iter()
|
||||||
.map(|loc| {
|
.map(|loc| {
|
||||||
let (byte_lo, byte_hi) = loc.call_expr.byte_span;
|
let (byte_lo, byte_hi) = loc.call_ident.byte_span;
|
||||||
let (line_lo, line_hi) = loc.call_expr.line_span;
|
let (line_lo, line_hi) = loc.call_expr.line_span;
|
||||||
let byte_range = (byte_lo - byte_min, byte_hi - byte_min);
|
let byte_range = (byte_lo - byte_min, byte_hi - byte_min);
|
||||||
|
|
||||||
let line_range = (line_lo - line_min, line_hi - line_min);
|
let line_range = (line_lo - line_min, line_hi - line_min);
|
||||||
let (line_url, line_title) = link_to_loc(call_data, loc);
|
let (line_url, line_title) = link_to_loc(call_data, loc);
|
||||||
|
|
||||||
@ -2913,6 +2946,7 @@ fn render_call_locations(w: &mut Buffer, cx: &Context<'_>, item: &clean::Item) {
|
|||||||
<summary class=\"hideme\">\
|
<summary class=\"hideme\">\
|
||||||
<span>More examples</span>\
|
<span>More examples</span>\
|
||||||
</summary>\
|
</summary>\
|
||||||
|
<div class=\"hide-more\">Hide additional examples</div>\
|
||||||
<div class=\"more-scraped-examples\">\
|
<div class=\"more-scraped-examples\">\
|
||||||
<div class=\"toggle-line\"><div class=\"toggle-line-inner\"></div></div>\
|
<div class=\"toggle-line\"><div class=\"toggle-line-inner\"></div></div>\
|
||||||
<div class=\"more-scraped-examples-inner\">"
|
<div class=\"more-scraped-examples-inner\">"
|
||||||
|
@ -618,7 +618,7 @@ h2.location a {
|
|||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
|
|
||||||
.docblock > :not(.information) {
|
.docblock > :not(.information):not(.more-examples-toggle) {
|
||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
overflow-x: auto;
|
overflow-x: auto;
|
||||||
}
|
}
|
||||||
@ -840,8 +840,8 @@ h2.small-section-header > .anchor {
|
|||||||
content: '§';
|
content: '§';
|
||||||
}
|
}
|
||||||
|
|
||||||
.docblock a:not(.srclink):not(.test-arrow):hover,
|
.docblock a:not(.srclink):not(.test-arrow):not(.scrape-help):hover,
|
||||||
.docblock-short a:not(.srclink):not(.test-arrow):hover, .item-info a {
|
.docblock-short a:not(.srclink):not(.test-arrow):not(.scrape-help):hover, .item-info a {
|
||||||
text-decoration: underline;
|
text-decoration: underline;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2038,21 +2038,45 @@ details.rustdoc-toggle[open] > summary.hideme::after {
|
|||||||
|
|
||||||
/* Begin: styles for --scrape-examples feature */
|
/* Begin: styles for --scrape-examples feature */
|
||||||
|
|
||||||
|
.scraped-example-list .scrape-help {
|
||||||
|
margin-left: 10px;
|
||||||
|
padding: 0 4px;
|
||||||
|
font-weight: normal;
|
||||||
|
font-size: 12px;
|
||||||
|
position: relative;
|
||||||
|
bottom: 1px;
|
||||||
|
background: transparent;
|
||||||
|
border-width: 1px;
|
||||||
|
border-style: solid;
|
||||||
|
border-radius: 50px;
|
||||||
|
}
|
||||||
|
|
||||||
.scraped-example-title {
|
.scraped-example-title {
|
||||||
font-family: 'Fira Sans';
|
font-family: 'Fira Sans';
|
||||||
}
|
}
|
||||||
|
|
||||||
.scraped-example:not(.expanded) .code-wrapper pre.line-numbers {
|
.scraped-example .code-wrapper {
|
||||||
overflow: hidden;
|
position: relative;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.scraped-example:not(.expanded) .code-wrapper {
|
||||||
max-height: 240px;
|
max-height: 240px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.scraped-example:not(.expanded) .code-wrapper .example-wrap pre.rust {
|
.scraped-example:not(.expanded) .code-wrapper pre {
|
||||||
overflow-y: hidden;
|
overflow-y: hidden;
|
||||||
max-height: 240px;
|
max-height: 240px;
|
||||||
padding-bottom: 0;
|
padding-bottom: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.scraped-example:not(.expanded) .code-wrapper pre.line-numbers {
|
||||||
|
overflow-x: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
.scraped-example .code-wrapper .prev {
|
.scraped-example .code-wrapper .prev {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 0.25em;
|
top: 0.25em;
|
||||||
@ -2077,14 +2101,6 @@ details.rustdoc-toggle[open] > summary.hideme::after {
|
|||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
.scraped-example .code-wrapper {
|
|
||||||
position: relative;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.scraped-example:not(.expanded) .code-wrapper:before {
|
.scraped-example:not(.expanded) .code-wrapper:before {
|
||||||
content: " ";
|
content: " ";
|
||||||
width: 100%;
|
width: 100%;
|
||||||
@ -2092,7 +2108,6 @@ details.rustdoc-toggle[open] > summary.hideme::after {
|
|||||||
position: absolute;
|
position: absolute;
|
||||||
z-index: 100;
|
z-index: 100;
|
||||||
top: 0;
|
top: 0;
|
||||||
background: linear-gradient(to bottom, rgba(255, 255, 255, 1), rgba(255, 255, 255, 0));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.scraped-example:not(.expanded) .code-wrapper:after {
|
.scraped-example:not(.expanded) .code-wrapper:after {
|
||||||
@ -2102,12 +2117,6 @@ details.rustdoc-toggle[open] > summary.hideme::after {
|
|||||||
position: absolute;
|
position: absolute;
|
||||||
z-index: 100;
|
z-index: 100;
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
background: linear-gradient(to top, rgba(255, 255, 255, 1), rgba(255, 255, 255, 0));
|
|
||||||
}
|
|
||||||
|
|
||||||
.scraped-example:not(.expanded) .code-wrapper {
|
|
||||||
overflow: hidden;
|
|
||||||
max-height: 240px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.scraped-example .code-wrapper .line-numbers {
|
.scraped-example .code-wrapper .line-numbers {
|
||||||
@ -2126,34 +2135,37 @@ details.rustdoc-toggle[open] > summary.hideme::after {
|
|||||||
margin-bottom: 0;
|
margin-bottom: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.scraped-example:not(.expanded) .code-wrapper .example-wrap {
|
||||||
|
overflow-x: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
.scraped-example .code-wrapper .example-wrap pre.rust {
|
.scraped-example .code-wrapper .example-wrap pre.rust {
|
||||||
overflow-x: inherit;
|
overflow-x: inherit;
|
||||||
width: inherit;
|
width: inherit;
|
||||||
overflow-y: hidden;
|
overflow-y: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
.scraped-example .example-wrap .rust span.highlight {
|
|
||||||
background: #fcffd6;
|
|
||||||
}
|
|
||||||
|
|
||||||
.scraped-example .example-wrap .rust span.highlight.focus {
|
|
||||||
background: #f6fdb0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.more-examples-toggle {
|
.more-examples-toggle {
|
||||||
|
max-width: calc(100% + 25px);
|
||||||
margin-top: 10px;
|
margin-top: 10px;
|
||||||
|
margin-left: -25px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.more-examples-toggle summary {
|
.more-examples-toggle .hide-more {
|
||||||
color: #999;
|
margin-left: 25px;
|
||||||
|
margin-bottom: 5px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.more-examples-toggle summary, .more-examples-toggle .hide-more {
|
||||||
font-family: 'Fira Sans';
|
font-family: 'Fira Sans';
|
||||||
}
|
}
|
||||||
|
|
||||||
.more-scraped-examples {
|
.more-scraped-examples {
|
||||||
margin-left: 25px;
|
margin-left: 5px;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
width: calc(100% - 25px);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.more-scraped-examples-inner {
|
.more-scraped-examples-inner {
|
||||||
@ -2169,13 +2181,8 @@ details.rustdoc-toggle[open] > summary.hideme::after {
|
|||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
.toggle-line:hover .toggle-line-inner {
|
|
||||||
background: #aaa;
|
|
||||||
}
|
|
||||||
|
|
||||||
.toggle-line-inner {
|
.toggle-line-inner {
|
||||||
min-width: 2px;
|
min-width: 2px;
|
||||||
background: #ddd;
|
|
||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -611,6 +611,18 @@ input:checked + .slider {
|
|||||||
background-color: #ffb454 !important;
|
background-color: #ffb454 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.scraped-example-list .scrape-help {
|
||||||
|
border-color: #aaa;
|
||||||
|
color: #eee;
|
||||||
|
}
|
||||||
|
.scraped-example-list .scrape-help:hover {
|
||||||
|
border-color: white;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
.more-examples-toggle summary, .more-examples-toggle .hide-more {
|
||||||
|
color: #999;
|
||||||
|
}
|
||||||
.scraped-example .example-wrap .rust span.highlight {
|
.scraped-example .example-wrap .rust span.highlight {
|
||||||
background: rgb(91, 59, 1);
|
background: rgb(91, 59, 1);
|
||||||
}
|
}
|
||||||
@ -624,8 +636,8 @@ input:checked + .slider {
|
|||||||
background: linear-gradient(to top, rgba(15, 20, 25, 1), rgba(15, 20, 25, 0));
|
background: linear-gradient(to top, rgba(15, 20, 25, 1), rgba(15, 20, 25, 0));
|
||||||
}
|
}
|
||||||
.toggle-line-inner {
|
.toggle-line-inner {
|
||||||
background: #616161;
|
background: #999;
|
||||||
}
|
}
|
||||||
.toggle-line:hover .toggle-line-inner {
|
.toggle-line:hover .toggle-line-inner {
|
||||||
background: #898989;
|
background: #c5c5c5;
|
||||||
}
|
}
|
||||||
|
@ -478,6 +478,17 @@ div.files > .selected {
|
|||||||
border-bottom-color: #ddd;
|
border-bottom-color: #ddd;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.scraped-example-list .scrape-help {
|
||||||
|
border-color: #aaa;
|
||||||
|
color: #eee;
|
||||||
|
}
|
||||||
|
.scraped-example-list .scrape-help:hover {
|
||||||
|
border-color: white;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
.more-examples-toggle summary, .more-examples-toggle .hide-more {
|
||||||
|
color: #999;
|
||||||
|
}
|
||||||
.scraped-example .example-wrap .rust span.highlight {
|
.scraped-example .example-wrap .rust span.highlight {
|
||||||
background: rgb(91, 59, 1);
|
background: rgb(91, 59, 1);
|
||||||
}
|
}
|
||||||
@ -491,8 +502,8 @@ div.files > .selected {
|
|||||||
background: linear-gradient(to top, rgba(53, 53, 53, 1), rgba(53, 53, 53, 0));
|
background: linear-gradient(to top, rgba(53, 53, 53, 1), rgba(53, 53, 53, 0));
|
||||||
}
|
}
|
||||||
.toggle-line-inner {
|
.toggle-line-inner {
|
||||||
background: #616161;
|
background: #999;
|
||||||
}
|
}
|
||||||
.toggle-line:hover .toggle-line-inner {
|
.toggle-line:hover .toggle-line-inner {
|
||||||
background: #898989;
|
background: #c5c5c5;
|
||||||
}
|
}
|
||||||
|
@ -462,3 +462,33 @@ div.files > .selected {
|
|||||||
.setting-line > .title {
|
.setting-line > .title {
|
||||||
border-bottom-color: #D5D5D5;
|
border-bottom-color: #D5D5D5;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.scraped-example-list .scrape-help {
|
||||||
|
border-color: #555;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
.scraped-example-list .scrape-help:hover {
|
||||||
|
border-color: black;
|
||||||
|
color: black;
|
||||||
|
}
|
||||||
|
.more-examples-toggle summary, .more-examples-toggle .hide-more {
|
||||||
|
color: #999;
|
||||||
|
}
|
||||||
|
.scraped-example .example-wrap .rust span.highlight {
|
||||||
|
background: #fcffd6;
|
||||||
|
}
|
||||||
|
.scraped-example .example-wrap .rust span.highlight.focus {
|
||||||
|
background: #f6fdb0;
|
||||||
|
}
|
||||||
|
.scraped-example:not(.expanded) .code-wrapper:before {
|
||||||
|
background: linear-gradient(to bottom, rgba(255, 255, 255, 1), rgba(255, 255, 255, 0));
|
||||||
|
}
|
||||||
|
.scraped-example:not(.expanded) .code-wrapper:after {
|
||||||
|
background: linear-gradient(to top, rgba(255, 255, 255, 1), rgba(255, 255, 255, 0));
|
||||||
|
}
|
||||||
|
.toggle-line-inner {
|
||||||
|
background: #ccc;
|
||||||
|
}
|
||||||
|
.toggle-line:hover .toggle-line-inner {
|
||||||
|
background: #999;
|
||||||
|
}
|
||||||
|
@ -1,14 +1,28 @@
|
|||||||
/* global addClass, hasClass, removeClass, onEach */
|
/* global addClass, hasClass, removeClass, onEach */
|
||||||
|
|
||||||
(function () {
|
(function () {
|
||||||
// Scroll code block to put the given code location in the middle of the viewer
|
// Number of lines shown when code viewer is not expanded
|
||||||
|
const MAX_LINES = 10;
|
||||||
|
|
||||||
|
// Scroll code block to the given code location
|
||||||
function scrollToLoc(elt, loc) {
|
function scrollToLoc(elt, loc) {
|
||||||
var wrapper = elt.querySelector(".code-wrapper");
|
|
||||||
var halfHeight = wrapper.offsetHeight / 2;
|
|
||||||
var lines = elt.querySelector('.line-numbers');
|
var lines = elt.querySelector('.line-numbers');
|
||||||
var offsetMid = (lines.children[loc[0]].offsetTop
|
var scrollOffset;
|
||||||
+ lines.children[loc[1]].offsetTop) / 2;
|
|
||||||
var scrollOffset = offsetMid - halfHeight;
|
// If the block is greater than the size of the viewer,
|
||||||
|
// then scroll to the top of the block. Otherwise scroll
|
||||||
|
// to the middle of the block.
|
||||||
|
if (loc[1] - loc[0] > MAX_LINES) {
|
||||||
|
var line = Math.max(0, loc[0] - 1);
|
||||||
|
scrollOffset = lines.children[line].offsetTop;
|
||||||
|
} else {
|
||||||
|
var wrapper = elt.querySelector(".code-wrapper");
|
||||||
|
var halfHeight = wrapper.offsetHeight / 2;
|
||||||
|
var offsetMid = (lines.children[loc[0]].offsetTop
|
||||||
|
+ lines.children[loc[1]].offsetTop) / 2;
|
||||||
|
scrollOffset = offsetMid - halfHeight;
|
||||||
|
}
|
||||||
|
|
||||||
lines.scrollTo(0, scrollOffset);
|
lines.scrollTo(0, scrollOffset);
|
||||||
elt.querySelector(".rust").scrollTo(0, scrollOffset);
|
elt.querySelector(".rust").scrollTo(0, scrollOffset);
|
||||||
}
|
}
|
||||||
@ -70,8 +84,10 @@
|
|||||||
onEach(document.querySelectorAll('.more-examples-toggle'), function(toggle) {
|
onEach(document.querySelectorAll('.more-examples-toggle'), function(toggle) {
|
||||||
// Allow users to click the left border of the <details> section to close it,
|
// Allow users to click the left border of the <details> section to close it,
|
||||||
// since the section can be large and finding the [+] button is annoying.
|
// since the section can be large and finding the [+] button is annoying.
|
||||||
toggle.querySelector('.toggle-line').addEventListener('click', function() {
|
toggle.querySelectorAll('.toggle-line, .hide-more').forEach(button => {
|
||||||
toggle.open = false;
|
button.addEventListener('click', function() {
|
||||||
|
toggle.open = false;
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
var moreExamples = toggle.querySelectorAll('.scraped-example');
|
var moreExamples = toggle.querySelectorAll('.scraped-example');
|
||||||
|
34
src/librustdoc/html/static/scrape-examples-help.md
Normal file
34
src/librustdoc/html/static/scrape-examples-help.md
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
Rustdoc will automatically scrape examples of documented items from the `examples/` directory of a project. These examples will be included within the generated documentation for that item. For example, if your library contains a public function:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
// src/lib.rs
|
||||||
|
pub fn a_func() {}
|
||||||
|
```
|
||||||
|
|
||||||
|
And you have an example calling this function:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
// examples/ex.rs
|
||||||
|
fn main() {
|
||||||
|
a_crate::a_func();
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Then this code snippet will be included in the documentation for `a_func`.
|
||||||
|
|
||||||
|
## How to read scraped examples
|
||||||
|
|
||||||
|
Scraped examples are shown as blocks of code from a given file. The relevant item will be highlighted. If the file is larger than a couple lines, only a small window will be shown which you can expand by clicking ↕ in the top-right. If a file contains multiple instances of an item, you can use the ≺ and ≻ buttons to toggle through each instance.
|
||||||
|
|
||||||
|
If there is more than one file that contains examples, then you should click "More examples" to see these examples.
|
||||||
|
|
||||||
|
|
||||||
|
## How Rustdoc scrapes examples
|
||||||
|
|
||||||
|
When you run `cargo doc`, Rustdoc will analyze all the crates that match Cargo's `--examples` filter for instances of items that occur in the crates being documented. Then Rustdoc will include the source code of these instances in the generated documentation.
|
||||||
|
|
||||||
|
Rustdoc has a few techniques to ensure this doesn't overwhelm documentation readers, and that it doesn't blow up the page size:
|
||||||
|
|
||||||
|
1. For a given item, a maximum of 5 examples are included in the page. The remaining examples are just links to source code.
|
||||||
|
2. Only one example is shown by default, and the remaining examples are hidden behind a toggle.
|
||||||
|
3. For a given file that contains examples, only the item containing the examples will be included in the generated documentation.
|
@ -39,6 +39,8 @@ crate static STORAGE_JS: &str = include_str!("static/js/storage.js");
|
|||||||
/// --scrape-examples flag that inserts automatically-found examples of usages of items.
|
/// --scrape-examples flag that inserts automatically-found examples of usages of items.
|
||||||
crate static SCRAPE_EXAMPLES_JS: &str = include_str!("static/js/scrape-examples.js");
|
crate static SCRAPE_EXAMPLES_JS: &str = include_str!("static/js/scrape-examples.js");
|
||||||
|
|
||||||
|
crate static SCRAPE_EXAMPLES_HELP_MD: &str = include_str!("static/scrape-examples-help.md");
|
||||||
|
|
||||||
/// The file contents of `brush.svg`, the icon used for the theme-switch button.
|
/// The file contents of `brush.svg`, the icon used for the theme-switch button.
|
||||||
crate static BRUSH_SVG: &[u8] = include_bytes!("static/images/brush.svg");
|
crate static BRUSH_SVG: &[u8] = include_bytes!("static/images/brush.svg");
|
||||||
|
|
||||||
|
@ -71,33 +71,36 @@ crate struct SyntaxRange {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl SyntaxRange {
|
impl SyntaxRange {
|
||||||
fn new(span: rustc_span::Span, file: &SourceFile) -> Self {
|
fn new(span: rustc_span::Span, file: &SourceFile) -> Option<Self> {
|
||||||
let get_pos = |bytepos: BytePos| file.original_relative_byte_pos(bytepos).0;
|
let get_pos = |bytepos: BytePos| file.original_relative_byte_pos(bytepos).0;
|
||||||
let get_line = |bytepos: BytePos| file.lookup_line(bytepos).unwrap();
|
let get_line = |bytepos: BytePos| file.lookup_line(bytepos);
|
||||||
|
|
||||||
SyntaxRange {
|
Some(SyntaxRange {
|
||||||
byte_span: (get_pos(span.lo()), get_pos(span.hi())),
|
byte_span: (get_pos(span.lo()), get_pos(span.hi())),
|
||||||
line_span: (get_line(span.lo()), get_line(span.hi())),
|
line_span: (get_line(span.lo())?, get_line(span.hi())?),
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Encodable, Decodable, Debug, Clone)]
|
#[derive(Encodable, Decodable, Debug, Clone)]
|
||||||
crate struct CallLocation {
|
crate struct CallLocation {
|
||||||
crate call_expr: SyntaxRange,
|
crate call_expr: SyntaxRange,
|
||||||
|
crate call_ident: SyntaxRange,
|
||||||
crate enclosing_item: SyntaxRange,
|
crate enclosing_item: SyntaxRange,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CallLocation {
|
impl CallLocation {
|
||||||
fn new(
|
fn new(
|
||||||
expr_span: rustc_span::Span,
|
expr_span: rustc_span::Span,
|
||||||
|
ident_span: rustc_span::Span,
|
||||||
enclosing_item_span: rustc_span::Span,
|
enclosing_item_span: rustc_span::Span,
|
||||||
source_file: &SourceFile,
|
source_file: &SourceFile,
|
||||||
) -> Self {
|
) -> Option<Self> {
|
||||||
CallLocation {
|
Some(CallLocation {
|
||||||
call_expr: SyntaxRange::new(expr_span, source_file),
|
call_expr: SyntaxRange::new(expr_span, source_file)?,
|
||||||
enclosing_item: SyntaxRange::new(enclosing_item_span, source_file),
|
call_ident: SyntaxRange::new(ident_span, source_file)?,
|
||||||
}
|
enclosing_item: SyntaxRange::new(enclosing_item_span, source_file)?,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -146,24 +149,26 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Get type of function if expression is a function call
|
// Get type of function if expression is a function call
|
||||||
let (ty, span) = match ex.kind {
|
let (ty, call_span, ident_span) = match ex.kind {
|
||||||
hir::ExprKind::Call(f, _) => {
|
hir::ExprKind::Call(f, _) => {
|
||||||
let types = tcx.typeck(ex.hir_id.owner);
|
let types = tcx.typeck(ex.hir_id.owner);
|
||||||
|
|
||||||
if let Some(ty) = types.node_type_opt(f.hir_id) {
|
if let Some(ty) = types.node_type_opt(f.hir_id) {
|
||||||
(ty, ex.span)
|
(ty, ex.span, f.span)
|
||||||
} else {
|
} else {
|
||||||
trace!("node_type_opt({}) = None", f.hir_id);
|
trace!("node_type_opt({}) = None", f.hir_id);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
hir::ExprKind::MethodCall(_, _, span) => {
|
hir::ExprKind::MethodCall(path, _, call_span) => {
|
||||||
let types = tcx.typeck(ex.hir_id.owner);
|
let types = tcx.typeck(ex.hir_id.owner);
|
||||||
let Some(def_id) = types.type_dependent_def_id(ex.hir_id) else {
|
let Some(def_id) = types.type_dependent_def_id(ex.hir_id) else {
|
||||||
trace!("type_dependent_def_id({}) = None", ex.hir_id);
|
trace!("type_dependent_def_id({}) = None", ex.hir_id);
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
(tcx.type_of(def_id), span)
|
|
||||||
|
let ident_span = path.ident.span;
|
||||||
|
(tcx.type_of(def_id), call_span, ident_span)
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
return;
|
return;
|
||||||
@ -172,8 +177,8 @@ where
|
|||||||
|
|
||||||
// If this span comes from a macro expansion, then the source code may not actually show
|
// If this span comes from a macro expansion, then the source code may not actually show
|
||||||
// a use of the given item, so it would be a poor example. Hence, we skip all uses in macros.
|
// a use of the given item, so it would be a poor example. Hence, we skip all uses in macros.
|
||||||
if span.from_expansion() {
|
if call_span.from_expansion() {
|
||||||
trace!("Rejecting expr from macro: {:?}", span);
|
trace!("Rejecting expr from macro: {call_span:?}");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -183,49 +188,82 @@ where
|
|||||||
.hir()
|
.hir()
|
||||||
.span_with_body(tcx.hir().local_def_id_to_hir_id(tcx.hir().get_parent_item(ex.hir_id)));
|
.span_with_body(tcx.hir().local_def_id_to_hir_id(tcx.hir().get_parent_item(ex.hir_id)));
|
||||||
if enclosing_item_span.from_expansion() {
|
if enclosing_item_span.from_expansion() {
|
||||||
trace!("Rejecting expr ({:?}) from macro item: {:?}", span, enclosing_item_span);
|
trace!("Rejecting expr ({call_span:?}) from macro item: {enclosing_item_span:?}");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
assert!(
|
// If the enclosing item doesn't actually enclose the call, this means we probably have a weird
|
||||||
enclosing_item_span.contains(span),
|
// macro issue even though the spans aren't tagged as being from an expansion.
|
||||||
"Attempted to scrape call at [{:?}] whose enclosing item [{:?}] doesn't contain the span of the call.",
|
if !enclosing_item_span.contains(call_span) {
|
||||||
span,
|
warn!(
|
||||||
enclosing_item_span
|
"Attempted to scrape call at [{call_span:?}] whose enclosing item [{enclosing_item_span:?}] doesn't contain the span of the call."
|
||||||
);
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Similarly for the call w/ the function ident.
|
||||||
|
if !call_span.contains(ident_span) {
|
||||||
|
warn!(
|
||||||
|
"Attempted to scrape call at [{call_span:?}] whose identifier [{ident_span:?}] was not contained in the span of the call."
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Save call site if the function resolves to a concrete definition
|
// Save call site if the function resolves to a concrete definition
|
||||||
if let ty::FnDef(def_id, _) = ty.kind() {
|
if let ty::FnDef(def_id, _) = ty.kind() {
|
||||||
if self.target_crates.iter().all(|krate| *krate != def_id.krate) {
|
if self.target_crates.iter().all(|krate| *krate != def_id.krate) {
|
||||||
trace!("Rejecting expr from crate not being documented: {:?}", span);
|
trace!("Rejecting expr from crate not being documented: {call_span:?}");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let source_map = tcx.sess.source_map();
|
let source_map = tcx.sess.source_map();
|
||||||
let file = source_map.lookup_char_pos(span.lo()).file;
|
let file = source_map.lookup_char_pos(call_span.lo()).file;
|
||||||
let file_path = match file.name.clone() {
|
let file_path = match file.name.clone() {
|
||||||
FileName::Real(real_filename) => real_filename.into_local_path(),
|
FileName::Real(real_filename) => real_filename.into_local_path(),
|
||||||
_ => None,
|
_ => None,
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Some(file_path) = file_path {
|
if let Some(file_path) = file_path {
|
||||||
let abs_path = fs::canonicalize(file_path.clone()).unwrap();
|
let abs_path = match fs::canonicalize(file_path.clone()) {
|
||||||
|
Ok(abs_path) => abs_path,
|
||||||
|
Err(_) => {
|
||||||
|
trace!("Could not canonicalize file path: {}", file_path.display());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
let cx = &self.cx;
|
let cx = &self.cx;
|
||||||
|
let clean_span = crate::clean::types::Span::new(call_span);
|
||||||
|
let url = match cx.href_from_span(clean_span, false) {
|
||||||
|
Some(url) => url,
|
||||||
|
None => {
|
||||||
|
trace!(
|
||||||
|
"Rejecting expr ({call_span:?}) whose clean span ({clean_span:?}) cannot be turned into a link"
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
let mk_call_data = || {
|
let mk_call_data = || {
|
||||||
let clean_span = crate::clean::types::Span::new(span);
|
|
||||||
let url = cx.href_from_span(clean_span, false).unwrap();
|
|
||||||
let display_name = file_path.display().to_string();
|
let display_name = file_path.display().to_string();
|
||||||
let edition = span.edition();
|
let edition = call_span.edition();
|
||||||
CallData { locations: Vec::new(), url, display_name, edition }
|
CallData { locations: Vec::new(), url, display_name, edition }
|
||||||
};
|
};
|
||||||
|
|
||||||
let fn_key = tcx.def_path_hash(*def_id);
|
let fn_key = tcx.def_path_hash(*def_id);
|
||||||
let fn_entries = self.calls.entry(fn_key).or_default();
|
let fn_entries = self.calls.entry(fn_key).or_default();
|
||||||
|
|
||||||
trace!("Including expr: {:?}", span);
|
trace!("Including expr: {:?}", call_span);
|
||||||
let enclosing_item_span =
|
let enclosing_item_span =
|
||||||
source_map.span_extend_to_prev_char(enclosing_item_span, '\n', false);
|
source_map.span_extend_to_prev_char(enclosing_item_span, '\n', false);
|
||||||
let location = CallLocation::new(span, enclosing_item_span, &file);
|
let location =
|
||||||
|
match CallLocation::new(call_span, ident_span, enclosing_item_span, &file) {
|
||||||
|
Some(location) => location,
|
||||||
|
None => {
|
||||||
|
trace!("Could not get serializable call location for {call_span:?}");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
fn_entries.entry(abs_path).or_insert_with(mk_call_data).locations.push(location);
|
fn_entries.entry(abs_path).or_insert_with(mk_call_data).locations.push(location);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -259,8 +297,8 @@ crate fn run(
|
|||||||
.map(|(crate_num, _)| **crate_num)
|
.map(|(crate_num, _)| **crate_num)
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
debug!("All crates in TyCtxt: {:?}", all_crates);
|
debug!("All crates in TyCtxt: {all_crates:?}");
|
||||||
debug!("Scrape examples target_crates: {:?}", target_crates);
|
debug!("Scrape examples target_crates: {target_crates:?}");
|
||||||
|
|
||||||
// Run call-finder on all items
|
// Run call-finder on all items
|
||||||
let mut calls = FxHashMap::default();
|
let mut calls = FxHashMap::default();
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
// @has foobar/fn.ok.html '//*[@class="docblock scraped-example-list"]' 'ex2'
|
// @has foobar/fn.ok.html '//*[@class="docblock scraped-example-list"]' 'ex2'
|
||||||
// @has foobar/fn.ok.html '//*[@class="more-scraped-examples"]' 'ex1'
|
// @has foobar/fn.ok.html '//*[@class="more-scraped-examples"]' 'ex1'
|
||||||
// @has foobar/fn.ok.html '//*[@class="highlight focus"]' '1'
|
// @has foobar/fn.ok.html '//*[@class="highlight focus"]' 'ok'
|
||||||
// @has foobar/fn.ok.html '//*[@class="highlight"]' '2'
|
// @has foobar/fn.ok.html '//*[@class="highlight"]' 'ok'
|
||||||
// @has foobar/fn.ok.html '//*[@class="highlight focus"]' '0'
|
|
||||||
|
|
||||||
pub fn ok(_x: i32) {}
|
pub fn ok(_x: i32) {}
|
||||||
|
9
src/test/ui/traits/issue-95898.rs
Normal file
9
src/test/ui/traits/issue-95898.rs
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
// Test for #95898: The trait suggestion had an extra `:` after the trait.
|
||||||
|
// edition:2021
|
||||||
|
|
||||||
|
fn foo<T:>(t: T) {
|
||||||
|
t.clone();
|
||||||
|
//~^ ERROR no method named `clone` found for type parameter `T` in the current scope
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {}
|
15
src/test/ui/traits/issue-95898.stderr
Normal file
15
src/test/ui/traits/issue-95898.stderr
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
error[E0599]: no method named `clone` found for type parameter `T` in the current scope
|
||||||
|
--> $DIR/issue-95898.rs:5:7
|
||||||
|
|
|
||||||
|
LL | t.clone();
|
||||||
|
| ^^^^^ method not found in `T`
|
||||||
|
|
|
||||||
|
= help: items from traits can only be used if the type parameter is bounded by the trait
|
||||||
|
help: the following trait defines an item `clone`, perhaps you need to restrict type parameter `T` with it:
|
||||||
|
|
|
||||||
|
LL | fn foo<T: Clone>(t: T) {
|
||||||
|
| ~~~~~~~~
|
||||||
|
|
||||||
|
error: aborting due to previous error
|
||||||
|
|
||||||
|
For more information about this error, try `rustc --explain E0599`.
|
Loading…
x
Reference in New Issue
Block a user