diff options
| author | Ralf Jung <post@ralfj.de> | 2025-06-15 20:46:23 +0900 |
|---|---|---|
| committer | Ralf Jung <post@ralfj.de> | 2025-06-15 20:46:23 +0900 |
| commit | 19beef420548f5cd55387d79593f0b39490145a7 (patch) | |
| tree | 47690c255eb9b79639b9eb3ae1aa4c9c732e138b /src | |
| parent | a576180419a95ee01ecae1cd9974235f0bd5a814 (diff) | |
| parent | 0cbc0764380630780a275c437260e4d4d5f28c92 (diff) | |
| download | rust-19beef420548f5cd55387d79593f0b39490145a7.tar.gz rust-19beef420548f5cd55387d79593f0b39490145a7.zip | |
Merge from rustc
Diffstat (limited to 'src')
| -rw-r--r-- | src/doc/rustc-dev-guide/src/implementing_new_features.md | 4 | ||||
| -rw-r--r-- | src/doc/rustdoc/src/unstable-features.md | 24 | ||||
| -rw-r--r-- | src/doc/unstable-book/src/language-features/unsized-locals.md | 175 | ||||
| -rw-r--r-- | src/librustdoc/doctest.rs | 4 | ||||
| -rw-r--r-- | src/librustdoc/doctest/extracted.rs | 55 | ||||
| -rw-r--r-- | src/librustdoc/doctest/make.rs | 148 | ||||
| -rw-r--r-- | src/librustdoc/doctest/tests.rs | 59 | ||||
| -rw-r--r-- | src/librustdoc/formats/renderer.rs | 16 | ||||
| -rw-r--r-- | src/librustdoc/html/markdown.rs | 3 | ||||
| -rw-r--r-- | src/librustdoc/html/render/context.rs | 4 | ||||
| -rw-r--r-- | src/librustdoc/json/conversions.rs | 232 | ||||
| -rw-r--r-- | src/librustdoc/json/mod.rs | 48 | ||||
| -rw-r--r-- | src/tools/clippy/tests/ui/large_stack_frames.rs | 8 | ||||
| -rw-r--r-- | src/tools/clippy/tests/ui/large_stack_frames.stderr | 8 | ||||
| -rw-r--r-- | src/tools/miri/tests/fail/unsized-local.rs | 23 | ||||
| -rw-r--r-- | src/tools/miri/tests/fail/unsized-local.stderr | 14 | ||||
| -rw-r--r-- | src/tools/tidy/src/issues.txt | 1 |
17 files changed, 404 insertions, 422 deletions
diff --git a/src/doc/rustc-dev-guide/src/implementing_new_features.md b/src/doc/rustc-dev-guide/src/implementing_new_features.md index d7561bbbad2..5d0e875cbc1 100644 --- a/src/doc/rustc-dev-guide/src/implementing_new_features.md +++ b/src/doc/rustc-dev-guide/src/implementing_new_features.md @@ -156,8 +156,8 @@ a new unstable feature: [`incomplete_features` lint]: https://doc.rust-lang.org/rustc/lints/listing/warn-by-default.html#incomplete-features ```rust ignore - /// Allows unsized rvalues at arguments and parameters. - (incomplete, unsized_locals, "CURRENT_RUSTC_VERSION", Some(48055), None), + /// Allows deref patterns. + (incomplete, deref_patterns, "CURRENT_RUSTC_VERSION", Some(87121), None), ``` To avoid [semantic merge conflicts], please use `CURRENT_RUSTC_VERSION` instead of `1.70` or diff --git a/src/doc/rustdoc/src/unstable-features.md b/src/doc/rustdoc/src/unstable-features.md index 69e5a5adbec..27910ad0ab7 100644 --- a/src/doc/rustdoc/src/unstable-features.md +++ b/src/doc/rustdoc/src/unstable-features.md @@ -581,7 +581,9 @@ For this rust code: ```rust /// ``` +/// #![allow(dead_code)] /// let x = 12; +/// Ok(()) /// ``` pub trait Trait {} ``` @@ -590,10 +592,10 @@ The generated output (formatted) will look like this: ```json { - "format_version": 1, + "format_version": 2, "doctests": [ { - "file": "foo.rs", + "file": "src/lib.rs", "line": 1, "doctest_attributes": { "original": "", @@ -609,9 +611,17 @@ The generated output (formatted) will look like this: "added_css_classes": [], "unknown": [] }, - "original_code": "let x = 12;", - "doctest_code": "#![allow(unused)]\nfn main() {\nlet x = 12;\n}", - "name": "foo.rs - Trait (line 1)" + "original_code": "#![allow(dead_code)]\nlet x = 12;\nOk(())", + "doctest_code": { + "crate_level": "#![allow(unused)]\n#![allow(dead_code)]\n\n", + "code": "let x = 12;\nOk(())", + "wrapper": { + "before": "fn main() { fn _inner() -> core::result::Result<(), impl core::fmt::Debug> {\n", + "after": "\n} _inner().unwrap() }", + "returns_result": true + } + }, + "name": "src/lib.rs - (line 1)" } ] } @@ -624,6 +634,10 @@ The generated output (formatted) will look like this: * `doctest_attributes` contains computed information about the attributes used on the doctests. For more information about doctest attributes, take a look [here](write-documentation/documentation-tests.html#attributes). * `original_code` is the code as written in the source code before rustdoc modifies it. * `doctest_code` is the code modified by rustdoc that will be run. If there is a fatal syntax error, this field will not be present. + * `crate_level` is the crate level code (like attributes or `extern crate`) that will be added at the top-level of the generated doctest. + * `code` is "naked" doctest without anything from `crate_level` and `wrapper` content. + * `wrapper` contains extra code that will be added before and after `code`. + * `returns_result` is a boolean. If `true`, it means that the doctest returns a `Result` type. * `name` is the name generated by rustdoc which represents this doctest. ### html diff --git a/src/doc/unstable-book/src/language-features/unsized-locals.md b/src/doc/unstable-book/src/language-features/unsized-locals.md deleted file mode 100644 index d5b01a3d616..00000000000 --- a/src/doc/unstable-book/src/language-features/unsized-locals.md +++ /dev/null @@ -1,175 +0,0 @@ -# `unsized_locals` - -The tracking issue for this feature is: [#48055] - -[#48055]: https://github.com/rust-lang/rust/issues/48055 - ------------------------- - -This implements [RFC1909]. When turned on, you can have unsized arguments and locals: - -[RFC1909]: https://github.com/rust-lang/rfcs/blob/master/text/1909-unsized-rvalues.md - -```rust -#![allow(incomplete_features)] -#![feature(unsized_locals, unsized_fn_params)] - -use std::any::Any; - -fn main() { - let x: Box<dyn Any> = Box::new(42); - let x: dyn Any = *x; - // ^ unsized local variable - // ^^ unsized temporary - foo(x); -} - -fn foo(_: dyn Any) {} -// ^^^^^^ unsized argument -``` - -The RFC still forbids the following unsized expressions: - -```rust,compile_fail -#![feature(unsized_locals)] - -use std::any::Any; - -struct MyStruct<T: ?Sized> { - content: T, -} - -struct MyTupleStruct<T: ?Sized>(T); - -fn answer() -> Box<dyn Any> { - Box::new(42) -} - -fn main() { - // You CANNOT have unsized statics. - static X: dyn Any = *answer(); // ERROR - const Y: dyn Any = *answer(); // ERROR - - // You CANNOT have struct initialized unsized. - MyStruct { content: *answer() }; // ERROR - MyTupleStruct(*answer()); // ERROR - (42, *answer()); // ERROR - - // You CANNOT have unsized return types. - fn my_function() -> dyn Any { *answer() } // ERROR - - // You CAN have unsized local variables... - let mut x: dyn Any = *answer(); // OK - // ...but you CANNOT reassign to them. - x = *answer(); // ERROR - - // You CANNOT even initialize them separately. - let y: dyn Any; // OK - y = *answer(); // ERROR - - // Not mentioned in the RFC, but by-move captured variables are also Sized. - let x: dyn Any = *answer(); - (move || { // ERROR - let y = x; - })(); - - // You CAN create a closure with unsized arguments, - // but you CANNOT call it. - // This is an implementation detail and may be changed in the future. - let f = |x: dyn Any| {}; - f(*answer()); // ERROR -} -``` - -## By-value trait objects - -With this feature, you can have by-value `self` arguments without `Self: Sized` bounds. - -```rust -#![feature(unsized_fn_params)] - -trait Foo { - fn foo(self) {} -} - -impl<T: ?Sized> Foo for T {} - -fn main() { - let slice: Box<[i32]> = Box::new([1, 2, 3]); - <[i32] as Foo>::foo(*slice); -} -``` - -And `Foo` will also be object-safe. - -```rust -#![feature(unsized_fn_params)] - -trait Foo { - fn foo(self) {} -} - -impl<T: ?Sized> Foo for T {} - -fn main () { - let slice: Box<dyn Foo> = Box::new([1, 2, 3]); - // doesn't compile yet - <dyn Foo as Foo>::foo(*slice); -} -``` - -One of the objectives of this feature is to allow `Box<dyn FnOnce>`. - -## Variable length arrays - -The RFC also describes an extension to the array literal syntax: `[e; dyn n]`. In the syntax, `n` isn't necessarily a constant expression. The array is dynamically allocated on the stack and has the type of `[T]`, instead of `[T; n]`. - -```rust,ignore (not-yet-implemented) -#![feature(unsized_locals)] - -fn mergesort<T: Ord>(a: &mut [T]) { - let mut tmp = [T; dyn a.len()]; - // ... -} - -fn main() { - let mut a = [3, 1, 5, 6]; - mergesort(&mut a); - assert_eq!(a, [1, 3, 5, 6]); -} -``` - -VLAs are not implemented yet. The syntax isn't final, either. We may need an alternative syntax for Rust 2015 because, in Rust 2015, expressions like `[e; dyn(1)]` would be ambiguous. One possible alternative proposed in the RFC is `[e; n]`: if `n` captures one or more local variables, then it is considered as `[e; dyn n]`. - -## Advisory on stack usage - -It's advised not to casually use the `#![feature(unsized_locals)]` feature. Typical use-cases are: - -- When you need a by-value trait objects. -- When you really need a fast allocation of small temporary arrays. - -Another pitfall is repetitive allocation and temporaries. Currently the compiler simply extends the stack frame every time it encounters an unsized assignment. So for example, the code - -```rust -#![feature(unsized_locals)] - -fn main() { - let x: Box<[i32]> = Box::new([1, 2, 3, 4, 5]); - let _x = {{{{{{{{{{*x}}}}}}}}}}; -} -``` - -and the code - -```rust -#![feature(unsized_locals)] - -fn main() { - for _ in 0..10 { - let x: Box<[i32]> = Box::new([1, 2, 3, 4, 5]); - let _x = *x; - } -} -``` - -will unnecessarily extend the stack frame. diff --git a/src/librustdoc/doctest.rs b/src/librustdoc/doctest.rs index a81d6020f71..130fdff1afe 100644 --- a/src/librustdoc/doctest.rs +++ b/src/librustdoc/doctest.rs @@ -1053,14 +1053,14 @@ fn doctest_run_fn( let report_unused_externs = |uext| { unused_externs.lock().unwrap().push(uext); }; - let (full_test_code, full_test_line_offset) = doctest.generate_unique_doctest( + let (wrapped, full_test_line_offset) = doctest.generate_unique_doctest( &scraped_test.text, scraped_test.langstr.test_harness, &global_opts, Some(&global_opts.crate_name), ); let runnable_test = RunnableDocTest { - full_test_code, + full_test_code: wrapped.to_string(), full_test_line_offset, test_opts, global_opts, diff --git a/src/librustdoc/doctest/extracted.rs b/src/librustdoc/doctest/extracted.rs index ebe6bfd22ba..925fb6fee2c 100644 --- a/src/librustdoc/doctest/extracted.rs +++ b/src/librustdoc/doctest/extracted.rs @@ -3,8 +3,10 @@ //! This module contains the logic to extract doctests and output a JSON containing this //! information. +use rustc_span::edition::Edition; use serde::Serialize; +use super::make::DocTestWrapResult; use super::{BuildDocTestBuilder, ScrapedDocTest}; use crate::config::Options as RustdocOptions; use crate::html::markdown; @@ -14,7 +16,7 @@ use crate::html::markdown; /// This integer is incremented with every breaking change to the API, /// and is returned along with the JSON blob into the `format_version` root field. /// Consuming code should assert that this value matches the format version(s) that it supports. -const FORMAT_VERSION: u32 = 1; +const FORMAT_VERSION: u32 = 2; #[derive(Serialize)] pub(crate) struct ExtractedDocTests { @@ -34,7 +36,16 @@ impl ExtractedDocTests { options: &RustdocOptions, ) { let edition = scraped_test.edition(options); + self.add_test_with_edition(scraped_test, opts, edition) + } + /// This method is used by unit tests to not have to provide a `RustdocOptions`. + pub(crate) fn add_test_with_edition( + &mut self, + scraped_test: ScrapedDocTest, + opts: &super::GlobalTestOptions, + edition: Edition, + ) { let ScrapedDocTest { filename, line, langstr, text, name, global_crate_attrs, .. } = scraped_test; @@ -44,8 +55,7 @@ impl ExtractedDocTests { .edition(edition) .lang_str(&langstr) .build(None); - - let (full_test_code, size) = doctest.generate_unique_doctest( + let (wrapped, _size) = doctest.generate_unique_doctest( &text, langstr.test_harness, opts, @@ -55,11 +65,46 @@ impl ExtractedDocTests { file: filename.prefer_remapped_unconditionaly().to_string(), line, doctest_attributes: langstr.into(), - doctest_code: if size != 0 { Some(full_test_code) } else { None }, + doctest_code: match wrapped { + DocTestWrapResult::Valid { crate_level_code, wrapper, code } => Some(DocTest { + crate_level: crate_level_code, + code, + wrapper: wrapper.map( + |super::make::WrapperInfo { before, after, returns_result, .. }| { + WrapperInfo { before, after, returns_result } + }, + ), + }), + DocTestWrapResult::SyntaxError { .. } => None, + }, original_code: text, name, }); } + + #[cfg(test)] + pub(crate) fn doctests(&self) -> &[ExtractedDocTest] { + &self.doctests + } +} + +#[derive(Serialize)] +pub(crate) struct WrapperInfo { + before: String, + after: String, + returns_result: bool, +} + +#[derive(Serialize)] +pub(crate) struct DocTest { + crate_level: String, + code: String, + /// This field can be `None` if one of the following conditions is true: + /// + /// * The doctest's codeblock has the `test_harness` attribute. + /// * The doctest has a `main` function. + /// * The doctest has the `![no_std]` attribute. + pub(crate) wrapper: Option<WrapperInfo>, } #[derive(Serialize)] @@ -69,7 +114,7 @@ pub(crate) struct ExtractedDocTest { doctest_attributes: LangString, original_code: String, /// `None` if the code syntax is invalid. - doctest_code: Option<String>, + pub(crate) doctest_code: Option<DocTest>, name: String, } diff --git a/src/librustdoc/doctest/make.rs b/src/librustdoc/doctest/make.rs index 5e571613d6f..3ff6828e52f 100644 --- a/src/librustdoc/doctest/make.rs +++ b/src/librustdoc/doctest/make.rs @@ -196,6 +196,80 @@ pub(crate) struct DocTestBuilder { pub(crate) can_be_merged: bool, } +/// Contains needed information for doctest to be correctly generated with expected "wrapping". +pub(crate) struct WrapperInfo { + pub(crate) before: String, + pub(crate) after: String, + pub(crate) returns_result: bool, + insert_indent_space: bool, +} + +impl WrapperInfo { + fn len(&self) -> usize { + self.before.len() + self.after.len() + } +} + +/// Contains a doctest information. Can be converted into code with the `to_string()` method. +pub(crate) enum DocTestWrapResult { + Valid { + crate_level_code: String, + /// This field can be `None` if one of the following conditions is true: + /// + /// * The doctest's codeblock has the `test_harness` attribute. + /// * The doctest has a `main` function. + /// * The doctest has the `![no_std]` attribute. + wrapper: Option<WrapperInfo>, + /// Contains the doctest processed code without the wrappers (which are stored in the + /// `wrapper` field). + code: String, + }, + /// Contains the original source code. + SyntaxError(String), +} + +impl std::string::ToString for DocTestWrapResult { + fn to_string(&self) -> String { + match self { + Self::SyntaxError(s) => s.clone(), + Self::Valid { crate_level_code, wrapper, code } => { + let mut prog_len = code.len() + crate_level_code.len(); + if let Some(wrapper) = wrapper { + prog_len += wrapper.len(); + if wrapper.insert_indent_space { + prog_len += code.lines().count() * 4; + } + } + let mut prog = String::with_capacity(prog_len); + + prog.push_str(crate_level_code); + if let Some(wrapper) = wrapper { + prog.push_str(&wrapper.before); + + // add extra 4 spaces for each line to offset the code block + if wrapper.insert_indent_space { + write!( + prog, + "{}", + fmt::from_fn(|f| code + .lines() + .map(|line| fmt::from_fn(move |f| write!(f, " {line}"))) + .joined("\n", f)) + ) + .unwrap(); + } else { + prog.push_str(code); + } + prog.push_str(&wrapper.after); + } else { + prog.push_str(code); + } + prog + } + } + } +} + impl DocTestBuilder { fn invalid( global_crate_attrs: Vec<String>, @@ -228,50 +302,49 @@ impl DocTestBuilder { dont_insert_main: bool, opts: &GlobalTestOptions, crate_name: Option<&str>, - ) -> (String, usize) { + ) -> (DocTestWrapResult, usize) { if self.invalid_ast { // If the AST failed to compile, no need to go generate a complete doctest, the error // will be better this way. debug!("invalid AST:\n{test_code}"); - return (test_code.to_string(), 0); + return (DocTestWrapResult::SyntaxError(test_code.to_string()), 0); } let mut line_offset = 0; - let mut prog = String::new(); - let everything_else = self.everything_else.trim(); - + let mut crate_level_code = String::new(); + let processed_code = self.everything_else.trim(); if self.global_crate_attrs.is_empty() { // If there aren't any attributes supplied by #![doc(test(attr(...)))], then allow some // lints that are commonly triggered in doctests. The crate-level test attributes are // commonly used to make tests fail in case they trigger warnings, so having this there in // that case may cause some tests to pass when they shouldn't have. - prog.push_str("#![allow(unused)]\n"); + crate_level_code.push_str("#![allow(unused)]\n"); line_offset += 1; } // Next, any attributes that came from #![doc(test(attr(...)))]. for attr in &self.global_crate_attrs { - prog.push_str(&format!("#![{attr}]\n")); + crate_level_code.push_str(&format!("#![{attr}]\n")); line_offset += 1; } // Now push any outer attributes from the example, assuming they // are intended to be crate attributes. if !self.crate_attrs.is_empty() { - prog.push_str(&self.crate_attrs); + crate_level_code.push_str(&self.crate_attrs); if !self.crate_attrs.ends_with('\n') { - prog.push('\n'); + crate_level_code.push('\n'); } } if !self.maybe_crate_attrs.is_empty() { - prog.push_str(&self.maybe_crate_attrs); + crate_level_code.push_str(&self.maybe_crate_attrs); if !self.maybe_crate_attrs.ends_with('\n') { - prog.push('\n'); + crate_level_code.push('\n'); } } if !self.crates.is_empty() { - prog.push_str(&self.crates); + crate_level_code.push_str(&self.crates); if !self.crates.ends_with('\n') { - prog.push('\n'); + crate_level_code.push('\n'); } } @@ -289,17 +362,20 @@ impl DocTestBuilder { { // rustdoc implicitly inserts an `extern crate` item for the own crate // which may be unused, so we need to allow the lint. - prog.push_str("#[allow(unused_extern_crates)]\n"); + crate_level_code.push_str("#[allow(unused_extern_crates)]\n"); - prog.push_str(&format!("extern crate r#{crate_name};\n")); + crate_level_code.push_str(&format!("extern crate r#{crate_name};\n")); line_offset += 1; } // FIXME: This code cannot yet handle no_std test cases yet - if dont_insert_main || self.has_main_fn || prog.contains("![no_std]") { - prog.push_str(everything_else); + let wrapper = if dont_insert_main + || self.has_main_fn + || crate_level_code.contains("![no_std]") + { + None } else { - let returns_result = everything_else.ends_with("(())"); + let returns_result = processed_code.ends_with("(())"); // Give each doctest main function a unique name. // This is for example needed for the tooling around `-C instrument-coverage`. let inner_fn_name = if let Some(ref test_id) = self.test_id { @@ -333,28 +409,22 @@ impl DocTestBuilder { // /// ``` <- end of the inner main line_offset += 1; - prog.push_str(&main_pre); - - // add extra 4 spaces for each line to offset the code block - if opts.insert_indent_space { - write!( - prog, - "{}", - fmt::from_fn(|f| everything_else - .lines() - .map(|line| fmt::from_fn(move |f| write!(f, " {line}"))) - .joined("\n", f)) - ) - .unwrap(); - } else { - prog.push_str(everything_else); - }; - prog.push_str(&main_post); - } - - debug!("final doctest:\n{prog}"); + Some(WrapperInfo { + before: main_pre, + after: main_post, + returns_result, + insert_indent_space: opts.insert_indent_space, + }) + }; - (prog, line_offset) + ( + DocTestWrapResult::Valid { + code: processed_code.to_string(), + wrapper, + crate_level_code, + }, + line_offset, + ) } } diff --git a/src/librustdoc/doctest/tests.rs b/src/librustdoc/doctest/tests.rs index ce2984ced79..ccc3e55a331 100644 --- a/src/librustdoc/doctest/tests.rs +++ b/src/librustdoc/doctest/tests.rs @@ -1,6 +1,11 @@ use std::path::PathBuf; -use super::{BuildDocTestBuilder, GlobalTestOptions}; +use rustc_span::edition::Edition; +use rustc_span::{DUMMY_SP, FileName}; + +use super::extracted::ExtractedDocTests; +use super::{BuildDocTestBuilder, GlobalTestOptions, ScrapedDocTest}; +use crate::html::markdown::LangString; fn make_test( test_code: &str, @@ -19,9 +24,9 @@ fn make_test( builder = builder.test_id(test_id.to_string()); } let doctest = builder.build(None); - let (code, line_offset) = + let (wrapped, line_offset) = doctest.generate_unique_doctest(test_code, dont_insert_main, opts, crate_name); - (code, line_offset) + (wrapped.to_string(), line_offset) } /// Default [`GlobalTestOptions`] for these unit tests. @@ -461,3 +466,51 @@ pub mod outer_module { let (output, len) = make_test(input, None, false, &opts, Vec::new(), None); assert_eq!((output, len), (expected, 2)); } + +fn get_extracted_doctests(code: &str) -> ExtractedDocTests { + let opts = default_global_opts(""); + let mut extractor = ExtractedDocTests::new(); + extractor.add_test_with_edition( + ScrapedDocTest::new( + FileName::Custom(String::new()), + 0, + Vec::new(), + LangString::default(), + code.to_string(), + DUMMY_SP, + Vec::new(), + ), + &opts, + Edition::Edition2018, + ); + extractor +} + +// Test that `extracted::DocTest::wrapper` is `None` if the doctest has a `main` function. +#[test] +fn test_extracted_doctest_wrapper_field() { + let extractor = get_extracted_doctests("fn main() {}"); + + assert_eq!(extractor.doctests().len(), 1); + let doctest_code = extractor.doctests()[0].doctest_code.as_ref().unwrap(); + assert!(doctest_code.wrapper.is_none()); +} + +// Test that `ExtractedDocTest::doctest_code` is `None` if the doctest has syntax error. +#[test] +fn test_extracted_doctest_doctest_code_field() { + let extractor = get_extracted_doctests("let x +="); + + assert_eq!(extractor.doctests().len(), 1); + assert!(extractor.doctests()[0].doctest_code.is_none()); +} + +// Test that `extracted::DocTest::wrapper` is `Some` if the doctest needs wrapping. +#[test] +fn test_extracted_doctest_wrapper_field_with_info() { + let extractor = get_extracted_doctests("let x = 12;"); + + assert_eq!(extractor.doctests().len(), 1); + let doctest_code = extractor.doctests()[0].doctest_code.as_ref().unwrap(); + assert!(doctest_code.wrapper.is_some()); +} diff --git a/src/librustdoc/formats/renderer.rs b/src/librustdoc/formats/renderer.rs index 5e4e6f27a15..48626171404 100644 --- a/src/librustdoc/formats/renderer.rs +++ b/src/librustdoc/formats/renderer.rs @@ -56,7 +56,7 @@ pub(crate) trait FormatRenderer<'tcx>: Sized { fn restore_module_data(&mut self, info: Self::ModuleData); /// Renders a single non-module item. This means no recursive sub-item rendering is required. - fn item(&mut self, item: clean::Item) -> Result<(), Error>; + fn item(&mut self, item: &clean::Item) -> Result<(), Error>; /// Renders a module (should not handle recursing into children). fn mod_item_in(&mut self, item: &clean::Item) -> Result<(), Error>; @@ -67,14 +67,14 @@ pub(crate) trait FormatRenderer<'tcx>: Sized { } /// Post processing hook for cleanup and dumping output to files. - fn after_krate(&mut self) -> Result<(), Error>; + fn after_krate(self) -> Result<(), Error>; fn cache(&self) -> &Cache; } fn run_format_inner<'tcx, T: FormatRenderer<'tcx>>( cx: &mut T, - item: clean::Item, + item: &clean::Item, prof: &SelfProfilerRef, ) -> Result<(), Error> { if item.is_mod() && T::RUN_ON_MODULE { @@ -84,12 +84,12 @@ fn run_format_inner<'tcx, T: FormatRenderer<'tcx>>( prof.generic_activity_with_arg("render_mod_item", item.name.unwrap().to_string()); cx.mod_item_in(&item)?; - let (clean::StrippedItem(box clean::ModuleItem(module)) | clean::ModuleItem(module)) = - item.inner.kind + let (clean::StrippedItem(box clean::ModuleItem(ref module)) + | clean::ModuleItem(ref module)) = item.inner.kind else { unreachable!() }; - for it in module.items { + for it in module.items.iter() { let info = cx.save_module_data(); run_format_inner(cx, it, prof)?; cx.restore_module_data(info); @@ -101,7 +101,7 @@ fn run_format_inner<'tcx, T: FormatRenderer<'tcx>>( } else if let Some(item_name) = item.name && !item.is_extern_crate() { - prof.generic_activity_with_arg("render_item", item_name.as_str()).run(|| cx.item(item))?; + prof.generic_activity_with_arg("render_item", item_name.as_str()).run(|| cx.item(&item))?; } Ok(()) } @@ -125,7 +125,7 @@ pub(crate) fn run_format<'tcx, T: FormatRenderer<'tcx>>( } // Render the crate documentation - run_format_inner(&mut format_renderer, krate.module, prof)?; + run_format_inner(&mut format_renderer, &krate.module, prof)?; prof.verbose_generic_activity_with_arg("renderer_after_krate", T::descr()) .run(|| format_renderer.after_krate()) diff --git a/src/librustdoc/html/markdown.rs b/src/librustdoc/html/markdown.rs index d3701784f9d..f626e07b000 100644 --- a/src/librustdoc/html/markdown.rs +++ b/src/librustdoc/html/markdown.rs @@ -307,7 +307,8 @@ impl<'a, I: Iterator<Item = Event<'a>>> Iterator for CodeBlocks<'_, 'a, I> { builder = builder.crate_name(krate); } let doctest = builder.build(None); - let (test, _) = doctest.generate_unique_doctest(&test, false, &opts, krate); + let (wrapped, _) = doctest.generate_unique_doctest(&test, false, &opts, krate); + let test = wrapped.to_string(); let channel = if test.contains("#![feature(") { "&version=nightly" } else { "" }; let test_escaped = small_url_encode(test); diff --git a/src/librustdoc/html/render/context.rs b/src/librustdoc/html/render/context.rs index 5984dcd74ca..38214451657 100644 --- a/src/librustdoc/html/render/context.rs +++ b/src/librustdoc/html/render/context.rs @@ -609,7 +609,7 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> { self.info = info; } - fn after_krate(&mut self) -> Result<(), Error> { + fn after_krate(mut self) -> Result<(), Error> { let crate_name = self.tcx().crate_name(LOCAL_CRATE); let final_file = self.dst.join(crate_name.as_str()).join("all.html"); let settings_file = self.dst.join("settings.html"); @@ -830,7 +830,7 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> { Ok(()) } - fn item(&mut self, item: clean::Item) -> Result<(), Error> { + fn item(&mut self, item: &clean::Item) -> Result<(), Error> { // Stripped modules survive the rustdoc passes (i.e., `strip-private`) // if they contain impls for public types. These modules can also // contain items such as publicly re-exported structures. diff --git a/src/librustdoc/json/conversions.rs b/src/librustdoc/json/conversions.rs index 3ade40940bc..6bdf3b5fe38 100644 --- a/src/librustdoc/json/conversions.rs +++ b/src/librustdoc/json/conversions.rs @@ -13,6 +13,7 @@ use rustc_metadata::rendered_const; use rustc_middle::{bug, ty}; use rustc_span::{Pos, Symbol, kw}; use rustdoc_json_types::*; +use thin_vec::ThinVec; use crate::clean::{self, ItemId}; use crate::formats::FormatRenderer; @@ -21,7 +22,7 @@ use crate::json::JsonRenderer; use crate::passes::collect_intra_doc_links::UrlFragment; impl JsonRenderer<'_> { - pub(super) fn convert_item(&self, item: clean::Item) -> Option<Item> { + pub(super) fn convert_item(&self, item: &clean::Item) -> Option<Item> { let deprecation = item.deprecation(self.tcx); let links = self .cache @@ -107,49 +108,54 @@ impl JsonRenderer<'_> { } } - fn ids(&self, items: impl IntoIterator<Item = clean::Item>) -> Vec<Id> { + fn ids(&self, items: &[clean::Item]) -> Vec<Id> { items - .into_iter() - .filter(|x| !x.is_stripped() && !x.is_keyword()) + .iter() + .filter(|i| !i.is_stripped() && !i.is_keyword()) .map(|i| self.id_from_item(&i)) .collect() } - fn ids_keeping_stripped( - &self, - items: impl IntoIterator<Item = clean::Item>, - ) -> Vec<Option<Id>> { + fn ids_keeping_stripped(&self, items: &[clean::Item]) -> Vec<Option<Id>> { items - .into_iter() + .iter() .map(|i| (!i.is_stripped() && !i.is_keyword()).then(|| self.id_from_item(&i))) .collect() } } pub(crate) trait FromClean<T> { - fn from_clean(f: T, renderer: &JsonRenderer<'_>) -> Self; + fn from_clean(f: &T, renderer: &JsonRenderer<'_>) -> Self; } pub(crate) trait IntoJson<T> { - fn into_json(self, renderer: &JsonRenderer<'_>) -> T; + fn into_json(&self, renderer: &JsonRenderer<'_>) -> T; } impl<T, U> IntoJson<U> for T where U: FromClean<T>, { - fn into_json(self, renderer: &JsonRenderer<'_>) -> U { + fn into_json(&self, renderer: &JsonRenderer<'_>) -> U { U::from_clean(self, renderer) } } -impl<I, T, U> FromClean<I> for Vec<U> +impl<T, U> FromClean<Vec<T>> for Vec<U> where - I: IntoIterator<Item = T>, U: FromClean<T>, { - fn from_clean(f: I, renderer: &JsonRenderer<'_>) -> Vec<U> { - f.into_iter().map(|x| x.into_json(renderer)).collect() + fn from_clean(items: &Vec<T>, renderer: &JsonRenderer<'_>) -> Vec<U> { + items.iter().map(|i| i.into_json(renderer)).collect() + } +} + +impl<T, U> FromClean<ThinVec<T>> for Vec<U> +where + U: FromClean<T>, +{ + fn from_clean(items: &ThinVec<T>, renderer: &JsonRenderer<'_>) -> Vec<U> { + items.iter().map(|i| i.into_json(renderer)).collect() } } @@ -165,7 +171,7 @@ pub(crate) fn from_deprecation(deprecation: attrs::Deprecation) -> Deprecation { } impl FromClean<clean::GenericArgs> for GenericArgs { - fn from_clean(args: clean::GenericArgs, renderer: &JsonRenderer<'_>) -> Self { + fn from_clean(args: &clean::GenericArgs, renderer: &JsonRenderer<'_>) -> Self { use clean::GenericArgs::*; match args { AngleBracketed { args, constraints } => GenericArgs::AngleBracketed { @@ -174,7 +180,7 @@ impl FromClean<clean::GenericArgs> for GenericArgs { }, Parenthesized { inputs, output } => GenericArgs::Parenthesized { inputs: inputs.into_json(renderer), - output: output.map(|a| (*a).into_json(renderer)), + output: output.as_ref().map(|a| a.as_ref().into_json(renderer)), }, ReturnTypeNotation => GenericArgs::ReturnTypeNotation, } @@ -182,7 +188,7 @@ impl FromClean<clean::GenericArgs> for GenericArgs { } impl FromClean<clean::GenericArg> for GenericArg { - fn from_clean(arg: clean::GenericArg, renderer: &JsonRenderer<'_>) -> Self { + fn from_clean(arg: &clean::GenericArg, renderer: &JsonRenderer<'_>) -> Self { use clean::GenericArg::*; match arg { Lifetime(l) => GenericArg::Lifetime(convert_lifetime(l)), @@ -195,7 +201,7 @@ impl FromClean<clean::GenericArg> for GenericArg { impl FromClean<clean::Constant> for Constant { // FIXME(generic_const_items): Add support for generic const items. - fn from_clean(constant: clean::Constant, renderer: &JsonRenderer<'_>) -> Self { + fn from_clean(constant: &clean::Constant, renderer: &JsonRenderer<'_>) -> Self { let tcx = renderer.tcx; let expr = constant.expr(tcx); let value = constant.value(tcx); @@ -206,7 +212,7 @@ impl FromClean<clean::Constant> for Constant { impl FromClean<clean::ConstantKind> for Constant { // FIXME(generic_const_items): Add support for generic const items. - fn from_clean(constant: clean::ConstantKind, renderer: &JsonRenderer<'_>) -> Self { + fn from_clean(constant: &clean::ConstantKind, renderer: &JsonRenderer<'_>) -> Self { let tcx = renderer.tcx; let expr = constant.expr(tcx); let value = constant.value(tcx); @@ -216,7 +222,7 @@ impl FromClean<clean::ConstantKind> for Constant { } impl FromClean<clean::AssocItemConstraint> for AssocItemConstraint { - fn from_clean(constraint: clean::AssocItemConstraint, renderer: &JsonRenderer<'_>) -> Self { + fn from_clean(constraint: &clean::AssocItemConstraint, renderer: &JsonRenderer<'_>) -> Self { AssocItemConstraint { name: constraint.assoc.name.to_string(), args: constraint.assoc.args.into_json(renderer), @@ -226,7 +232,7 @@ impl FromClean<clean::AssocItemConstraint> for AssocItemConstraint { } impl FromClean<clean::AssocItemConstraintKind> for AssocItemConstraintKind { - fn from_clean(kind: clean::AssocItemConstraintKind, renderer: &JsonRenderer<'_>) -> Self { + fn from_clean(kind: &clean::AssocItemConstraintKind, renderer: &JsonRenderer<'_>) -> Self { use clean::AssocItemConstraintKind::*; match kind { Equality { term } => AssocItemConstraintKind::Equality(term.into_json(renderer)), @@ -235,15 +241,15 @@ impl FromClean<clean::AssocItemConstraintKind> for AssocItemConstraintKind { } } -fn from_clean_item(item: clean::Item, renderer: &JsonRenderer<'_>) -> ItemEnum { +fn from_clean_item(item: &clean::Item, renderer: &JsonRenderer<'_>) -> ItemEnum { use clean::ItemKind::*; let name = item.name; let is_crate = item.is_crate(); let header = item.fn_header(renderer.tcx); - match item.inner.kind { + match &item.inner.kind { ModuleItem(m) => { - ItemEnum::Module(Module { is_crate, items: renderer.ids(m.items), is_stripped: false }) + ItemEnum::Module(Module { is_crate, items: renderer.ids(&m.items), is_stripped: false }) } ImportItem(i) => ItemEnum::Use(i.into_json(renderer)), StructItem(s) => ItemEnum::Struct(s.into_json(renderer)), @@ -251,27 +257,27 @@ fn from_clean_item(item: clean::Item, renderer: &JsonRenderer<'_>) -> ItemEnum { StructFieldItem(f) => ItemEnum::StructField(f.into_json(renderer)), EnumItem(e) => ItemEnum::Enum(e.into_json(renderer)), VariantItem(v) => ItemEnum::Variant(v.into_json(renderer)), - FunctionItem(f) => ItemEnum::Function(from_function(*f, true, header.unwrap(), renderer)), + FunctionItem(f) => ItemEnum::Function(from_function(f, true, header.unwrap(), renderer)), ForeignFunctionItem(f, _) => { - ItemEnum::Function(from_function(*f, false, header.unwrap(), renderer)) + ItemEnum::Function(from_function(f, false, header.unwrap(), renderer)) } - TraitItem(t) => ItemEnum::Trait((*t).into_json(renderer)), + TraitItem(t) => ItemEnum::Trait(t.as_ref().into_json(renderer)), TraitAliasItem(t) => ItemEnum::TraitAlias(t.into_json(renderer)), - MethodItem(m, _) => ItemEnum::Function(from_function(*m, true, header.unwrap(), renderer)), + MethodItem(m, _) => ItemEnum::Function(from_function(m, true, header.unwrap(), renderer)), RequiredMethodItem(m) => { - ItemEnum::Function(from_function(*m, false, header.unwrap(), renderer)) + ItemEnum::Function(from_function(m, false, header.unwrap(), renderer)) } - ImplItem(i) => ItemEnum::Impl((*i).into_json(renderer)), - StaticItem(s) => ItemEnum::Static(convert_static(s, rustc_hir::Safety::Safe, renderer)), + ImplItem(i) => ItemEnum::Impl(i.as_ref().into_json(renderer)), + StaticItem(s) => ItemEnum::Static(convert_static(s, &rustc_hir::Safety::Safe, renderer)), ForeignStaticItem(s, safety) => ItemEnum::Static(convert_static(s, safety, renderer)), ForeignTypeItem => ItemEnum::ExternType, - TypeAliasItem(t) => ItemEnum::TypeAlias(t.into_json(renderer)), + TypeAliasItem(t) => ItemEnum::TypeAlias(t.as_ref().into_json(renderer)), // FIXME(generic_const_items): Add support for generic free consts ConstantItem(ci) => ItemEnum::Constant { type_: ci.type_.into_json(renderer), const_: ci.kind.into_json(renderer), }, - MacroItem(m) => ItemEnum::Macro(m.source), + MacroItem(m) => ItemEnum::Macro(m.source.clone()), ProcMacroItem(m) => ItemEnum::ProcMacro(m.into_json(renderer)), PrimitiveItem(p) => { ItemEnum::Primitive(Primitive { @@ -281,7 +287,7 @@ fn from_clean_item(item: clean::Item, renderer: &JsonRenderer<'_>) -> ItemEnum { } // FIXME(generic_const_items): Add support for generic associated consts. RequiredAssocConstItem(_generics, ty) => { - ItemEnum::AssocConst { type_: (*ty).into_json(renderer), value: None } + ItemEnum::AssocConst { type_: ty.as_ref().into_json(renderer), value: None } } // FIXME(generic_const_items): Add support for generic associated consts. ProvidedAssocConstItem(ci) | ImplAssocConstItem(ci) => ItemEnum::AssocConst { @@ -296,22 +302,22 @@ fn from_clean_item(item: clean::Item, renderer: &JsonRenderer<'_>) -> ItemEnum { AssocTypeItem(t, b) => ItemEnum::AssocType { generics: t.generics.into_json(renderer), bounds: b.into_json(renderer), - type_: Some(t.item_type.unwrap_or(t.type_).into_json(renderer)), + type_: Some(t.item_type.as_ref().unwrap_or(&t.type_).into_json(renderer)), }, // `convert_item` early returns `None` for stripped items and keywords. KeywordItem => unreachable!(), StrippedItem(inner) => { - match *inner { + match inner.as_ref() { ModuleItem(m) => ItemEnum::Module(Module { is_crate, - items: renderer.ids(m.items), + items: renderer.ids(&m.items), is_stripped: true, }), // `convert_item` early returns `None` for stripped items we're not including _ => unreachable!(), } } - ExternCrateItem { ref src } => ItemEnum::ExternCrate { + ExternCrateItem { src } => ItemEnum::ExternCrate { name: name.as_ref().unwrap().to_string(), rename: src.map(|x| x.to_string()), }, @@ -319,17 +325,17 @@ fn from_clean_item(item: clean::Item, renderer: &JsonRenderer<'_>) -> ItemEnum { } impl FromClean<clean::Struct> for Struct { - fn from_clean(struct_: clean::Struct, renderer: &JsonRenderer<'_>) -> Self { + fn from_clean(struct_: &clean::Struct, renderer: &JsonRenderer<'_>) -> Self { let has_stripped_fields = struct_.has_stripped_entries(); let clean::Struct { ctor_kind, generics, fields } = struct_; let kind = match ctor_kind { - Some(CtorKind::Fn) => StructKind::Tuple(renderer.ids_keeping_stripped(fields)), + Some(CtorKind::Fn) => StructKind::Tuple(renderer.ids_keeping_stripped(&fields)), Some(CtorKind::Const) => { assert!(fields.is_empty()); StructKind::Unit } - None => StructKind::Plain { fields: renderer.ids(fields), has_stripped_fields }, + None => StructKind::Plain { fields: renderer.ids(&fields), has_stripped_fields }, }; Struct { @@ -341,13 +347,13 @@ impl FromClean<clean::Struct> for Struct { } impl FromClean<clean::Union> for Union { - fn from_clean(union_: clean::Union, renderer: &JsonRenderer<'_>) -> Self { + fn from_clean(union_: &clean::Union, renderer: &JsonRenderer<'_>) -> Self { let has_stripped_fields = union_.has_stripped_entries(); let clean::Union { generics, fields } = union_; Union { generics: generics.into_json(renderer), has_stripped_fields, - fields: renderer.ids(fields), + fields: renderer.ids(&fields), impls: Vec::new(), // Added in JsonRenderer::item } } @@ -377,12 +383,12 @@ fn convert_abi(a: ExternAbi) -> Abi { } } -fn convert_lifetime(l: clean::Lifetime) -> String { +fn convert_lifetime(l: &clean::Lifetime) -> String { l.0.to_string() } impl FromClean<clean::Generics> for Generics { - fn from_clean(generics: clean::Generics, renderer: &JsonRenderer<'_>) -> Self { + fn from_clean(generics: &clean::Generics, renderer: &JsonRenderer<'_>) -> Self { Generics { params: generics.params.into_json(renderer), where_predicates: generics.where_predicates.into_json(renderer), @@ -391,7 +397,7 @@ impl FromClean<clean::Generics> for Generics { } impl FromClean<clean::GenericParamDef> for GenericParamDef { - fn from_clean(generic_param: clean::GenericParamDef, renderer: &JsonRenderer<'_>) -> Self { + fn from_clean(generic_param: &clean::GenericParamDef, renderer: &JsonRenderer<'_>) -> Self { GenericParamDef { name: generic_param.name.to_string(), kind: generic_param.kind.into_json(renderer), @@ -400,7 +406,7 @@ impl FromClean<clean::GenericParamDef> for GenericParamDef { } impl FromClean<clean::GenericParamDefKind> for GenericParamDefKind { - fn from_clean(kind: clean::GenericParamDefKind, renderer: &JsonRenderer<'_>) -> Self { + fn from_clean(kind: &clean::GenericParamDefKind, renderer: &JsonRenderer<'_>) -> Self { use clean::GenericParamDefKind::*; match kind { Lifetime { outlives } => GenericParamDefKind::Lifetime { @@ -408,29 +414,29 @@ impl FromClean<clean::GenericParamDefKind> for GenericParamDefKind { }, Type { bounds, default, synthetic } => GenericParamDefKind::Type { bounds: bounds.into_json(renderer), - default: default.map(|x| (*x).into_json(renderer)), - is_synthetic: synthetic, + default: default.as_ref().map(|x| x.as_ref().into_json(renderer)), + is_synthetic: *synthetic, }, Const { ty, default, synthetic: _ } => GenericParamDefKind::Const { - type_: (*ty).into_json(renderer), - default: default.map(|x| *x), + type_: ty.as_ref().into_json(renderer), + default: default.as_ref().map(|x| x.as_ref().clone()), }, } } } impl FromClean<clean::WherePredicate> for WherePredicate { - fn from_clean(predicate: clean::WherePredicate, renderer: &JsonRenderer<'_>) -> Self { + fn from_clean(predicate: &clean::WherePredicate, renderer: &JsonRenderer<'_>) -> Self { use clean::WherePredicate::*; match predicate { BoundPredicate { ty, bounds, bound_params } => WherePredicate::BoundPredicate { type_: ty.into_json(renderer), bounds: bounds.into_json(renderer), generic_params: bound_params - .into_iter() + .iter() .map(|x| { let name = x.name.to_string(); - let kind = match x.kind { + let kind = match &x.kind { clean::GenericParamDefKind::Lifetime { outlives } => { GenericParamDefKind::Lifetime { outlives: outlives.iter().map(|lt| lt.0.to_string()).collect(), @@ -442,14 +448,16 @@ impl FromClean<clean::WherePredicate> for WherePredicate { .into_iter() .map(|bound| bound.into_json(renderer)) .collect(), - default: default.map(|ty| (*ty).into_json(renderer)), - is_synthetic: synthetic, + default: default + .as_ref() + .map(|ty| ty.as_ref().into_json(renderer)), + is_synthetic: *synthetic, } } clean::GenericParamDefKind::Const { ty, default, synthetic: _ } => { GenericParamDefKind::Const { - type_: (*ty).into_json(renderer), - default: default.map(|d| *d), + type_: ty.as_ref().into_json(renderer), + default: default.as_ref().map(|d| d.as_ref().clone()), } } }; @@ -462,7 +470,7 @@ impl FromClean<clean::WherePredicate> for WherePredicate { outlives: bounds .iter() .map(|bound| match bound { - clean::GenericBound::Outlives(lt) => convert_lifetime(*lt), + clean::GenericBound::Outlives(lt) => convert_lifetime(lt), _ => bug!("found non-outlives-bound on lifetime predicate"), }) .collect(), @@ -479,7 +487,7 @@ impl FromClean<clean::WherePredicate> for WherePredicate { } impl FromClean<clean::GenericBound> for GenericBound { - fn from_clean(bound: clean::GenericBound, renderer: &JsonRenderer<'_>) -> Self { + fn from_clean(bound: &clean::GenericBound, renderer: &JsonRenderer<'_>) -> Self { use clean::GenericBound::*; match bound { TraitBound(clean::PolyTrait { trait_, generic_params }, modifier) => { @@ -494,7 +502,7 @@ impl FromClean<clean::GenericBound> for GenericBound { args.iter() .map(|arg| match arg { clean::PreciseCapturingArg::Lifetime(lt) => { - PreciseCapturingArg::Lifetime(convert_lifetime(*lt)) + PreciseCapturingArg::Lifetime(convert_lifetime(lt)) } clean::PreciseCapturingArg::Param(param) => { PreciseCapturingArg::Param(param.to_string()) @@ -507,7 +515,7 @@ impl FromClean<clean::GenericBound> for GenericBound { } pub(crate) fn from_trait_bound_modifier( - modifiers: rustc_hir::TraitBoundModifiers, + modifiers: &rustc_hir::TraitBoundModifiers, ) -> TraitBoundModifier { use rustc_hir as hir; let hir::TraitBoundModifiers { constness, polarity } = modifiers; @@ -523,7 +531,7 @@ pub(crate) fn from_trait_bound_modifier( } impl FromClean<clean::Type> for Type { - fn from_clean(ty: clean::Type, renderer: &JsonRenderer<'_>) -> Self { + fn from_clean(ty: &clean::Type, renderer: &JsonRenderer<'_>) -> Self { use clean::Type::{ Array, BareFunction, BorrowedRef, Generic, ImplTrait, Infer, Primitive, QPath, RawPointer, SelfTy, Slice, Tuple, UnsafeBinder, @@ -532,35 +540,35 @@ impl FromClean<clean::Type> for Type { match ty { clean::Type::Path { path } => Type::ResolvedPath(path.into_json(renderer)), clean::Type::DynTrait(bounds, lt) => Type::DynTrait(DynTrait { - lifetime: lt.map(convert_lifetime), + lifetime: lt.as_ref().map(convert_lifetime), traits: bounds.into_json(renderer), }), Generic(s) => Type::Generic(s.to_string()), // FIXME: add dedicated variant to json Type? SelfTy => Type::Generic("Self".to_owned()), Primitive(p) => Type::Primitive(p.as_sym().to_string()), - BareFunction(f) => Type::FunctionPointer(Box::new((*f).into_json(renderer))), + BareFunction(f) => Type::FunctionPointer(Box::new(f.as_ref().into_json(renderer))), Tuple(t) => Type::Tuple(t.into_json(renderer)), - Slice(t) => Type::Slice(Box::new((*t).into_json(renderer))), + Slice(t) => Type::Slice(Box::new(t.as_ref().into_json(renderer))), Array(t, s) => { - Type::Array { type_: Box::new((*t).into_json(renderer)), len: s.to_string() } + Type::Array { type_: Box::new(t.as_ref().into_json(renderer)), len: s.to_string() } } clean::Type::Pat(t, p) => Type::Pat { - type_: Box::new((*t).into_json(renderer)), + type_: Box::new(t.as_ref().into_json(renderer)), __pat_unstable_do_not_use: p.to_string(), }, ImplTrait(g) => Type::ImplTrait(g.into_json(renderer)), Infer => Type::Infer, RawPointer(mutability, type_) => Type::RawPointer { - is_mutable: mutability == ast::Mutability::Mut, - type_: Box::new((*type_).into_json(renderer)), + is_mutable: *mutability == ast::Mutability::Mut, + type_: Box::new(type_.as_ref().into_json(renderer)), }, BorrowedRef { lifetime, mutability, type_ } => Type::BorrowedRef { - lifetime: lifetime.map(convert_lifetime), - is_mutable: mutability == ast::Mutability::Mut, - type_: Box::new((*type_).into_json(renderer)), + lifetime: lifetime.as_ref().map(convert_lifetime), + is_mutable: *mutability == ast::Mutability::Mut, + type_: Box::new(type_.as_ref().into_json(renderer)), }, - QPath(qpath) => (*qpath).into_json(renderer), + QPath(qpath) => qpath.as_ref().into_json(renderer), // FIXME(unsafe_binder): Implement rustdoc-json. UnsafeBinder(_) => todo!(), } @@ -568,30 +576,30 @@ impl FromClean<clean::Type> for Type { } impl FromClean<clean::Path> for Path { - fn from_clean(path: clean::Path, renderer: &JsonRenderer<'_>) -> Path { + fn from_clean(path: &clean::Path, renderer: &JsonRenderer<'_>) -> Path { Path { path: path.whole_name(), id: renderer.id_from_item_default(path.def_id().into()), - args: path.segments.last().map(|args| Box::new(args.clone().args.into_json(renderer))), + args: path.segments.last().map(|args| Box::new(args.args.into_json(renderer))), } } } impl FromClean<clean::QPathData> for Type { - fn from_clean(qpath: clean::QPathData, renderer: &JsonRenderer<'_>) -> Self { + fn from_clean(qpath: &clean::QPathData, renderer: &JsonRenderer<'_>) -> Self { let clean::QPathData { assoc, self_type, should_fully_qualify: _, trait_ } = qpath; Self::QualifiedPath { name: assoc.name.to_string(), args: Box::new(assoc.args.into_json(renderer)), self_type: Box::new(self_type.into_json(renderer)), - trait_: trait_.map(|trait_| trait_.into_json(renderer)), + trait_: trait_.as_ref().map(|trait_| trait_.into_json(renderer)), } } } impl FromClean<clean::Term> for Term { - fn from_clean(term: clean::Term, renderer: &JsonRenderer<'_>) -> Term { + fn from_clean(term: &clean::Term, renderer: &JsonRenderer<'_>) -> Term { match term { clean::Term::Type(ty) => Term::Type(ty.into_json(renderer)), clean::Term::Constant(c) => Term::Constant(c.into_json(renderer)), @@ -600,14 +608,14 @@ impl FromClean<clean::Term> for Term { } impl FromClean<clean::BareFunctionDecl> for FunctionPointer { - fn from_clean(bare_decl: clean::BareFunctionDecl, renderer: &JsonRenderer<'_>) -> Self { + fn from_clean(bare_decl: &clean::BareFunctionDecl, renderer: &JsonRenderer<'_>) -> Self { let clean::BareFunctionDecl { safety, generic_params, decl, abi } = bare_decl; FunctionPointer { header: FunctionHeader { is_unsafe: safety.is_unsafe(), is_const: false, is_async: false, - abi: convert_abi(abi), + abi: convert_abi(*abi), }, generic_params: generic_params.into_json(renderer), sig: decl.into_json(renderer), @@ -616,7 +624,7 @@ impl FromClean<clean::BareFunctionDecl> for FunctionPointer { } impl FromClean<clean::FnDecl> for FunctionSignature { - fn from_clean(decl: clean::FnDecl, renderer: &JsonRenderer<'_>) -> Self { + fn from_clean(decl: &clean::FnDecl, renderer: &JsonRenderer<'_>) -> Self { let clean::FnDecl { inputs, output, c_variadic } = decl; FunctionSignature { inputs: inputs @@ -629,13 +637,13 @@ impl FromClean<clean::FnDecl> for FunctionSignature { }) .collect(), output: if output.is_unit() { None } else { Some(output.into_json(renderer)) }, - is_c_variadic: c_variadic, + is_c_variadic: *c_variadic, } } } impl FromClean<clean::Trait> for Trait { - fn from_clean(trait_: clean::Trait, renderer: &JsonRenderer<'_>) -> Self { + fn from_clean(trait_: &clean::Trait, renderer: &JsonRenderer<'_>) -> Self { let tcx = renderer.tcx; let is_auto = trait_.is_auto(tcx); let is_unsafe = trait_.safety(tcx).is_unsafe(); @@ -645,7 +653,7 @@ impl FromClean<clean::Trait> for Trait { is_auto, is_unsafe, is_dyn_compatible, - items: renderer.ids(items), + items: renderer.ids(&items), generics: generics.into_json(renderer), bounds: bounds.into_json(renderer), implementations: Vec::new(), // Added in JsonRenderer::item @@ -655,7 +663,7 @@ impl FromClean<clean::Trait> for Trait { impl FromClean<clean::PolyTrait> for PolyTrait { fn from_clean( - clean::PolyTrait { trait_, generic_params }: clean::PolyTrait, + clean::PolyTrait { trait_, generic_params }: &clean::PolyTrait, renderer: &JsonRenderer<'_>, ) -> Self { PolyTrait { @@ -666,14 +674,14 @@ impl FromClean<clean::PolyTrait> for PolyTrait { } impl FromClean<clean::Impl> for Impl { - fn from_clean(impl_: clean::Impl, renderer: &JsonRenderer<'_>) -> Self { + fn from_clean(impl_: &clean::Impl, renderer: &JsonRenderer<'_>) -> Self { let provided_trait_methods = impl_.provided_trait_methods(renderer.tcx); let clean::Impl { safety, generics, trait_, for_, items, polarity, kind } = impl_; // FIXME: use something like ImplKind in JSON? let (is_synthetic, blanket_impl) = match kind { clean::ImplKind::Normal | clean::ImplKind::FakeVariadic => (false, None), clean::ImplKind::Auto => (true, None), - clean::ImplKind::Blanket(ty) => (false, Some(*ty)), + clean::ImplKind::Blanket(ty) => (false, Some(ty)), }; let is_negative = match polarity { ty::ImplPolarity::Positive | ty::ImplPolarity::Reservation => false, @@ -686,18 +694,18 @@ impl FromClean<clean::Impl> for Impl { .into_iter() .map(|x| x.to_string()) .collect(), - trait_: trait_.map(|path| path.into_json(renderer)), + trait_: trait_.as_ref().map(|path| path.into_json(renderer)), for_: for_.into_json(renderer), - items: renderer.ids(items), + items: renderer.ids(&items), is_negative, is_synthetic, - blanket_impl: blanket_impl.map(|x| x.into_json(renderer)), + blanket_impl: blanket_impl.map(|x| x.as_ref().into_json(renderer)), } } } pub(crate) fn from_function( - clean::Function { decl, generics }: clean::Function, + clean::Function { decl, generics }: &clean::Function, has_body: bool, header: rustc_hir::FnHeader, renderer: &JsonRenderer<'_>, @@ -711,30 +719,30 @@ pub(crate) fn from_function( } impl FromClean<clean::Enum> for Enum { - fn from_clean(enum_: clean::Enum, renderer: &JsonRenderer<'_>) -> Self { + fn from_clean(enum_: &clean::Enum, renderer: &JsonRenderer<'_>) -> Self { let has_stripped_variants = enum_.has_stripped_entries(); let clean::Enum { variants, generics } = enum_; Enum { generics: generics.into_json(renderer), has_stripped_variants, - variants: renderer.ids(variants), + variants: renderer.ids(&variants.as_slice().raw), impls: Vec::new(), // Added in JsonRenderer::item } } } impl FromClean<clean::Variant> for Variant { - fn from_clean(variant: clean::Variant, renderer: &JsonRenderer<'_>) -> Self { + fn from_clean(variant: &clean::Variant, renderer: &JsonRenderer<'_>) -> Self { use clean::VariantKind::*; - let discriminant = variant.discriminant.map(|d| d.into_json(renderer)); + let discriminant = variant.discriminant.as_ref().map(|d| d.into_json(renderer)); - let kind = match variant.kind { + let kind = match &variant.kind { CLike => VariantKind::Plain, - Tuple(fields) => VariantKind::Tuple(renderer.ids_keeping_stripped(fields)), + Tuple(fields) => VariantKind::Tuple(renderer.ids_keeping_stripped(&fields)), Struct(s) => VariantKind::Struct { has_stripped_fields: s.has_stripped_entries(), - fields: renderer.ids(s.fields), + fields: renderer.ids(&s.fields), }, }; @@ -743,7 +751,7 @@ impl FromClean<clean::Variant> for Variant { } impl FromClean<clean::Discriminant> for Discriminant { - fn from_clean(disr: clean::Discriminant, renderer: &JsonRenderer<'_>) -> Self { + fn from_clean(disr: &clean::Discriminant, renderer: &JsonRenderer<'_>) -> Self { let tcx = renderer.tcx; Discriminant { // expr is only none if going through the inlining path, which gets @@ -756,7 +764,7 @@ impl FromClean<clean::Discriminant> for Discriminant { } impl FromClean<clean::Import> for Use { - fn from_clean(import: clean::Import, renderer: &JsonRenderer<'_>) -> Self { + fn from_clean(import: &clean::Import, renderer: &JsonRenderer<'_>) -> Self { use clean::ImportKind::*; let (name, is_glob) = match import.kind { Simple(s) => (s.to_string(), false), @@ -775,7 +783,7 @@ impl FromClean<clean::Import> for Use { } impl FromClean<clean::ProcMacro> for ProcMacro { - fn from_clean(mac: clean::ProcMacro, _renderer: &JsonRenderer<'_>) -> Self { + fn from_clean(mac: &clean::ProcMacro, _renderer: &JsonRenderer<'_>) -> Self { ProcMacro { kind: from_macro_kind(mac.kind), helpers: mac.helpers.iter().map(|x| x.to_string()).collect(), @@ -792,21 +800,21 @@ pub(crate) fn from_macro_kind(kind: rustc_span::hygiene::MacroKind) -> MacroKind } } -impl FromClean<Box<clean::TypeAlias>> for TypeAlias { - fn from_clean(type_alias: Box<clean::TypeAlias>, renderer: &JsonRenderer<'_>) -> Self { - let clean::TypeAlias { type_, generics, item_type: _, inner_type: _ } = *type_alias; +impl FromClean<clean::TypeAlias> for TypeAlias { + fn from_clean(type_alias: &clean::TypeAlias, renderer: &JsonRenderer<'_>) -> Self { + let clean::TypeAlias { type_, generics, item_type: _, inner_type: _ } = type_alias; TypeAlias { type_: type_.into_json(renderer), generics: generics.into_json(renderer) } } } fn convert_static( - stat: clean::Static, - safety: rustc_hir::Safety, + stat: &clean::Static, + safety: &rustc_hir::Safety, renderer: &JsonRenderer<'_>, ) -> Static { let tcx = renderer.tcx; Static { - type_: (*stat.type_).into_json(renderer), + type_: stat.type_.as_ref().into_json(renderer), is_mutable: stat.mutability == ast::Mutability::Mut, is_unsafe: safety.is_unsafe(), expr: stat @@ -817,7 +825,7 @@ fn convert_static( } impl FromClean<clean::TraitAlias> for TraitAlias { - fn from_clean(alias: clean::TraitAlias, renderer: &JsonRenderer<'_>) -> Self { + fn from_clean(alias: &clean::TraitAlias, renderer: &JsonRenderer<'_>) -> Self { TraitAlias { generics: alias.generics.into_json(renderer), params: alias.bounds.into_json(renderer), @@ -826,7 +834,7 @@ impl FromClean<clean::TraitAlias> for TraitAlias { } impl FromClean<ItemType> for ItemKind { - fn from_clean(kind: ItemType, _renderer: &JsonRenderer<'_>) -> Self { + fn from_clean(kind: &ItemType, _renderer: &JsonRenderer<'_>) -> Self { use ItemType::*; match kind { Module => ItemKind::Module, diff --git a/src/librustdoc/json/mod.rs b/src/librustdoc/json/mod.rs index 0ceeea2b9b1..2feadce26d0 100644 --- a/src/librustdoc/json/mod.rs +++ b/src/librustdoc/json/mod.rs @@ -37,19 +37,18 @@ use crate::formats::cache::Cache; use crate::json::conversions::IntoJson; use crate::{clean, try_err}; -#[derive(Clone)] pub(crate) struct JsonRenderer<'tcx> { tcx: TyCtxt<'tcx>, /// A mapping of IDs that contains all local items for this crate which gets output as a top /// level field of the JSON blob. - index: Rc<RefCell<FxHashMap<types::Id, types::Item>>>, + index: FxHashMap<types::Id, types::Item>, /// The directory where the JSON blob should be written to. /// /// If this is `None`, the blob will be printed to `stdout` instead. out_dir: Option<PathBuf>, cache: Rc<Cache>, imported_items: DefIdSet, - id_interner: Rc<RefCell<ids::IdInterner>>, + id_interner: RefCell<ids::IdInterner>, } impl<'tcx> JsonRenderer<'tcx> { @@ -66,7 +65,7 @@ impl<'tcx> JsonRenderer<'tcx> { .iter() .map(|i| { let item = &i.impl_item; - self.item(item.clone()).unwrap(); + self.item(item).unwrap(); self.id_from_item(item) }) .collect() @@ -97,7 +96,7 @@ impl<'tcx> JsonRenderer<'tcx> { } if item.item_id.is_local() || is_primitive_impl { - self.item(item.clone()).unwrap(); + self.item(item).unwrap(); Some(self.id_from_item(item)) } else { None @@ -198,7 +197,7 @@ impl<'tcx> FormatRenderer<'tcx> for JsonRenderer<'tcx> { Ok(( JsonRenderer { tcx, - index: Rc::new(RefCell::new(FxHashMap::default())), + index: FxHashMap::default(), out_dir: if options.output_to_stdout { None } else { Some(options.output) }, cache: Rc::new(cache), imported_items, @@ -218,7 +217,9 @@ impl<'tcx> FormatRenderer<'tcx> for JsonRenderer<'tcx> { /// Inserts an item into the index. This should be used rather than directly calling insert on /// the hashmap because certain items (traits and types) need to have their mappings for trait /// implementations filled out before they're inserted. - fn item(&mut self, item: clean::Item) -> Result<(), Error> { + fn item(&mut self, item: &clean::Item) -> Result<(), Error> { + use std::collections::hash_map::Entry; + let item_type = item.type_(); let item_name = item.name; trace!("rendering {item_type} {item_name:?}"); @@ -226,11 +227,11 @@ impl<'tcx> FormatRenderer<'tcx> for JsonRenderer<'tcx> { // Flatten items that recursively store other items. We include orphaned items from // stripped modules and etc that are otherwise reachable. if let ItemKind::StrippedItem(inner) = &item.kind { - inner.inner_items().for_each(|i| self.item(i.clone()).unwrap()); + inner.inner_items().for_each(|i| self.item(i).unwrap()); } // Flatten items that recursively store other items - item.kind.inner_items().for_each(|i| self.item(i.clone()).unwrap()); + item.kind.inner_items().for_each(|i| self.item(i).unwrap()); let item_id = item.item_id; if let Some(mut new_item) = self.convert_item(item) { @@ -273,18 +274,25 @@ impl<'tcx> FormatRenderer<'tcx> for JsonRenderer<'tcx> { | types::ItemEnum::Macro(_) | types::ItemEnum::ProcMacro(_) => false, }; - let removed = self.index.borrow_mut().insert(new_item.id, new_item.clone()); // FIXME(adotinthevoid): Currently, the index is duplicated. This is a sanity check // to make sure the items are unique. The main place this happens is when an item, is // reexported in more than one place. See `rustdoc-json/reexport/in_root_and_mod` - if let Some(old_item) = removed { - // In case of generic implementations (like `impl<T> Trait for T {}`), all the - // inner items will be duplicated so we can ignore if they are slightly different. - if !can_be_ignored { - assert_eq!(old_item, new_item); + match self.index.entry(new_item.id) { + Entry::Vacant(entry) => { + entry.insert(new_item); + } + Entry::Occupied(mut entry) => { + // In case of generic implementations (like `impl<T> Trait for T {}`), all the + // inner items will be duplicated so we can ignore if they are slightly + // different. + let old_item = entry.get_mut(); + if !can_be_ignored { + assert_eq!(*old_item, new_item); + } + trace!("replaced {old_item:?}\nwith {new_item:?}"); + *old_item = new_item; } - trace!("replaced {old_item:?}\nwith {new_item:?}"); } } @@ -296,11 +304,13 @@ impl<'tcx> FormatRenderer<'tcx> for JsonRenderer<'tcx> { unreachable!("RUN_ON_MODULE = false, should never call mod_item_in") } - fn after_krate(&mut self) -> Result<(), Error> { + fn after_krate(mut self) -> Result<(), Error> { debug!("Done with crate"); let e = ExternalCrate { crate_num: LOCAL_CRATE }; - let index = (*self.index).clone().into_inner(); + + // We've finished using the index, and don't want to clone it, because it is big. + let index = std::mem::take(&mut self.index); // Note that tcx.rust_target_features is inappropriate here because rustdoc tries to run for // multiple targets: https://github.com/rust-lang/rust/pull/137632 @@ -325,7 +335,7 @@ impl<'tcx> FormatRenderer<'tcx> for JsonRenderer<'tcx> { types::ItemSummary { crate_id: k.krate.as_u32(), path: path.iter().map(|s| s.to_string()).collect(), - kind: kind.into_json(self), + kind: kind.into_json(&self), }, ) }) diff --git a/src/tools/clippy/tests/ui/large_stack_frames.rs b/src/tools/clippy/tests/ui/large_stack_frames.rs index 3ed124f69ef..132f1450b6d 100644 --- a/src/tools/clippy/tests/ui/large_stack_frames.rs +++ b/src/tools/clippy/tests/ui/large_stack_frames.rs @@ -1,8 +1,7 @@ //@ normalize-stderr-test: "\b10000(08|16|32)\b" -> "100$$PTR" //@ normalize-stderr-test: "\b2500(060|120)\b" -> "250$$PTR" -#![allow(unused, incomplete_features)] +#![allow(unused)] #![warn(clippy::large_stack_frames)] -#![feature(unsized_locals)] use std::hint::black_box; @@ -11,11 +10,6 @@ fn generic<T: Default>() { black_box(&x); } -fn unsized_local() { - let x: dyn std::fmt::Display = *(Box::new(1) as Box<dyn std::fmt::Display>); - black_box(&x); -} - struct ArrayDefault<const N: usize>([u8; N]); impl<const N: usize> Default for ArrayDefault<N> { diff --git a/src/tools/clippy/tests/ui/large_stack_frames.stderr b/src/tools/clippy/tests/ui/large_stack_frames.stderr index 0ff49e9f5b3..79482e65c3e 100644 --- a/src/tools/clippy/tests/ui/large_stack_frames.stderr +++ b/src/tools/clippy/tests/ui/large_stack_frames.stderr @@ -1,5 +1,5 @@ error: this function may allocate 250$PTR bytes on the stack - --> tests/ui/large_stack_frames.rs:27:4 + --> tests/ui/large_stack_frames.rs:21:4 | LL | fn many_small_arrays() { | ^^^^^^^^^^^^^^^^^ @@ -13,7 +13,7 @@ LL | let x5 = [0u8; 500_000]; = help: to override `-D warnings` add `#[allow(clippy::large_stack_frames)]` error: this function may allocate 1000000 bytes on the stack - --> tests/ui/large_stack_frames.rs:38:4 + --> tests/ui/large_stack_frames.rs:32:4 | LL | fn large_return_value() -> ArrayDefault<1_000_000> { | ^^^^^^^^^^^^^^^^^^ ----------------------- this is the largest part, at 1000000 bytes for type `ArrayDefault<1000000>` @@ -21,7 +21,7 @@ LL | fn large_return_value() -> ArrayDefault<1_000_000> { = note: 1000000 bytes is larger than Clippy's configured `stack-size-threshold` of 512000 error: this function may allocate 100$PTR bytes on the stack - --> tests/ui/large_stack_frames.rs:44:4 + --> tests/ui/large_stack_frames.rs:38:4 | LL | fn large_fn_arg(x: ArrayDefault<1_000_000>) { | ^^^^^^^^^^^^ - `x` is the largest part, at 1000000 bytes for type `ArrayDefault<1000000>` @@ -29,7 +29,7 @@ LL | fn large_fn_arg(x: ArrayDefault<1_000_000>) { = note: 100$PTR bytes is larger than Clippy's configured `stack-size-threshold` of 512000 error: this function may allocate 100$PTR bytes on the stack - --> tests/ui/large_stack_frames.rs:51:13 + --> tests/ui/large_stack_frames.rs:45:13 | LL | let f = || black_box(&[0u8; 1_000_000]); | ^^^^^^^^^^^^^^----------------^ diff --git a/src/tools/miri/tests/fail/unsized-local.rs b/src/tools/miri/tests/fail/unsized-local.rs deleted file mode 100644 index ceccae4e3e7..00000000000 --- a/src/tools/miri/tests/fail/unsized-local.rs +++ /dev/null @@ -1,23 +0,0 @@ -#![feature(unsized_locals)] -#![allow(incomplete_features)] - -fn main() { - pub trait Foo { - fn foo(self) -> String; - } - - struct A; - - impl Foo for A { - fn foo(self) -> String { - format!("hello") - } - } - - let x = *(Box::new(A) as Box<dyn Foo>); //~ERROR: unsized locals are not supported - assert_eq!(x.foo(), format!("hello")); - - // I'm not sure whether we want this to work - let x = Box::new(A) as Box<dyn Foo>; - assert_eq!(x.foo(), format!("hello")); -} diff --git a/src/tools/miri/tests/fail/unsized-local.stderr b/src/tools/miri/tests/fail/unsized-local.stderr deleted file mode 100644 index 1cfb461b78f..00000000000 --- a/src/tools/miri/tests/fail/unsized-local.stderr +++ /dev/null @@ -1,14 +0,0 @@ -error: unsupported operation: unsized locals are not supported - --> tests/fail/unsized-local.rs:LL:CC - | -LL | let x = *(Box::new(A) as Box<dyn Foo>); - | ^ unsupported operation occurred here - | - = help: this is likely not a bug in the program; it indicates that the program performed an operation that Miri does not support - = note: BACKTRACE: - = note: inside `main` at tests/fail/unsized-local.rs:LL:CC - -note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace - -error: aborting due to 1 previous error - diff --git a/src/tools/tidy/src/issues.txt b/src/tools/tidy/src/issues.txt index 3e9d79224fd..045f2f0692a 100644 --- a/src/tools/tidy/src/issues.txt +++ b/src/tools/tidy/src/issues.txt @@ -754,7 +754,6 @@ ui/consts/issue-46553.rs ui/consts/issue-47789.rs ui/consts/issue-50439.rs ui/consts/issue-52023-array-size-pointer-cast.rs -ui/consts/issue-54224.rs ui/consts/issue-54348.rs ui/consts/issue-54387.rs ui/consts/issue-54582.rs |
