diff options
516 files changed, 10588 insertions, 4100 deletions
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b55ab229811..3680136d89f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -323,17 +323,6 @@ jobs: NO_DEBUG_ASSERTIONS: 1 NO_OVERFLOW_CHECKS: 1 os: macos-13 - - name: dist-x86_64-apple-alt - env: - SCRIPT: "./x.py dist bootstrap --include-default-paths" - RUST_CONFIGURE_ARGS: "--enable-extended --enable-profiler --set rust.jemalloc --set llvm.ninja=false" - RUSTC_RETRY_LINKER_ON_SEGFAULT: 1 - MACOSX_DEPLOYMENT_TARGET: 10.7 - SELECT_XCODE: /Applications/Xcode_13.4.1.app - NO_LLVM_ASSERTIONS: 1 - NO_DEBUG_ASSERTIONS: 1 - NO_OVERFLOW_CHECKS: 1 - os: macos-13 - name: x86_64-apple-1 env: SCRIPT: "./x.py --stage 2 test --skip tests/ui --skip tests/rustdoc --skip tests/run-make-fulldeps" diff --git a/Cargo.lock b/Cargo.lock index 8d707067bb9..4ebbb16442e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -167,9 +167,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.71" +version = "1.0.75" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c7d0618f0e0b7e8ff11427422b64564d5fb0be1940354bfe2e0529b18a9d9b8" +checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6" dependencies = [ "backtrace", ] @@ -218,7 +218,7 @@ dependencies = [ "proc-macro2", "quote", "serde", - "syn 2.0.27", + "syn 2.0.29", ] [[package]] @@ -500,7 +500,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.27", + "syn 2.0.29", ] [[package]] @@ -525,7 +525,7 @@ dependencies = [ "regex", "rustc_tools_util", "serde", - "syn 2.0.27", + "syn 2.0.29", "tempfile", "termize", "tester", @@ -849,7 +849,7 @@ dependencies = [ "proc-macro2", "quote", "strsim", - "syn 2.0.27", + "syn 2.0.29", ] [[package]] @@ -860,7 +860,7 @@ checksum = "836a9bbc7ad63342d6d6e7b815ccab164bc77a2d95d84bc3117a8c0d5c98e2d5" dependencies = [ "darling_core", "quote", - "syn 2.0.27", + "syn 2.0.29", ] [[package]] @@ -875,7 +875,7 @@ version = "0.1.73" dependencies = [ "itertools", "quote", - "syn 2.0.27", + "syn 2.0.29", ] [[package]] @@ -911,7 +911,7 @@ dependencies = [ "darling", "proc-macro2", "quote", - "syn 2.0.27", + "syn 2.0.29", ] [[package]] @@ -988,7 +988,7 @@ checksum = "487585f4d0c6655fe74905e2504d8ad6908e4db67f744eb140876906c2f3175d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.27", + "syn 2.0.29", ] [[package]] @@ -1354,7 +1354,7 @@ checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72" dependencies = [ "proc-macro2", "quote", - "syn 2.0.27", + "syn 2.0.29", ] [[package]] @@ -2334,6 +2334,7 @@ dependencies = [ "rand", "regex", "rustc_version", + "serde", "smallvec", "ui_test", ] @@ -2503,7 +2504,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.27", + "syn 2.0.29", ] [[package]] @@ -2691,7 +2692,7 @@ dependencies = [ "pest_meta", "proc-macro2", "quote", - "syn 2.0.27", + "syn 2.0.29", ] [[package]] @@ -3636,7 +3637,7 @@ dependencies = [ "fluent-syntax", "proc-macro2", "quote", - "syn 2.0.27", + "syn 2.0.29", "unic-langid", ] @@ -3906,7 +3907,7 @@ version = "0.1.0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.27", + "syn 2.0.29", "synstructure 0.13.0", ] @@ -4514,7 +4515,7 @@ dependencies = [ "proc-macro2", "quote", "serde", - "syn 2.0.27", + "syn 2.0.29", ] [[package]] @@ -4666,22 +4667,22 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.164" +version = "1.0.185" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e8c8cf938e98f769bc164923b06dce91cea1751522f46f8466461af04c9027d" +checksum = "be9b6f69f1dfd54c3b568ffa45c310d6973a5e5148fd40cf515acaf38cf5bc31" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.164" +version = "1.0.185" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9735b638ccc51c28bf6914d90a2e9725b377144fc612c49a611fddd1b631d68" +checksum = "dc59dfdcbad1437773485e0367fea4b090a2e0a16d9ffc46af47764536a298ec" dependencies = [ "proc-macro2", "quote", - "syn 2.0.27", + "syn 2.0.29", ] [[package]] @@ -4962,9 +4963,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.27" +version = "2.0.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b60f673f44a8255b9c8c657daf66a596d435f2da81a555b06dc644d080ba45e0" +checksum = "c324c494eba9d92503e6f1ef2e6df781e78f6a7705a0202d9801b198807d518a" dependencies = [ "proc-macro2", "quote", @@ -4991,7 +4992,7 @@ checksum = "285ba80e733fac80aa4270fbcdf83772a79b80aa35c97075320abfee4a915b06" dependencies = [ "proc-macro2", "quote", - "syn 2.0.27", + "syn 2.0.29", "unicode-xid", ] @@ -5126,9 +5127,9 @@ checksum = "aac81b6fd6beb5884b0cf3321b8117e6e5d47ecb6fc89f414cfdcca8b2fe2dd8" [[package]] name = "thiserror" -version = "1.0.40" +version = "1.0.47" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "978c9a314bd8dc99be594bc3c175faaa9794be04a5a5e153caba6915336cebac" +checksum = "97a802ec30afc17eee47b2855fc72e0c4cd62be9b4efe6591edde0ec5bd68d8f" dependencies = [ "thiserror-impl", ] @@ -5155,13 +5156,13 @@ dependencies = [ [[package]] name = "thiserror-impl" -version = "1.0.40" +version = "1.0.47" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f" +checksum = "6bb623b56e39ab7dcd4b1b98bb6c8f8d907ed255b18de254088016b27a8ee19b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.27", + "syn 2.0.29", ] [[package]] @@ -5382,7 +5383,7 @@ checksum = "5f4f31f56159e98206da9efd823404b79b6ef3143b4a7ab76e67b1751b25a4ab" dependencies = [ "proc-macro2", "quote", - "syn 2.0.27", + "syn 2.0.29", ] [[package]] @@ -5777,7 +5778,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.27", + "syn 2.0.29", "wasm-bindgen-shared", ] @@ -5811,7 +5812,7 @@ checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.27", + "syn 2.0.29", "wasm-bindgen-backend", "wasm-bindgen-shared", ] diff --git a/RELEASES.md b/RELEASES.md index e8c79c573f9..242b1c2eefd 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -1,3 +1,108 @@ +Version 1.72.0 (2023-08-24) +========================== + +<a id="1.72.0-Language"></a> + +Language +-------- + +- [Replace const eval limit by a lint and add an exponential backoff warning](https://github.com/rust-lang/rust/pull/103877/) +- [expand: Change how `#![cfg(FALSE)]` behaves on crate root](https://github.com/rust-lang/rust/pull/110141/) +- [Stabilize inline asm for LoongArch64](https://github.com/rust-lang/rust/pull/111235/) +- [Uplift `clippy::undropped_manually_drops` lint](https://github.com/rust-lang/rust/pull/111530/) +- [Uplift `clippy::invalid_utf8_in_unchecked` lint](https://github.com/rust-lang/rust/pull/111543/) +- [Uplift `clippy::cast_ref_to_mut` lint](https://github.com/rust-lang/rust/pull/111567/) +- [Uplift `clippy::cmp_nan` lint](https://github.com/rust-lang/rust/pull/111818/) +- [resolve: Remove artificial import ambiguity errors](https://github.com/rust-lang/rust/pull/112086/) +- [Don't require associated types with Self: Sized bounds in `dyn Trait` objects](https://github.com/rust-lang/rust/pull/112319/) + +<a id="1.72.0-Compiler"></a> + +Compiler +-------- + +- [Remember names of `cfg`-ed out items to mention them in diagnostics](https://github.com/rust-lang/rust/pull/109005/) +- [Support for native WASM exceptions](https://github.com/rust-lang/rust/pull/111322/) +- [Add support for NetBSD/aarch64-be (big-endian arm64).](https://github.com/rust-lang/rust/pull/111326/) +- [Write to stdout if `-` is given as output file](https://github.com/rust-lang/rust/pull/111626/) +- [Force all native libraries to be statically linked when linking a static binary](https://github.com/rust-lang/rust/pull/111698/) +- [Add Tier 3 support for `loongarch64-unknown-none*`](https://github.com/rust-lang/rust/pull/112310/) +- [Prevent `.eh_frame` from being emitted for `-C panic=abort`](https://github.com/rust-lang/rust/pull/112403/) +- [Support 128-bit enum variant in debuginfo codegen](https://github.com/rust-lang/rust/pull/112474/) +- [compiler: update solaris/illumos to enable tsan support.](https://github.com/rust-lang/rust/pull/112039/) + +Refer to Rust's [platform support page][platform-support-doc] +for more information on Rust's tiered platform support. + +<a id="1.72.0-Libraries"></a> + +Libraries +--------- + +- [Document memory orderings of `thread::{park, unpark}`](https://github.com/rust-lang/rust/pull/99587/) +- [io: soften ‘at most one write attempt’ requirement in io::Write::write](https://github.com/rust-lang/rust/pull/107200/) +- [Specify behavior of HashSet::insert](https://github.com/rust-lang/rust/pull/107619/) +- [Relax implicit `T: Sized` bounds on `BufReader<T>`, `BufWriter<T>` and `LineWriter<T>`](https://github.com/rust-lang/rust/pull/111074/) +- [Update runtime guarantee for `select_nth_unstable`](https://github.com/rust-lang/rust/pull/111974/) +- [Return `Ok` on kill if process has already exited](https://github.com/rust-lang/rust/pull/112594/) +- [Implement PartialOrd for `Vec`s over different allocators](https://github.com/rust-lang/rust/pull/112632/) +- [Use 128 bits for TypeId hash](https://github.com/rust-lang/rust/pull/109953/) +- [Don't drain-on-drop in DrainFilter impls of various collections.](https://github.com/rust-lang/rust/pull/104455/) +- [Make `{Arc,Rc,Weak}::ptr_eq` ignore pointer metadata](https://github.com/rust-lang/rust/pull/106450/) + +<a id="1.72.0-Rustdoc"></a> + +Rustdoc +------- + +- [Allow whitespace as path separator like double colon](https://github.com/rust-lang/rust/pull/108537/) +- [Add search result item types after their name](https://github.com/rust-lang/rust/pull/110688/) +- [Search for slices and arrays by type with `[]`](https://github.com/rust-lang/rust/pull/111958/) +- [Clean up type unification and "unboxing"](https://github.com/rust-lang/rust/pull/112233/) + +<a id="1.72.0-Stabilized-APIs"></a> + +Stabilized APIs +--------------- + +- [`impl<T: Send> Sync for mpsc::Sender<T>`](https://doc.rust-lang.org/stable/std/sync/mpsc/struct.Sender.html#impl-Sync-for-Sender%3CT%3E) +- [`impl TryFrom<&OsStr> for &str`](https://doc.rust-lang.org/stable/std/primitive.str.html#impl-TryFrom%3C%26'a+OsStr%3E-for-%26'a+str) +- [`String::leak`](https://doc.rust-lang.org/stable/alloc/string/struct.String.html#method.leak) + +These APIs are now stable in const contexts: + +- [`CStr::from_bytes_with_nul`](https://doc.rust-lang.org/stable/std/ffi/struct.CStr.html#method.from_bytes_with_nul) +- [`CStr::to_bytes`](https://doc.rust-lang.org/stable/std/ffi/struct.CStr.html#method.to_bytes) +- [`CStr::to_bytes_with_nul`](https://doc.rust-lang.org/stable/std/ffi/struct.CStr.html#method.to_bytes_with_nul) +- [`CStr::to_str`](https://doc.rust-lang.org/stable/std/ffi/struct.CStr.html#method.to_str) + +<a id="1.72.0-Cargo"></a> + +Cargo +----- + +- Enable `-Zdoctest-in-workspace` by default. When running each documentation + test, the working directory is set to the root directory of the package the + test belongs to. + [docs](https://doc.rust-lang.org/nightly/cargo/commands/cargo-test.html#working-directory-of-tests) + [#12221](https://github.com/rust-lang/cargo/pull/12221) + [#12288](https://github.com/rust-lang/cargo/pull/12288) +- Add support of the "default" keyword to reset previously set `build.jobs` + parallelism back to the default. + [#12222](https://github.com/rust-lang/cargo/pull/12222) + +<a id="1.72.0-Compatibility-Notes"></a> + +Compatibility Notes +------------------- + +- [Alter `Display` for `Ipv6Addr` for IPv4-compatible addresses](https://github.com/rust-lang/rust/pull/112606/) +- Cargo changed feature name validation check to a hard error. The warning was + added in Rust 1.49. These extended characters aren't allowed on crates.io, so + this should only impact users of other registries, or people who don't publish + to a registry. + [#12291](https://github.com/rust-lang/cargo/pull/12291) + Version 1.71.0 (2023-07-13) ========================== diff --git a/compiler/rustc_ast/src/mut_visit.rs b/compiler/rustc_ast/src/mut_visit.rs index bae3979fbf9..48e9b180b74 100644 --- a/compiler/rustc_ast/src/mut_visit.rs +++ b/compiler/rustc_ast/src/mut_visit.rs @@ -13,6 +13,7 @@ use crate::tokenstream::*; use crate::{ast::*, StaticItem}; use rustc_data_structures::flat_map_in_place::FlatMapInPlace; +use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_data_structures::sync::Lrc; use rustc_span::source_map::Spanned; use rustc_span::symbol::Ident; @@ -1369,7 +1370,7 @@ pub fn noop_visit_expr<T: MutVisitor>( ExprKind::If(cond, tr, fl) => { vis.visit_expr(cond); vis.visit_block(tr); - visit_opt(fl, |fl| vis.visit_expr(fl)); + visit_opt(fl, |fl| ensure_sufficient_stack(|| vis.visit_expr(fl))); } ExprKind::While(cond, body, label) => { vis.visit_expr(cond); diff --git a/compiler/rustc_ast_pretty/src/pprust/state.rs b/compiler/rustc_ast_pretty/src/pprust/state.rs index 068b255e9f2..58ce73047bc 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state.rs @@ -150,6 +150,8 @@ pub fn print_crate<'a>( /// and also addresses some specific regressions described in #63896 and #73345. fn tt_prepend_space(tt: &TokenTree, prev: &TokenTree) -> bool { if let TokenTree::Token(token, _) = prev { + // No space after these tokens, e.g. `x.y`, `$e` + // (The carets point to `prev`.) ^ ^ if matches!(token.kind, token::Dot | token::Dollar) { return false; } @@ -158,10 +160,19 @@ fn tt_prepend_space(tt: &TokenTree, prev: &TokenTree) -> bool { } } match tt { + // No space before these tokens, e.g. `foo,`, `println!`, `x.y` + // (The carets point to `token`.) ^ ^ ^ + // + // FIXME: having `Not` here works well for macro invocations like + // `println!()`, but is bad when `!` means "logical not" or "the never + // type", where the lack of space causes ugliness like this: + // `Fn() ->!`, `x =! y`, `if! x { f(); }`. TokenTree::Token(token, _) => !matches!(token.kind, token::Comma | token::Not | token::Dot), + // No space before parentheses if preceded by these tokens, e.g. `foo(...)` TokenTree::Delimited(_, Delimiter::Parenthesis, _) => { !matches!(prev, TokenTree::Token(Token { kind: token::Ident(..), .. }, _)) } + // No space before brackets if preceded by these tokens, e.g. `#[...]` TokenTree::Delimited(_, Delimiter::Bracket, _) => { !matches!(prev, TokenTree::Token(Token { kind: token::Pound, .. }, _)) } diff --git a/compiler/rustc_borrowck/src/consumers.rs b/compiler/rustc_borrowck/src/consumers.rs index d257145373f..becfa535a59 100644 --- a/compiler/rustc_borrowck/src/consumers.rs +++ b/compiler/rustc_borrowck/src/consumers.rs @@ -30,7 +30,7 @@ pub use super::{ /// will be retrieved. #[derive(Debug, Copy, Clone)] pub enum ConsumerOptions { - /// Retrieve the [`Body`] along with the [`BorrowSet`](super::borrow_set::BorrowSet) + /// Retrieve the [`Body`] along with the [`BorrowSet`] /// and [`RegionInferenceContext`]. If you would like the body only, use /// [`TyCtxt::mir_promoted`]. /// diff --git a/compiler/rustc_borrowck/src/invalidation.rs b/compiler/rustc_borrowck/src/invalidation.rs index df5e383ad40..d4c42a75874 100644 --- a/compiler/rustc_borrowck/src/invalidation.rs +++ b/compiler/rustc_borrowck/src/invalidation.rs @@ -159,7 +159,9 @@ impl<'cx, 'tcx> Visitor<'tcx> for InvalidationGenerator<'cx, 'tcx> { self.mutate_place(location, *resume_arg, Deep); } - TerminatorKind::Resume | TerminatorKind::Return | TerminatorKind::GeneratorDrop => { + TerminatorKind::UnwindResume + | TerminatorKind::Return + | TerminatorKind::GeneratorDrop => { // Invalidate all borrows of local places let borrow_set = self.borrow_set; let start = self.location_table.start_index(location); @@ -200,7 +202,7 @@ impl<'cx, 'tcx> Visitor<'tcx> for InvalidationGenerator<'cx, 'tcx> { } } TerminatorKind::Goto { target: _ } - | TerminatorKind::Terminate + | TerminatorKind::UnwindTerminate | TerminatorKind::Unreachable | TerminatorKind::FalseEdge { real_target: _, imaginary_target: _ } | TerminatorKind::FalseUnwind { real_target: _, unwind: _ } => { diff --git a/compiler/rustc_borrowck/src/lib.rs b/compiler/rustc_borrowck/src/lib.rs index efe525c224d..ef2788efbcf 100644 --- a/compiler/rustc_borrowck/src/lib.rs +++ b/compiler/rustc_borrowck/src/lib.rs @@ -770,9 +770,9 @@ impl<'cx, 'tcx, R> rustc_mir_dataflow::ResultsVisitor<'cx, 'tcx, R> for MirBorro } TerminatorKind::Goto { target: _ } - | TerminatorKind::Terminate + | TerminatorKind::UnwindTerminate | TerminatorKind::Unreachable - | TerminatorKind::Resume + | TerminatorKind::UnwindResume | TerminatorKind::Return | TerminatorKind::GeneratorDrop | TerminatorKind::FalseEdge { real_target: _, imaginary_target: _ } @@ -803,7 +803,9 @@ impl<'cx, 'tcx, R> rustc_mir_dataflow::ResultsVisitor<'cx, 'tcx, R> for MirBorro } } - TerminatorKind::Resume | TerminatorKind::Return | TerminatorKind::GeneratorDrop => { + TerminatorKind::UnwindResume + | TerminatorKind::Return + | TerminatorKind::GeneratorDrop => { // Returning from the function implicitly kills storage for all locals and statics. // Often, the storage will already have been killed by an explicit // StorageDead, but we don't always emit those (notably on unwind paths), @@ -815,7 +817,7 @@ impl<'cx, 'tcx, R> rustc_mir_dataflow::ResultsVisitor<'cx, 'tcx, R> for MirBorro } } - TerminatorKind::Terminate + TerminatorKind::UnwindTerminate | TerminatorKind::Assert { .. } | TerminatorKind::Call { .. } | TerminatorKind::Drop { .. } diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs index 50d875dfae9..d91a3d94045 100644 --- a/compiler/rustc_borrowck/src/type_check/mod.rs +++ b/compiler/rustc_borrowck/src/type_check/mod.rs @@ -1333,8 +1333,8 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { debug!("terminator kind: {:?}", term.kind); match &term.kind { TerminatorKind::Goto { .. } - | TerminatorKind::Resume - | TerminatorKind::Terminate + | TerminatorKind::UnwindResume + | TerminatorKind::UnwindTerminate | TerminatorKind::Return | TerminatorKind::GeneratorDrop | TerminatorKind::Unreachable @@ -1608,12 +1608,12 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { self.assert_iscleanup(body, block_data, *target, is_cleanup); } } - TerminatorKind::Resume => { + TerminatorKind::UnwindResume => { if !is_cleanup { span_mirbug!(self, block_data, "resume on non-cleanup block!") } } - TerminatorKind::Terminate => { + TerminatorKind::UnwindTerminate => { if !is_cleanup { span_mirbug!(self, block_data, "abort on non-cleanup block!") } diff --git a/compiler/rustc_codegen_cranelift/src/base.rs b/compiler/rustc_codegen_cranelift/src/base.rs index 522dd7189fe..ed371a04c53 100644 --- a/compiler/rustc_codegen_cranelift/src/base.rs +++ b/compiler/rustc_codegen_cranelift/src/base.rs @@ -474,10 +474,10 @@ fn codegen_fn_body(fx: &mut FunctionCx<'_, '_, '_>, start_block: Block) { *destination, ); } - TerminatorKind::Terminate => { + TerminatorKind::UnwindTerminate => { codegen_panic_cannot_unwind(fx, source_info); } - TerminatorKind::Resume => { + TerminatorKind::UnwindResume => { // FIXME implement unwinding fx.bcx.ins().trap(TrapCode::UnreachableCodeReached); } diff --git a/compiler/rustc_codegen_cranelift/src/constant.rs b/compiler/rustc_codegen_cranelift/src/constant.rs index c3153574295..7db5f79eead 100644 --- a/compiler/rustc_codegen_cranelift/src/constant.rs +++ b/compiler/rustc_codegen_cranelift/src/constant.rs @@ -550,8 +550,8 @@ pub(crate) fn mir_operand_get_const_val<'tcx>( match &bb_data.terminator().kind { TerminatorKind::Goto { .. } | TerminatorKind::SwitchInt { .. } - | TerminatorKind::Resume - | TerminatorKind::Terminate + | TerminatorKind::UnwindResume + | TerminatorKind::UnwindTerminate | TerminatorKind::Return | TerminatorKind::Unreachable | TerminatorKind::Drop { .. } diff --git a/compiler/rustc_codegen_gcc/src/debuginfo.rs b/compiler/rustc_codegen_gcc/src/debuginfo.rs index a81585d4128..d1bfd833cd8 100644 --- a/compiler/rustc_codegen_gcc/src/debuginfo.rs +++ b/compiler/rustc_codegen_gcc/src/debuginfo.rs @@ -55,7 +55,7 @@ impl<'gcc, 'tcx> DebugInfoMethods<'tcx> for CodegenCx<'gcc, 'tcx> { _fn_abi: &FnAbi<'tcx, Ty<'tcx>>, _llfn: RValue<'gcc>, _mir: &mir::Body<'tcx>, - ) -> Option<FunctionDebugContext<Self::DIScope, Self::DILocation>> { + ) -> Option<FunctionDebugContext<'tcx, Self::DIScope, Self::DILocation>> { // TODO(antoyo) None } diff --git a/compiler/rustc_codegen_llvm/src/attributes.rs b/compiler/rustc_codegen_llvm/src/attributes.rs index 4c9094bf1f5..b6c01545f30 100644 --- a/compiler/rustc_codegen_llvm/src/attributes.rs +++ b/compiler/rustc_codegen_llvm/src/attributes.rs @@ -128,7 +128,10 @@ fn instrument_function_attr<'ll>(cx: &CodegenCx<'ll, '_>) -> SmallVec<[&'ll Attr // The function name varies on platforms. // See test/CodeGen/mcount.c in clang. - let mcount_name = cx.sess().target.mcount.as_ref(); + let mcount_name = match &cx.sess().target.llvm_mcount_intrinsic { + Some(llvm_mcount_intrinsic) => llvm_mcount_intrinsic.as_ref(), + None => cx.sess().target.mcount.as_ref(), + }; attrs.push(llvm::CreateAttrStringValue( cx.llcx, diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/create_scope_map.rs b/compiler/rustc_codegen_llvm/src/debuginfo/create_scope_map.rs index d174a3593b9..7a68c291aa5 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/create_scope_map.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/create_scope_map.rs @@ -20,7 +20,7 @@ pub fn compute_mir_scopes<'ll, 'tcx>( cx: &CodegenCx<'ll, 'tcx>, instance: Instance<'tcx>, mir: &Body<'tcx>, - debug_context: &mut FunctionDebugContext<&'ll DIScope, &'ll DILocation>, + debug_context: &mut FunctionDebugContext<'tcx, &'ll DIScope, &'ll DILocation>, ) { // Find all scopes with variables defined in them. let variables = if cx.sess().opts.debuginfo == DebugInfo::Full { @@ -51,7 +51,7 @@ fn make_mir_scope<'ll, 'tcx>( instance: Instance<'tcx>, mir: &Body<'tcx>, variables: &Option<BitSet<SourceScope>>, - debug_context: &mut FunctionDebugContext<&'ll DIScope, &'ll DILocation>, + debug_context: &mut FunctionDebugContext<'tcx, &'ll DIScope, &'ll DILocation>, instantiated: &mut BitSet<SourceScope>, scope: SourceScope, ) { @@ -84,7 +84,6 @@ fn make_mir_scope<'ll, 'tcx>( } let loc = cx.lookup_debug_loc(scope_data.span.lo()); - let file_metadata = file_metadata(cx, &loc.file); let dbg_scope = match scope_data.inlined { Some((callee, _)) => { @@ -95,18 +94,26 @@ fn make_mir_scope<'ll, 'tcx>( ty::ParamEnv::reveal_all(), ty::EarlyBinder::bind(callee), ); - let callee_fn_abi = cx.fn_abi_of_instance(callee, ty::List::empty()); - cx.dbg_scope_fn(callee, callee_fn_abi, None) + debug_context.inlined_function_scopes.entry(callee).or_insert_with(|| { + let callee_fn_abi = cx.fn_abi_of_instance(callee, ty::List::empty()); + cx.dbg_scope_fn(callee, callee_fn_abi, None) + }) + } + None => { + let file_metadata = file_metadata(cx, &loc.file); + debug_context + .lexical_blocks + .entry((parent_scope.dbg_scope, loc.line, loc.col, file_metadata)) + .or_insert_with(|| unsafe { + llvm::LLVMRustDIBuilderCreateLexicalBlock( + DIB(cx), + parent_scope.dbg_scope, + file_metadata, + loc.line, + loc.col, + ) + }) } - None => unsafe { - llvm::LLVMRustDIBuilderCreateLexicalBlock( - DIB(cx), - parent_scope.dbg_scope, - file_metadata, - loc.line, - loc.col, - ) - }, }; let inlined_at = scope_data.inlined.map(|(_, callsite_span)| { diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs b/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs index 40714a0afe9..9cdeae2841b 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs @@ -5,7 +5,7 @@ use rustc_codegen_ssa::mir::debuginfo::VariableKind::*; use self::metadata::{file_metadata, type_di_node}; use self::metadata::{UNKNOWN_COLUMN_NUMBER, UNKNOWN_LINE_NUMBER}; use self::namespace::mangled_name_of_instance; -use self::utils::{create_DIArray, is_node_local_to_unit, DIB}; +use self::utils::{create_DIArray, debug_context, is_node_local_to_unit, DIB}; use crate::abi::FnAbi; use crate::builder::Builder; @@ -67,6 +67,8 @@ pub struct CodegenUnitDebugContext<'ll, 'tcx> { type_map: metadata::TypeMap<'ll, 'tcx>, namespace_map: RefCell<DefIdMap<&'ll DIScope>>, recursion_marker_type: OnceCell<&'ll DIType>, + /// Maps a variable (name, scope, kind (argument or local), span) to its debug information. + variables: RefCell<FxHashMap<(Symbol, &'ll DIScope, VariableKind, Span), &'ll DIVariable>>, } impl Drop for CodegenUnitDebugContext<'_, '_> { @@ -91,6 +93,7 @@ impl<'ll, 'tcx> CodegenUnitDebugContext<'ll, 'tcx> { type_map: Default::default(), namespace_map: RefCell::new(Default::default()), recursion_marker_type: OnceCell::new(), + variables: RefCell::new(Default::default()), } } @@ -292,7 +295,7 @@ impl<'ll, 'tcx> DebugInfoMethods<'tcx> for CodegenCx<'ll, 'tcx> { fn_abi: &FnAbi<'tcx, Ty<'tcx>>, llfn: &'ll Value, mir: &mir::Body<'tcx>, - ) -> Option<FunctionDebugContext<&'ll DIScope, &'ll DILocation>> { + ) -> Option<FunctionDebugContext<'tcx, &'ll DIScope, &'ll DILocation>> { if self.sess().opts.debuginfo == DebugInfo::None { return None; } @@ -304,8 +307,11 @@ impl<'ll, 'tcx> DebugInfoMethods<'tcx> for CodegenCx<'ll, 'tcx> { file_start_pos: BytePos(0), file_end_pos: BytePos(0), }; - let mut fn_debug_context = - FunctionDebugContext { scopes: IndexVec::from_elem(empty_scope, &mir.source_scopes) }; + let mut fn_debug_context = FunctionDebugContext { + scopes: IndexVec::from_elem(empty_scope, &mir.source_scopes), + inlined_function_scopes: Default::default(), + lexical_blocks: Default::default(), + }; // Fill in all the scopes, with the information from the MIR body. compute_mir_scopes(self, instance, mir, &mut fn_debug_context); @@ -606,33 +612,39 @@ impl<'ll, 'tcx> DebugInfoMethods<'tcx> for CodegenCx<'ll, 'tcx> { variable_kind: VariableKind, span: Span, ) -> &'ll DIVariable { - let loc = self.lookup_debug_loc(span.lo()); - let file_metadata = file_metadata(self, &loc.file); - - let type_metadata = type_di_node(self, variable_type); - - let (argument_index, dwarf_tag) = match variable_kind { - ArgumentVariable(index) => (index as c_uint, DW_TAG_arg_variable), - LocalVariable => (0, DW_TAG_auto_variable), - }; - let align = self.align_of(variable_type); - - let name = variable_name.as_str(); - unsafe { - llvm::LLVMRustDIBuilderCreateVariable( - DIB(self), - dwarf_tag, - scope_metadata, - name.as_ptr().cast(), - name.len(), - file_metadata, - loc.line, - type_metadata, - true, - DIFlags::FlagZero, - argument_index, - align.bytes() as u32, - ) - } + debug_context(self) + .variables + .borrow_mut() + .entry((variable_name, scope_metadata, variable_kind, span)) + .or_insert_with(|| { + let loc = self.lookup_debug_loc(span.lo()); + let file_metadata = file_metadata(self, &loc.file); + + let type_metadata = type_di_node(self, variable_type); + + let (argument_index, dwarf_tag) = match variable_kind { + ArgumentVariable(index) => (index as c_uint, DW_TAG_arg_variable), + LocalVariable => (0, DW_TAG_auto_variable), + }; + let align = self.align_of(variable_type); + + let name = variable_name.as_str(); + unsafe { + llvm::LLVMRustDIBuilderCreateVariable( + DIB(self), + dwarf_tag, + scope_metadata, + name.as_ptr().cast(), + name.len(), + file_metadata, + loc.line, + type_metadata, + true, + DIFlags::FlagZero, + argument_index, + align.bytes() as u32, + ) + } + }) } } diff --git a/compiler/rustc_codegen_ssa/src/back/metadata.rs b/compiler/rustc_codegen_ssa/src/back/metadata.rs index 0be84c9fa83..4c854740753 100644 --- a/compiler/rustc_codegen_ssa/src/back/metadata.rs +++ b/compiler/rustc_codegen_ssa/src/back/metadata.rs @@ -16,6 +16,7 @@ use rustc_metadata::fs::METADATA_FILENAME; use rustc_metadata::EncodedMetadata; use rustc_session::cstore::MetadataLoader; use rustc_session::Session; +use rustc_span::sym; use rustc_target::abi::Endian; use rustc_target::spec::{ef_avr_arch, RelocModel, Target}; @@ -272,35 +273,38 @@ pub(crate) fn create_object_file(sess: &Session) -> Option<write::Object<'static Architecture::Riscv32 | Architecture::Riscv64 => { // Source: https://github.com/riscv-non-isa/riscv-elf-psabi-doc/blob/079772828bd10933d34121117a222b4cc0ee2200/riscv-elf.adoc let mut e_flags: u32 = 0x0; - let features = &sess.target.options.features; + // Check if compressed is enabled - if features.contains("+c") { + // `unstable_target_features` is used here because "c" is gated behind riscv_target_feature. + if sess.unstable_target_features.contains(&sym::c) { e_flags |= elf::EF_RISCV_RVC; } - // Select the appropriate floating-point ABI - if features.contains("+d") { - e_flags |= elf::EF_RISCV_FLOAT_ABI_DOUBLE; - } else if features.contains("+f") { - e_flags |= elf::EF_RISCV_FLOAT_ABI_SINGLE; - } else { - e_flags |= elf::EF_RISCV_FLOAT_ABI_SOFT; + // Set the appropriate flag based on ABI + // This needs to match LLVM `RISCVELFStreamer.cpp` + match &*sess.target.llvm_abiname { + "" | "ilp32" | "lp64" => (), + "ilp32f" | "lp64f" => e_flags |= elf::EF_RISCV_FLOAT_ABI_SINGLE, + "ilp32d" | "lp64d" => e_flags |= elf::EF_RISCV_FLOAT_ABI_DOUBLE, + "ilp32e" => e_flags |= elf::EF_RISCV_RVE, + _ => bug!("unknown RISC-V ABI name"), } + e_flags } Architecture::LoongArch64 => { // Source: https://github.com/loongson/la-abi-specs/blob/release/laelf.adoc#e_flags-identifies-abi-type-and-version let mut e_flags: u32 = elf::EF_LARCH_OBJABI_V1; - let features = &sess.target.options.features; - // Select the appropriate floating-point ABI - if features.contains("+d") { - e_flags |= elf::EF_LARCH_ABI_DOUBLE_FLOAT; - } else if features.contains("+f") { - e_flags |= elf::EF_LARCH_ABI_SINGLE_FLOAT; - } else { - e_flags |= elf::EF_LARCH_ABI_SOFT_FLOAT; + // Set the appropriate flag based on ABI + // This needs to match LLVM `LoongArchELFStreamer.cpp` + match &*sess.target.llvm_abiname { + "ilp32s" | "lp64s" => e_flags |= elf::EF_LARCH_ABI_SOFT_FLOAT, + "ilp32f" | "lp64f" => e_flags |= elf::EF_LARCH_ABI_SINGLE_FLOAT, + "ilp32d" | "lp64d" => e_flags |= elf::EF_LARCH_ABI_DOUBLE_FLOAT, + _ => bug!("unknown RISC-V ABI name"), } + e_flags } Architecture::Avr => { diff --git a/compiler/rustc_codegen_ssa/src/mir/analyze.rs b/compiler/rustc_codegen_ssa/src/mir/analyze.rs index 22c1f05974d..3f5b46333d9 100644 --- a/compiler/rustc_codegen_ssa/src/mir/analyze.rs +++ b/compiler/rustc_codegen_ssa/src/mir/analyze.rs @@ -284,8 +284,8 @@ pub fn cleanup_kinds(mir: &mir::Body<'_>) -> IndexVec<mir::BasicBlock, CleanupKi for (bb, data) in mir.basic_blocks.iter_enumerated() { match data.terminator().kind { TerminatorKind::Goto { .. } - | TerminatorKind::Resume - | TerminatorKind::Terminate + | TerminatorKind::UnwindResume + | TerminatorKind::UnwindTerminate | TerminatorKind::Return | TerminatorKind::GeneratorDrop | TerminatorKind::Unreachable diff --git a/compiler/rustc_codegen_ssa/src/mir/block.rs b/compiler/rustc_codegen_ssa/src/mir/block.rs index 4f26383ed05..19228183462 100644 --- a/compiler/rustc_codegen_ssa/src/mir/block.rs +++ b/compiler/rustc_codegen_ssa/src/mir/block.rs @@ -1224,12 +1224,12 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { self.set_debug_loc(bx, terminator.source_info); match terminator.kind { - mir::TerminatorKind::Resume => { + mir::TerminatorKind::UnwindResume => { self.codegen_resume_terminator(helper, bx); MergingSucc::False } - mir::TerminatorKind::Terminate => { + mir::TerminatorKind::UnwindTerminate => { self.codegen_terminate_terminator(helper, bx, terminator); MergingSucc::False } diff --git a/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs b/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs index 4167a85ccd5..7df830692d3 100644 --- a/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs +++ b/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs @@ -1,10 +1,12 @@ use crate::traits::*; +use rustc_data_structures::fx::FxHashMap; use rustc_index::IndexVec; use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags; use rustc_middle::mir; use rustc_middle::ty; use rustc_middle::ty::layout::TyAndLayout; use rustc_middle::ty::layout::{HasTyCtxt, LayoutOf}; +use rustc_middle::ty::Instance; use rustc_middle::ty::Ty; use rustc_session::config::DebugInfo; use rustc_span::symbol::{kw, Symbol}; @@ -17,11 +19,19 @@ use super::{FunctionCx, LocalRef}; use std::ops::Range; -pub struct FunctionDebugContext<S, L> { +pub struct FunctionDebugContext<'tcx, S, L> { + /// Maps from source code to the corresponding debug info scope. pub scopes: IndexVec<mir::SourceScope, DebugScope<S, L>>, + + /// Maps from a given inlined function to its debug info declaration. + pub inlined_function_scopes: FxHashMap<Instance<'tcx>, S>, + + /// Maps from a lexical block (parent scope, line, column, file) to its debug info declaration. + /// This is particularily useful if the parent scope is an inlined function. + pub lexical_blocks: FxHashMap<(S, u32, u32, S), S>, } -#[derive(Copy, Clone)] +#[derive(Copy, Clone, Eq, PartialEq, Hash)] pub enum VariableKind { ArgumentVariable(usize /*index*/), LocalVariable, @@ -42,9 +52,6 @@ pub struct PerLocalVarDebugInfo<'tcx, D> { /// `.place.projection` from `mir::VarDebugInfo`. pub projection: &'tcx ty::List<mir::PlaceElem<'tcx>>, - - /// `references` from `mir::VarDebugInfo`. - pub references: u8, } #[derive(Clone, Copy, Debug)] @@ -323,7 +330,6 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { dbg_var, fragment: None, projection: ty::List::empty(), - references: 0, }) } } else { @@ -399,15 +405,14 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { &self, bx: &mut Bx, local: mir::Local, - mut base: PlaceRef<'tcx, Bx::Value>, + base: PlaceRef<'tcx, Bx::Value>, var: PerLocalVarDebugInfo<'tcx, Bx::DIVariable>, ) { let Some(dbg_var) = var.dbg_var else { return }; let Some(dbg_loc) = self.dbg_loc(var.source_info) else { return }; - let DebugInfoOffset { mut direct_offset, indirect_offsets, result: _ } = + let DebugInfoOffset { direct_offset, indirect_offsets, result: _ } = calculate_debuginfo_offset(bx, local, &var, base.layout); - let mut indirect_offsets = &indirect_offsets[..]; // When targeting MSVC, create extra allocas for arguments instead of pointing multiple // dbg_var_addr() calls into the same alloca with offsets. MSVC uses CodeView records @@ -421,9 +426,12 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { // LLVM can handle simple things but anything more complex than just a direct // offset or one indirect offset of 0 is too complex for it to generate CV records // correctly. - && (direct_offset != Size::ZERO || !matches!(indirect_offsets, [Size::ZERO] | [])); + && (direct_offset != Size::ZERO || !matches!(&indirect_offsets[..], [Size::ZERO] | [])); + + if should_create_individual_allocas { + let DebugInfoOffset { direct_offset: _, indirect_offsets: _, result: place } = + calculate_debuginfo_offset(bx, local, &var, base); - let create_alloca = |bx: &mut Bx, place: PlaceRef<'tcx, Bx::Value>, refcount| { // Create a variable which will be a pointer to the actual value let ptr_ty = Ty::new_ptr( bx.tcx(), @@ -431,35 +439,16 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { ); let ptr_layout = bx.layout_of(ptr_ty); let alloca = PlaceRef::alloca(bx, ptr_layout); - bx.set_var_name(alloca.llval, &format!("{}.ref{}.dbg.spill", var.name, refcount)); + bx.set_var_name(alloca.llval, &(var.name.to_string() + ".dbg.spill")); // Write the pointer to the variable bx.store(place.llval, alloca.llval, alloca.align); // Point the debug info to `*alloca` for the current variable - alloca - }; - - if var.references > 0 { - base = calculate_debuginfo_offset(bx, local, &var, base).result; - - // Point the debug info to `&...&base == alloca` for the current variable - for refcount in 0..var.references { - base = create_alloca(bx, base, refcount); - } - - direct_offset = Size::ZERO; - indirect_offsets = &[]; - } else if should_create_individual_allocas { - let place = calculate_debuginfo_offset(bx, local, &var, base).result; - - // Point the debug info to `*alloca` for the current variable - base = create_alloca(bx, place, 0); - direct_offset = Size::ZERO; - indirect_offsets = &[Size::ZERO]; + bx.dbg_var_addr(dbg_var, dbg_loc, alloca.llval, Size::ZERO, &[Size::ZERO], None); + } else { + bx.dbg_var_addr(dbg_var, dbg_loc, base.llval, direct_offset, &indirect_offsets, None); } - - bx.dbg_var_addr(dbg_var, dbg_loc, base.llval, direct_offset, indirect_offsets, None); } pub fn debug_introduce_locals(&self, bx: &mut Bx) { @@ -492,7 +481,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { }; let dbg_var = dbg_scope_and_span.map(|(dbg_scope, _, span)| { - let (mut var_ty, var_kind) = match var.value { + let (var_ty, var_kind) = match var.value { mir::VarDebugInfoContents::Place(place) => { let var_ty = self.monomorphized_place_ty(place.as_ref()); let var_kind = if let Some(arg_index) = var.argument_index @@ -529,13 +518,6 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { } }; - for _ in 0..var.references { - var_ty = Ty::new_ptr( - bx.tcx(), - ty::TypeAndMut { mutbl: mir::Mutability::Mut, ty: var_ty }, - ); - } - self.cx.create_dbg_var(var.name, var_ty, dbg_scope, var_kind, span) }); @@ -547,7 +529,6 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { dbg_var, fragment: None, projection: place.projection, - references: var.references, }); } mir::VarDebugInfoContents::Const(c) => { @@ -601,7 +582,6 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { Some(fragment_start..fragment_start + fragment_layout.size) }, projection: place.projection, - references: var.references, }); } } diff --git a/compiler/rustc_codegen_ssa/src/mir/mod.rs b/compiler/rustc_codegen_ssa/src/mir/mod.rs index 3464f910829..ccf93f208d5 100644 --- a/compiler/rustc_codegen_ssa/src/mir/mod.rs +++ b/compiler/rustc_codegen_ssa/src/mir/mod.rs @@ -45,7 +45,7 @@ pub struct FunctionCx<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> { mir: &'tcx mir::Body<'tcx>, - debug_context: Option<FunctionDebugContext<Bx::DIScope, Bx::DILocation>>, + debug_context: Option<FunctionDebugContext<'tcx, Bx::DIScope, Bx::DILocation>>, llfn: Bx::Function, diff --git a/compiler/rustc_codegen_ssa/src/traits/debuginfo.rs b/compiler/rustc_codegen_ssa/src/traits/debuginfo.rs index 63fecaf34fd..4acc0ea076c 100644 --- a/compiler/rustc_codegen_ssa/src/traits/debuginfo.rs +++ b/compiler/rustc_codegen_ssa/src/traits/debuginfo.rs @@ -26,7 +26,7 @@ pub trait DebugInfoMethods<'tcx>: BackendTypes { fn_abi: &FnAbi<'tcx, Ty<'tcx>>, llfn: Self::Function, mir: &mir::Body<'tcx>, - ) -> Option<FunctionDebugContext<Self::DIScope, Self::DILocation>>; + ) -> Option<FunctionDebugContext<'tcx, Self::DIScope, Self::DILocation>>; // FIXME(eddyb) find a common convention for all of the debuginfo-related // names (choose between `dbg`, `debug`, `debuginfo`, `debug_info` etc.). diff --git a/compiler/rustc_const_eval/src/const_eval/error.rs b/compiler/rustc_const_eval/src/const_eval/error.rs index d39a7e8a192..f146b93ff0c 100644 --- a/compiler/rustc_const_eval/src/const_eval/error.rs +++ b/compiler/rustc_const_eval/src/const_eval/error.rs @@ -18,7 +18,6 @@ pub enum ConstEvalErrKind { ModifiedGlobal, AssertFailure(AssertKind<ConstInt>), Panic { msg: Symbol, line: u32, col: u32, file: Symbol }, - Abort(String), } impl MachineStopType for ConstEvalErrKind { @@ -30,7 +29,6 @@ impl MachineStopType for ConstEvalErrKind { ModifiedGlobal => const_eval_modified_global, Panic { .. } => const_eval_panic, AssertFailure(x) => x.diagnostic_message(), - Abort(msg) => msg.to_string().into(), } } fn add_args( @@ -39,7 +37,7 @@ impl MachineStopType for ConstEvalErrKind { ) { use ConstEvalErrKind::*; match *self { - ConstAccessesStatic | ModifiedGlobal | Abort(_) => {} + ConstAccessesStatic | ModifiedGlobal => {} AssertFailure(kind) => kind.add_args(adder), Panic { msg, line, col, file } => { adder("msg".into(), msg.into_diagnostic_arg()); diff --git a/compiler/rustc_const_eval/src/const_eval/machine.rs b/compiler/rustc_const_eval/src/const_eval/machine.rs index 032f4be6c99..f16aea6f34b 100644 --- a/compiler/rustc_const_eval/src/const_eval/machine.rs +++ b/compiler/rustc_const_eval/src/const_eval/machine.rs @@ -427,52 +427,48 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir, fn find_mir_or_eval_fn( ecx: &mut InterpCx<'mir, 'tcx, Self>, - instance: ty::Instance<'tcx>, + orig_instance: ty::Instance<'tcx>, _abi: CallAbi, args: &[FnArg<'tcx>], dest: &PlaceTy<'tcx>, ret: Option<mir::BasicBlock>, _unwind: mir::UnwindAction, // unwinding is not supported in consts ) -> InterpResult<'tcx, Option<(&'mir mir::Body<'tcx>, ty::Instance<'tcx>)>> { - debug!("find_mir_or_eval_fn: {:?}", instance); + debug!("find_mir_or_eval_fn: {:?}", orig_instance); + + // Replace some functions. + let Some(instance) = ecx.hook_special_const_fn(orig_instance, args, dest, ret)? else { + // Call has already been handled. + return Ok(None); + }; // Only check non-glue functions if let ty::InstanceDef::Item(def) = instance.def { // Execution might have wandered off into other crates, so we cannot do a stability- - // sensitive check here. But we can at least rule out functions that are not const - // at all. - if !ecx.tcx.is_const_fn_raw(def) { - // allow calling functions inside a trait marked with #[const_trait]. - if !ecx.tcx.is_const_default_method(def) { - // We certainly do *not* want to actually call the fn - // though, so be sure we return here. - throw_unsup_format!("calling non-const function `{}`", instance) - } - } - - let Some(new_instance) = ecx.hook_special_const_fn(instance, args, dest, ret)? else { - return Ok(None); - }; - - if new_instance != instance { - // We call another const fn instead. - // However, we return the *original* instance to make backtraces work out - // (and we hope this does not confuse the FnAbi checks too much). - return Ok(Self::find_mir_or_eval_fn( - ecx, - new_instance, - _abi, - args, - dest, - ret, - _unwind, - )? - .map(|(body, _instance)| (body, instance))); + // sensitive check here. But we can at least rule out functions that are not const at + // all. That said, we have to allow calling functions inside a trait marked with + // #[const_trait]. These *are* const-checked! + // FIXME: why does `is_const_fn_raw` not classify them as const? + if (!ecx.tcx.is_const_fn_raw(def) && !ecx.tcx.is_const_default_method(def)) + || ecx.tcx.has_attr(def, sym::rustc_do_not_const_check) + { + // We certainly do *not* want to actually call the fn + // though, so be sure we return here. + throw_unsup_format!("calling non-const function `{}`", instance) } } // This is a const fn. Call it. - Ok(Some((ecx.load_mir(instance.def, None)?, instance))) + // In case of replacement, we return the *original* instance to make backtraces work out + // (and we hope this does not confuse the FnAbi checks too much). + Ok(Some((ecx.load_mir(instance.def, None)?, orig_instance))) + } + + fn panic_nounwind(ecx: &mut InterpCx<'mir, 'tcx, Self>, msg: &str) -> InterpResult<'tcx> { + let msg = Symbol::intern(msg); + let span = ecx.find_closest_untracked_caller_location(); + let (file, line, col) = ecx.location_triple_for_span(span); + Err(ConstEvalErrKind::Panic { msg, file, line, col }.into()) } fn call_intrinsic( @@ -595,10 +591,6 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir, Err(ConstEvalErrKind::AssertFailure(err).into()) } - fn abort(_ecx: &mut InterpCx<'mir, 'tcx, Self>, msg: String) -> InterpResult<'tcx, !> { - Err(ConstEvalErrKind::Abort(msg).into()) - } - fn binary_ptr_op( _ecx: &InterpCx<'mir, 'tcx, Self>, _bin_op: mir::BinOp, diff --git a/compiler/rustc_const_eval/src/interpret/eval_context.rs b/compiler/rustc_const_eval/src/interpret/eval_context.rs index 3ac6f07e8b7..61d3de5c405 100644 --- a/compiler/rustc_const_eval/src/interpret/eval_context.rs +++ b/compiler/rustc_const_eval/src/interpret/eval_context.rs @@ -765,7 +765,10 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { } mir::UnwindAction::Terminate => { self.frame_mut().loc = Right(self.frame_mut().body.span); - M::abort(self, "panic in a function that cannot unwind".to_owned())?; + M::unwind_terminate(self)?; + // This might have pushed a new stack frame, or it terminated execution. + // Either way, `loc` will not be updated. + return Ok(()); } }; Ok(()) @@ -865,6 +868,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { panic!("encountered StackPopCleanup::Root when unwinding!") } }; + // This must be the very last thing that happens, since it can in fact push a new stack frame. self.unwind_to_block(unwind) } else { // Follow the normal return edge. diff --git a/compiler/rustc_const_eval/src/interpret/intrinsics.rs b/compiler/rustc_const_eval/src/interpret/intrinsics.rs index f22cd919c36..d6ca6fe73a6 100644 --- a/compiler/rustc_const_eval/src/interpret/intrinsics.rs +++ b/compiler/rustc_const_eval/src/interpret/intrinsics.rs @@ -125,15 +125,9 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { ) -> InterpResult<'tcx, bool> { let instance_args = instance.args; let intrinsic_name = self.tcx.item_name(instance.def_id()); - - // First handle intrinsics without return place. - let ret = match ret { - None => match intrinsic_name { - sym::abort => M::abort(self, "the program aborted execution".to_owned())?, - // Unsupported diverging intrinsic. - _ => return Ok(false), - }, - Some(p) => p, + let Some(ret) = ret else { + // We don't support any intrinsic without return place. + return Ok(false); }; match intrinsic_name { @@ -410,7 +404,9 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { ValidityRequirement::Uninit => bug!("assert_uninit_valid doesn't exist"), }; - M::abort(self, msg)?; + M::panic_nounwind(self, &msg)?; + // Skip the `go_to_block` at the end. + return Ok(true); } } sym::simd_insert => { diff --git a/compiler/rustc_const_eval/src/interpret/machine.rs b/compiler/rustc_const_eval/src/interpret/machine.rs index e101785b6e2..d3c73b896c0 100644 --- a/compiler/rustc_const_eval/src/interpret/machine.rs +++ b/compiler/rustc_const_eval/src/interpret/machine.rs @@ -218,10 +218,11 @@ pub trait Machine<'mir, 'tcx: 'mir>: Sized { unwind: mir::UnwindAction, ) -> InterpResult<'tcx>; - /// Called to abort evaluation. - fn abort(_ecx: &mut InterpCx<'mir, 'tcx, Self>, _msg: String) -> InterpResult<'tcx, !> { - throw_unsup_format!("aborting execution is not supported") - } + /// Called to trigger a non-unwinding panic. + fn panic_nounwind(_ecx: &mut InterpCx<'mir, 'tcx, Self>, msg: &str) -> InterpResult<'tcx>; + + /// Called when unwinding reached a state where execution should be terminated. + fn unwind_terminate(_ecx: &mut InterpCx<'mir, 'tcx, Self>) -> InterpResult<'tcx>; /// Called for all binary operations where the LHS has pointer type. /// @@ -500,6 +501,11 @@ pub macro compile_time_machine(<$mir: lifetime, $tcx: lifetime>) { } #[inline(always)] + fn unwind_terminate(_ecx: &mut InterpCx<$mir, $tcx, Self>) -> InterpResult<$tcx> { + unreachable!("unwinding cannot happen during compile-time evaluation") + } + + #[inline(always)] fn call_extra_fn( _ecx: &mut InterpCx<$mir, $tcx, Self>, fn_val: !, diff --git a/compiler/rustc_const_eval/src/interpret/terminator.rs b/compiler/rustc_const_eval/src/interpret/terminator.rs index 3c03172bbef..b2ebcceceb3 100644 --- a/compiler/rustc_const_eval/src/interpret/terminator.rs +++ b/compiler/rustc_const_eval/src/interpret/terminator.rs @@ -196,15 +196,14 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { } } - Terminate => { - // FIXME: maybe should call `panic_no_unwind` lang item instead. - M::abort(self, "panic in a function that cannot unwind".to_owned())?; + UnwindTerminate => { + M::unwind_terminate(self)?; } // When we encounter Resume, we've finished unwinding // cleanup for the current stack frame. We pop it in order // to continue unwinding the next frame - Resume => { + UnwindResume => { trace!("unwinding: resuming from cleanup"); // By definition, a Resume terminator means // that we're unwinding diff --git a/compiler/rustc_const_eval/src/transform/check_consts/check.rs b/compiler/rustc_const_eval/src/transform/check_consts/check.rs index fae047bff9e..e67098a3ac3 100644 --- a/compiler/rustc_const_eval/src/transform/check_consts/check.rs +++ b/compiler/rustc_const_eval/src/transform/check_consts/check.rs @@ -1037,7 +1037,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> { self.check_op(ops::Generator(hir::GeneratorKind::Gen)) } - TerminatorKind::Terminate => { + TerminatorKind::UnwindTerminate => { // Cleanup blocks are skipped for const checking (see `visit_basic_block_data`). span_bug!(self.span, "`Terminate` terminator outside of cleanup block") } @@ -1046,7 +1046,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> { | TerminatorKind::FalseEdge { .. } | TerminatorKind::FalseUnwind { .. } | TerminatorKind::Goto { .. } - | TerminatorKind::Resume + | TerminatorKind::UnwindResume | TerminatorKind::Return | TerminatorKind::SwitchInt { .. } | TerminatorKind::Unreachable => {} diff --git a/compiler/rustc_const_eval/src/transform/check_consts/post_drop_elaboration.rs b/compiler/rustc_const_eval/src/transform/check_consts/post_drop_elaboration.rs index e3377bd10c6..a8c61d2c8fd 100644 --- a/compiler/rustc_const_eval/src/transform/check_consts/post_drop_elaboration.rs +++ b/compiler/rustc_const_eval/src/transform/check_consts/post_drop_elaboration.rs @@ -106,7 +106,7 @@ impl<'tcx> Visitor<'tcx> for CheckLiveDrops<'_, 'tcx> { } } - mir::TerminatorKind::Terminate + mir::TerminatorKind::UnwindTerminate | mir::TerminatorKind::Call { .. } | mir::TerminatorKind::Assert { .. } | mir::TerminatorKind::FalseEdge { .. } @@ -114,7 +114,7 @@ impl<'tcx> Visitor<'tcx> for CheckLiveDrops<'_, 'tcx> { | mir::TerminatorKind::GeneratorDrop | mir::TerminatorKind::Goto { .. } | mir::TerminatorKind::InlineAsm { .. } - | mir::TerminatorKind::Resume + | mir::TerminatorKind::UnwindResume | mir::TerminatorKind::Return | mir::TerminatorKind::SwitchInt { .. } | mir::TerminatorKind::Unreachable diff --git a/compiler/rustc_const_eval/src/transform/validate.rs b/compiler/rustc_const_eval/src/transform/validate.rs index 83004492c8b..0dfbbf73dce 100644 --- a/compiler/rustc_const_eval/src/transform/validate.rs +++ b/compiler/rustc_const_eval/src/transform/validate.rs @@ -18,6 +18,7 @@ use rustc_mir_dataflow::impls::MaybeStorageLive; use rustc_mir_dataflow::storage::always_storage_live_locals; use rustc_mir_dataflow::{Analysis, ResultsCursor}; use rustc_target::abi::{Size, FIRST_VARIANT}; +use rustc_target::spec::abi::Abi; #[derive(Copy, Clone, Debug, PartialEq, Eq)] enum EdgeKind { @@ -58,6 +59,25 @@ impl<'tcx> MirPass<'tcx> for Validator { .iterate_to_fixpoint() .into_results_cursor(body); + let can_unwind = if mir_phase <= MirPhase::Runtime(RuntimePhase::Initial) { + // In this case `AbortUnwindingCalls` haven't yet been executed. + true + } else if !tcx.def_kind(def_id).is_fn_like() { + true + } else { + let body_ty = tcx.type_of(def_id).skip_binder(); + let body_abi = match body_ty.kind() { + ty::FnDef(..) => body_ty.fn_sig(tcx).abi(), + ty::Closure(..) => Abi::RustCall, + ty::Generator(..) => Abi::Rust, + _ => { + span_bug!(body.span, "unexpected body ty: {:?} phase {:?}", body_ty, mir_phase) + } + }; + + ty::layout::fn_can_unwind(tcx, Some(def_id), body_abi) + }; + let mut cfg_checker = CfgChecker { when: &self.when, body, @@ -68,6 +88,7 @@ impl<'tcx> MirPass<'tcx> for Validator { storage_liveness, place_cache: FxHashSet::default(), value_cache: FxHashSet::default(), + can_unwind, }; cfg_checker.visit_body(body); cfg_checker.check_cleanup_control_flow(); @@ -99,6 +120,9 @@ struct CfgChecker<'a, 'tcx> { storage_liveness: ResultsCursor<'a, 'tcx, MaybeStorageLive<'static>>, place_cache: FxHashSet<PlaceRef<'tcx>>, value_cache: FxHashSet<u128>, + // If `false`, then the MIR must not contain `UnwindAction::Continue` or + // `TerminatorKind::Resume`. + can_unwind: bool, } impl<'a, 'tcx> CfgChecker<'a, 'tcx> { @@ -237,13 +261,17 @@ impl<'a, 'tcx> CfgChecker<'a, 'tcx> { match unwind { UnwindAction::Cleanup(unwind) => { if is_cleanup { - self.fail(location, "unwind on cleanup block"); + self.fail(location, "`UnwindAction::Cleanup` in cleanup block"); } self.check_edge(location, unwind, EdgeKind::Unwind); } UnwindAction::Continue => { if is_cleanup { - self.fail(location, "unwind on cleanup block"); + self.fail(location, "`UnwindAction::Continue` in cleanup block"); + } + + if !self.can_unwind { + self.fail(location, "`UnwindAction::Continue` in no-unwind function"); } } UnwindAction::Unreachable | UnwindAction::Terminate => (), @@ -464,13 +492,19 @@ impl<'a, 'tcx> Visitor<'tcx> for CfgChecker<'a, 'tcx> { ); } } - TerminatorKind::Resume | TerminatorKind::Terminate => { + TerminatorKind::UnwindResume => { let bb = location.block; if !self.body.basic_blocks[bb].is_cleanup { - self.fail( - location, - "Cannot `Resume` or `Terminate` from non-cleanup basic block", - ) + self.fail(location, "Cannot `UnwindResume` from non-cleanup basic block") + } + if !self.can_unwind { + self.fail(location, "Cannot `UnwindResume` in a function that cannot unwind") + } + } + TerminatorKind::UnwindTerminate => { + let bb = location.block; + if !self.body.basic_blocks[bb].is_cleanup { + self.fail(location, "Cannot `UnwindTerminate` from non-cleanup basic block") } } TerminatorKind::Return => { @@ -665,7 +699,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { return; }; - f_ty.ty + ty::EarlyBinder::bind(f_ty.ty).instantiate(self.tcx, args) } else { let Some(&f_ty) = args.as_generator().prefix_tys().get(f.index()) else { @@ -701,12 +735,6 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { VarDebugInfoContents::Const(_) => {} VarDebugInfoContents::Place(place) => { check_place(self, place); - if debuginfo.references != 0 && place.projection.last() == Some(&PlaceElem::Deref) { - self.fail( - START_BLOCK.start_location(), - format!("debuginfo {debuginfo:?}, has both ref and deref"), - ); - } } VarDebugInfoContents::Composite { ty, ref fragments } => { for f in fragments { @@ -1204,8 +1232,8 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { | TerminatorKind::FalseUnwind { .. } | TerminatorKind::InlineAsm { .. } | TerminatorKind::GeneratorDrop - | TerminatorKind::Resume - | TerminatorKind::Terminate + | TerminatorKind::UnwindResume + | TerminatorKind::UnwindTerminate | TerminatorKind::Return | TerminatorKind::Unreachable => {} } diff --git a/compiler/rustc_error_codes/src/error_codes/E0132.md b/compiler/rustc_error_codes/src/error_codes/E0132.md index a23cc988bdb..51258739b89 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0132.md +++ b/compiler/rustc_error_codes/src/error_codes/E0132.md @@ -13,7 +13,7 @@ It is not possible to declare type parameters on a function that has the `start` attribute. Such a function must have the following type signature (for more information, view [the unstable book][1]): -[1]: https://doc.rust-lang.org/unstable-book/language-features/lang-items.html#writing-an-executable-without-stdlib +[1]: https://doc.rust-lang.org/unstable-book/language-features/start.html ``` # let _: diff --git a/compiler/rustc_error_codes/src/error_codes/E0152.md b/compiler/rustc_error_codes/src/error_codes/E0152.md index ef17b8b4c75..d862766571d 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0152.md +++ b/compiler/rustc_error_codes/src/error_codes/E0152.md @@ -20,6 +20,6 @@ attributes: #![no_std] ``` -See also the [unstable book][1]. +See also [this section of the Rustonomicon][beneath std]. -[1]: https://doc.rust-lang.org/unstable-book/language-features/lang-items.html#writing-an-executable-without-stdlib +[beneath std]: https://doc.rust-lang.org/nomicon/beneath-std.html diff --git a/compiler/rustc_error_codes/src/error_codes/E0191.md b/compiler/rustc_error_codes/src/error_codes/E0191.md index 46b773bdc50..344ac221698 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0191.md +++ b/compiler/rustc_error_codes/src/error_codes/E0191.md @@ -7,8 +7,8 @@ trait Trait { type Bar; } -type Foo = Trait; // error: the value of the associated type `Bar` (from - // the trait `Trait`) must be specified +type Foo = dyn Trait; // error: the value of the associated type `Bar` (from + // the trait `Trait`) must be specified ``` Trait objects need to have all associated types specified. Please verify that @@ -20,5 +20,5 @@ trait Trait { type Bar; } -type Foo = Trait<Bar=i32>; // ok! +type Foo = dyn Trait<Bar=i32>; // ok! ``` diff --git a/compiler/rustc_errors/src/lib.rs b/compiler/rustc_errors/src/lib.rs index 7d660d2dbaa..34518b53759 100644 --- a/compiler/rustc_errors/src/lib.rs +++ b/compiler/rustc_errors/src/lib.rs @@ -452,11 +452,11 @@ struct HandlerInner { /// have been converted. check_unstable_expect_diagnostics: bool, - /// Expected [`Diagnostic`][diagnostic::Diagnostic]s store a [`LintExpectationId`] as part of + /// Expected [`Diagnostic`][struct@diagnostic::Diagnostic]s store a [`LintExpectationId`] as part of /// the lint level. [`LintExpectationId`]s created early during the compilation /// (before `HirId`s have been defined) are not stable and can therefore not be /// stored on disk. This buffer stores these diagnostics until the ID has been - /// replaced by a stable [`LintExpectationId`]. The [`Diagnostic`][diagnostic::Diagnostic]s are the + /// replaced by a stable [`LintExpectationId`]. The [`Diagnostic`][struct@diagnostic::Diagnostic]s are the /// submitted for storage and added to the list of fulfilled expectations. unstable_expect_diagnostics: Vec<Diagnostic>, diff --git a/compiler/rustc_expand/src/mbe/macro_parser.rs b/compiler/rustc_expand/src/mbe/macro_parser.rs index 05c0cd952b8..7e85beaadcb 100644 --- a/compiler/rustc_expand/src/mbe/macro_parser.rs +++ b/compiler/rustc_expand/src/mbe/macro_parser.rs @@ -81,7 +81,7 @@ use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::sync::Lrc; use rustc_errors::ErrorGuaranteed; use rustc_lint_defs::pluralize; -use rustc_parse::parser::{NtOrTt, Parser}; +use rustc_parse::parser::{ParseNtResult, Parser}; use rustc_span::symbol::Ident; use rustc_span::symbol::MacroRulesNormalizedIdent; use rustc_span::Span; @@ -692,8 +692,8 @@ impl TtParser { Ok(nt) => nt, }; let m = match nt { - NtOrTt::Nt(nt) => MatchedNonterminal(Lrc::new(nt)), - NtOrTt::Tt(tt) => MatchedTokenTree(tt), + ParseNtResult::Nt(nt) => MatchedNonterminal(Lrc::new(nt)), + ParseNtResult::Tt(tt) => MatchedTokenTree(tt), }; mp.push_match(next_metavar, seq_depth, m); mp.idx += 1; diff --git a/compiler/rustc_expand/src/mbe/macro_rules.rs b/compiler/rustc_expand/src/mbe/macro_rules.rs index ce8b4621720..a5959d68fbc 100644 --- a/compiler/rustc_expand/src/mbe/macro_rules.rs +++ b/compiler/rustc_expand/src/mbe/macro_rules.rs @@ -1328,7 +1328,7 @@ fn is_in_follow(tok: &mbe::TokenTree, kind: NonterminalKind) -> IsInFollow { _ => IsInFollow::No(TOKENS), } } - NonterminalKind::PatWithOr { .. } => { + NonterminalKind::PatWithOr => { const TOKENS: &[&str] = &["`=>`", "`,`", "`=`", "`if`", "`in`"]; match tok { TokenTree::Token(token) => match token.kind { diff --git a/compiler/rustc_expand/src/mbe/metavar_expr.rs b/compiler/rustc_expand/src/mbe/metavar_expr.rs index b6382dcb894..7c37aadc67a 100644 --- a/compiler/rustc_expand/src/mbe/metavar_expr.rs +++ b/compiler/rustc_expand/src/mbe/metavar_expr.rs @@ -93,7 +93,17 @@ fn parse_count<'sess>( span: Span, ) -> PResult<'sess, MetaVarExpr> { let ident = parse_ident(iter, sess, span)?; - let depth = if try_eat_comma(iter) { Some(parse_depth(iter, sess, span)?) } else { None }; + let depth = if try_eat_comma(iter) { + if iter.look_ahead(0).is_none() { + return Err(sess.span_diagnostic.struct_span_err( + span, + "`count` followed by a comma must have an associated index indicating its depth", + )); + } + Some(parse_depth(iter, sess, span)?) + } else { + None + }; Ok(MetaVarExpr::Count(ident, depth)) } diff --git a/compiler/rustc_expand/src/mbe/transcribe.rs b/compiler/rustc_expand/src/mbe/transcribe.rs index a5f83b88f7e..15e7ab3fe3e 100644 --- a/compiler/rustc_expand/src/mbe/transcribe.rs +++ b/compiler/rustc_expand/src/mbe/transcribe.rs @@ -220,16 +220,15 @@ pub(super) fn transcribe<'a>( MatchedTokenTree(tt) => { // `tt`s are emitted into the output stream directly as "raw tokens", // without wrapping them into groups. - let token = tt.clone(); - result.push(token); + result.push(tt.clone()); } MatchedNonterminal(nt) => { // Other variables are emitted into the output stream as groups with // `Delimiter::Invisible` to maintain parsing priorities. // `Interpolated` is currently used for such groups in rustc parser. marker.visit_span(&mut sp); - let token = TokenTree::token_alone(token::Interpolated(nt.clone()), sp); - result.push(token); + result + .push(TokenTree::token_alone(token::Interpolated(nt.clone()), sp)); } MatchedSeq(..) => { // We were unable to descend far enough. This is an error. diff --git a/compiler/rustc_feature/src/active.rs b/compiler/rustc_feature/src/active.rs index 953ea1bf523..f5708f933d5 100644 --- a/compiler/rustc_feature/src/active.rs +++ b/compiler/rustc_feature/src/active.rs @@ -352,6 +352,8 @@ declare_features! ( (active, c_variadic, "1.34.0", Some(44930), None), /// Allows the use of `#[cfg(overflow_checks)` to check if integer overflow behaviour. (active, cfg_overflow_checks, "1.71.0", Some(111466), None), + /// Provides the relocation model information as cfg entry + (active, cfg_relocation_model, "CURRENT_RUSTC_VERSION", Some(114929), None), /// Allows the use of `#[cfg(sanitize = "option")]`; set when -Zsanitizer is used. (active, cfg_sanitize, "1.41.0", Some(39699), None), /// Allows `cfg(target_abi = "...")`. diff --git a/compiler/rustc_feature/src/builtin_attrs.rs b/compiler/rustc_feature/src/builtin_attrs.rs index a183cfd8776..2f7cff3ce5c 100644 --- a/compiler/rustc_feature/src/builtin_attrs.rs +++ b/compiler/rustc_feature/src/builtin_attrs.rs @@ -35,6 +35,7 @@ const GATED_CFGS: &[GatedCfg] = &[ (sym::target_has_atomic_load_store, sym::cfg_target_has_atomic, cfg_fn!(cfg_target_has_atomic)), (sym::sanitize, sym::cfg_sanitize, cfg_fn!(cfg_sanitize)), (sym::version, sym::cfg_version, cfg_fn!(cfg_version)), + (sym::relocation_model, sym::cfg_relocation_model, cfg_fn!(cfg_relocation_model)), ]; /// Find a gated cfg determined by the `pred`icate which is given the cfg's name. diff --git a/compiler/rustc_hir_analysis/src/astconv/errors.rs b/compiler/rustc_hir_analysis/src/astconv/errors.rs index bd311c98fac..6082d446979 100644 --- a/compiler/rustc_hir_analysis/src/astconv/errors.rs +++ b/compiler/rustc_hir_analysis/src/astconv/errors.rs @@ -597,7 +597,21 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { } } } - if !suggestions.is_empty() { + suggestions.sort_by_key(|&(span, _)| span); + // There are cases where one bound points to a span within another bound's span, like when + // you have code like the following (#115019), so we skip providing a suggestion in those + // cases to avoid having a malformed suggestion. + // + // pub struct Flatten<I> { + // inner: <IntoIterator<Item: IntoIterator<Item: >>::IntoIterator as Item>::core, + // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + // | ^^^^^^^^^^^^^^^^^^^^^ + // | | + // | associated types `Item`, `IntoIter` must be specified + // associated types `Item`, `IntoIter` must be specified + // } + let overlaps = suggestions.windows(2).any(|pair| pair[0].0.overlaps(pair[1].0)); + if !suggestions.is_empty() && !overlaps { err.multipart_suggestion( format!("specify the associated type{}", pluralize!(types_count)), suggestions, diff --git a/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs b/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs index ad02ca252c4..bd0ab6463f0 100644 --- a/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs +++ b/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs @@ -342,9 +342,16 @@ fn compare_method_predicate_entailment<'tcx>( continue; }; for obligation in obligations { + debug!(?obligation); match obligation.predicate.kind().skip_binder() { + // We need to register Projection oblgiations too, because we may end up with + // an implied `X::Item: 'a`, which gets desugared into `X::Item = ?0`, `?0: 'a`. + // If we only register the region outlives obligation, this leads to an unconstrained var. + // See `implied_bounds_entailment_alias_var` test. ty::PredicateKind::Clause( - ty::ClauseKind::RegionOutlives(..) | ty::ClauseKind::TypeOutlives(..), + ty::ClauseKind::RegionOutlives(..) + | ty::ClauseKind::TypeOutlives(..) + | ty::ClauseKind::Projection(..), ) => ocx.register_obligation(obligation), ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(arg)) => { if wf_args_seen.insert(arg) { diff --git a/compiler/rustc_hir_analysis/src/check/mod.rs b/compiler/rustc_hir_analysis/src/check/mod.rs index 4cf3587327d..d8331661366 100644 --- a/compiler/rustc_hir_analysis/src/check/mod.rs +++ b/compiler/rustc_hir_analysis/src/check/mod.rs @@ -289,6 +289,7 @@ fn default_body_is_unstable( &tcx.sess.parse_sess, feature, rustc_feature::GateIssue::Library(issue), + false, ); err.emit(); diff --git a/compiler/rustc_hir_typeck/src/cast.rs b/compiler/rustc_hir_typeck/src/cast.rs index 5bc0e2ee86c..31a03fabe4f 100644 --- a/compiler/rustc_hir_typeck/src/cast.rs +++ b/compiler/rustc_hir_typeck/src/cast.rs @@ -389,34 +389,26 @@ impl<'a, 'tcx> CastCheck<'tcx> { if let ty::Ref(reg, cast_ty, mutbl) = *self.cast_ty.kind() { if let ty::RawPtr(TypeAndMut { ty: expr_ty, .. }) = *self.expr_ty.kind() && fcx - .try_coerce( - self.expr, + .can_coerce( Ty::new_ref(fcx.tcx, fcx.tcx.lifetimes.re_erased, TypeAndMut { ty: expr_ty, mutbl }, ), self.cast_ty, - AllowTwoPhase::No, - None, ) - .is_ok() { sugg = Some((format!("&{}*", mutbl.prefix_str()), cast_ty == expr_ty)); } else if let ty::Ref(expr_reg, expr_ty, expr_mutbl) = *self.expr_ty.kind() && expr_mutbl == Mutability::Not && mutbl == Mutability::Mut && fcx - .try_coerce( - self.expr, + .can_coerce( Ty::new_ref(fcx.tcx, expr_reg, TypeAndMut { ty: expr_ty, mutbl: Mutability::Mut }, ), self.cast_ty, - AllowTwoPhase::No, - None, ) - .is_ok() { sugg_mutref = true; } @@ -424,30 +416,22 @@ impl<'a, 'tcx> CastCheck<'tcx> { if !sugg_mutref && sugg == None && fcx - .try_coerce( - self.expr, + .can_coerce( Ty::new_ref(fcx.tcx,reg, TypeAndMut { ty: self.expr_ty, mutbl }), self.cast_ty, - AllowTwoPhase::No, - None, ) - .is_ok() { sugg = Some((format!("&{}", mutbl.prefix_str()), false)); } } else if let ty::RawPtr(TypeAndMut { mutbl, .. }) = *self.cast_ty.kind() && fcx - .try_coerce( - self.expr, + .can_coerce( Ty::new_ref(fcx.tcx, fcx.tcx.lifetimes.re_erased, TypeAndMut { ty: self.expr_ty, mutbl }, ), self.cast_ty, - AllowTwoPhase::No, - None, ) - .is_ok() { sugg = Some((format!("&{}", mutbl.prefix_str()), false)); } @@ -760,7 +744,7 @@ impl<'a, 'tcx> CastCheck<'tcx> { ty::FnDef(..) => { // Attempt a coercion to a fn pointer type. let f = fcx.normalize(self.expr_span, self.expr_ty.fn_sig(fcx.tcx)); - let res = fcx.try_coerce( + let res = fcx.coerce( self.expr, self.expr_ty, Ty::new_fn_ptr(fcx.tcx, f), @@ -860,7 +844,7 @@ impl<'a, 'tcx> CastCheck<'tcx> { (_, DynStar) => { if fcx.tcx.features().dyn_star { - bug!("should be handled by `try_coerce`") + bug!("should be handled by `coerce`") } else { Err(CastError::IllegalCast) } @@ -956,7 +940,7 @@ impl<'a, 'tcx> CastCheck<'tcx> { // Coerce to a raw pointer so that we generate AddressOf in MIR. let array_ptr_type = Ty::new_ptr(fcx.tcx, m_expr); - fcx.try_coerce(self.expr, self.expr_ty, array_ptr_type, AllowTwoPhase::No, None) + fcx.coerce(self.expr, self.expr_ty, array_ptr_type, AllowTwoPhase::No, None) .unwrap_or_else(|_| { bug!( "could not cast from reference to array to pointer to array ({:?} to {:?})", @@ -992,7 +976,7 @@ impl<'a, 'tcx> CastCheck<'tcx> { } fn try_coercion_cast(&self, fcx: &FnCtxt<'a, 'tcx>) -> Result<(), ty::error::TypeError<'tcx>> { - match fcx.try_coerce(self.expr, self.expr_ty, self.cast_ty, AllowTwoPhase::No, None) { + match fcx.coerce(self.expr, self.expr_ty, self.cast_ty, AllowTwoPhase::No, None) { Ok(_) => Ok(()), Err(err) => Err(err), } diff --git a/compiler/rustc_hir_typeck/src/closure.rs b/compiler/rustc_hir_typeck/src/closure.rs index 3e67afb1c3d..b19fb6da6de 100644 --- a/compiler/rustc_hir_typeck/src/closure.rs +++ b/compiler/rustc_hir_typeck/src/closure.rs @@ -711,6 +711,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } }; + let span = self.tcx.def_span(expr_def_id); + let output_ty = match *ret_ty.kind() { ty::Infer(ty::TyVar(ret_vid)) => { self.obligations_for_self_ty(ret_vid).find_map(|obligation| { @@ -724,20 +726,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .find_map(|(p, s)| get_future_output(p.as_predicate(), s))?, ty::Error(_) => return None, _ => span_bug!( - self.tcx.def_span(expr_def_id), + span, "async fn generator return type not an inference variable: {ret_ty}" ), }; + let output_ty = self.normalize(span, output_ty); + // async fn that have opaque types in their return type need to redo the conversion to inference variables // as they fetch the still opaque version from the signature. let InferOk { value: output_ty, obligations } = self - .replace_opaque_types_with_inference_vars( - output_ty, - body_def_id, - self.tcx.def_span(expr_def_id), - self.param_env, - ); + .replace_opaque_types_with_inference_vars(output_ty, body_def_id, span, self.param_env); self.register_predicates(obligations); Some(output_ty) diff --git a/compiler/rustc_hir_typeck/src/coercion.rs b/compiler/rustc_hir_typeck/src/coercion.rs index 726914a995b..fca675ea9d8 100644 --- a/compiler/rustc_hir_typeck/src/coercion.rs +++ b/compiler/rustc_hir_typeck/src/coercion.rs @@ -1005,7 +1005,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// adjusted type of the expression, if successful. /// Adjustments are only recorded if the coercion succeeded. /// The expressions *must not* have any preexisting adjustments. - pub fn try_coerce( + pub fn coerce( &self, expr: &hir::Expr<'_>, expr_ty: Ty<'tcx>, @@ -1036,7 +1036,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }) } - /// Same as `try_coerce()`, but without side-effects. + /// Same as `coerce()`, but without side-effects. /// /// Returns false if the coercion creates any obligations that result in /// errors. @@ -1494,7 +1494,7 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> { // Special-case the first expression we are coercing. // To be honest, I'm not entirely sure why we do this. // We don't allow two-phase borrows, see comment in try_find_coercion_lub for why - fcx.try_coerce( + fcx.coerce( expression, expression_ty, self.expected_ty, diff --git a/compiler/rustc_hir_typeck/src/demand.rs b/compiler/rustc_hir_typeck/src/demand.rs index 5b06088c348..2c16f21b491 100644 --- a/compiler/rustc_hir_typeck/src/demand.rs +++ b/compiler/rustc_hir_typeck/src/demand.rs @@ -53,7 +53,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { || self.suggest_no_capture_closure(err, expected, expr_ty) || self.suggest_boxing_when_appropriate(err, expr.span, expr.hir_id, expected, expr_ty) || self.suggest_block_to_brackets_peeling_refs(err, expr, expr_ty, expected) - || self.suggest_copied_cloned_or_as_ref(err, expr, expr_ty, expected, expected_ty_expr) + || self.suggest_copied_cloned_or_as_ref(err, expr, expr_ty, expected) || self.suggest_clone_for_ref(err, expr, expr_ty, expected) || self.suggest_into(err, expr, expr_ty, expected) || self.suggest_floating_point_literal(err, expr, expected) @@ -254,7 +254,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ) -> (Ty<'tcx>, Option<DiagnosticBuilder<'tcx, ErrorGuaranteed>>) { let expected = self.resolve_vars_with_obligations(expected); - let e = match self.try_coerce(expr, checked_ty, expected, allow_two_phase, None) { + let e = match self.coerce(expr, checked_ty, expected, allow_two_phase, None) { Ok(ty) => return (ty, None), Err(e) => e, }; @@ -475,7 +475,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { { let Some(arg_ty) = self.node_ty_opt(arg_expr.hir_id) else { continue; }; let arg_ty = arg_ty.fold_with(&mut fudger); - let _ = self.try_coerce( + let _ = self.coerce( arg_expr, arg_ty, *expected_arg_ty, diff --git a/compiler/rustc_hir_typeck/src/errors.rs b/compiler/rustc_hir_typeck/src/errors.rs index 36096aa35d4..054d23c71d4 100644 --- a/compiler/rustc_hir_typeck/src/errors.rs +++ b/compiler/rustc_hir_typeck/src/errors.rs @@ -253,7 +253,7 @@ impl HelpUseLatestEdition { } #[derive(Subdiagnostic)] -pub enum OptionResultRefMismatch<'tcx> { +pub enum OptionResultRefMismatch { #[suggestion( hir_typeck_option_result_copied, code = ".copied()", @@ -276,19 +276,20 @@ pub enum OptionResultRefMismatch<'tcx> { span: Span, def_path: String, }, - #[suggestion( - hir_typeck_option_result_asref, - code = ".as_ref()", - style = "verbose", - applicability = "machine-applicable" - )] - AsRef { - #[primary_span] - span: Span, - def_path: String, - expected_ty: Ty<'tcx>, - expr_ty: Ty<'tcx>, - }, + // FIXME: #114050 + // #[suggestion( + // hir_typeck_option_result_asref, + // code = ".as_ref()", + // style = "verbose", + // applicability = "machine-applicable" + // )] + // AsRef { + // #[primary_span] + // span: Span, + // def_path: String, + // expected_ty: Ty<'tcx>, + // expr_ty: Ty<'tcx>, + // }, } #[derive(Diagnostic)] diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs index 004c8affcf0..4def7867384 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs @@ -260,9 +260,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // fulfillment error to be more accurate. let coerced_ty = self.resolve_vars_with_obligations(coerced_ty); - let coerce_error = self - .try_coerce(provided_arg, checked_ty, coerced_ty, AllowTwoPhase::Yes, None) - .err(); + let coerce_error = + self.coerce(provided_arg, checked_ty, coerced_ty, AllowTwoPhase::Yes, None).err(); if coerce_error.is_some() { return Compatibility::Incompatible(coerce_error); diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs index 48358e338da..d2a53ee8b5e 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs @@ -1097,7 +1097,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { expr: &hir::Expr<'_>, expr_ty: Ty<'tcx>, expected_ty: Ty<'tcx>, - expected_ty_expr: Option<&'tcx hir::Expr<'tcx>>, ) -> bool { let ty::Adt(adt_def, args) = expr_ty.kind() else { return false; @@ -1115,7 +1114,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { { let expr_inner_ty = args.type_at(0); let expected_inner_ty = expected_args.type_at(0); - if let &ty::Ref(_, ty, mutability) = expr_inner_ty.kind() + if let &ty::Ref(_, ty, _mutability) = expr_inner_ty.kind() && self.can_eq(self.param_env, ty, expected_inner_ty) { let def_path = self.tcx.def_path_str(adt_def.did()); @@ -1124,14 +1123,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { errors::OptionResultRefMismatch::Copied { span, def_path } - } else if let Some(expected_ty_expr) = expected_ty_expr - // FIXME: suggest changes to both expressions to convert both to - // Option/Result<&T> - && mutability.is_not() - { - errors::OptionResultRefMismatch::AsRef { - span: expected_ty_expr.span.shrink_to_hi(), expected_ty, expr_ty, def_path - } } else if let Some(clone_did) = self.tcx.lang_items().clone_trait() && rustc_trait_selection::traits::type_known_to_meet_bound_modulo_regions( self, diff --git a/compiler/rustc_hir_typeck/src/method/prelude2021.rs b/compiler/rustc_hir_typeck/src/method/prelude2021.rs index 5b19a4c525f..3f1dca5b1de 100644 --- a/compiler/rustc_hir_typeck/src/method/prelude2021.rs +++ b/compiler/rustc_hir_typeck/src/method/prelude2021.rs @@ -14,6 +14,7 @@ use rustc_span::symbol::kw::{Empty, Underscore}; use rustc_span::symbol::{sym, Ident}; use rustc_span::Span; use rustc_trait_selection::infer::InferCtxtExt; +use std::fmt::Write; impl<'a, 'tcx> FnCtxt<'a, 'tcx> { pub(super) fn lint_dot_call_from_2018( @@ -143,16 +144,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let (self_adjusted, precise) = self.adjust_expr(pick, self_expr, sp); if precise { - let args = args - .iter() - .map(|arg| { - let span = arg.span.find_ancestor_inside(sp).unwrap_or_default(); - format!( - ", {}", - self.sess().source_map().span_to_snippet(span).unwrap() - ) - }) - .collect::<String>(); + let args = args.iter().fold(String::new(), |mut string, arg| { + let span = arg.span.find_ancestor_inside(sp).unwrap_or_default(); + write!( + string, + ", {}", + self.sess().source_map().span_to_snippet(span).unwrap() + ) + .unwrap(); + string + }); lint.span_suggestion( sp, diff --git a/compiler/rustc_incremental/src/persist/fs.rs b/compiler/rustc_incremental/src/persist/fs.rs index 1111a1a17e2..db8ea2bfe48 100644 --- a/compiler/rustc_incremental/src/persist/fs.rs +++ b/compiler/rustc_incremental/src/persist/fs.rs @@ -538,9 +538,13 @@ where continue; } - let timestamp = extract_timestamp_from_session_dir(&directory_name).unwrap_or_else(|_| { - bug!("unexpected incr-comp session dir: {}", session_dir.display()) - }); + let timestamp = match extract_timestamp_from_session_dir(&directory_name) { + Ok(timestamp) => timestamp, + Err(e) => { + debug!("unexpected incr-comp session dir: {}: {}", session_dir.display(), e); + continue; + } + }; if timestamp > best_candidate.0 { best_candidate = (timestamp, Some(session_dir.clone())); @@ -562,14 +566,14 @@ fn is_session_directory_lock_file(file_name: &str) -> bool { file_name.starts_with("s-") && file_name.ends_with(LOCK_FILE_EXT) } -fn extract_timestamp_from_session_dir(directory_name: &str) -> Result<SystemTime, ()> { +fn extract_timestamp_from_session_dir(directory_name: &str) -> Result<SystemTime, &'static str> { if !is_session_directory(directory_name) { - return Err(()); + return Err("not a directory"); } let dash_indices: Vec<_> = directory_name.match_indices('-').map(|(idx, _)| idx).collect(); if dash_indices.len() != 3 { - return Err(()); + return Err("not three dashes in name"); } string_to_timestamp(&directory_name[dash_indices[0] + 1..dash_indices[1]]) @@ -581,11 +585,11 @@ fn timestamp_to_string(timestamp: SystemTime) -> String { base_n::encode(micros as u128, INT_ENCODE_BASE) } -fn string_to_timestamp(s: &str) -> Result<SystemTime, ()> { +fn string_to_timestamp(s: &str) -> Result<SystemTime, &'static str> { let micros_since_unix_epoch = u64::from_str_radix(s, INT_ENCODE_BASE as u32); if micros_since_unix_epoch.is_err() { - return Err(()); + return Err("timestamp not an int"); } let micros_since_unix_epoch = micros_since_unix_epoch.unwrap(); diff --git a/compiler/rustc_index/src/lib.rs b/compiler/rustc_index/src/lib.rs index 9942c70c4ae..76b493353cc 100644 --- a/compiler/rustc_index/src/lib.rs +++ b/compiler/rustc_index/src/lib.rs @@ -29,6 +29,18 @@ pub use {idx::Idx, slice::IndexSlice, vec::IndexVec}; pub use rustc_macros::newtype_index; /// Type size assertion. The first argument is a type and the second argument is its expected size. +/// +/// <div class="warning"> +/// +/// Emitting hard errors from size assertions like this is generally not +/// recommended, especially in libraries, because they can cause build failures if the layout +/// algorithm or dependencies change. Here in rustc we control the toolchain and layout algorithm, +/// so the former is not a problem. For the latter we have a lockfile as rustc is an application and +/// precompiled library. +/// +/// Short version: Don't copy this macro into your own code. Use a `#[test]` instead. +/// +/// </div> #[macro_export] macro_rules! static_assert_size { ($ty:ty, $size:expr) => { diff --git a/compiler/rustc_infer/src/traits/mod.rs b/compiler/rustc_infer/src/traits/mod.rs index dc41630196b..a5b2ccce85e 100644 --- a/compiler/rustc_infer/src/traits/mod.rs +++ b/compiler/rustc_infer/src/traits/mod.rs @@ -9,6 +9,7 @@ mod structural_impls; pub mod util; use std::cmp; +use std::hash::{Hash, Hasher}; use hir::def_id::LocalDefId; use rustc_hir as hir; @@ -36,7 +37,7 @@ pub use rustc_middle::traits::*; /// either identifying an `impl` (e.g., `impl Eq for i32`) that /// satisfies the obligation, or else finding a bound that is in /// scope. The eventual result is usually a `Selection` (defined below). -#[derive(Clone, PartialEq, Eq, Hash)] +#[derive(Clone)] pub struct Obligation<'tcx, T> { /// The reason we have to prove this thing. pub cause: ObligationCause<'tcx>, @@ -55,6 +56,27 @@ pub struct Obligation<'tcx, T> { pub recursion_depth: usize, } +impl<'tcx, T: PartialEq> PartialEq<Obligation<'tcx, T>> for Obligation<'tcx, T> { + #[inline] + fn eq(&self, other: &Obligation<'tcx, T>) -> bool { + // Ignore `cause` and `recursion_depth`. This is a small performance + // win for a few crates, and a huge performance win for the crate in + // https://github.com/rust-lang/rustc-perf/pull/1680, which greatly + // stresses the trait system. + self.param_env == other.param_env && self.predicate == other.predicate + } +} + +impl<T: Eq> Eq for Obligation<'_, T> {} + +impl<T: Hash> Hash for Obligation<'_, T> { + fn hash<H: Hasher>(&self, state: &mut H) -> () { + // See the comment on `Obligation::eq`. + self.param_env.hash(state); + self.predicate.hash(state); + } +} + impl<'tcx, P> From<Obligation<'tcx, P>> for solve::Goal<'tcx, P> { fn from(value: Obligation<'tcx, P>) -> Self { solve::Goal { param_env: value.param_env, predicate: value.predicate } diff --git a/compiler/rustc_lint/src/context.rs b/compiler/rustc_lint/src/context.rs index f73797415bc..aabefb729f3 100644 --- a/compiler/rustc_lint/src/context.rs +++ b/compiler/rustc_lint/src/context.rs @@ -966,6 +966,14 @@ pub trait LintContext: Sized { Applicability::MachineApplicable ); } + BuiltinLintDiagnostics::AssociatedConstElidedLifetime { elided, span } => { + db.span_suggestion_verbose( + if elided { span.shrink_to_hi() } else { span }, + "use the `'static` lifetime", + if elided { "'static " } else { "'static" }, + Applicability::MachineApplicable + ); + } } // Rewrap `db`, and pass control to the user. decorate(db) diff --git a/compiler/rustc_lint/src/levels.rs b/compiler/rustc_lint/src/levels.rs index 1f4e5fa4d3b..0a40d17f98e 100644 --- a/compiler/rustc_lint/src/levels.rs +++ b/compiler/rustc_lint/src/levels.rs @@ -12,7 +12,7 @@ use rustc_ast as ast; use rustc_ast_pretty::pprust; use rustc_data_structures::fx::FxHashMap; use rustc_errors::{DecorateLint, DiagnosticBuilder, DiagnosticMessage, MultiSpan}; -use rustc_feature::Features; +use rustc_feature::{Features, GateIssue}; use rustc_hir as hir; use rustc_hir::intravisit::{self, Visitor}; use rustc_hir::HirId; @@ -24,12 +24,14 @@ use rustc_middle::lint::{ }; use rustc_middle::query::Providers; use rustc_middle::ty::{RegisteredTools, TyCtxt}; -use rustc_session::lint::builtin::{RENAMED_AND_REMOVED_LINTS, UNKNOWN_LINTS, UNUSED_ATTRIBUTES}; use rustc_session::lint::{ - builtin::{self, FORBIDDEN_LINT_GROUPS, SINGLE_USE_LIFETIMES, UNFULFILLED_LINT_EXPECTATIONS}, + builtin::{ + self, FORBIDDEN_LINT_GROUPS, RENAMED_AND_REMOVED_LINTS, SINGLE_USE_LIFETIMES, + UNFULFILLED_LINT_EXPECTATIONS, UNKNOWN_LINTS, UNUSED_ATTRIBUTES, + }, Level, Lint, LintExpectationId, LintId, }; -use rustc_session::parse::{add_feature_diagnostics, feature_err}; +use rustc_session::parse::feature_err; use rustc_session::Session; use rustc_span::symbol::{sym, Symbol}; use rustc_span::{Span, DUMMY_SP}; @@ -566,7 +568,7 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> { continue; } - if self.check_gated_lint(id, DUMMY_SP) { + if self.check_gated_lint(id, DUMMY_SP, true) { let src = LintLevelSource::CommandLine(lint_flag_val, orig_level); self.insert(id, (level, src)); } @@ -837,7 +839,7 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> { reason, }; for &id in *ids { - if self.check_gated_lint(id, attr.span) { + if self.check_gated_lint(id, attr.span, false) { self.insert_spec(id, (level, src)); } } @@ -854,7 +856,7 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> { reason, }; for &id in ids { - if self.check_gated_lint(id, attr.span) { + if self.check_gated_lint(id, attr.span, false) { self.insert_spec(id, (level, src)); } } @@ -955,7 +957,7 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> { reason, }; for &id in ids { - if self.check_gated_lint(id, attr.span) { + if self.check_gated_lint(id, attr.span, false) { self.insert_spec(id, (level, src)); } } @@ -1000,7 +1002,7 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> { // FIXME only emit this once for each attribute, instead of repeating it 4 times for // pre-expansion lints, post-expansion lints, `shallow_lint_levels_on` and `lint_expectations`. #[track_caller] - fn check_gated_lint(&self, lint_id: LintId, span: Span) -> bool { + fn check_gated_lint(&self, lint_id: LintId, span: Span, lint_from_cli: bool) -> bool { if let Some(feature) = lint_id.lint.feature_gate { if !self.features.enabled(feature) { let lint = builtin::UNKNOWN_LINTS; @@ -1015,7 +1017,13 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> { |lint| { lint.set_arg("name", lint_id.lint.name_lower()); lint.note(fluent::lint_note); - add_feature_diagnostics(lint, &self.sess.parse_sess, feature); + rustc_session::parse::add_feature_diagnostics_for_issue( + lint, + &self.sess.parse_sess, + feature, + GateIssue::Language, + lint_from_cli, + ); lint }, ); diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs index 96c31a90da8..0aa37642b74 100644 --- a/compiler/rustc_lint_defs/src/builtin.rs +++ b/compiler/rustc_lint_defs/src/builtin.rs @@ -3376,6 +3376,7 @@ declare_lint_pass! { DEPRECATED_IN_FUTURE, DEPRECATED_WHERE_CLAUSE_LOCATION, DUPLICATE_MACRO_ATTRIBUTES, + ELIDED_LIFETIMES_IN_ASSOCIATED_CONSTANT, ELIDED_LIFETIMES_IN_PATHS, EXPORTED_PRIVATE_DEPENDENCIES, FFI_UNWIND_CALLS, @@ -4527,3 +4528,44 @@ declare_lint! { reference: "issue #114095 <https://github.com/rust-lang/rust/issues/114095>", }; } + +declare_lint! { + /// The `elided_lifetimes_in_associated_constant` lint detects elided lifetimes + /// that were erroneously allowed in associated constants. + /// + /// ### Example + /// + /// ```rust,compile_fail + /// #![deny(elided_lifetimes_in_associated_constant)] + /// + /// struct Foo; + /// + /// impl Foo { + /// const STR: &str = "hello, world"; + /// } + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// Previous version of Rust + /// + /// Implicit static-in-const behavior was decided [against] for associated + /// constants because of ambiguity. This, however, regressed and the compiler + /// erroneously treats elided lifetimes in associated constants as lifetime + /// parameters on the impl. + /// + /// This is a [future-incompatible] lint to transition this to a + /// hard error in the future. + /// + /// [against]: https://github.com/rust-lang/rust/issues/38831 + /// [future-incompatible]: ../index.md#future-incompatible-lints + pub ELIDED_LIFETIMES_IN_ASSOCIATED_CONSTANT, + Warn, + "elided lifetimes cannot be used in associated constants in impls", + @future_incompatible = FutureIncompatibleInfo { + reason: FutureIncompatibilityReason::FutureReleaseError, + reference: "issue #115010 <https://github.com/rust-lang/rust/issues/115010>", + }; +} diff --git a/compiler/rustc_lint_defs/src/lib.rs b/compiler/rustc_lint_defs/src/lib.rs index f350957f72f..76e736859d3 100644 --- a/compiler/rustc_lint_defs/src/lib.rs +++ b/compiler/rustc_lint_defs/src/lib.rs @@ -23,8 +23,9 @@ pub mod builtin; #[macro_export] macro_rules! pluralize { + // Pluralize based on count (e.g., apples) ($x:expr) => { - if $x != 1 { "s" } else { "" } + if $x == 1 { "" } else { "s" } }; ("has", $x:expr) => { if $x == 1 { "has" } else { "have" } @@ -572,6 +573,10 @@ pub enum BuiltinLintDiagnostics { /// The span of the unnecessarily-qualified path to remove. removal_span: Span, }, + AssociatedConstElidedLifetime { + elided: bool, + span: Span, + }, } /// Lints that are buffered up early on in the `Session` before the diff --git a/compiler/rustc_macros/src/serialize.rs b/compiler/rustc_macros/src/serialize.rs index f1e7b8eb6c7..ba75517d7a6 100644 --- a/compiler/rustc_macros/src/serialize.rs +++ b/compiler/rustc_macros/src/serialize.rs @@ -59,14 +59,14 @@ fn decodable_body( }) .collect(); let message = format!( - "invalid enum variant tag while decoding `{}`, expected 0..{}", + "invalid enum variant tag while decoding `{}`, expected 0..{}, actual {{}}", ty_name, variants.len() ); quote! { match ::rustc_serialize::Decoder::read_usize(__decoder) { #match_inner - _ => panic!(#message), + n => panic!(#message, n), } } } diff --git a/compiler/rustc_middle/src/hir/mod.rs b/compiler/rustc_middle/src/hir/mod.rs index e8fd469e1fb..0da8fe9cca7 100644 --- a/compiler/rustc_middle/src/hir/mod.rs +++ b/compiler/rustc_middle/src/hir/mod.rs @@ -164,18 +164,15 @@ pub fn provide(providers: &mut Providers) { tcx.hir_crate(()).owners[id.def_id].as_owner().map_or(AttributeMap::EMPTY, |o| &o.attrs) }; providers.def_span = |tcx, def_id| { - let def_id = def_id; let hir_id = tcx.hir().local_def_id_to_hir_id(def_id); tcx.hir().opt_span(hir_id).unwrap_or(DUMMY_SP) }; providers.def_ident_span = |tcx, def_id| { - let def_id = def_id; let hir_id = tcx.hir().local_def_id_to_hir_id(def_id); tcx.hir().opt_ident_span(hir_id) }; - providers.fn_arg_names = |tcx, id| { + providers.fn_arg_names = |tcx, def_id| { let hir = tcx.hir(); - let def_id = id; let hir_id = hir.local_def_id_to_hir_id(def_id); if let Some(body_id) = hir.maybe_body_owned_by(def_id) { tcx.arena.alloc_from_iter(hir.body_param_names(body_id)) @@ -190,7 +187,7 @@ pub fn provide(providers: &mut Providers) { { idents } else { - span_bug!(hir.span(hir_id), "fn_arg_names: unexpected item {:?}", id); + span_bug!(hir.span(hir_id), "fn_arg_names: unexpected item {:?}", def_id); } }; providers.opt_def_kind = |tcx, def_id| tcx.hir().opt_def_kind(def_id); diff --git a/compiler/rustc_middle/src/mir/coverage.rs b/compiler/rustc_middle/src/mir/coverage.rs index d7d6e3a0086..1efb54bdb08 100644 --- a/compiler/rustc_middle/src/mir/coverage.rs +++ b/compiler/rustc_middle/src/mir/coverage.rs @@ -96,21 +96,6 @@ pub enum CoverageKind { Unreachable, } -impl CoverageKind { - pub fn as_operand(&self) -> Operand { - use CoverageKind::*; - match *self { - Counter { id, .. } => Operand::Counter(id), - Expression { id, .. } => Operand::Expression(id), - Unreachable => bug!("Unreachable coverage cannot be part of an expression"), - } - } - - pub fn is_expression(&self) -> bool { - matches!(self, Self::Expression { .. }) - } -} - impl Debug for CoverageKind { fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result { use CoverageKind::*; diff --git a/compiler/rustc_middle/src/mir/mod.rs b/compiler/rustc_middle/src/mir/mod.rs index ddb5e248cdc..9ef3a1b30e4 100644 --- a/compiler/rustc_middle/src/mir/mod.rs +++ b/compiler/rustc_middle/src/mir/mod.rs @@ -1109,10 +1109,6 @@ pub struct VarDebugInfo<'tcx> { /// originated from (starting from 1). Note, if MIR inlining is enabled, then this is the /// argument number in the original function before it was inlined. pub argument_index: Option<u16>, - - /// The data represents `name` dereferenced `references` times, - /// and not the direct value. - pub references: u8, } /////////////////////////////////////////////////////////////////////////// diff --git a/compiler/rustc_middle/src/mir/patch.rs b/compiler/rustc_middle/src/mir/patch.rs index c4c3341f873..cd74a403ff6 100644 --- a/compiler/rustc_middle/src/mir/patch.rs +++ b/compiler/rustc_middle/src/mir/patch.rs @@ -35,7 +35,7 @@ impl<'tcx> MirPatch<'tcx> { for (bb, block) in body.basic_blocks.iter_enumerated() { // Check if we already have a resume block - if let TerminatorKind::Resume = block.terminator().kind && block.statements.is_empty() { + if let TerminatorKind::UnwindResume = block.terminator().kind && block.statements.is_empty() { result.resume_block = Some(bb); continue; } @@ -50,7 +50,7 @@ impl<'tcx> MirPatch<'tcx> { } // Check if we already have a terminate block - if let TerminatorKind::Terminate = block.terminator().kind && block.statements.is_empty() { + if let TerminatorKind::UnwindTerminate = block.terminator().kind && block.statements.is_empty() { result.terminate_block = Some(bb); continue; } @@ -68,7 +68,7 @@ impl<'tcx> MirPatch<'tcx> { statements: vec![], terminator: Some(Terminator { source_info: SourceInfo::outermost(self.body_span), - kind: TerminatorKind::Resume, + kind: TerminatorKind::UnwindResume, }), is_cleanup: true, }); @@ -102,7 +102,7 @@ impl<'tcx> MirPatch<'tcx> { statements: vec![], terminator: Some(Terminator { source_info: SourceInfo::outermost(self.body_span), - kind: TerminatorKind::Terminate, + kind: TerminatorKind::UnwindTerminate, }), is_cleanup: true, }); diff --git a/compiler/rustc_middle/src/mir/pretty.rs b/compiler/rustc_middle/src/mir/pretty.rs index 27e39137092..773056e8a17 100644 --- a/compiler/rustc_middle/src/mir/pretty.rs +++ b/compiler/rustc_middle/src/mir/pretty.rs @@ -555,13 +555,8 @@ fn write_scope_tree( } let indented_debug_info = format!( - "{0:1$}debug {2} => {3:&<4$}{5:?};", - INDENT, - indent, - var_debug_info.name, - "", - var_debug_info.references as usize, - var_debug_info.value, + "{0:1$}debug {2} => {3:?};", + INDENT, indent, var_debug_info.name, var_debug_info.value, ); if tcx.sess.opts.unstable_opts.mir_include_spans { diff --git a/compiler/rustc_middle/src/mir/spanview.rs b/compiler/rustc_middle/src/mir/spanview.rs index 20a9e6889e4..a5358687c14 100644 --- a/compiler/rustc_middle/src/mir/spanview.rs +++ b/compiler/rustc_middle/src/mir/spanview.rs @@ -238,45 +238,6 @@ pub fn source_range_no_file(tcx: TyCtxt<'_>, span: Span) -> String { format!("{}:{}-{}:{}", start.line, start.col.to_usize() + 1, end.line, end.col.to_usize() + 1) } -pub fn statement_kind_name(statement: &Statement<'_>) -> &'static str { - use StatementKind::*; - match statement.kind { - Assign(..) => "Assign", - FakeRead(..) => "FakeRead", - SetDiscriminant { .. } => "SetDiscriminant", - Deinit(..) => "Deinit", - StorageLive(..) => "StorageLive", - StorageDead(..) => "StorageDead", - Retag(..) => "Retag", - PlaceMention(..) => "PlaceMention", - AscribeUserType(..) => "AscribeUserType", - Coverage(..) => "Coverage", - Intrinsic(..) => "Intrinsic", - ConstEvalCounter => "ConstEvalCounter", - Nop => "Nop", - } -} - -pub fn terminator_kind_name(term: &Terminator<'_>) -> &'static str { - use TerminatorKind::*; - match term.kind { - Goto { .. } => "Goto", - SwitchInt { .. } => "SwitchInt", - Resume => "Resume", - Terminate => "Terminate", - Return => "Return", - Unreachable => "Unreachable", - Drop { .. } => "Drop", - Call { .. } => "Call", - Assert { .. } => "Assert", - Yield { .. } => "Yield", - GeneratorDrop => "GeneratorDrop", - FalseEdge { .. } => "FalseEdge", - FalseUnwind { .. } => "FalseUnwind", - InlineAsm { .. } => "InlineAsm", - } -} - fn statement_span_viewable<'tcx>( tcx: TyCtxt<'tcx>, body_span: Span, @@ -304,7 +265,7 @@ fn terminator_span_viewable<'tcx>( if !body_span.contains(span) { return None; } - let id = format!("{}:{}", bb.index(), terminator_kind_name(term)); + let id = format!("{}:{}", bb.index(), term.kind.name()); let tooltip = tooltip(tcx, &id, span, vec![], &data.terminator); Some(SpanViewable { bb, span, id, tooltip }) } @@ -631,7 +592,7 @@ fn tooltip<'tcx>( "\n{}{}: {}: {:?}", TOOLTIP_INDENT, source_range, - statement_kind_name(&statement), + statement.kind.name(), statement )); } @@ -641,7 +602,7 @@ fn tooltip<'tcx>( "\n{}{}: {}: {:?}", TOOLTIP_INDENT, source_range, - terminator_kind_name(term), + term.kind.name(), term.kind )); } diff --git a/compiler/rustc_middle/src/mir/syntax.rs b/compiler/rustc_middle/src/mir/syntax.rs index be27bf75dbd..e91e822f915 100644 --- a/compiler/rustc_middle/src/mir/syntax.rs +++ b/compiler/rustc_middle/src/mir/syntax.rs @@ -380,6 +380,28 @@ pub enum StatementKind<'tcx> { Nop, } +impl StatementKind<'_> { + /// Returns a simple string representation of a `StatementKind` variant, independent of any + /// values it might hold (e.g. `StatementKind::Assign` always returns `"Assign"`). + pub const fn name(&self) -> &'static str { + match self { + StatementKind::Assign(..) => "Assign", + StatementKind::FakeRead(..) => "FakeRead", + StatementKind::SetDiscriminant { .. } => "SetDiscriminant", + StatementKind::Deinit(..) => "Deinit", + StatementKind::StorageLive(..) => "StorageLive", + StatementKind::StorageDead(..) => "StorageDead", + StatementKind::Retag(..) => "Retag", + StatementKind::PlaceMention(..) => "PlaceMention", + StatementKind::AscribeUserType(..) => "AscribeUserType", + StatementKind::Coverage(..) => "Coverage", + StatementKind::Intrinsic(..) => "Intrinsic", + StatementKind::ConstEvalCounter => "ConstEvalCounter", + StatementKind::Nop => "Nop", + } + } +} + #[derive( Clone, TyEncodable, @@ -593,13 +615,13 @@ pub enum TerminatorKind<'tcx> { /// /// Only permitted in cleanup blocks. `Resume` is not permitted with `-C unwind=abort` after /// deaggregation runs. - Resume, + UnwindResume, /// Indicates that the landing pad is finished and that the process should terminate. /// /// Used to prevent unwinding for foreign items or with `-C unwind=abort`. Only permitted in /// cleanup blocks. - Terminate, + UnwindTerminate, /// Returns from the function. /// @@ -790,8 +812,8 @@ impl TerminatorKind<'_> { match self { TerminatorKind::Goto { .. } => "Goto", TerminatorKind::SwitchInt { .. } => "SwitchInt", - TerminatorKind::Resume => "Resume", - TerminatorKind::Terminate => "Terminate", + TerminatorKind::UnwindResume => "UnwindResume", + TerminatorKind::UnwindTerminate => "UnwindTerminate", TerminatorKind::Return => "Return", TerminatorKind::Unreachable => "Unreachable", TerminatorKind::Drop { .. } => "Drop", diff --git a/compiler/rustc_middle/src/mir/terminator.rs b/compiler/rustc_middle/src/mir/terminator.rs index 1f878d23b44..bd87563e2bb 100644 --- a/compiler/rustc_middle/src/mir/terminator.rs +++ b/compiler/rustc_middle/src/mir/terminator.rs @@ -155,8 +155,8 @@ impl<'tcx> TerminatorKind<'tcx> { | InlineAsm { destination: Some(t), unwind: _, .. } => { Some(t).into_iter().chain((&[]).into_iter().copied()) } - Resume - | Terminate + UnwindResume + | UnwindTerminate | GeneratorDrop | Return | Unreachable @@ -197,8 +197,8 @@ impl<'tcx> TerminatorKind<'tcx> { | InlineAsm { destination: Some(ref mut t), unwind: _, .. } => { Some(t).into_iter().chain(&mut []) } - Resume - | Terminate + UnwindResume + | UnwindTerminate | GeneratorDrop | Return | Unreachable @@ -214,8 +214,8 @@ impl<'tcx> TerminatorKind<'tcx> { pub fn unwind(&self) -> Option<&UnwindAction> { match *self { TerminatorKind::Goto { .. } - | TerminatorKind::Resume - | TerminatorKind::Terminate + | TerminatorKind::UnwindResume + | TerminatorKind::UnwindTerminate | TerminatorKind::Return | TerminatorKind::Unreachable | TerminatorKind::GeneratorDrop @@ -233,8 +233,8 @@ impl<'tcx> TerminatorKind<'tcx> { pub fn unwind_mut(&mut self) -> Option<&mut UnwindAction> { match *self { TerminatorKind::Goto { .. } - | TerminatorKind::Resume - | TerminatorKind::Terminate + | TerminatorKind::UnwindResume + | TerminatorKind::UnwindTerminate | TerminatorKind::Return | TerminatorKind::Unreachable | TerminatorKind::GeneratorDrop @@ -311,8 +311,8 @@ impl<'tcx> TerminatorKind<'tcx> { SwitchInt { discr, .. } => write!(fmt, "switchInt({discr:?})"), Return => write!(fmt, "return"), GeneratorDrop => write!(fmt, "generator_drop"), - Resume => write!(fmt, "resume"), - Terminate => write!(fmt, "abort"), + UnwindResume => write!(fmt, "resume"), + UnwindTerminate => write!(fmt, "abort"), Yield { value, resume_arg, .. } => write!(fmt, "{resume_arg:?} = yield({value:?})"), Unreachable => write!(fmt, "unreachable"), Drop { place, .. } => write!(fmt, "drop({place:?})"), @@ -391,7 +391,7 @@ impl<'tcx> TerminatorKind<'tcx> { pub fn fmt_successor_labels(&self) -> Vec<Cow<'static, str>> { use self::TerminatorKind::*; match *self { - Return | Resume | Terminate | Unreachable | GeneratorDrop => vec![], + Return | UnwindResume | UnwindTerminate | Unreachable | GeneratorDrop => vec![], Goto { .. } => vec!["".into()], SwitchInt { ref targets, .. } => targets .values @@ -486,7 +486,9 @@ impl<'tcx> TerminatorKind<'tcx> { pub fn edges(&self) -> TerminatorEdges<'_, 'tcx> { use TerminatorKind::*; match *self { - Return | Resume | Terminate | GeneratorDrop | Unreachable => TerminatorEdges::None, + Return | UnwindResume | UnwindTerminate | GeneratorDrop | Unreachable => { + TerminatorEdges::None + } Goto { target } => TerminatorEdges::Single(target), diff --git a/compiler/rustc_middle/src/mir/visit.rs b/compiler/rustc_middle/src/mir/visit.rs index 64bc4fa7926..b3d3366ae10 100644 --- a/compiler/rustc_middle/src/mir/visit.rs +++ b/compiler/rustc_middle/src/mir/visit.rs @@ -469,8 +469,8 @@ macro_rules! make_mir_visitor { self.visit_source_info(source_info); match kind { TerminatorKind::Goto { .. } | - TerminatorKind::Resume | - TerminatorKind::Terminate | + TerminatorKind::UnwindResume | + TerminatorKind::UnwindTerminate | TerminatorKind::GeneratorDrop | TerminatorKind::Unreachable | TerminatorKind::FalseEdge { .. } | @@ -840,7 +840,6 @@ macro_rules! make_mir_visitor { source_info, value, argument_index: _, - references: _, } = var_debug_info; self.visit_source_info(source_info); diff --git a/compiler/rustc_middle/src/ty/diagnostics.rs b/compiler/rustc_middle/src/ty/diagnostics.rs index e71482326da..5db9b775a0f 100644 --- a/compiler/rustc_middle/src/ty/diagnostics.rs +++ b/compiler/rustc_middle/src/ty/diagnostics.rs @@ -1,6 +1,7 @@ //! Diagnostics related methods for `Ty`. use std::borrow::Cow; +use std::fmt::Write; use std::ops::ControlFlow; use crate::ty::{ @@ -335,10 +336,10 @@ pub fn suggest_constraining_type_params<'a>( // - insert: `, X: Bar` suggestions.push(( generics.tail_span_for_predicate_suggestion(), - constraints - .iter() - .map(|&(constraint, _)| format!(", {param_name}: {constraint}")) - .collect::<String>(), + constraints.iter().fold(String::new(), |mut string, &(constraint, _)| { + write!(string, ", {param_name}: {constraint}").unwrap(); + string + }), SuggestChangingConstraintsMessage::RestrictTypeFurther { ty: param_name }, )); continue; diff --git a/compiler/rustc_middle/src/ty/structural_impls.rs b/compiler/rustc_middle/src/ty/structural_impls.rs index 1347b35556d..f979ddd00fa 100644 --- a/compiler/rustc_middle/src/ty/structural_impls.rs +++ b/compiler/rustc_middle/src/ty/structural_impls.rs @@ -438,7 +438,6 @@ CloneLiftImpls! { (), bool, usize, - u8, u16, u32, u64, diff --git a/compiler/rustc_mir_build/src/build/custom/parse/instruction.rs b/compiler/rustc_mir_build/src/build/custom/parse/instruction.rs index fe5190900e9..26662f5de45 100644 --- a/compiler/rustc_mir_build/src/build/custom/parse/instruction.rs +++ b/compiler/rustc_mir_build/src/build/custom/parse/instruction.rs @@ -61,9 +61,7 @@ impl<'tcx, 'body> ParseCtxt<'tcx, 'body> { }) }, @call("mir_call", args) => { - let destination = self.parse_place(args[0])?; - let target = self.parse_block(args[1])?; - self.parse_call(args[2], destination, target) + self.parse_call(args) }, ExprKind::Match { scrutinee, arms, .. } => { let discr = self.parse_operand(*scrutinee)?; @@ -109,13 +107,14 @@ impl<'tcx, 'body> ParseCtxt<'tcx, 'body> { Ok(SwitchTargets::new(values.into_iter().zip(targets), otherwise)) } - fn parse_call( - &self, - expr_id: ExprId, - destination: Place<'tcx>, - target: BasicBlock, - ) -> PResult<TerminatorKind<'tcx>> { - parse_by_kind!(self, expr_id, _, "function call", + fn parse_call(&self, args: &[ExprId]) -> PResult<TerminatorKind<'tcx>> { + let (destination, call) = parse_by_kind!(self, args[0], _, "function call", + ExprKind::Assign { lhs, rhs } => (*lhs, *rhs), + ); + let destination = self.parse_place(destination)?; + let target = self.parse_block(args[1])?; + + parse_by_kind!(self, call, _, "function call", ExprKind::Call { fun, args, from_hir_call, fn_span, .. } => { let fun = self.parse_operand(*fun)?; let args = args diff --git a/compiler/rustc_mir_build/src/build/matches/mod.rs b/compiler/rustc_mir_build/src/build/matches/mod.rs index ed3ac7cb3ec..3c450740712 100644 --- a/compiler/rustc_mir_build/src/build/matches/mod.rs +++ b/compiler/rustc_mir_build/src/build/matches/mod.rs @@ -2242,7 +2242,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { self.var_debug_info.push(VarDebugInfo { name, source_info: debug_source_info, - references: 0, value: VarDebugInfoContents::Place(for_arm_body.into()), argument_index: None, }); @@ -2262,7 +2261,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { self.var_debug_info.push(VarDebugInfo { name, source_info: debug_source_info, - references: 0, value: VarDebugInfoContents::Place(ref_for_guard.into()), argument_index: None, }); diff --git a/compiler/rustc_mir_build/src/build/mod.rs b/compiler/rustc_mir_build/src/build/mod.rs index c66eba5520e..2a23a69b584 100644 --- a/compiler/rustc_mir_build/src/build/mod.rs +++ b/compiler/rustc_mir_build/src/build/mod.rs @@ -820,7 +820,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { }; self.var_debug_info.push(VarDebugInfo { name, - references: 0, source_info: SourceInfo::outermost(captured_place.var_ident.span), value: VarDebugInfoContents::Place(use_place), argument_index: None, @@ -851,7 +850,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { self.var_debug_info.push(VarDebugInfo { name, source_info, - references: 0, value: VarDebugInfoContents::Place(arg_local.into()), argument_index: Some(argument_index as u16 + 1), }); diff --git a/compiler/rustc_mir_build/src/build/scope.rs b/compiler/rustc_mir_build/src/build/scope.rs index a96288a11e5..567e7bfb5bf 100644 --- a/compiler/rustc_mir_build/src/build/scope.rs +++ b/compiler/rustc_mir_build/src/build/scope.rs @@ -685,9 +685,9 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { drops.add_entry(block, drop_idx); // `build_drop_trees` doesn't have access to our source_info, so we - // create a dummy terminator now. `TerminatorKind::Resume` is used + // create a dummy terminator now. `TerminatorKind::UnwindResume` is used // because MIR type checking will panic if it hasn't been overwritten. - self.cfg.terminate(block, source_info, TerminatorKind::Resume); + self.cfg.terminate(block, source_info, TerminatorKind::UnwindResume); self.cfg.start_new_block().unit() } @@ -717,9 +717,9 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { drops.add_entry(block, drop_idx); // `build_drop_trees` doesn't have access to our source_info, so we - // create a dummy terminator now. `TerminatorKind::Resume` is used + // create a dummy terminator now. `TerminatorKind::UnwindResume` is used // because MIR type checking will panic if it hasn't been overwritten. - self.cfg.terminate(block, source_info, TerminatorKind::Resume); + self.cfg.terminate(block, source_info, TerminatorKind::UnwindResume); } // Add a dummy `Assign` statement to the CFG, with the span for the source code's `continue` @@ -1441,7 +1441,7 @@ impl<'a, 'tcx: 'a> Builder<'a, 'tcx> { blocks[ROOT_NODE] = *resume_block; drops.build_mir::<Unwind>(cfg, &mut blocks); if let (None, Some(resume)) = (*resume_block, blocks[ROOT_NODE]) { - cfg.terminate(resume, SourceInfo::outermost(fn_span), TerminatorKind::Resume); + cfg.terminate(resume, SourceInfo::outermost(fn_span), TerminatorKind::UnwindResume); *resume_block = blocks[ROOT_NODE]; } @@ -1506,8 +1506,8 @@ impl<'tcx> DropTreeBuilder<'tcx> for Unwind { } TerminatorKind::Goto { .. } | TerminatorKind::SwitchInt { .. } - | TerminatorKind::Resume - | TerminatorKind::Terminate + | TerminatorKind::UnwindResume + | TerminatorKind::UnwindTerminate | TerminatorKind::Return | TerminatorKind::Unreachable | TerminatorKind::Yield { .. } diff --git a/compiler/rustc_mir_build/src/lints.rs b/compiler/rustc_mir_build/src/lints.rs index 7fb73b5c7b2..84c80bf41a4 100644 --- a/compiler/rustc_mir_build/src/lints.rs +++ b/compiler/rustc_mir_build/src/lints.rs @@ -186,9 +186,9 @@ impl<'mir, 'tcx, C: TerminatorClassifier<'tcx>> TriColorVisitor<BasicBlocks<'tcx match self.body[bb].terminator().kind { // These terminators return control flow to the caller. - TerminatorKind::Terminate + TerminatorKind::UnwindTerminate | TerminatorKind::GeneratorDrop - | TerminatorKind::Resume + | TerminatorKind::UnwindResume | TerminatorKind::Return | TerminatorKind::Unreachable | TerminatorKind::Yield { .. } => ControlFlow::Break(NonRecursive), diff --git a/compiler/rustc_mir_dataflow/src/impls/borrowed_locals.rs b/compiler/rustc_mir_dataflow/src/impls/borrowed_locals.rs index 8d7b50796bb..5ed8f20b73f 100644 --- a/compiler/rustc_mir_dataflow/src/impls/borrowed_locals.rs +++ b/compiler/rustc_mir_dataflow/src/impls/borrowed_locals.rs @@ -131,7 +131,7 @@ where } } - TerminatorKind::Terminate + TerminatorKind::UnwindTerminate | TerminatorKind::Assert { .. } | TerminatorKind::Call { .. } | TerminatorKind::FalseEdge { .. } @@ -139,7 +139,7 @@ where | TerminatorKind::GeneratorDrop | TerminatorKind::Goto { .. } | TerminatorKind::InlineAsm { .. } - | TerminatorKind::Resume + | TerminatorKind::UnwindResume | TerminatorKind::Return | TerminatorKind::SwitchInt { .. } | TerminatorKind::Unreachable diff --git a/compiler/rustc_mir_dataflow/src/impls/storage_liveness.rs b/compiler/rustc_mir_dataflow/src/impls/storage_liveness.rs index bea23b7f7ae..531390c2f07 100644 --- a/compiler/rustc_mir_dataflow/src/impls/storage_liveness.rs +++ b/compiler/rustc_mir_dataflow/src/impls/storage_liveness.rs @@ -291,14 +291,14 @@ impl<'tcx> crate::GenKillAnalysis<'tcx> for MaybeRequiresStorage<'_, '_, 'tcx> { // Nothing to do for these. Match exhaustively so this fails to compile when new // variants are added. - TerminatorKind::Terminate + TerminatorKind::UnwindTerminate | TerminatorKind::Assert { .. } | TerminatorKind::Drop { .. } | TerminatorKind::FalseEdge { .. } | TerminatorKind::FalseUnwind { .. } | TerminatorKind::GeneratorDrop | TerminatorKind::Goto { .. } - | TerminatorKind::Resume + | TerminatorKind::UnwindResume | TerminatorKind::Return | TerminatorKind::SwitchInt { .. } | TerminatorKind::Unreachable => {} @@ -328,14 +328,14 @@ impl<'tcx> crate::GenKillAnalysis<'tcx> for MaybeRequiresStorage<'_, '_, 'tcx> { // Nothing to do for these. Match exhaustively so this fails to compile when new // variants are added. TerminatorKind::Yield { .. } - | TerminatorKind::Terminate + | TerminatorKind::UnwindTerminate | TerminatorKind::Assert { .. } | TerminatorKind::Drop { .. } | TerminatorKind::FalseEdge { .. } | TerminatorKind::FalseUnwind { .. } | TerminatorKind::GeneratorDrop | TerminatorKind::Goto { .. } - | TerminatorKind::Resume + | TerminatorKind::UnwindResume | TerminatorKind::Return | TerminatorKind::SwitchInt { .. } | TerminatorKind::Unreachable => {} diff --git a/compiler/rustc_mir_dataflow/src/move_paths/builder.rs b/compiler/rustc_mir_dataflow/src/move_paths/builder.rs index 5052de99184..4adf3dec61b 100644 --- a/compiler/rustc_mir_dataflow/src/move_paths/builder.rs +++ b/compiler/rustc_mir_dataflow/src/move_paths/builder.rs @@ -370,8 +370,8 @@ impl<'b, 'a, 'tcx> Gatherer<'b, 'a, 'tcx> { // this that could possibly access the return place, this doesn't // need recording. | TerminatorKind::Return - | TerminatorKind::Resume - | TerminatorKind::Terminate + | TerminatorKind::UnwindResume + | TerminatorKind::UnwindTerminate | TerminatorKind::GeneratorDrop | TerminatorKind::Unreachable | TerminatorKind::Drop { .. } => {} diff --git a/compiler/rustc_mir_dataflow/src/value_analysis.rs b/compiler/rustc_mir_dataflow/src/value_analysis.rs index 766e0257efd..1eea8eef0ad 100644 --- a/compiler/rustc_mir_dataflow/src/value_analysis.rs +++ b/compiler/rustc_mir_dataflow/src/value_analysis.rs @@ -269,8 +269,8 @@ pub trait ValueAnalysis<'tcx> { return self.handle_switch_int(discr, targets, state); } TerminatorKind::Goto { .. } - | TerminatorKind::Resume - | TerminatorKind::Terminate + | TerminatorKind::UnwindResume + | TerminatorKind::UnwindTerminate | TerminatorKind::Return | TerminatorKind::Unreachable | TerminatorKind::Assert { .. } diff --git a/compiler/rustc_mir_transform/src/check_unsafety.rs b/compiler/rustc_mir_transform/src/check_unsafety.rs index 58e9786ec1a..e72db1a59a0 100644 --- a/compiler/rustc_mir_transform/src/check_unsafety.rs +++ b/compiler/rustc_mir_transform/src/check_unsafety.rs @@ -57,8 +57,8 @@ impl<'tcx> Visitor<'tcx> for UnsafetyChecker<'_, 'tcx> { | TerminatorKind::Yield { .. } | TerminatorKind::Assert { .. } | TerminatorKind::GeneratorDrop - | TerminatorKind::Resume - | TerminatorKind::Terminate + | TerminatorKind::UnwindResume + | TerminatorKind::UnwindTerminate | TerminatorKind::Return | TerminatorKind::Unreachable | TerminatorKind::FalseEdge { .. } diff --git a/compiler/rustc_mir_transform/src/const_prop.rs b/compiler/rustc_mir_transform/src/const_prop.rs index 7529ed8186b..a793b384d81 100644 --- a/compiler/rustc_mir_transform/src/const_prop.rs +++ b/compiler/rustc_mir_transform/src/const_prop.rs @@ -180,6 +180,10 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for ConstPropMachine<'mir, 'tcx> throw_machine_stop_str!("calling functions isn't supported in ConstProp") } + fn panic_nounwind(_ecx: &mut InterpCx<'mir, 'tcx, Self>, _msg: &str) -> InterpResult<'tcx> { + throw_machine_stop_str!("panicking isn't supported in ConstProp") + } + fn find_mir_or_eval_fn( _ecx: &mut InterpCx<'mir, 'tcx, Self>, _instance: ty::Instance<'tcx>, diff --git a/compiler/rustc_mir_transform/src/const_prop_lint.rs b/compiler/rustc_mir_transform/src/const_prop_lint.rs index ac07c25763b..4f8ca916d5b 100644 --- a/compiler/rustc_mir_transform/src/const_prop_lint.rs +++ b/compiler/rustc_mir_transform/src/const_prop_lint.rs @@ -678,8 +678,8 @@ impl<'tcx> Visitor<'tcx> for ConstPropagator<'_, 'tcx> { } // None of these have Operands to const-propagate. TerminatorKind::Goto { .. } - | TerminatorKind::Resume - | TerminatorKind::Terminate + | TerminatorKind::UnwindResume + | TerminatorKind::UnwindTerminate | TerminatorKind::Return | TerminatorKind::Unreachable | TerminatorKind::Drop { .. } diff --git a/compiler/rustc_mir_transform/src/coverage/counters.rs b/compiler/rustc_mir_transform/src/coverage/counters.rs index d1f2f0c76c8..3d442e5dca9 100644 --- a/compiler/rustc_mir_transform/src/coverage/counters.rs +++ b/compiler/rustc_mir_transform/src/coverage/counters.rs @@ -14,36 +14,76 @@ use rustc_index::bit_set::BitSet; use rustc_index::IndexVec; use rustc_middle::mir::coverage::*; +use std::fmt::{self, Debug}; + +/// The coverage counter or counter expression associated with a particular +/// BCB node or BCB edge. +#[derive(Clone)] +pub(super) enum BcbCounter { + Counter { id: CounterId }, + Expression { id: ExpressionId, lhs: Operand, op: Op, rhs: Operand }, +} + +impl BcbCounter { + fn is_expression(&self) -> bool { + matches!(self, Self::Expression { .. }) + } + + pub(super) fn as_operand(&self) -> Operand { + match *self { + BcbCounter::Counter { id, .. } => Operand::Counter(id), + BcbCounter::Expression { id, .. } => Operand::Expression(id), + } + } +} + +impl Debug for BcbCounter { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::Counter { id, .. } => write!(fmt, "Counter({:?})", id.index()), + Self::Expression { id, lhs, op, rhs } => write!( + fmt, + "Expression({:?}) = {:?} {} {:?}", + id.index(), + lhs, + match op { + Op::Add => "+", + Op::Subtract => "-", + }, + rhs, + ), + } + } +} + /// Generates and stores coverage counter and coverage expression information /// associated with nodes/edges in the BCB graph. pub(super) struct CoverageCounters { - function_source_hash: u64, next_counter_id: CounterId, next_expression_id: ExpressionId, /// Coverage counters/expressions that are associated with individual BCBs. - bcb_counters: IndexVec<BasicCoverageBlock, Option<CoverageKind>>, + bcb_counters: IndexVec<BasicCoverageBlock, Option<BcbCounter>>, /// Coverage counters/expressions that are associated with the control-flow /// edge between two BCBs. - bcb_edge_counters: FxHashMap<(BasicCoverageBlock, BasicCoverageBlock), CoverageKind>, + bcb_edge_counters: FxHashMap<(BasicCoverageBlock, BasicCoverageBlock), BcbCounter>, /// Tracks which BCBs have a counter associated with some incoming edge. /// Only used by debug assertions, to verify that BCBs with incoming edge /// counters do not have their own physical counters (expressions are allowed). bcb_has_incoming_edge_counters: BitSet<BasicCoverageBlock>, /// Expression nodes that are not directly associated with any particular /// BCB/edge, but are needed as operands to more complex expressions. - /// These are always `CoverageKind::Expression`. - pub(super) intermediate_expressions: Vec<CoverageKind>, + /// These are always [`BcbCounter::Expression`]. + pub(super) intermediate_expressions: Vec<BcbCounter>, pub debug_counters: DebugCounters, } impl CoverageCounters { - pub(super) fn new(function_source_hash: u64, basic_coverage_blocks: &CoverageGraph) -> Self { + pub(super) fn new(basic_coverage_blocks: &CoverageGraph) -> Self { let num_bcbs = basic_coverage_blocks.num_nodes(); Self { - function_source_hash, next_counter_id: CounterId::START, next_expression_id: ExpressionId::START, @@ -57,12 +97,12 @@ impl CoverageCounters { } /// Activate the `DebugCounters` data structures, to provide additional debug formatting - /// features when formatting `CoverageKind` (counter) values. + /// features when formatting [`BcbCounter`] (counter) values. pub fn enable_debug(&mut self) { self.debug_counters.enable(); } - /// Makes `CoverageKind` `Counter`s and `Expressions` for the `BasicCoverageBlock`s directly or + /// Makes [`BcbCounter`] `Counter`s and `Expressions` for the `BasicCoverageBlock`s directly or /// indirectly associated with `CoverageSpans`, and accumulates additional `Expression`s /// representing intermediate values. pub fn make_bcb_counters( @@ -73,14 +113,11 @@ impl CoverageCounters { MakeBcbCounters::new(self, basic_coverage_blocks).make_bcb_counters(coverage_spans) } - fn make_counter<F>(&mut self, debug_block_label_fn: F) -> CoverageKind + fn make_counter<F>(&mut self, debug_block_label_fn: F) -> BcbCounter where F: Fn() -> Option<String>, { - let counter = CoverageKind::Counter { - function_source_hash: self.function_source_hash, - id: self.next_counter(), - }; + let counter = BcbCounter::Counter { id: self.next_counter() }; if self.debug_counters.is_enabled() { self.debug_counters.add_counter(&counter, (debug_block_label_fn)()); } @@ -93,19 +130,19 @@ impl CoverageCounters { op: Op, rhs: Operand, debug_block_label_fn: F, - ) -> CoverageKind + ) -> BcbCounter where F: Fn() -> Option<String>, { let id = self.next_expression(); - let expression = CoverageKind::Expression { id, lhs, op, rhs }; + let expression = BcbCounter::Expression { id, lhs, op, rhs }; if self.debug_counters.is_enabled() { self.debug_counters.add_counter(&expression, (debug_block_label_fn)()); } expression } - pub fn make_identity_counter(&mut self, counter_operand: Operand) -> CoverageKind { + pub fn make_identity_counter(&mut self, counter_operand: Operand) -> BcbCounter { let some_debug_block_label = if self.debug_counters.is_enabled() { self.debug_counters.some_block_label(counter_operand).cloned() } else { @@ -134,7 +171,7 @@ impl CoverageCounters { fn set_bcb_counter( &mut self, bcb: BasicCoverageBlock, - counter_kind: CoverageKind, + counter_kind: BcbCounter, ) -> Result<Operand, Error> { debug_assert!( // If the BCB has an edge counter (to be injected into a new `BasicBlock`), it can also @@ -158,7 +195,7 @@ impl CoverageCounters { &mut self, from_bcb: BasicCoverageBlock, to_bcb: BasicCoverageBlock, - counter_kind: CoverageKind, + counter_kind: BcbCounter, ) -> Result<Operand, Error> { if level_enabled!(tracing::Level::DEBUG) { // If the BCB has an edge counter (to be injected into a new `BasicBlock`), it can also @@ -183,17 +220,17 @@ impl CoverageCounters { } } - pub(super) fn bcb_counter(&self, bcb: BasicCoverageBlock) -> Option<&CoverageKind> { + pub(super) fn bcb_counter(&self, bcb: BasicCoverageBlock) -> Option<&BcbCounter> { self.bcb_counters[bcb].as_ref() } - pub(super) fn take_bcb_counter(&mut self, bcb: BasicCoverageBlock) -> Option<CoverageKind> { + pub(super) fn take_bcb_counter(&mut self, bcb: BasicCoverageBlock) -> Option<BcbCounter> { self.bcb_counters[bcb].take() } pub(super) fn drain_bcb_counters( &mut self, - ) -> impl Iterator<Item = (BasicCoverageBlock, CoverageKind)> + '_ { + ) -> impl Iterator<Item = (BasicCoverageBlock, BcbCounter)> + '_ { self.bcb_counters .iter_enumerated_mut() .filter_map(|(bcb, counter)| Some((bcb, counter.take()?))) @@ -201,7 +238,7 @@ impl CoverageCounters { pub(super) fn drain_bcb_edge_counters( &mut self, - ) -> impl Iterator<Item = ((BasicCoverageBlock, BasicCoverageBlock), CoverageKind)> + '_ { + ) -> impl Iterator<Item = ((BasicCoverageBlock, BasicCoverageBlock), BcbCounter)> + '_ { self.bcb_edge_counters.drain() } } @@ -653,7 +690,7 @@ impl<'a> MakeBcbCounters<'a> { self.branch_counter(branch).is_none() } - fn branch_counter(&self, branch: &BcbBranch) -> Option<&CoverageKind> { + fn branch_counter(&self, branch: &BcbBranch) -> Option<&BcbCounter> { let to_bcb = branch.target_bcb; if let Some(from_bcb) = branch.edge_from_bcb { self.coverage_counters.bcb_edge_counters.get(&(from_bcb, to_bcb)) @@ -675,7 +712,7 @@ impl<'a> MakeBcbCounters<'a> { } #[inline] - fn format_counter(&self, counter_kind: &CoverageKind) -> String { + fn format_counter(&self, counter_kind: &BcbCounter) -> String { self.coverage_counters.debug_counters.format_counter(counter_kind) } } diff --git a/compiler/rustc_mir_transform/src/coverage/debug.rs b/compiler/rustc_mir_transform/src/coverage/debug.rs index d2c0c4ba069..af616c498fd 100644 --- a/compiler/rustc_mir_transform/src/coverage/debug.rs +++ b/compiler/rustc_mir_transform/src/coverage/debug.rs @@ -108,7 +108,7 @@ //! recursively, generating labels with nested operations, enclosed in parentheses //! (for example: `bcb2 + (bcb0 - bcb1)`). -use super::counters::CoverageCounters; +use super::counters::{BcbCounter, CoverageCounters}; use super::graph::{BasicCoverageBlock, BasicCoverageBlockData, CoverageGraph}; use super::spans::CoverageSpan; @@ -199,9 +199,9 @@ impl DebugOptions { fn bool_option_val(option: &str, some_strval: Option<&str>) -> bool { if let Some(val) = some_strval { - if vec!["yes", "y", "on", "true"].contains(&val) { + if ["yes", "y", "on", "true"].contains(&val) { true - } else if vec!["no", "n", "off", "false"].contains(&val) { + } else if ["no", "n", "off", "false"].contains(&val) { false } else { bug!( @@ -247,11 +247,11 @@ impl Default for ExpressionFormat { } } -/// If enabled, this struct maintains a map from `CoverageKind` IDs (as `Operand`) to -/// the `CoverageKind` data and optional label (normally, the counter's associated +/// If enabled, this struct maintains a map from `BcbCounter` IDs (as `Operand`) to +/// the `BcbCounter` data and optional label (normally, the counter's associated /// `BasicCoverageBlock` format string, if any). /// -/// Use `format_counter` to convert one of these `CoverageKind` counters to a debug output string, +/// Use `format_counter` to convert one of these `BcbCounter` counters to a debug output string, /// as directed by the `DebugOptions`. This allows the format of counter labels in logs and dump /// files (including the `CoverageGraph` graphviz file) to be changed at runtime, via environment /// variable. @@ -276,7 +276,7 @@ impl DebugCounters { self.some_counters.is_some() } - pub fn add_counter(&mut self, counter_kind: &CoverageKind, some_block_label: Option<String>) { + pub fn add_counter(&mut self, counter_kind: &BcbCounter, some_block_label: Option<String>) { if let Some(counters) = &mut self.some_counters { let id = counter_kind.as_operand(); counters @@ -291,21 +291,20 @@ impl DebugCounters { }) } - pub fn format_counter(&self, counter_kind: &CoverageKind) -> String { + pub fn format_counter(&self, counter_kind: &BcbCounter) -> String { match *counter_kind { - CoverageKind::Counter { .. } => { + BcbCounter::Counter { .. } => { format!("Counter({})", self.format_counter_kind(counter_kind)) } - CoverageKind::Expression { .. } => { + BcbCounter::Expression { .. } => { format!("Expression({})", self.format_counter_kind(counter_kind)) } - CoverageKind::Unreachable { .. } => "Unreachable".to_owned(), } } - fn format_counter_kind(&self, counter_kind: &CoverageKind) -> String { + fn format_counter_kind(&self, counter_kind: &BcbCounter) -> String { let counter_format = &debug_options().counter_format; - if let CoverageKind::Expression { id, lhs, op, rhs } = *counter_kind { + if let BcbCounter::Expression { id, lhs, op, rhs } = *counter_kind { if counter_format.operation { return format!( "{}{} {} {}", @@ -346,7 +345,7 @@ impl DebugCounters { } if let Some(counters) = &self.some_counters { if let Some(DebugCounter { counter_kind, some_block_label }) = counters.get(&operand) { - if let CoverageKind::Expression { .. } = counter_kind { + if let BcbCounter::Expression { .. } = counter_kind { if let Some(label) = some_block_label && debug_options().counter_format.block { return format!( "{}:({})", @@ -366,12 +365,12 @@ impl DebugCounters { /// A non-public support class to `DebugCounters`. #[derive(Debug)] struct DebugCounter { - counter_kind: CoverageKind, + counter_kind: BcbCounter, some_block_label: Option<String>, } impl DebugCounter { - fn new(counter_kind: CoverageKind, some_block_label: Option<String>) -> Self { + fn new(counter_kind: BcbCounter, some_block_label: Option<String>) -> Self { Self { counter_kind, some_block_label } } } @@ -380,9 +379,9 @@ impl DebugCounter { /// a Graphviz (.dot file) representation of the `CoverageGraph`, for debugging purposes. pub(super) struct GraphvizData { some_bcb_to_coverage_spans_with_counters: - Option<FxHashMap<BasicCoverageBlock, Vec<(CoverageSpan, CoverageKind)>>>, - some_bcb_to_dependency_counters: Option<FxHashMap<BasicCoverageBlock, Vec<CoverageKind>>>, - some_edge_to_counter: Option<FxHashMap<(BasicCoverageBlock, BasicBlock), CoverageKind>>, + Option<FxHashMap<BasicCoverageBlock, Vec<(CoverageSpan, BcbCounter)>>>, + some_bcb_to_dependency_counters: Option<FxHashMap<BasicCoverageBlock, Vec<BcbCounter>>>, + some_edge_to_counter: Option<FxHashMap<(BasicCoverageBlock, BasicBlock), BcbCounter>>, } impl GraphvizData { @@ -409,7 +408,7 @@ impl GraphvizData { &mut self, bcb: BasicCoverageBlock, coverage_span: &CoverageSpan, - counter_kind: &CoverageKind, + counter_kind: &BcbCounter, ) { if let Some(bcb_to_coverage_spans_with_counters) = self.some_bcb_to_coverage_spans_with_counters.as_mut() @@ -424,7 +423,7 @@ impl GraphvizData { pub fn get_bcb_coverage_spans_with_counters( &self, bcb: BasicCoverageBlock, - ) -> Option<&[(CoverageSpan, CoverageKind)]> { + ) -> Option<&[(CoverageSpan, BcbCounter)]> { if let Some(bcb_to_coverage_spans_with_counters) = self.some_bcb_to_coverage_spans_with_counters.as_ref() { @@ -437,7 +436,7 @@ impl GraphvizData { pub fn add_bcb_dependency_counter( &mut self, bcb: BasicCoverageBlock, - counter_kind: &CoverageKind, + counter_kind: &BcbCounter, ) { if let Some(bcb_to_dependency_counters) = self.some_bcb_to_dependency_counters.as_mut() { bcb_to_dependency_counters @@ -447,7 +446,7 @@ impl GraphvizData { } } - pub fn get_bcb_dependency_counters(&self, bcb: BasicCoverageBlock) -> Option<&[CoverageKind]> { + pub fn get_bcb_dependency_counters(&self, bcb: BasicCoverageBlock) -> Option<&[BcbCounter]> { if let Some(bcb_to_dependency_counters) = self.some_bcb_to_dependency_counters.as_ref() { bcb_to_dependency_counters.get(&bcb).map(Deref::deref) } else { @@ -459,7 +458,7 @@ impl GraphvizData { &mut self, from_bcb: BasicCoverageBlock, to_bb: BasicBlock, - counter_kind: &CoverageKind, + counter_kind: &BcbCounter, ) { if let Some(edge_to_counter) = self.some_edge_to_counter.as_mut() { edge_to_counter @@ -472,7 +471,7 @@ impl GraphvizData { &self, from_bcb: BasicCoverageBlock, to_bb: BasicBlock, - ) -> Option<&CoverageKind> { + ) -> Option<&BcbCounter> { if let Some(edge_to_counter) = self.some_edge_to_counter.as_ref() { edge_to_counter.get(&(from_bcb, to_bb)) } else { @@ -488,7 +487,7 @@ impl GraphvizData { pub(super) struct UsedExpressions { some_used_expression_operands: Option<FxHashMap<Operand, Vec<ExpressionId>>>, some_unused_expressions: - Option<Vec<(CoverageKind, Option<BasicCoverageBlock>, BasicCoverageBlock)>>, + Option<Vec<(BcbCounter, Option<BasicCoverageBlock>, BasicCoverageBlock)>>, } impl UsedExpressions { @@ -506,16 +505,16 @@ impl UsedExpressions { self.some_used_expression_operands.is_some() } - pub fn add_expression_operands(&mut self, expression: &CoverageKind) { + pub fn add_expression_operands(&mut self, expression: &BcbCounter) { if let Some(used_expression_operands) = self.some_used_expression_operands.as_mut() { - if let CoverageKind::Expression { id, lhs, rhs, .. } = *expression { + if let BcbCounter::Expression { id, lhs, rhs, .. } = *expression { used_expression_operands.entry(lhs).or_insert_with(Vec::new).push(id); used_expression_operands.entry(rhs).or_insert_with(Vec::new).push(id); } } } - pub fn expression_is_used(&self, expression: &CoverageKind) -> bool { + pub fn expression_is_used(&self, expression: &BcbCounter) -> bool { if let Some(used_expression_operands) = self.some_used_expression_operands.as_ref() { used_expression_operands.contains_key(&expression.as_operand()) } else { @@ -525,7 +524,7 @@ impl UsedExpressions { pub fn add_unused_expression_if_not_found( &mut self, - expression: &CoverageKind, + expression: &BcbCounter, edge_from_bcb: Option<BasicCoverageBlock>, target_bcb: BasicCoverageBlock, ) { @@ -540,11 +539,11 @@ impl UsedExpressions { } } - /// Return the list of unused counters (if any) as a tuple with the counter (`CoverageKind`), + /// Return the list of unused counters (if any) as a tuple with the counter (`BcbCounter`), /// optional `from_bcb` (if it was an edge counter), and `target_bcb`. pub fn get_unused_expressions( &self, - ) -> Vec<(CoverageKind, Option<BasicCoverageBlock>, BasicCoverageBlock)> { + ) -> Vec<(BcbCounter, Option<BasicCoverageBlock>, BasicCoverageBlock)> { if let Some(unused_expressions) = self.some_unused_expressions.as_ref() { unused_expressions.clone() } else { @@ -560,7 +559,7 @@ impl UsedExpressions { bcb_counters_without_direct_coverage_spans: &[( Option<BasicCoverageBlock>, BasicCoverageBlock, - CoverageKind, + BcbCounter, )], ) { if self.is_enabled() { @@ -662,7 +661,7 @@ pub(super) fn dump_coverage_graphviz<'tcx>( basic_coverage_blocks: &CoverageGraph, coverage_counters: &CoverageCounters, graphviz_data: &GraphvizData, - intermediate_expressions: &[CoverageKind], + intermediate_expressions: &[BcbCounter], debug_used_expressions: &UsedExpressions, ) { let debug_counters = &coverage_counters.debug_counters; @@ -743,9 +742,9 @@ fn bcb_to_string_sections<'tcx>( coverage_counters: &CoverageCounters, bcb: BasicCoverageBlock, bcb_data: &BasicCoverageBlockData, - some_coverage_spans_with_counters: Option<&[(CoverageSpan, CoverageKind)]>, - some_dependency_counters: Option<&[CoverageKind]>, - some_intermediate_expressions: Option<&[CoverageKind]>, + some_coverage_spans_with_counters: Option<&[(CoverageSpan, BcbCounter)]>, + some_dependency_counters: Option<&[BcbCounter]>, + some_intermediate_expressions: Option<&[BcbCounter]>, ) -> Vec<String> { let debug_counters = &coverage_counters.debug_counters; diff --git a/compiler/rustc_mir_transform/src/coverage/graph.rs b/compiler/rustc_mir_transform/src/coverage/graph.rs index 59b01ffec0f..d3d4fcd3a52 100644 --- a/compiler/rustc_mir_transform/src/coverage/graph.rs +++ b/compiler/rustc_mir_transform/src/coverage/graph.rs @@ -116,7 +116,7 @@ impl CoverageGraph { match term.kind { TerminatorKind::Return { .. } - | TerminatorKind::Terminate + | TerminatorKind::UnwindTerminate | TerminatorKind::Yield { .. } | TerminatorKind::SwitchInt { .. } => { // The `bb` has more than one _outgoing_ edge, or exits the function. Save the @@ -146,7 +146,7 @@ impl CoverageGraph { // is as intended. (See Issue #78544 for a possible future option to support // coverage in test programs that panic.) TerminatorKind::Goto { .. } - | TerminatorKind::Resume + | TerminatorKind::UnwindResume | TerminatorKind::Unreachable | TerminatorKind::Drop { .. } | TerminatorKind::Call { .. } diff --git a/compiler/rustc_mir_transform/src/coverage/mod.rs b/compiler/rustc_mir_transform/src/coverage/mod.rs index e08b6d6f6e8..8c9eae508b4 100644 --- a/compiler/rustc_mir_transform/src/coverage/mod.rs +++ b/compiler/rustc_mir_transform/src/coverage/mod.rs @@ -8,9 +8,9 @@ mod spans; #[cfg(test)] mod tests; -use counters::CoverageCounters; -use graph::{BasicCoverageBlock, BasicCoverageBlockData, CoverageGraph}; -use spans::{CoverageSpan, CoverageSpans}; +use self::counters::{BcbCounter, CoverageCounters}; +use self::graph::{BasicCoverageBlock, BasicCoverageBlockData, CoverageGraph}; +use self::spans::{CoverageSpan, CoverageSpans}; use crate::MirPass; @@ -106,6 +106,7 @@ struct Instrumentor<'a, 'tcx> { source_file: Lrc<SourceFile>, fn_sig_span: Span, body_span: Span, + function_source_hash: u64, basic_coverage_blocks: CoverageGraph, coverage_counters: CoverageCounters, } @@ -137,7 +138,7 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> { let function_source_hash = hash_mir_source(tcx, hir_body); let basic_coverage_blocks = CoverageGraph::from_mir(mir_body); - let coverage_counters = CoverageCounters::new(function_source_hash, &basic_coverage_blocks); + let coverage_counters = CoverageCounters::new(&basic_coverage_blocks); Self { pass_name, @@ -146,6 +147,7 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> { source_file, fn_sig_span, body_span, + function_source_hash, basic_coverage_blocks, coverage_counters, } @@ -270,8 +272,11 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> { //////////////////////////////////////////////////// // Finally, inject the intermediate expressions collected along the way. - for intermediate_expression in self.coverage_counters.intermediate_expressions.drain(..) { - inject_intermediate_expression(self.mir_body, intermediate_expression); + for intermediate_expression in &self.coverage_counters.intermediate_expressions { + inject_intermediate_expression( + self.mir_body, + self.make_mir_coverage_kind(intermediate_expression), + ); } } @@ -309,19 +314,14 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> { }; graphviz_data.add_bcb_coverage_span_with_counter(bcb, &covspan, &counter_kind); - debug!( - "Calling make_code_region(file_name={}, source_file={:?}, span={}, body_span={})", - file_name, - self.source_file, - source_map.span_to_diagnostic_string(span), - source_map.span_to_diagnostic_string(body_span) - ); + let code_region = + make_code_region(source_map, file_name, &self.source_file, span, body_span); inject_statement( self.mir_body, - counter_kind, + self.make_mir_coverage_kind(&counter_kind), self.bcb_leader_bb(bcb), - Some(make_code_region(source_map, file_name, &self.source_file, span, body_span)), + Some(code_region), ); } } @@ -367,7 +367,7 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> { ); match counter_kind { - CoverageKind::Counter { .. } => { + BcbCounter::Counter { .. } => { let inject_to_bb = if let Some(from_bcb) = edge_from_bcb { // The MIR edge starts `from_bb` (the outgoing / last BasicBlock in // `from_bcb`) and ends at `to_bb` (the incoming / first BasicBlock in the @@ -400,12 +400,17 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> { target_bb }; - inject_statement(self.mir_body, counter_kind, inject_to_bb, None); + inject_statement( + self.mir_body, + self.make_mir_coverage_kind(&counter_kind), + inject_to_bb, + None, + ); } - CoverageKind::Expression { .. } => { - inject_intermediate_expression(self.mir_body, counter_kind) - } - _ => bug!("CoverageKind should be a counter"), + BcbCounter::Expression { .. } => inject_intermediate_expression( + self.mir_body, + self.make_mir_coverage_kind(&counter_kind), + ), } } } @@ -426,9 +431,20 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> { } #[inline] - fn format_counter(&self, counter_kind: &CoverageKind) -> String { + fn format_counter(&self, counter_kind: &BcbCounter) -> String { self.coverage_counters.debug_counters.format_counter(counter_kind) } + + fn make_mir_coverage_kind(&self, counter_kind: &BcbCounter) -> CoverageKind { + match *counter_kind { + BcbCounter::Counter { id } => { + CoverageKind::Counter { function_source_hash: self.function_source_hash, id } + } + BcbCounter::Expression { id, lhs, op, rhs } => { + CoverageKind::Expression { id, lhs, op, rhs } + } + } + } } fn inject_edge_counter_basic_block( @@ -498,6 +514,14 @@ fn make_code_region( span: Span, body_span: Span, ) -> CodeRegion { + debug!( + "Called make_code_region(file_name={}, source_file={:?}, span={}, body_span={})", + file_name, + source_file, + source_map.span_to_diagnostic_string(span), + source_map.span_to_diagnostic_string(body_span) + ); + let (start_line, mut start_col) = source_file.lookup_file_pos(span.lo()); let (end_line, end_col) = if span.hi() == span.lo() { let (end_line, mut end_col) = (start_line, start_col); diff --git a/compiler/rustc_mir_transform/src/coverage/spans.rs b/compiler/rustc_mir_transform/src/coverage/spans.rs index deebf5345ba..6fabaca524a 100644 --- a/compiler/rustc_mir_transform/src/coverage/spans.rs +++ b/compiler/rustc_mir_transform/src/coverage/spans.rs @@ -867,8 +867,8 @@ pub(super) fn filtered_terminator_span(terminator: &Terminator<'_>) -> Option<Sp } // Retain spans from all other terminators - TerminatorKind::Resume - | TerminatorKind::Terminate + TerminatorKind::UnwindResume + | TerminatorKind::UnwindTerminate | TerminatorKind::Return | TerminatorKind::Yield { .. } | TerminatorKind::GeneratorDrop diff --git a/compiler/rustc_mir_transform/src/coverage/tests.rs b/compiler/rustc_mir_transform/src/coverage/tests.rs index d797a6057a7..4a066ed3abd 100644 --- a/compiler/rustc_mir_transform/src/coverage/tests.rs +++ b/compiler/rustc_mir_transform/src/coverage/tests.rs @@ -34,7 +34,6 @@ use itertools::Itertools; use rustc_data_structures::graph::WithNumNodes; use rustc_data_structures::graph::WithSuccessors; use rustc_index::{Idx, IndexVec}; -use rustc_middle::mir::coverage::CoverageKind; use rustc_middle::mir::*; use rustc_middle::ty; use rustc_span::{self, BytePos, Pos, Span, DUMMY_SP}; @@ -675,8 +674,8 @@ fn test_make_bcb_counters() { )); } } - let mut coverage_counters = counters::CoverageCounters::new(0, &basic_coverage_blocks); - let () = coverage_counters + let mut coverage_counters = counters::CoverageCounters::new(&basic_coverage_blocks); + coverage_counters .make_bcb_counters(&mut basic_coverage_blocks, &coverage_spans) .expect("should be Ok"); assert_eq!(coverage_counters.intermediate_expressions.len(), 0); @@ -685,7 +684,7 @@ fn test_make_bcb_counters() { assert_eq!( 0, // bcb1 has a `Counter` with id = 0 match coverage_counters.bcb_counter(bcb1).expect("should have a counter") { - CoverageKind::Counter { id, .. } => id, + counters::BcbCounter::Counter { id, .. } => id, _ => panic!("expected a Counter"), } .as_u32() @@ -695,7 +694,7 @@ fn test_make_bcb_counters() { assert_eq!( 1, // bcb2 has a `Counter` with id = 1 match coverage_counters.bcb_counter(bcb2).expect("should have a counter") { - CoverageKind::Counter { id, .. } => id, + counters::BcbCounter::Counter { id, .. } => id, _ => panic!("expected a Counter"), } .as_u32() diff --git a/compiler/rustc_mir_transform/src/dataflow_const_prop.rs b/compiler/rustc_mir_transform/src/dataflow_const_prop.rs index 8f4dc9f69e9..3a1ef3e7d64 100644 --- a/compiler/rustc_mir_transform/src/dataflow_const_prop.rs +++ b/compiler/rustc_mir_transform/src/dataflow_const_prop.rs @@ -541,6 +541,13 @@ impl<'mir, 'tcx: 'mir> rustc_const_eval::interpret::Machine<'mir, 'tcx> for Dumm unimplemented!() } + fn panic_nounwind( + _ecx: &mut InterpCx<'mir, 'tcx, Self>, + _msg: &str, + ) -> interpret::InterpResult<'tcx> { + unimplemented!() + } + fn call_intrinsic( _ecx: &mut InterpCx<'mir, 'tcx, Self>, _instance: ty::Instance<'tcx>, diff --git a/compiler/rustc_mir_transform/src/deduce_param_attrs.rs b/compiler/rustc_mir_transform/src/deduce_param_attrs.rs index 60ca3dfb2da..79645310a39 100644 --- a/compiler/rustc_mir_transform/src/deduce_param_attrs.rs +++ b/compiler/rustc_mir_transform/src/deduce_param_attrs.rs @@ -203,7 +203,12 @@ pub fn deduced_param_attrs<'tcx>( body.local_decls.iter().skip(1).take(body.arg_count).enumerate().map( |(arg_index, local_decl)| DeducedParamAttrs { read_only: !deduce_read_only.mutable_args.contains(arg_index) - && local_decl.ty.is_freeze(tcx, param_env), + // We must normalize here to reveal opaques and normalize + // their substs, otherwise we'll see exponential blow-up in + // compile times: #113372 + && tcx + .normalize_erasing_regions(param_env, local_decl.ty) + .is_freeze(tcx, param_env), }, ), ); diff --git a/compiler/rustc_mir_transform/src/dest_prop.rs b/compiler/rustc_mir_transform/src/dest_prop.rs index b73b72c3192..041f7c7221e 100644 --- a/compiler/rustc_mir_transform/src/dest_prop.rs +++ b/compiler/rustc_mir_transform/src/dest_prop.rs @@ -647,8 +647,8 @@ impl WriteInfo { } } TerminatorKind::Goto { .. } - | TerminatorKind::Resume - | TerminatorKind::Terminate + | TerminatorKind::UnwindResume + | TerminatorKind::UnwindTerminate | TerminatorKind::Return | TerminatorKind::Unreachable { .. } => (), TerminatorKind::Drop { .. } => { diff --git a/compiler/rustc_mir_transform/src/elaborate_drops.rs b/compiler/rustc_mir_transform/src/elaborate_drops.rs index b6b1ae6d3c3..a80ae480089 100644 --- a/compiler/rustc_mir_transform/src/elaborate_drops.rs +++ b/compiler/rustc_mir_transform/src/elaborate_drops.rs @@ -470,7 +470,7 @@ impl<'b, 'tcx> ElaborateDropsCtxt<'b, 'tcx> { // drop elaboration should handle that by itself continue; } - TerminatorKind::Resume => { + TerminatorKind::UnwindResume => { // It is possible for `Resume` to be patched // (in particular it can be patched to be replaced with // a Goto; see `MirPatch::new`). diff --git a/compiler/rustc_mir_transform/src/generator.rs b/compiler/rustc_mir_transform/src/generator.rs index 797a1a86846..96077322575 100644 --- a/compiler/rustc_mir_transform/src/generator.rs +++ b/compiler/rustc_mir_transform/src/generator.rs @@ -50,8 +50,10 @@ //! For generators with state 1 (returned) and state 2 (poisoned) it does nothing. //! Otherwise it drops all the values in scope at the last suspension point. +use crate::abort_unwinding_calls; use crate::deref_separator::deref_finder; use crate::errors; +use crate::pass_manager as pm; use crate::simplify; use crate::MirPass; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; @@ -64,6 +66,7 @@ use rustc_index::{Idx, IndexVec}; use rustc_middle::mir::dump_mir; use rustc_middle::mir::visit::{MutVisitor, PlaceContext, Visitor}; use rustc_middle::mir::*; +use rustc_middle::ty::InstanceDef; use rustc_middle::ty::{self, AdtDef, Ty, TyCtxt}; use rustc_middle::ty::{GeneratorArgs, GenericArgsRef}; use rustc_mir_dataflow::impls::{ @@ -1147,7 +1150,25 @@ fn create_generator_drop_shim<'tcx>( // unrelated code from the resume part of the function simplify::remove_dead_blocks(tcx, &mut body); + // Update the body's def to become the drop glue. + // This needs to be updated before the AbortUnwindingCalls pass. + let gen_instance = body.source.instance; + let drop_in_place = tcx.require_lang_item(LangItem::DropInPlace, None); + let drop_instance = InstanceDef::DropGlue(drop_in_place, Some(gen_ty)); + body.source.instance = drop_instance; + + pm::run_passes_no_validate( + tcx, + &mut body, + &[&abort_unwinding_calls::AbortUnwindingCalls], + None, + ); + + // Temporary change MirSource to generator's instance so that dump_mir produces more sensible + // filename. + body.source.instance = gen_instance; dump_mir(tcx, false, "generator_drop", &0, &body, |_, _| Ok(())); + body.source.instance = drop_instance; body } @@ -1218,7 +1239,7 @@ fn can_unwind<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'tcx>) -> bool { // These never unwind. TerminatorKind::Goto { .. } | TerminatorKind::SwitchInt { .. } - | TerminatorKind::Terminate + | TerminatorKind::UnwindTerminate | TerminatorKind::Return | TerminatorKind::Unreachable | TerminatorKind::GeneratorDrop @@ -1227,7 +1248,7 @@ fn can_unwind<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'tcx>) -> bool { // Resume will *continue* unwinding, but if there's no other unwinding terminator it // will never be reached. - TerminatorKind::Resume => {} + TerminatorKind::UnwindResume => {} TerminatorKind::Yield { .. } => { unreachable!("`can_unwind` called before generator transform") @@ -1258,14 +1279,14 @@ fn create_generator_resume_function<'tcx>( let source_info = SourceInfo::outermost(body.span); let poison_block = body.basic_blocks_mut().push(BasicBlockData { statements: vec![transform.set_discr(VariantIdx::new(POISONED), source_info)], - terminator: Some(Terminator { source_info, kind: TerminatorKind::Resume }), + terminator: Some(Terminator { source_info, kind: TerminatorKind::UnwindResume }), is_cleanup: true, }); for (idx, block) in body.basic_blocks_mut().iter_enumerated_mut() { let source_info = block.terminator().source_info; - if let TerminatorKind::Resume = block.terminator().kind { + if let TerminatorKind::UnwindResume = block.terminator().kind { // An existing `Resume` terminator is redirected to jump to our dedicated // "poisoning block" above. if idx != poison_block { @@ -1317,6 +1338,8 @@ fn create_generator_resume_function<'tcx>( // unrelated code from the drop part of the function simplify::remove_dead_blocks(tcx, body); + pm::run_passes_no_validate(tcx, body, &[&abort_unwinding_calls::AbortUnwindingCalls], None); + dump_mir(tcx, false, "generator_resume", &0, body, |_, _| Ok(())); } @@ -1735,8 +1758,8 @@ impl<'tcx> Visitor<'tcx> for EnsureGeneratorFieldAssignmentsNeverAlias<'_> { TerminatorKind::Call { .. } | TerminatorKind::Goto { .. } | TerminatorKind::SwitchInt { .. } - | TerminatorKind::Resume - | TerminatorKind::Terminate + | TerminatorKind::UnwindResume + | TerminatorKind::UnwindTerminate | TerminatorKind::Return | TerminatorKind::Unreachable | TerminatorKind::Drop { .. } diff --git a/compiler/rustc_mir_transform/src/inline.rs b/compiler/rustc_mir_transform/src/inline.rs index fc9e18378d5..734e93783d1 100644 --- a/compiler/rustc_mir_transform/src/inline.rs +++ b/compiler/rustc_mir_transform/src/inline.rs @@ -839,7 +839,7 @@ impl<'tcx> Visitor<'tcx> for CostChecker<'_, 'tcx> { self.cost += LANDINGPAD_PENALTY; } } - TerminatorKind::Resume => self.cost += RESUME_PENALTY, + TerminatorKind::UnwindResume => self.cost += RESUME_PENALTY, TerminatorKind::InlineAsm { unwind, .. } => { self.cost += INSTR_COST; if let UnwindAction::Cleanup(_) = unwind { @@ -1017,15 +1017,15 @@ impl<'tcx> MutVisitor<'tcx> for Integrator<'_, 'tcx> { TerminatorKind::Unreachable } } - TerminatorKind::Resume => { + TerminatorKind::UnwindResume => { terminator.kind = match self.cleanup_block { UnwindAction::Cleanup(tgt) => TerminatorKind::Goto { target: tgt }, - UnwindAction::Continue => TerminatorKind::Resume, + UnwindAction::Continue => TerminatorKind::UnwindResume, UnwindAction::Unreachable => TerminatorKind::Unreachable, - UnwindAction::Terminate => TerminatorKind::Terminate, + UnwindAction::Terminate => TerminatorKind::UnwindTerminate, }; } - TerminatorKind::Terminate => {} + TerminatorKind::UnwindTerminate => {} TerminatorKind::Unreachable => {} TerminatorKind::FalseEdge { ref mut real_target, ref mut imaginary_target } => { *real_target = self.map_block(*real_target); diff --git a/compiler/rustc_mir_transform/src/ref_prop.rs b/compiler/rustc_mir_transform/src/ref_prop.rs index c17c791f9c3..49a940b5779 100644 --- a/compiler/rustc_mir_transform/src/ref_prop.rs +++ b/compiler/rustc_mir_transform/src/ref_prop.rs @@ -265,7 +265,6 @@ fn compute_replacement<'tcx>( targets, storage_to_remove, allowed_replacements, - fully_replacable_locals, any_replacement: false, }; @@ -346,7 +345,6 @@ struct Replacer<'tcx> { storage_to_remove: BitSet<Local>, allowed_replacements: FxHashSet<(Local, Location)>, any_replacement: bool, - fully_replacable_locals: BitSet<Local>, } impl<'tcx> MutVisitor<'tcx> for Replacer<'tcx> { @@ -366,12 +364,6 @@ impl<'tcx> MutVisitor<'tcx> for Replacer<'tcx> { if let Some((&PlaceElem::Deref, rest)) = target.projection.split_last() { *place = Place::from(target.local).project_deeper(rest, self.tcx); self.any_replacement = true; - } else if self.fully_replacable_locals.contains(place.local) - && let Some(references) = debuginfo.references.checked_add(1) - { - debuginfo.references = references; - *place = target; - self.any_replacement = true; } else { break } diff --git a/compiler/rustc_mir_transform/src/remove_noop_landing_pads.rs b/compiler/rustc_mir_transform/src/remove_noop_landing_pads.rs index 4941c9edce3..5782adbb3ff 100644 --- a/compiler/rustc_mir_transform/src/remove_noop_landing_pads.rs +++ b/compiler/rustc_mir_transform/src/remove_noop_landing_pads.rs @@ -6,8 +6,8 @@ use rustc_middle::ty::TyCtxt; use rustc_target::spec::PanicStrategy; /// A pass that removes noop landing pads and replaces jumps to them with -/// `None`. This is important because otherwise LLVM generates terrible -/// code for these. +/// `UnwindAction::Continue`. This is important because otherwise LLVM generates +/// terrible code for these. pub struct RemoveNoopLandingPads; impl<'tcx> MirPass<'tcx> for RemoveNoopLandingPads { @@ -63,7 +63,7 @@ impl RemoveNoopLandingPads { let terminator = body[bb].terminator(); match terminator.kind { TerminatorKind::Goto { .. } - | TerminatorKind::Resume + | TerminatorKind::UnwindResume | TerminatorKind::SwitchInt { .. } | TerminatorKind::FalseEdge { .. } | TerminatorKind::FalseUnwind { .. } => { @@ -72,7 +72,7 @@ impl RemoveNoopLandingPads { TerminatorKind::GeneratorDrop | TerminatorKind::Yield { .. } | TerminatorKind::Return - | TerminatorKind::Terminate + | TerminatorKind::UnwindTerminate | TerminatorKind::Unreachable | TerminatorKind::Call { .. } | TerminatorKind::Assert { .. } @@ -84,7 +84,17 @@ impl RemoveNoopLandingPads { fn remove_nop_landing_pads(&self, body: &mut Body<'_>) { debug!("body: {:#?}", body); - // make sure there's a resume block + // Skip the pass if there are no blocks with a resume terminator. + let has_resume = body + .basic_blocks + .iter_enumerated() + .any(|(_bb, block)| matches!(block.terminator().kind, TerminatorKind::UnwindResume)); + if !has_resume { + debug!("remove_noop_landing_pads: no resume block in MIR"); + return; + } + + // make sure there's a resume block without any statements let resume_block = { let mut patch = MirPatch::new(body); let resume_block = patch.resume_block(); diff --git a/compiler/rustc_mir_transform/src/separate_const_switch.rs b/compiler/rustc_mir_transform/src/separate_const_switch.rs index 1d8e54cdca0..de1b80585d1 100644 --- a/compiler/rustc_mir_transform/src/separate_const_switch.rs +++ b/compiler/rustc_mir_transform/src/separate_const_switch.rs @@ -108,13 +108,13 @@ pub fn separate_const_switch(body: &mut Body<'_>) -> usize { } // The following terminators are not allowed - TerminatorKind::Resume + TerminatorKind::UnwindResume | TerminatorKind::Drop { .. } | TerminatorKind::Call { .. } | TerminatorKind::Assert { .. } | TerminatorKind::FalseUnwind { .. } | TerminatorKind::Yield { .. } - | TerminatorKind::Terminate + | TerminatorKind::UnwindTerminate | TerminatorKind::Return | TerminatorKind::Unreachable | TerminatorKind::InlineAsm { .. } @@ -165,8 +165,8 @@ pub fn separate_const_switch(body: &mut Body<'_>) -> usize { }); } - TerminatorKind::Resume - | TerminatorKind::Terminate + TerminatorKind::UnwindResume + | TerminatorKind::UnwindTerminate | TerminatorKind::Return | TerminatorKind::Unreachable | TerminatorKind::GeneratorDrop diff --git a/compiler/rustc_mir_transform/src/shim.rs b/compiler/rustc_mir_transform/src/shim.rs index 5e8ba4f544c..079cf9eb4e9 100644 --- a/compiler/rustc_mir_transform/src/shim.rs +++ b/compiler/rustc_mir_transform/src/shim.rs @@ -71,8 +71,17 @@ fn make_shim<'tcx>(tcx: TyCtxt<'tcx>, instance: ty::InstanceDef<'tcx>) -> Body<' // of this function. Is this intentional? if let Some(ty::Generator(gen_def_id, args, _)) = ty.map(Ty::kind) { let body = tcx.optimized_mir(*gen_def_id).generator_drop().unwrap(); - let body = EarlyBinder::bind(body.clone()).instantiate(tcx, args); + let mut body = EarlyBinder::bind(body.clone()).instantiate(tcx, args); debug!("make_shim({:?}) = {:?}", instance, body); + + // Run empty passes to mark phase change and perform validation. + pm::run_passes( + tcx, + &mut body, + &[], + Some(MirPhase::Runtime(RuntimePhase::Optimized)), + ); + return body; } @@ -90,7 +99,11 @@ fn make_shim<'tcx>(tcx: TyCtxt<'tcx>, instance: ty::InstanceDef<'tcx>) -> Body<' }; debug!("make_shim({:?}) = untransformed {:?}", instance, result); - pm::run_passes( + // We don't validate MIR here because the shims may generate code that's + // only valid in a reveal-all param-env. However, since we do initial + // validation with the MirBuilt phase, which uses a user-facing param-env. + // This causes validation errors when TAITs are involved. + pm::run_passes_no_validate( tcx, &mut result, &[ @@ -574,7 +587,7 @@ impl<'tcx> CloneShimBuilder<'tcx> { I: IntoIterator<Item = Ty<'tcx>>, { self.block(vec![], TerminatorKind::Goto { target: self.block_index_offset(3) }, false); - let unwind = self.block(vec![], TerminatorKind::Resume, true); + let unwind = self.block(vec![], TerminatorKind::UnwindResume, true); let target = self.block(vec![], TerminatorKind::Return, false); let _final_cleanup_block = self.clone_fields(dest, src, target, unwind, tys); @@ -588,7 +601,7 @@ impl<'tcx> CloneShimBuilder<'tcx> { args: GeneratorArgs<'tcx>, ) { self.block(vec![], TerminatorKind::Goto { target: self.block_index_offset(3) }, false); - let unwind = self.block(vec![], TerminatorKind::Resume, true); + let unwind = self.block(vec![], TerminatorKind::UnwindResume, true); // This will get overwritten with a switch once we know the target blocks let switch = self.block(vec![], TerminatorKind::Unreachable, false); let unwind = self.clone_fields(dest, src, switch, unwind, args.upvar_tys()); @@ -845,7 +858,7 @@ fn build_call_shim<'tcx>( ); // BB #4 - resume - block(&mut blocks, vec![], TerminatorKind::Resume, true); + block(&mut blocks, vec![], TerminatorKind::UnwindResume, true); } let mut body = diff --git a/compiler/rustc_monomorphize/src/collector.rs b/compiler/rustc_monomorphize/src/collector.rs index 55b14ce1c3e..cd7908e75e2 100644 --- a/compiler/rustc_monomorphize/src/collector.rs +++ b/compiler/rustc_monomorphize/src/collector.rs @@ -776,7 +776,7 @@ impl<'a, 'tcx> MirVisitor<'tcx> for MirUsedCollector<'a, 'tcx> { self.output.push(create_fn_mono_item(tcx, instance, source)); } } - mir::TerminatorKind::Terminate { .. } => { + mir::TerminatorKind::UnwindTerminate { .. } => { let instance = Instance::mono( tcx, tcx.require_lang_item(LangItem::PanicCannotUnwind, Some(source)), @@ -787,7 +787,7 @@ impl<'a, 'tcx> MirVisitor<'tcx> for MirUsedCollector<'a, 'tcx> { } mir::TerminatorKind::Goto { .. } | mir::TerminatorKind::SwitchInt { .. } - | mir::TerminatorKind::Resume + | mir::TerminatorKind::UnwindResume | mir::TerminatorKind::Return | mir::TerminatorKind::Unreachable => {} mir::TerminatorKind::GeneratorDrop diff --git a/compiler/rustc_parse/messages.ftl b/compiler/rustc_parse/messages.ftl index 6888127f36c..34cc0998c9b 100644 --- a/compiler/rustc_parse/messages.ftl +++ b/compiler/rustc_parse/messages.ftl @@ -310,8 +310,8 @@ parse_inclusive_range_no_end = inclusive range with no end .suggestion_open_range = use `..` instead .note = inclusive ranges must be bounded at the end (`..=b` or `a..=b`) -parse_incorrect_braces_trait_bounds = incorrect braces around trait bounds - .suggestion = remove the parentheses +parse_incorrect_parens_trait_bounds = incorrect parentheses around trait bounds +parse_incorrect_parens_trait_bounds_sugg = fix the parentheses parse_incorrect_semicolon = expected item, found `;` diff --git a/compiler/rustc_parse/src/errors.rs b/compiler/rustc_parse/src/errors.rs index 26f38c9156a..e0b1e3678e4 100644 --- a/compiler/rustc_parse/src/errors.rs +++ b/compiler/rustc_parse/src/errors.rs @@ -2636,21 +2636,24 @@ pub(crate) struct MissingPlusBounds { } #[derive(Diagnostic)] -#[diag(parse_incorrect_braces_trait_bounds)] -pub(crate) struct IncorrectBracesTraitBounds { +#[diag(parse_incorrect_parens_trait_bounds)] +pub(crate) struct IncorrectParensTraitBounds { #[primary_span] pub span: Vec<Span>, #[subdiagnostic] - pub sugg: IncorrectBracesTraitBoundsSugg, + pub sugg: IncorrectParensTraitBoundsSugg, } #[derive(Subdiagnostic)] -#[multipart_suggestion(parse_suggestion, applicability = "machine-applicable")] -pub(crate) struct IncorrectBracesTraitBoundsSugg { +#[multipart_suggestion( + parse_incorrect_parens_trait_bounds_sugg, + applicability = "machine-applicable" +)] +pub(crate) struct IncorrectParensTraitBoundsSugg { #[suggestion_part(code = " ")] - pub l: Span, - #[suggestion_part(code = "")] - pub r: Span, + pub wrong_span: Span, + #[suggestion_part(code = "(")] + pub new_span: Span, } #[derive(Diagnostic)] diff --git a/compiler/rustc_parse/src/lexer/unescape_error_reporting.rs b/compiler/rustc_parse/src/lexer/unescape_error_reporting.rs index 446472d1294..b659c40b233 100644 --- a/compiler/rustc_parse/src/lexer/unescape_error_reporting.rs +++ b/compiler/rustc_parse/src/lexer/unescape_error_reporting.rs @@ -80,20 +80,14 @@ pub(crate) fn emit_unescape_error( let sugg = sugg.unwrap_or_else(|| { let prefix = mode.prefix_noraw(); let mut escaped = String::with_capacity(lit.len()); - let mut chrs = lit.chars().peekable(); - while let Some(first) = chrs.next() { - match (first, chrs.peek()) { - ('\\', Some('"')) => { - escaped.push('\\'); - escaped.push('"'); - chrs.next(); - } - ('"', _) => { - escaped.push('\\'); - escaped.push('"') - } - (c, _) => escaped.push(c), - }; + let mut in_escape = false; + for c in lit.chars() { + match c { + '\\' => in_escape = !in_escape, + '"' if !in_escape => escaped.push('\\'), + _ => in_escape = false, + } + escaped.push(c); } let sugg = format!("{prefix}\"{escaped}\""); MoreThanOneCharSugg::Quotes { diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index e409c7c6781..9ae3ef6172c 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -22,6 +22,7 @@ use rustc_ast::{AnonConst, BinOp, BinOpKind, FnDecl, FnRetTy, MacCall, Param, Ty use rustc_ast::{Arm, Async, BlockCheckMode, Expr, ExprKind, Label, Movability, RangeLimits}; use rustc_ast::{ClosureBinder, MetaItemLit, StmtKind}; use rustc_ast_pretty::pprust; +use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_errors::{ AddToDiagnostic, Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed, IntoDiagnostic, PResult, StashKey, @@ -193,13 +194,7 @@ impl<'a> Parser<'a> { self.expected_tokens.push(TokenType::Operator); while let Some(op) = self.check_assoc_op() { - // Adjust the span for interpolated LHS to point to the `$lhs` token - // and not to what it refers to. - let lhs_span = match self.prev_token.kind { - TokenKind::Interpolated(..) => self.prev_token.span, - _ => lhs.span, - }; - + let lhs_span = self.interpolated_or_expr_span(&lhs); let cur_op_span = self.token.span; let restrictions = if op.node.is_assign_like() { self.restrictions & Restrictions::NO_STRUCT_LITERAL @@ -626,8 +621,8 @@ impl<'a> Parser<'a> { fn parse_expr_prefix_common(&mut self, lo: Span) -> PResult<'a, (Span, P<Expr>)> { self.bump(); - let expr = self.parse_expr_prefix(None); - let (span, expr) = self.interpolated_or_expr_span(expr)?; + let expr = self.parse_expr_prefix(None)?; + let span = self.interpolated_or_expr_span(&expr); Ok((lo.to(span), expr)) } @@ -702,20 +697,12 @@ impl<'a> Parser<'a> { self.parse_expr_unary(lo, UnOp::Not) } - /// Returns the span of expr, if it was not interpolated or the span of the interpolated token. - fn interpolated_or_expr_span( - &self, - expr: PResult<'a, P<Expr>>, - ) -> PResult<'a, (Span, P<Expr>)> { - expr.map(|e| { - ( - match self.prev_token.kind { - TokenKind::Interpolated(..) => self.prev_token.span, - _ => e.span, - }, - e, - ) - }) + /// Returns the span of expr if it was not interpolated, or the span of the interpolated token. + fn interpolated_or_expr_span(&self, expr: &Expr) -> Span { + match self.prev_token.kind { + TokenKind::Interpolated(..) => self.prev_token.span, + _ => expr.span, + } } fn parse_assoc_op_cast( @@ -898,8 +885,8 @@ impl<'a> Parser<'a> { self.parse_expr_prefix_range(None) } else { self.parse_expr_prefix(None) - }; - let (hi, expr) = self.interpolated_or_expr_span(expr)?; + }?; + let hi = self.interpolated_or_expr_span(&expr); let span = lo.to(hi); if let Some(lt) = lifetime { self.error_remove_borrow_lifetime(span, lt.ident.span); @@ -930,8 +917,8 @@ impl<'a> Parser<'a> { fn parse_expr_dot_or_call(&mut self, attrs: Option<AttrWrapper>) -> PResult<'a, P<Expr>> { let attrs = self.parse_or_use_outer_attributes(attrs)?; self.collect_tokens_for_expr(attrs, |this, attrs| { - let base = this.parse_expr_bottom(); - let (span, base) = this.interpolated_or_expr_span(base)?; + let base = this.parse_expr_bottom()?; + let span = this.interpolated_or_expr_span(&base); this.parse_expr_dot_or_call_with(base, span, attrs) }) } @@ -2503,7 +2490,7 @@ impl<'a> Parser<'a> { let else_span = self.prev_token.span; // `else` let attrs = self.parse_outer_attributes()?; // For recovery. let expr = if self.eat_keyword(kw::If) { - self.parse_expr_if()? + ensure_sufficient_stack(|| self.parse_expr_if())? } else if self.check(&TokenKind::OpenDelim(Delimiter::Brace)) { self.parse_simple_block()? } else { diff --git a/compiler/rustc_parse/src/parser/mod.rs b/compiler/rustc_parse/src/parser/mod.rs index ce4d4a60551..77c59bb3881 100644 --- a/compiler/rustc_parse/src/parser/mod.rs +++ b/compiler/rustc_parse/src/parser/mod.rs @@ -1052,33 +1052,48 @@ impl<'a> Parser<'a> { } /// Look-ahead `dist` tokens of `self.token` and get access to that token there. - /// When `dist == 0` then the current token is looked at. + /// When `dist == 0` then the current token is looked at. `Eof` will be + /// returned if the look-ahead is any distance past the end of the tokens. pub fn look_ahead<R>(&self, dist: usize, looker: impl FnOnce(&Token) -> R) -> R { if dist == 0 { return looker(&self.token); } - let tree_cursor = &self.token_cursor.tree_cursor; if let Some(&(_, delim, span)) = self.token_cursor.stack.last() && delim != Delimiter::Invisible { + // We are not in the outermost token stream, and the token stream + // we are in has non-skipped delimiters. Look for skipped + // delimiters in the lookahead range. + let tree_cursor = &self.token_cursor.tree_cursor; let all_normal = (0..dist).all(|i| { let token = tree_cursor.look_ahead(i); !matches!(token, Some(TokenTree::Delimited(_, Delimiter::Invisible, _))) }); if all_normal { + // There were no skipped delimiters. Do lookahead by plain indexing. return match tree_cursor.look_ahead(dist - 1) { - Some(tree) => match tree { - TokenTree::Token(token, _) => looker(token), - TokenTree::Delimited(dspan, delim, _) => { - looker(&Token::new(token::OpenDelim(*delim), dspan.open)) + Some(tree) => { + // Indexing stayed within the current token stream. + match tree { + TokenTree::Token(token, _) => looker(token), + TokenTree::Delimited(dspan, delim, _) => { + looker(&Token::new(token::OpenDelim(*delim), dspan.open)) + } } - }, - None => looker(&Token::new(token::CloseDelim(delim), span.close)), + } + None => { + // Indexing went past the end of the current token + // stream. Use the close delimiter, no matter how far + // ahead `dist` went. + looker(&Token::new(token::CloseDelim(delim), span.close)) + } }; } } + // We are in a more complex case. Just clone the token cursor and use + // `next`, skipping delimiters as necessary. Slow but simple. let mut cursor = self.token_cursor.clone(); let mut i = 0; let mut token = Token::dummy(); @@ -1476,7 +1491,7 @@ pub enum FlatToken { } #[derive(Debug)] -pub enum NtOrTt { +pub enum ParseNtResult { Nt(Nonterminal), Tt(TokenTree), } diff --git a/compiler/rustc_parse/src/parser/nonterminal.rs b/compiler/rustc_parse/src/parser/nonterminal.rs index f5681532b3a..ff059a7e865 100644 --- a/compiler/rustc_parse/src/parser/nonterminal.rs +++ b/compiler/rustc_parse/src/parser/nonterminal.rs @@ -1,5 +1,5 @@ use rustc_ast::ptr::P; -use rustc_ast::token::{self, Delimiter, NonterminalKind, Token}; +use rustc_ast::token::{self, Delimiter, Nonterminal::*, NonterminalKind, Token}; use rustc_ast::HasTokens; use rustc_ast_pretty::pprust; use rustc_errors::IntoDiagnostic; @@ -8,7 +8,7 @@ use rustc_span::symbol::{kw, Ident}; use crate::errors::UnexpectedNonterminal; use crate::parser::pat::{CommaRecoveryMode, RecoverColon, RecoverComma}; -use crate::parser::{FollowedByType, ForceCollect, NtOrTt, Parser, PathStyle}; +use crate::parser::{FollowedByType, ForceCollect, ParseNtResult, Parser, PathStyle}; impl<'a> Parser<'a> { /// Checks whether a non-terminal may begin with a particular token. @@ -20,10 +20,21 @@ impl<'a> Parser<'a> { pub fn nonterminal_may_begin_with(kind: NonterminalKind, token: &Token) -> bool { /// Checks whether the non-terminal may contain a single (non-keyword) identifier. fn may_be_ident(nt: &token::Nonterminal) -> bool { - !matches!( - *nt, - token::NtItem(_) | token::NtBlock(_) | token::NtVis(_) | token::NtLifetime(_) - ) + match nt { + NtStmt(_) + | NtPat(_) + | NtExpr(_) + | NtTy(_) + | NtIdent(..) + | NtLiteral(_) // `true`, `false` + | NtMeta(_) + | NtPath(_) => true, + + NtItem(_) + | NtBlock(_) + | NtVis(_) + | NtLifetime(_) => false, + } } match kind { @@ -44,27 +55,19 @@ impl<'a> Parser<'a> { }, NonterminalKind::Block => match &token.kind { token::OpenDelim(Delimiter::Brace) => true, - token::Interpolated(nt) => !matches!( - **nt, - token::NtItem(_) - | token::NtPat(_) - | token::NtTy(_) - | token::NtIdent(..) - | token::NtMeta(_) - | token::NtPath(_) - | token::NtVis(_) - ), + token::Interpolated(nt) => match **nt { + NtBlock(_) | NtLifetime(_) | NtStmt(_) | NtExpr(_) | NtLiteral(_) => true, + NtItem(_) | NtPat(_) | NtTy(_) | NtIdent(..) | NtMeta(_) | NtPath(_) + | NtVis(_) => false, + }, _ => false, }, NonterminalKind::Path | NonterminalKind::Meta => match &token.kind { token::ModSep | token::Ident(..) => true, - token::Interpolated(nt) => match **nt { - token::NtPath(_) | token::NtMeta(_) => true, - _ => may_be_ident(&nt), - }, + token::Interpolated(nt) => may_be_ident(nt), _ => false, }, - NonterminalKind::PatParam { .. } | NonterminalKind::PatWithOr { .. } => { + NonterminalKind::PatParam { .. } | NonterminalKind::PatWithOr => { match &token.kind { token::Ident(..) | // box, ref, mut, and other identifiers (can stricten) token::OpenDelim(Delimiter::Parenthesis) | // tuple pattern @@ -79,7 +82,7 @@ impl<'a> Parser<'a> { token::Lt | // path (UFCS constant) token::BinOp(token::Shl) => true, // path (double UFCS) // leading vert `|` or-pattern - token::BinOp(token::Or) => matches!(kind, NonterminalKind::PatWithOr {..}), + token::BinOp(token::Or) => matches!(kind, NonterminalKind::PatWithOr), token::Interpolated(nt) => may_be_ident(nt), _ => false, } @@ -87,7 +90,7 @@ impl<'a> Parser<'a> { NonterminalKind::Lifetime => match &token.kind { token::Lifetime(_) => true, token::Interpolated(nt) => { - matches!(**nt, token::NtLifetime(_)) + matches!(**nt, NtLifetime(_)) } _ => false, }, @@ -100,18 +103,16 @@ impl<'a> Parser<'a> { /// Parse a non-terminal (e.g. MBE `:pat` or `:ident`). Inlined because there is only one call /// site. #[inline] - pub fn parse_nonterminal(&mut self, kind: NonterminalKind) -> PResult<'a, NtOrTt> { - // Any `Nonterminal` which stores its tokens (currently `NtItem` and `NtExpr`) - // needs to have them force-captured here. + pub fn parse_nonterminal(&mut self, kind: NonterminalKind) -> PResult<'a, ParseNtResult> { // A `macro_rules!` invocation may pass a captured item/expr to a proc-macro, // which requires having captured tokens available. Since we cannot determine // in advance whether or not a proc-macro will be (transitively) invoked, // we always capture tokens for any `Nonterminal` which needs them. let mut nt = match kind { // Note that TT is treated differently to all the others. - NonterminalKind::TT => return Ok(NtOrTt::Tt(self.parse_token_tree())), + NonterminalKind::TT => return Ok(ParseNtResult::Tt(self.parse_token_tree())), NonterminalKind::Item => match self.parse_item(ForceCollect::Yes)? { - Some(item) => token::NtItem(item), + Some(item) => NtItem(item), None => { return Err(UnexpectedNonterminal::Item(self.token.span) .into_diagnostic(&self.sess.span_diagnostic)); @@ -120,19 +121,19 @@ impl<'a> Parser<'a> { NonterminalKind::Block => { // While a block *expression* may have attributes (e.g. `#[my_attr] { ... }`), // the ':block' matcher does not support them - token::NtBlock(self.collect_tokens_no_attrs(|this| this.parse_block())?) + NtBlock(self.collect_tokens_no_attrs(|this| this.parse_block())?) } NonterminalKind::Stmt => match self.parse_stmt(ForceCollect::Yes)? { - Some(s) => token::NtStmt(P(s)), + Some(s) => NtStmt(P(s)), None => { return Err(UnexpectedNonterminal::Statement(self.token.span) .into_diagnostic(&self.sess.span_diagnostic)); } }, - NonterminalKind::PatParam { .. } | NonterminalKind::PatWithOr { .. } => { - token::NtPat(self.collect_tokens_no_attrs(|this| match kind { + NonterminalKind::PatParam { .. } | NonterminalKind::PatWithOr => { + NtPat(self.collect_tokens_no_attrs(|this| match kind { NonterminalKind::PatParam { .. } => this.parse_pat_no_top_alt(None, None), - NonterminalKind::PatWithOr { .. } => this.parse_pat_allow_top_alt( + NonterminalKind::PatWithOr => this.parse_pat_allow_top_alt( None, RecoverComma::No, RecoverColon::No, @@ -142,16 +143,16 @@ impl<'a> Parser<'a> { })?) } - NonterminalKind::Expr => token::NtExpr(self.parse_expr_force_collect()?), + NonterminalKind::Expr => NtExpr(self.parse_expr_force_collect()?), NonterminalKind::Literal => { // The `:literal` matcher does not support attributes - token::NtLiteral( + NtLiteral( self.collect_tokens_no_attrs(|this| this.parse_literal_maybe_minus())?, ) } - NonterminalKind::Ty => token::NtTy( - self.collect_tokens_no_attrs(|this| this.parse_no_question_mark_recover())?, + NonterminalKind::Ty => NtTy( + self.collect_tokens_no_attrs(|this| this.parse_ty_no_question_mark_recover())?, ), // this could be handled like a token, since it is one @@ -159,7 +160,7 @@ impl<'a> Parser<'a> { if let Some((ident, is_raw)) = get_macro_ident(&self.token) => { self.bump(); - token::NtIdent(ident, is_raw) + NtIdent(ident, is_raw) } NonterminalKind::Ident => { return Err(UnexpectedNonterminal::Ident { @@ -167,16 +168,16 @@ impl<'a> Parser<'a> { token: self.token.clone(), }.into_diagnostic(&self.sess.span_diagnostic)); } - NonterminalKind::Path => token::NtPath( + NonterminalKind::Path => NtPath( P(self.collect_tokens_no_attrs(|this| this.parse_path(PathStyle::Type))?), ), - NonterminalKind::Meta => token::NtMeta(P(self.parse_attr_item(true)?)), - NonterminalKind::Vis => token::NtVis( + NonterminalKind::Meta => NtMeta(P(self.parse_attr_item(true)?)), + NonterminalKind::Vis => NtVis( P(self.collect_tokens_no_attrs(|this| this.parse_visibility(FollowedByType::Yes))?), ), NonterminalKind::Lifetime => { if self.check_lifetime() { - token::NtLifetime(self.expect_lifetime().ident) + NtLifetime(self.expect_lifetime().ident) } else { return Err(UnexpectedNonterminal::Lifetime { span: self.token.span, @@ -196,7 +197,7 @@ impl<'a> Parser<'a> { ); } - Ok(NtOrTt::Nt(nt)) + Ok(ParseNtResult::Nt(nt)) } } diff --git a/compiler/rustc_parse/src/parser/ty.rs b/compiler/rustc_parse/src/parser/ty.rs index 3bb50b05aa3..2d888efb1f3 100644 --- a/compiler/rustc_parse/src/parser/ty.rs +++ b/compiler/rustc_parse/src/parser/ty.rs @@ -180,7 +180,7 @@ impl<'a> Parser<'a> { ) } - pub(super) fn parse_no_question_mark_recover(&mut self) -> PResult<'a, P<Ty>> { + pub(super) fn parse_ty_no_question_mark_recover(&mut self) -> PResult<'a, P<Ty>> { self.parse_ty_common( AllowPlus::Yes, AllowCVariadic::No, @@ -714,6 +714,7 @@ impl<'a> Parser<'a> { /// ``` fn parse_generic_bound(&mut self) -> PResult<'a, GenericBound> { let lo = self.token.span; + let leading_token = self.prev_token.clone(); let has_parens = self.eat(&token::OpenDelim(Delimiter::Parenthesis)); let inner_lo = self.token.span; @@ -722,7 +723,7 @@ impl<'a> Parser<'a> { self.error_lt_bound_with_modifiers(modifiers); self.parse_generic_lt_bound(lo, inner_lo, has_parens)? } else { - self.parse_generic_ty_bound(lo, has_parens, modifiers)? + self.parse_generic_ty_bound(lo, has_parens, modifiers, &leading_token)? }; Ok(bound) @@ -827,6 +828,7 @@ impl<'a> Parser<'a> { lo: Span, has_parens: bool, modifiers: BoundModifiers, + leading_token: &Token, ) -> PResult<'a, GenericBound> { let mut lifetime_defs = self.parse_late_bound_lifetime_defs()?; let mut path = if self.token.is_keyword(kw::Fn) @@ -873,18 +875,18 @@ impl<'a> Parser<'a> { } if has_parens { - if self.token.is_like_plus() { - // Someone has written something like `&dyn (Trait + Other)`. The correct code - // would be `&(dyn Trait + Other)`, but we don't have access to the appropriate - // span to suggest that. When written as `&dyn Trait + Other`, an appropriate - // suggestion is given. + // Someone has written something like `&dyn (Trait + Other)`. The correct code + // would be `&(dyn Trait + Other)` + if self.token.is_like_plus() && leading_token.is_keyword(kw::Dyn) { let bounds = vec![]; self.parse_remaining_bounds(bounds, true)?; self.expect(&token::CloseDelim(Delimiter::Parenthesis))?; - let sp = vec![lo, self.prev_token.span]; - self.sess.emit_err(errors::IncorrectBracesTraitBounds { - span: sp, - sugg: errors::IncorrectBracesTraitBoundsSugg { l: lo, r: self.prev_token.span }, + self.sess.emit_err(errors::IncorrectParensTraitBounds { + span: vec![lo, self.prev_token.span], + sugg: errors::IncorrectParensTraitBoundsSugg { + wrong_span: leading_token.span.shrink_to_hi().to(lo), + new_span: leading_token.span.shrink_to_lo(), + }, }); } else { self.expect(&token::CloseDelim(Delimiter::Parenthesis))?; diff --git a/compiler/rustc_passes/src/layout_test.rs b/compiler/rustc_passes/src/layout_test.rs index 0463ee2914b..a7a8af864ac 100644 --- a/compiler/rustc_passes/src/layout_test.rs +++ b/compiler/rustc_passes/src/layout_test.rs @@ -27,7 +27,6 @@ pub fn test_layout(tcx: TyCtxt<'_>) { } fn dump_layout_of(tcx: TyCtxt<'_>, item_def_id: LocalDefId, attr: &Attribute) { - let tcx = tcx; let param_env = tcx.param_env(item_def_id); let ty = tcx.type_of(item_def_id).instantiate_identity(); match tcx.layout_of(param_env.and(ty)) { diff --git a/compiler/rustc_passes/src/liveness.rs b/compiler/rustc_passes/src/liveness.rs index 0c0b8b6d094..20e996eaec4 100644 --- a/compiler/rustc_passes/src/liveness.rs +++ b/compiler/rustc_passes/src/liveness.rs @@ -1105,7 +1105,6 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> { } // Then do a second pass for inputs - let mut succ = succ; for (op, _op_sp) in asm.operands.iter().rev() { match op { hir::InlineAsmOperand::In { expr, .. } => { diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index c87db96a5dd..22d084c8e0b 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -311,6 +311,10 @@ enum LifetimeRibKind { /// error on default object bounds (e.g., `Box<dyn Foo>`). AnonymousReportError, + /// Resolves elided lifetimes to `'static`, but gives a warning that this behavior + /// is a bug and will be reverted soon. + AnonymousWarnToStatic(NodeId), + /// Signal we cannot find which should be the anonymous lifetime. ElisionFailure, @@ -1148,6 +1152,7 @@ impl<'a: 'ast, 'ast, 'tcx> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast, } LifetimeRibKind::AnonymousCreateParameter { .. } | LifetimeRibKind::AnonymousReportError + | LifetimeRibKind::AnonymousWarnToStatic(_) | LifetimeRibKind::Elided(_) | LifetimeRibKind::ElisionFailure | LifetimeRibKind::ConcreteAnonConst(_) @@ -1515,6 +1520,7 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> { // lifetime would be illegal. LifetimeRibKind::Item | LifetimeRibKind::AnonymousReportError + | LifetimeRibKind::AnonymousWarnToStatic(_) | LifetimeRibKind::ElisionFailure => Some(LifetimeUseSet::Many), // An anonymous lifetime is legal here, and bound to the right // place, go ahead. @@ -1576,7 +1582,8 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> { | LifetimeRibKind::Elided(_) | LifetimeRibKind::Generics { .. } | LifetimeRibKind::ElisionFailure - | LifetimeRibKind::AnonymousReportError => {} + | LifetimeRibKind::AnonymousReportError + | LifetimeRibKind::AnonymousWarnToStatic(_) => {} } } @@ -1616,6 +1623,25 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> { self.record_lifetime_res(lifetime.id, res, elision_candidate); return; } + LifetimeRibKind::AnonymousWarnToStatic(node_id) => { + self.record_lifetime_res(lifetime.id, LifetimeRes::Static, elision_candidate); + let msg = if elided { + "`&` without an explicit lifetime name cannot be used here" + } else { + "`'_` cannot be used here" + }; + self.r.lint_buffer.buffer_lint_with_diagnostic( + lint::builtin::ELIDED_LIFETIMES_IN_ASSOCIATED_CONSTANT, + node_id, + lifetime.ident.span, + msg, + lint::BuiltinLintDiagnostics::AssociatedConstElidedLifetime { + elided, + span: lifetime.ident.span, + }, + ); + return; + } LifetimeRibKind::AnonymousReportError => { let (msg, note) = if elided { ( @@ -1811,7 +1837,8 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> { // // impl Foo for std::cell::Ref<u32> // note lack of '_ // async fn foo(_: std::cell::Ref<u32>) { ... } - LifetimeRibKind::AnonymousCreateParameter { report_in_path: true, .. } => { + LifetimeRibKind::AnonymousCreateParameter { report_in_path: true, .. } + | LifetimeRibKind::AnonymousWarnToStatic(_) => { let sess = self.r.tcx.sess; let mut err = rustc_errors::struct_span_err!( sess, @@ -2898,7 +2925,6 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> { match &item.kind { AssocItemKind::Const(box ast::ConstItem { generics, ty, expr, .. }) => { debug!("resolve_implementation AssocItemKind::Const"); - self.with_generic_param_rib( &generics.params, RibKind::AssocItem, @@ -2908,28 +2934,33 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> { kind: LifetimeBinderKind::ConstItem, }, |this| { - // If this is a trait impl, ensure the const - // exists in trait - this.check_trait_item( - item.id, - item.ident, - &item.kind, - ValueNS, - item.span, - seen_trait_items, - |i, s, c| ConstNotMemberOfTrait(i, s, c), - ); + this.with_lifetime_rib( + LifetimeRibKind::AnonymousWarnToStatic(item.id), + |this| { + // If this is a trait impl, ensure the const + // exists in trait + this.check_trait_item( + item.id, + item.ident, + &item.kind, + ValueNS, + item.span, + seen_trait_items, + |i, s, c| ConstNotMemberOfTrait(i, s, c), + ); - this.visit_generics(generics); - this.visit_ty(ty); - if let Some(expr) = expr { - // We allow arbitrary const expressions inside of associated consts, - // even if they are potentially not const evaluatable. - // - // Type parameters can already be used and as associated consts are - // not used as part of the type system, this is far less surprising. - this.resolve_const_body(expr, None); - } + this.visit_generics(generics); + this.visit_ty(ty); + if let Some(expr) = expr { + // We allow arbitrary const expressions inside of associated consts, + // even if they are potentially not const evaluatable. + // + // Type parameters can already be used and as associated consts are + // not used as part of the type system, this is far less surprising. + this.resolve_const_body(expr, None); + } + }, + ); }, ); } diff --git a/compiler/rustc_resolve/src/rustdoc.rs b/compiler/rustc_resolve/src/rustdoc.rs index d433391f272..ba7417b6dda 100644 --- a/compiler/rustc_resolve/src/rustdoc.rs +++ b/compiler/rustc_resolve/src/rustdoc.rs @@ -1,4 +1,4 @@ -use pulldown_cmark::{BrokenLink, Event, LinkType, Options, Parser, Tag}; +use pulldown_cmark::{BrokenLink, CowStr, Event, LinkType, Options, Parser, Tag}; use rustc_ast as ast; use rustc_ast::util::comments::beautify_doc_string; use rustc_data_structures::fx::FxHashMap; @@ -392,16 +392,73 @@ pub(crate) fn attrs_to_preprocessed_links(attrs: &[ast::Attribute]) -> Vec<Box<s let (doc_fragments, _) = attrs_to_doc_fragments(attrs.iter().map(|attr| (attr, None)), true); let doc = prepare_to_doc_link_resolution(&doc_fragments).into_values().next().unwrap(); - Parser::new_with_broken_link_callback( + parse_links(&doc) +} + +/// Similiar version of `markdown_links` from rustdoc. +/// This will collect destination links and display text if exists. +fn parse_links<'md>(doc: &'md str) -> Vec<Box<str>> { + let mut broken_link_callback = |link: BrokenLink<'md>| Some((link.reference, "".into())); + let mut event_iter = Parser::new_with_broken_link_callback( &doc, main_body_opts(), - Some(&mut |link: BrokenLink<'_>| Some((link.reference, "".into()))), + Some(&mut broken_link_callback), ) - .filter_map(|event| match event { - Event::Start(Tag::Link(link_type, dest, _)) if may_be_doc_link(link_type) => { - Some(preprocess_link(&dest)) + .into_iter(); + let mut links = Vec::new(); + + while let Some(event) = event_iter.next() { + match event { + Event::Start(Tag::Link(link_type, dest, _)) if may_be_doc_link(link_type) => { + if matches!( + link_type, + LinkType::Inline + | LinkType::ReferenceUnknown + | LinkType::Reference + | LinkType::Shortcut + | LinkType::ShortcutUnknown + ) { + if let Some(display_text) = collect_link_data(&mut event_iter) { + links.push(display_text); + } + } + + links.push(preprocess_link(&dest)); + } + _ => {} + } + } + + links +} + +/// Collects additional data of link. +fn collect_link_data<'input, 'callback>( + event_iter: &mut Parser<'input, 'callback>, +) -> Option<Box<str>> { + let mut display_text: Option<String> = None; + let mut append_text = |text: CowStr<'_>| { + if let Some(display_text) = &mut display_text { + display_text.push_str(&text); + } else { + display_text = Some(text.to_string()); + } + }; + + while let Some(event) = event_iter.next() { + match event { + Event::Text(text) => { + append_text(text); + } + Event::Code(code) => { + append_text(code); + } + Event::End(_) => { + break; + } + _ => {} } - _ => None, - }) - .collect() + } + + display_text.map(String::into_boxed_str) } diff --git a/compiler/rustc_session/messages.ftl b/compiler/rustc_session/messages.ftl index b07c6db599e..e06b6380944 100644 --- a/compiler/rustc_session/messages.ftl +++ b/compiler/rustc_session/messages.ftl @@ -8,6 +8,9 @@ session_cannot_mix_and_match_sanitizers = `-Zsanitizer={$first}` is incompatible session_cgu_not_recorded = CGU-reuse for `{$cgu_user_name}` is (mangled: `{$cgu_name}`) was not recorded +session_cli_feature_diagnostic_help = + add `-Zcrate-attr="feature({$feature})"` to the command-line options to enable + session_crate_name_does_not_match = `--crate-name` and `#[crate_name]` are required to match, but `{$s}` != `{$name}` session_crate_name_empty = crate name must not be empty diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs index 28ae88424ab..f00472f181d 100644 --- a/compiler/rustc_session/src/config.rs +++ b/compiler/rustc_session/src/config.rs @@ -12,7 +12,7 @@ use crate::{EarlyErrorHandler, Session}; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_data_structures::stable_hasher::{StableOrd, ToStableHashKey}; use rustc_target::abi::Align; -use rustc_target::spec::{PanicStrategy, SanitizerSet, SplitDebuginfo}; +use rustc_target::spec::{PanicStrategy, RelocModel, SanitizerSet, SplitDebuginfo}; use rustc_target::spec::{Target, TargetTriple, TargetWarnings, TARGETS}; use crate::parse::{CrateCheckConfig, CrateConfig}; @@ -1193,6 +1193,7 @@ fn default_configuration(sess: &Session) -> CrateConfig { let os = &sess.target.os; let env = &sess.target.env; let abi = &sess.target.abi; + let relocation_model = sess.target.relocation_model.desc_symbol(); let vendor = &sess.target.vendor; let min_atomic_width = sess.target.min_atomic_width(); let max_atomic_width = sess.target.max_atomic_width(); @@ -1218,6 +1219,9 @@ fn default_configuration(sess: &Session) -> CrateConfig { ret.insert((sym::target_pointer_width, Some(Symbol::intern(&wordsz)))); ret.insert((sym::target_env, Some(Symbol::intern(env)))); ret.insert((sym::target_abi, Some(Symbol::intern(abi)))); + if sess.is_nightly_build() { + ret.insert((sym::relocation_model, Some(relocation_model))); + } ret.insert((sym::target_vendor, Some(Symbol::intern(vendor)))); if sess.target.has_thread_local { ret.insert((sym::target_thread_local, None)); @@ -1415,6 +1419,8 @@ impl CrateCheckConfig { .into_iter() .map(|sanitizer| Symbol::intern(sanitizer.as_str().unwrap())); + let relocation_model_values = RelocModel::all(); + // Unknown possible values: // - `feature` // - `target_feature` @@ -1453,6 +1459,10 @@ impl CrateCheckConfig { .entry(sym::target_has_atomic_equal_alignment) .or_insert_with(no_values) .extend(atomic_values); + self.expecteds + .entry(sym::relocation_model) + .or_insert_with(empty_values) + .extend(relocation_model_values); // Target specific values { diff --git a/compiler/rustc_session/src/errors.rs b/compiler/rustc_session/src/errors.rs index 78940462b2c..5f8bbfca890 100644 --- a/compiler/rustc_session/src/errors.rs +++ b/compiler/rustc_session/src/errors.rs @@ -57,6 +57,12 @@ pub struct FeatureDiagnosticHelp { pub feature: Symbol, } +#[derive(Subdiagnostic)] +#[help(session_cli_feature_diagnostic_help)] +pub struct CliFeatureDiagnosticHelp { + pub feature: Symbol, +} + #[derive(Diagnostic)] #[diag(session_not_circumvent_feature)] pub struct NotCircumventFeature; diff --git a/compiler/rustc_session/src/parse.rs b/compiler/rustc_session/src/parse.rs index 1cf63e9b7ba..671204c0d8e 100644 --- a/compiler/rustc_session/src/parse.rs +++ b/compiler/rustc_session/src/parse.rs @@ -2,7 +2,9 @@ //! It also serves as an input to the parser itself. use crate::config::CheckCfg; -use crate::errors::{FeatureDiagnosticForIssue, FeatureDiagnosticHelp, FeatureGateError}; +use crate::errors::{ + CliFeatureDiagnosticHelp, FeatureDiagnosticForIssue, FeatureDiagnosticHelp, FeatureGateError, +}; use crate::lint::{ builtin::UNSTABLE_SYNTAX_PRE_EXPANSION, BufferedEarlyLint, BuiltinLintDiagnostics, Lint, LintId, }; @@ -110,7 +112,7 @@ pub fn feature_err_issue( } let mut err = sess.create_err(FeatureGateError { span, explain: explain.into() }); - add_feature_diagnostics_for_issue(&mut err, sess, feature, issue); + add_feature_diagnostics_for_issue(&mut err, sess, feature, issue, false); err } @@ -139,7 +141,7 @@ pub fn feature_warn_issue( explain: &'static str, ) { let mut err = sess.span_diagnostic.struct_span_warn(span, explain); - add_feature_diagnostics_for_issue(&mut err, sess, feature, issue); + add_feature_diagnostics_for_issue(&mut err, sess, feature, issue, false); // Decorate this as a future-incompatibility lint as in rustc_middle::lint::struct_lint_level let lint = UNSTABLE_SYNTAX_PRE_EXPANSION; @@ -158,7 +160,7 @@ pub fn feature_warn_issue( /// Adds the diagnostics for a feature to an existing error. pub fn add_feature_diagnostics(err: &mut Diagnostic, sess: &ParseSess, feature: Symbol) { - add_feature_diagnostics_for_issue(err, sess, feature, GateIssue::Language); + add_feature_diagnostics_for_issue(err, sess, feature, GateIssue::Language, false); } /// Adds the diagnostics for a feature to an existing error. @@ -171,6 +173,7 @@ pub fn add_feature_diagnostics_for_issue( sess: &ParseSess, feature: Symbol, issue: GateIssue, + feature_from_cli: bool, ) { if let Some(n) = find_feature_issue(feature, issue) { err.subdiagnostic(FeatureDiagnosticForIssue { n }); @@ -178,7 +181,11 @@ pub fn add_feature_diagnostics_for_issue( // #23973: do not suggest `#![feature(...)]` if we are in beta/stable if sess.unstable_features.is_nightly_build() { - err.subdiagnostic(FeatureDiagnosticHelp { feature }); + if feature_from_cli { + err.subdiagnostic(CliFeatureDiagnosticHelp { feature }); + } else { + err.subdiagnostic(FeatureDiagnosticHelp { feature }); + } } } diff --git a/compiler/rustc_smir/src/rustc_smir/mod.rs b/compiler/rustc_smir/src/rustc_smir/mod.rs index 06b37008ebe..7e8ac9a141a 100644 --- a/compiler/rustc_smir/src/rustc_smir/mod.rs +++ b/compiler/rustc_smir/src/rustc_smir/mod.rs @@ -780,8 +780,8 @@ impl<'tcx> Stable<'tcx> for mir::Terminator<'tcx> { .collect(), otherwise: targets.otherwise().as_usize(), }, - Resume => Terminator::Resume, - Terminate => Terminator::Abort, + UnwindResume => Terminator::Resume, + UnwindTerminate => Terminator::Abort, Return => Terminator::Return, Unreachable => Terminator::Unreachable, Drop { place, target, unwind, replace: _ } => Terminator::Drop { diff --git a/compiler/rustc_smir/src/stable_mir/mod.rs b/compiler/rustc_smir/src/stable_mir/mod.rs index 19061742b64..2ae334c6a95 100644 --- a/compiler/rustc_smir/src/stable_mir/mod.rs +++ b/compiler/rustc_smir/src/stable_mir/mod.rs @@ -1,6 +1,6 @@ //! Module that implements the public interface to the Stable MIR. //! -//! This module shall contain all type definitions and APIs that we expect 3P tools to invoke to +//! This module shall contain all type definitions and APIs that we expect third-party tools to invoke to //! interact with the compiler. //! //! The goal is to eventually move this module to its own crate which shall be published on diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index edbf51e9b33..28a2dfebcfe 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -445,6 +445,7 @@ symbols! { bridge, bswap, builtin_syntax, + c, c_str, c_str_literals, c_unwind, @@ -468,6 +469,7 @@ symbols! { cfg_hide, cfg_overflow_checks, cfg_panic, + cfg_relocation_model, cfg_sanitize, cfg_target_abi, cfg_target_compact, @@ -661,6 +663,7 @@ symbols! { dyn_metadata, dyn_star, dyn_trait, + dynamic_no_pic: "dynamic-no-pic", e, edition_panic, effects, @@ -1115,6 +1118,8 @@ symbols! { path, pattern_parentheses, phantom_data, + pic, + pie, pin, platform_intrinsics, plugin, @@ -1223,6 +1228,7 @@ symbols! { register_tool, relaxed_adts, relaxed_struct_unsize, + relocation_model, rem, rem_assign, repr, @@ -1243,6 +1249,8 @@ symbols! { rintf64, riscv_target_feature, rlib, + ropi, + ropi_rwpi: "ropi-rwpi", rotate_left, rotate_right, roundevenf32, @@ -1354,6 +1362,7 @@ symbols! { rustdoc_missing_doc_code_examples, rustfmt, rvalue_static_promotion, + rwpi, s, safety, sanitize, diff --git a/compiler/rustc_target/src/spec/arm_unknown_linux_gnueabi.rs b/compiler/rustc_target/src/spec/arm_unknown_linux_gnueabi.rs index c0f1827ad3f..400030ca0c6 100644 --- a/compiler/rustc_target/src/spec/arm_unknown_linux_gnueabi.rs +++ b/compiler/rustc_target/src/spec/arm_unknown_linux_gnueabi.rs @@ -11,6 +11,7 @@ pub fn target() -> Target { features: "+strict-align,+v6".into(), max_atomic_width: Some(64), mcount: "\u{1}__gnu_mcount_nc".into(), + llvm_mcount_intrinsic: Some("llvm.arm.gnu.eabi.mcount".into()), ..super::linux_gnu_base::opts() }, } diff --git a/compiler/rustc_target/src/spec/arm_unknown_linux_gnueabihf.rs b/compiler/rustc_target/src/spec/arm_unknown_linux_gnueabihf.rs index 79b8958c22a..6228fb15a83 100644 --- a/compiler/rustc_target/src/spec/arm_unknown_linux_gnueabihf.rs +++ b/compiler/rustc_target/src/spec/arm_unknown_linux_gnueabihf.rs @@ -11,6 +11,7 @@ pub fn target() -> Target { features: "+strict-align,+v6,+vfp2,-d32".into(), max_atomic_width: Some(64), mcount: "\u{1}__gnu_mcount_nc".into(), + llvm_mcount_intrinsic: Some("llvm.arm.gnu.eabi.mcount".into()), ..super::linux_gnu_base::opts() }, } diff --git a/compiler/rustc_target/src/spec/armeb_unknown_linux_gnueabi.rs b/compiler/rustc_target/src/spec/armeb_unknown_linux_gnueabi.rs index 4836f3cf720..1d66515a72e 100644 --- a/compiler/rustc_target/src/spec/armeb_unknown_linux_gnueabi.rs +++ b/compiler/rustc_target/src/spec/armeb_unknown_linux_gnueabi.rs @@ -13,6 +13,7 @@ pub fn target() -> Target { endian: Endian::Big, max_atomic_width: Some(64), mcount: "\u{1}__gnu_mcount_nc".into(), + llvm_mcount_intrinsic: Some("llvm.arm.gnu.eabi.mcount".into()), ..super::linux_gnu_base::opts() }, } diff --git a/compiler/rustc_target/src/spec/armv4t_unknown_linux_gnueabi.rs b/compiler/rustc_target/src/spec/armv4t_unknown_linux_gnueabi.rs index 1de63a920c8..cffebcc9581 100644 --- a/compiler/rustc_target/src/spec/armv4t_unknown_linux_gnueabi.rs +++ b/compiler/rustc_target/src/spec/armv4t_unknown_linux_gnueabi.rs @@ -12,6 +12,7 @@ pub fn target() -> Target { // Atomic operations provided by compiler-builtins max_atomic_width: Some(32), mcount: "\u{1}__gnu_mcount_nc".into(), + llvm_mcount_intrinsic: Some("llvm.arm.gnu.eabi.mcount".into()), has_thumb_interworking: true, ..super::linux_gnu_base::opts() }, diff --git a/compiler/rustc_target/src/spec/armv6_unknown_freebsd.rs b/compiler/rustc_target/src/spec/armv6_unknown_freebsd.rs index b7cfccc8b3d..4a8aa31576f 100644 --- a/compiler/rustc_target/src/spec/armv6_unknown_freebsd.rs +++ b/compiler/rustc_target/src/spec/armv6_unknown_freebsd.rs @@ -13,6 +13,7 @@ pub fn target() -> Target { features: "+v6,+vfp2,-d32".into(), max_atomic_width: Some(64), mcount: "\u{1}__gnu_mcount_nc".into(), + llvm_mcount_intrinsic: Some("llvm.arm.gnu.eabi.mcount".into()), ..super::freebsd_base::opts() }, } diff --git a/compiler/rustc_target/src/spec/armv7_unknown_linux_gnueabi.rs b/compiler/rustc_target/src/spec/armv7_unknown_linux_gnueabi.rs index 903042d7e7a..73ae212a7a6 100644 --- a/compiler/rustc_target/src/spec/armv7_unknown_linux_gnueabi.rs +++ b/compiler/rustc_target/src/spec/armv7_unknown_linux_gnueabi.rs @@ -14,6 +14,7 @@ pub fn target() -> Target { features: "+v7,+thumb2,+soft-float,-neon".into(), max_atomic_width: Some(64), mcount: "\u{1}__gnu_mcount_nc".into(), + llvm_mcount_intrinsic: Some("llvm.arm.gnu.eabi.mcount".into()), ..super::linux_gnu_base::opts() }, } diff --git a/compiler/rustc_target/src/spec/mod.rs b/compiler/rustc_target/src/spec/mod.rs index af2b96ccb51..31b6961bb62 100644 --- a/compiler/rustc_target/src/spec/mod.rs +++ b/compiler/rustc_target/src/spec/mod.rs @@ -42,7 +42,7 @@ use crate::spec::crt_objects::{CrtObjects, LinkSelfContainedDefault}; use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; use rustc_fs_util::try_canonicalize; use rustc_serialize::{Decodable, Decoder, Encodable, Encoder}; -use rustc_span::symbol::{sym, Symbol}; +use rustc_span::symbol::{kw, sym, Symbol}; use serde_json::Value; use std::borrow::Cow; use std::collections::BTreeMap; @@ -655,6 +655,43 @@ pub enum RelocModel { RopiRwpi, } +impl RelocModel { + pub fn desc(&self) -> &str { + match *self { + RelocModel::Static => "static", + RelocModel::Pic => "pic", + RelocModel::Pie => "pie", + RelocModel::DynamicNoPic => "dynamic-no-pic", + RelocModel::Ropi => "ropi", + RelocModel::Rwpi => "rwpi", + RelocModel::RopiRwpi => "ropi-rwpi", + } + } + pub const fn desc_symbol(&self) -> Symbol { + match *self { + RelocModel::Static => kw::Static, + RelocModel::Pic => sym::pic, + RelocModel::Pie => sym::pie, + RelocModel::DynamicNoPic => sym::dynamic_no_pic, + RelocModel::Ropi => sym::ropi, + RelocModel::Rwpi => sym::rwpi, + RelocModel::RopiRwpi => sym::ropi_rwpi, + } + } + + pub const fn all() -> [Symbol; 7] { + [ + RelocModel::Static.desc_symbol(), + RelocModel::Pic.desc_symbol(), + RelocModel::Pie.desc_symbol(), + RelocModel::DynamicNoPic.desc_symbol(), + RelocModel::Ropi.desc_symbol(), + RelocModel::Rwpi.desc_symbol(), + RelocModel::RopiRwpi.desc_symbol(), + ] + } +} + impl FromStr for RelocModel { type Err = (); @@ -674,16 +711,7 @@ impl FromStr for RelocModel { impl ToJson for RelocModel { fn to_json(&self) -> Json { - match *self { - RelocModel::Static => "static", - RelocModel::Pic => "pic", - RelocModel::Pie => "pie", - RelocModel::DynamicNoPic => "dynamic-no-pic", - RelocModel::Ropi => "ropi", - RelocModel::Rwpi => "rwpi", - RelocModel::RopiRwpi => "ropi-rwpi", - } - .to_json() + self.desc().to_json() } } @@ -1922,6 +1950,9 @@ pub struct TargetOptions { /// Use platform dependent mcount function pub mcount: StaticCow<str>, + /// Use LLVM intrinsic for mcount function name + pub llvm_mcount_intrinsic: Option<StaticCow<str>>, + /// LLVM ABI name, corresponds to the '-mabi' parameter available in multilib C compilers pub llvm_abiname: StaticCow<str>, @@ -2183,6 +2214,7 @@ impl Default for TargetOptions { override_export_symbols: None, merge_functions: MergeFunctions::Aliases, mcount: "mcount".into(), + llvm_mcount_intrinsic: None, llvm_abiname: "".into(), relax_elf_relocations: false, llvm_args: cvs![], @@ -2840,6 +2872,7 @@ impl Target { key!(override_export_symbols, opt_list); key!(merge_functions, MergeFunctions)?; key!(mcount = "target-mcount"); + key!(llvm_mcount_intrinsic, optional); key!(llvm_abiname); key!(relax_elf_relocations, bool); key!(llvm_args, list); @@ -3096,6 +3129,7 @@ impl ToJson for Target { target_option_val!(override_export_symbols); target_option_val!(merge_functions); target_option_val!(mcount, "target-mcount"); + target_option_val!(llvm_mcount_intrinsic); target_option_val!(llvm_abiname); target_option_val!(relax_elf_relocations); target_option_val!(llvm_args); diff --git a/compiler/rustc_trait_selection/src/solve/eval_ctxt/canonical.rs b/compiler/rustc_trait_selection/src/solve/eval_ctxt/canonical.rs index 0990b9bee90..523841951b0 100644 --- a/compiler/rustc_trait_selection/src/solve/eval_ctxt/canonical.rs +++ b/compiler/rustc_trait_selection/src/solve/eval_ctxt/canonical.rs @@ -215,7 +215,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { // created inside of the query directly instead of returning them to the // caller. let prev_universe = self.infcx.universe(); - let universes_created_in_query = response.max_universe.index() + 1; + let universes_created_in_query = response.max_universe.index(); for _ in 0..universes_created_in_query { self.infcx.create_next_universe(); } diff --git a/compiler/rustc_trait_selection/src/traits/project.rs b/compiler/rustc_trait_selection/src/traits/project.rs index 3d800421b76..06a1027e5df 100644 --- a/compiler/rustc_trait_selection/src/traits/project.rs +++ b/compiler/rustc_trait_selection/src/traits/project.rs @@ -1214,7 +1214,7 @@ fn opt_normalize_projection_type<'a, 'b, 'tcx>( let projected_term = selcx.infcx.resolve_vars_if_possible(projected_term); - let mut result = if projected_term.has_projections() { + let result = if projected_term.has_projections() { let mut normalizer = AssocTypeNormalizer::new( selcx, param_env, @@ -1224,19 +1224,14 @@ fn opt_normalize_projection_type<'a, 'b, 'tcx>( ); let normalized_ty = normalizer.fold(projected_term); + let mut deduped = SsoHashSet::with_capacity(projected_obligations.len()); + projected_obligations.retain(|obligation| deduped.insert(obligation.clone())); + Normalized { value: normalized_ty, obligations: projected_obligations } } else { Normalized { value: projected_term, obligations: projected_obligations } }; - let mut deduped: SsoHashSet<_> = Default::default(); - result.obligations.retain(|projected_obligation| { - if !deduped.insert(projected_obligation.clone()) { - return false; - } - true - }); - if use_cache { infcx.inner.borrow_mut().projection_cache().insert_term(cache_key, result.clone()); } diff --git a/compiler/rustc_type_ir/src/structural_impls.rs b/compiler/rustc_type_ir/src/structural_impls.rs index 1a2d6d64eb0..f36f4ec8697 100644 --- a/compiler/rustc_type_ir/src/structural_impls.rs +++ b/compiler/rustc_type_ir/src/structural_impls.rs @@ -23,7 +23,6 @@ TrivialTypeTraversalImpls! { (), bool, usize, - u8, u16, u32, u64, diff --git a/library/alloc/src/boxed.rs b/library/alloc/src/boxed.rs index 8697a77db3b..96b93830f96 100644 --- a/library/alloc/src/boxed.rs +++ b/library/alloc/src/boxed.rs @@ -2183,7 +2183,7 @@ impl dyn Error + Send { let err: Box<dyn Error> = self; <dyn Error>::downcast(err).map_err(|s| unsafe { // Reapply the `Send` marker. - mem::transmute::<Box<dyn Error>, Box<dyn Error + Send>>(s) + Box::from_raw(Box::into_raw(s) as *mut (dyn Error + Send)) }) } } @@ -2197,7 +2197,7 @@ impl dyn Error + Send + Sync { let err: Box<dyn Error> = self; <dyn Error>::downcast(err).map_err(|s| unsafe { // Reapply the `Send + Sync` marker. - mem::transmute::<Box<dyn Error>, Box<dyn Error + Send + Sync>>(s) + Box::from_raw(Box::into_raw(s) as *mut (dyn Error + Send + Sync)) }) } } diff --git a/library/alloc/src/lib.rs b/library/alloc/src/lib.rs index 41aac02eaa9..efe8f5a8bcf 100644 --- a/library/alloc/src/lib.rs +++ b/library/alloc/src/lib.rs @@ -89,6 +89,7 @@ #![allow(explicit_outlives_requirements)] #![warn(multiple_supertrait_upcastable)] #![cfg_attr(not(bootstrap), allow(internal_features))] +#![cfg_attr(not(bootstrap), allow(rustdoc::redundant_explicit_links))] // // Library features: // tidy-alphabetical-start @@ -119,6 +120,7 @@ #![feature(const_waker)] #![feature(core_intrinsics)] #![feature(core_panic)] +#![feature(deprecated_suggestion)] #![feature(dispatch_from_dyn)] #![feature(error_generic_member_access)] #![feature(error_in_core)] diff --git a/library/alloc/src/slice.rs b/library/alloc/src/slice.rs index 093dcbbe8bf..aa3b7b7e191 100644 --- a/library/alloc/src/slice.rs +++ b/library/alloc/src/slice.rs @@ -592,7 +592,7 @@ impl<T> [T] { /// ``` #[rustc_allow_incoherent_impl] #[stable(feature = "rust1", since = "1.0.0")] - #[deprecated(since = "1.3.0", note = "renamed to join")] + #[deprecated(since = "1.3.0", note = "renamed to join", suggestion = "join")] pub fn connect<Separator>(&self, sep: Separator) -> <Self as Join<Separator>>::Output where Self: Join<Separator>, diff --git a/library/alloc/src/sync.rs b/library/alloc/src/sync.rs index e2a2fe932ab..476a4fea54f 100644 --- a/library/alloc/src/sync.rs +++ b/library/alloc/src/sync.rs @@ -153,7 +153,7 @@ macro_rules! acquire { /// /// ## `Deref` behavior /// -/// `Arc<T>` automatically dereferences to `T` (via the [`Deref`][deref] trait), +/// `Arc<T>` automatically dereferences to `T` (via the [`Deref`] trait), /// so you can call `T`'s methods on a value of type `Arc<T>`. To avoid name /// clashes with `T`'s methods, the methods of `Arc<T>` itself are associated /// functions, called using [fully qualified syntax]: @@ -187,7 +187,6 @@ macro_rules! acquire { /// [mutex]: ../../std/sync/struct.Mutex.html /// [rwlock]: ../../std/sync/struct.RwLock.html /// [atomic]: core::sync::atomic -/// [deref]: core::ops::Deref /// [downgrade]: Arc::downgrade /// [upgrade]: Weak::upgrade /// [RefCell\<T>]: core::cell::RefCell @@ -1495,7 +1494,7 @@ impl<T: ?Sized, A: Allocator> Arc<T, A> { /// alignment as `T`. This is trivially true if `U` is `T`. /// Note that if `U` is not `T` but has the same size and alignment, this is /// basically like transmuting references of different types. See - /// [`mem::transmute`][transmute] for more information on what + /// [`mem::transmute`] for more information on what /// restrictions apply in this case. /// /// The raw pointer must point to a block of memory allocated by `alloc` @@ -1507,7 +1506,6 @@ impl<T: ?Sized, A: Allocator> Arc<T, A> { /// even if the returned `Arc<T>` is never accessed. /// /// [into_raw]: Arc::into_raw - /// [transmute]: core::mem::transmute /// /// # Examples /// diff --git a/library/core/src/ffi/c_str.rs b/library/core/src/ffi/c_str.rs index 92e38df4049..163a65c909e 100644 --- a/library/core/src/ffi/c_str.rs +++ b/library/core/src/ffi/c_str.rs @@ -253,7 +253,7 @@ impl CStr { /// ``` /// /// [valid]: core::ptr#safety - #[inline] + #[inline] // inline is necessary for codegen to see strlen. #[must_use] #[stable(feature = "rust1", since = "1.0.0")] #[rustc_const_unstable(feature = "const_cstr_from_ptr", issue = "113219")] @@ -280,6 +280,8 @@ impl CStr { len } + // `inline` is necessary for codegen to see strlen. + #[inline] fn strlen_rt(s: *const c_char) -> usize { extern "C" { /// Provided by libc or compiler_builtins. diff --git a/library/core/src/intrinsics/mir.rs b/library/core/src/intrinsics/mir.rs index 036edbebbf3..ef0a2fd4ec4 100644 --- a/library/core/src/intrinsics/mir.rs +++ b/library/core/src/intrinsics/mir.rs @@ -104,17 +104,18 @@ //! } //! //! #[custom_mir(dialect = "runtime", phase = "optimized")] +#![cfg_attr(bootstrap, doc = "#[cfg(any())]")] // disable the following function in doctests when `bootstrap` is set //! fn push_and_pop<T>(v: &mut Vec<T>, value: T) { //! mir!( -//! let unused; +//! let _unused; //! let popped; //! //! { -//! Call(unused, pop, Vec::push(v, value)) +//! Call(_unused = Vec::push(v, value), pop) //! } //! //! pop = { -//! Call(popped, drop, Vec::pop(v)) +//! Call(popped = Vec::pop(v), drop) //! } //! //! drop = { @@ -275,7 +276,7 @@ define!("mir_return", fn Return() -> BasicBlock); define!("mir_goto", fn Goto(destination: BasicBlock) -> BasicBlock); define!("mir_unreachable", fn Unreachable() -> BasicBlock); define!("mir_drop", fn Drop<T>(place: T, goto: BasicBlock)); -define!("mir_call", fn Call<T>(place: T, goto: BasicBlock, call: T)); +define!("mir_call", fn Call(call: (), goto: BasicBlock)); define!("mir_storage_live", fn StorageLive<T>(local: T)); define!("mir_storage_dead", fn StorageDead<T>(local: T)); define!("mir_deinit", fn Deinit<T>(place: T)); diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs index 48c3c1f2123..a2729b3743c 100644 --- a/library/core/src/lib.rs +++ b/library/core/src/lib.rs @@ -97,6 +97,8 @@ #![allow(incomplete_features)] #![warn(multiple_supertrait_upcastable)] #![cfg_attr(not(bootstrap), allow(internal_features))] +// Do not check link redundancy on bootstraping phase +#![cfg_attr(not(bootstrap), allow(rustdoc::redundant_explicit_links))] // // Library features: // tidy-alphabetical-start diff --git a/library/core/src/mem/mod.rs b/library/core/src/mem/mod.rs index 2fff3f0efd7..732fcce0f29 100644 --- a/library/core/src/mem/mod.rs +++ b/library/core/src/mem/mod.rs @@ -413,7 +413,7 @@ pub const unsafe fn size_of_val_raw<T: ?Sized>(val: *const T) -> usize { #[inline] #[must_use] #[stable(feature = "rust1", since = "1.0.0")] -#[deprecated(note = "use `align_of` instead", since = "1.2.0")] +#[deprecated(note = "use `align_of` instead", since = "1.2.0", suggestion = "align_of")] pub fn min_align_of<T>() -> usize { intrinsics::min_align_of::<T>() } @@ -436,7 +436,7 @@ pub fn min_align_of<T>() -> usize { #[inline] #[must_use] #[stable(feature = "rust1", since = "1.0.0")] -#[deprecated(note = "use `align_of_val` instead", since = "1.2.0")] +#[deprecated(note = "use `align_of_val` instead", since = "1.2.0", suggestion = "align_of_val")] pub fn min_align_of_val<T: ?Sized>(val: &T) -> usize { // SAFETY: val is a reference, so it's a valid raw pointer unsafe { intrinsics::min_align_of_val(val) } diff --git a/library/core/src/num/int_macros.rs b/library/core/src/num/int_macros.rs index 1199d09b563..1f43520e1b3 100644 --- a/library/core/src/num/int_macros.rs +++ b/library/core/src/num/int_macros.rs @@ -2126,6 +2126,7 @@ macro_rules! int_impl { /// assert_eq!(a.rem_euclid(-b), 3); /// assert_eq!((-a).rem_euclid(-b), 1); /// ``` + #[doc(alias = "modulo", alias = "mod")] #[stable(feature = "euclidean_division", since = "1.38.0")] #[rustc_const_stable(feature = "const_euclidean_int_methods", since = "1.52.0")] #[must_use = "this returns the result of the operation, \ diff --git a/library/core/src/num/uint_macros.rs b/library/core/src/num/uint_macros.rs index 2136d29255f..81148c7cc51 100644 --- a/library/core/src/num/uint_macros.rs +++ b/library/core/src/num/uint_macros.rs @@ -2024,6 +2024,7 @@ macro_rules! uint_impl { /// ``` #[doc = concat!("assert_eq!(7", stringify!($SelfT), ".rem_euclid(4), 3); // or any other integer type")] /// ``` + #[doc(alias = "modulo", alias = "mod")] #[stable(feature = "euclidean_division", since = "1.38.0")] #[rustc_const_stable(feature = "const_euclidean_int_methods", since = "1.52.0")] #[must_use = "this returns the result of the operation, \ diff --git a/library/core/src/str/mod.rs b/library/core/src/str/mod.rs index e5f34952c7d..23cebdb6c3b 100644 --- a/library/core/src/str/mod.rs +++ b/library/core/src/str/mod.rs @@ -997,7 +997,7 @@ impl str { /// An iterator over the lines of a string. #[stable(feature = "rust1", since = "1.0.0")] - #[deprecated(since = "1.4.0", note = "use lines() instead now")] + #[deprecated(since = "1.4.0", note = "use lines() instead now", suggestion = "lines")] #[inline] #[allow(deprecated)] pub fn lines_any(&self) -> LinesAny<'_> { diff --git a/library/std/src/collections/hash/map.rs b/library/std/src/collections/hash/map.rs index a083b65604d..be173a7ace6 100644 --- a/library/std/src/collections/hash/map.rs +++ b/library/std/src/collections/hash/map.rs @@ -49,12 +49,14 @@ use crate::sys; /// ``` /// /// In other words, if two keys are equal, their hashes must be equal. +/// Violating this property is a logic error. /// -/// It is a logic error for a key to be modified in such a way that the key's +/// It is also a logic error for a key to be modified in such a way that the key's /// hash, as determined by the [`Hash`] trait, or its equality, as determined by /// the [`Eq`] trait, changes while it is in the map. This is normally only /// possible through [`Cell`], [`RefCell`], global state, I/O, or unsafe code. -/// The behavior resulting from such a logic error is not specified, but will +/// +/// The behavior resulting from either logic error is not specified, but will /// be encapsulated to the `HashMap` that observed the logic error and not /// result in undefined behavior. This could include panics, incorrect results, /// aborts, memory leaks, and non-termination. diff --git a/library/std/src/collections/hash/set.rs b/library/std/src/collections/hash/set.rs index 959403e1644..6d85b26af5f 100644 --- a/library/std/src/collections/hash/set.rs +++ b/library/std/src/collections/hash/set.rs @@ -24,13 +24,14 @@ use super::map::{map_try_reserve_error, RandomState}; /// ``` /// /// In other words, if two keys are equal, their hashes must be equal. +/// Violating this property is a logic error. /// -/// -/// It is a logic error for a key to be modified in such a way that the key's +/// It is also a logic error for a key to be modified in such a way that the key's /// hash, as determined by the [`Hash`] trait, or its equality, as determined by /// the [`Eq`] trait, changes while it is in the map. This is normally only /// possible through [`Cell`], [`RefCell`], global state, I/O, or unsafe code. -/// The behavior resulting from such a logic error is not specified, but will +/// +/// The behavior resulting from either logic error is not specified, but will /// be encapsulated to the `HashSet` that observed the logic error and not /// result in undefined behavior. This could include panics, incorrect results, /// aborts, memory leaks, and non-termination. diff --git a/library/std/src/error.rs b/library/std/src/error.rs index 7bc3af1793e..375ff2d2450 100644 --- a/library/std/src/error.rs +++ b/library/std/src/error.rs @@ -10,7 +10,7 @@ use crate::fmt::{self, Write}; #[stable(feature = "rust1", since = "1.0.0")] pub use core::error::Error; #[unstable(feature = "error_generic_member_access", issue = "99301")] -pub use core::error::{request_ref, Request}; +pub use core::error::{request_ref, request_value, Request}; mod private { // This is a hack to prevent `type_id` from being overridden by `Error` diff --git a/library/std/src/f32.rs b/library/std/src/f32.rs index a53b8535213..776899dbcfd 100644 --- a/library/std/src/f32.rs +++ b/library/std/src/f32.rs @@ -61,6 +61,7 @@ impl f32 { /// assert_eq!(f.ceil(), 4.0); /// assert_eq!(g.ceil(), 4.0); /// ``` + #[doc(alias = "ceiling")] #[rustc_allow_incoherent_impl] #[must_use = "method returns a new number and does not mutate the original value"] #[stable(feature = "rust1", since = "1.0.0")] @@ -135,6 +136,7 @@ impl f32 { /// assert_eq!(g.trunc(), 3.0); /// assert_eq!(h.trunc(), -3.0); /// ``` + #[doc(alias = "truncate")] #[rustc_allow_incoherent_impl] #[must_use = "method returns a new number and does not mutate the original value"] #[stable(feature = "rust1", since = "1.0.0")] @@ -321,6 +323,7 @@ impl f32 { /// // limitation due to round-off error /// assert!((-f32::EPSILON).rem_euclid(3.0) != 0.0); /// ``` + #[doc(alias = "modulo", alias = "mod")] #[rustc_allow_incoherent_impl] #[must_use = "method returns a new number and does not mutate the original value"] #[inline] @@ -672,6 +675,7 @@ impl f32 { /// /// assert!(abs_difference <= f32::EPSILON); /// ``` + #[doc(alias = "arcsin")] #[rustc_allow_incoherent_impl] #[must_use = "method returns a new number and does not mutate the original value"] #[stable(feature = "rust1", since = "1.0.0")] @@ -694,6 +698,7 @@ impl f32 { /// /// assert!(abs_difference <= f32::EPSILON); /// ``` + #[doc(alias = "arccos")] #[rustc_allow_incoherent_impl] #[must_use = "method returns a new number and does not mutate the original value"] #[stable(feature = "rust1", since = "1.0.0")] @@ -715,6 +720,7 @@ impl f32 { /// /// assert!(abs_difference <= f32::EPSILON); /// ``` + #[doc(alias = "arctan")] #[rustc_allow_incoherent_impl] #[must_use = "method returns a new number and does not mutate the original value"] #[stable(feature = "rust1", since = "1.0.0")] @@ -772,6 +778,7 @@ impl f32 { /// assert!(abs_difference_0 <= f32::EPSILON); /// assert!(abs_difference_1 <= f32::EPSILON); /// ``` + #[doc(alias = "sincos")] #[rustc_allow_incoherent_impl] #[stable(feature = "rust1", since = "1.0.0")] #[inline] @@ -815,6 +822,7 @@ impl f32 { /// /// assert!(abs_difference < 1e-10); /// ``` + #[doc(alias = "log1p")] #[rustc_allow_incoherent_impl] #[must_use = "method returns a new number and does not mutate the original value"] #[stable(feature = "rust1", since = "1.0.0")] @@ -904,6 +912,7 @@ impl f32 { /// /// assert!(abs_difference <= f32::EPSILON); /// ``` + #[doc(alias = "arcsinh")] #[rustc_allow_incoherent_impl] #[must_use = "method returns a new number and does not mutate the original value"] #[stable(feature = "rust1", since = "1.0.0")] @@ -926,6 +935,7 @@ impl f32 { /// /// assert!(abs_difference <= f32::EPSILON); /// ``` + #[doc(alias = "arccosh")] #[rustc_allow_incoherent_impl] #[must_use = "method returns a new number and does not mutate the original value"] #[stable(feature = "rust1", since = "1.0.0")] @@ -950,6 +960,7 @@ impl f32 { /// /// assert!(abs_difference <= 1e-5); /// ``` + #[doc(alias = "arctanh")] #[rustc_allow_incoherent_impl] #[must_use = "method returns a new number and does not mutate the original value"] #[stable(feature = "rust1", since = "1.0.0")] diff --git a/library/std/src/f64.rs b/library/std/src/f64.rs index a1cec22c97a..4f4f5f02471 100644 --- a/library/std/src/f64.rs +++ b/library/std/src/f64.rs @@ -61,6 +61,7 @@ impl f64 { /// assert_eq!(f.ceil(), 4.0); /// assert_eq!(g.ceil(), 4.0); /// ``` + #[doc(alias = "ceiling")] #[rustc_allow_incoherent_impl] #[must_use = "method returns a new number and does not mutate the original value"] #[stable(feature = "rust1", since = "1.0.0")] @@ -135,6 +136,7 @@ impl f64 { /// assert_eq!(g.trunc(), 3.0); /// assert_eq!(h.trunc(), -3.0); /// ``` + #[doc(alias = "truncate")] #[rustc_allow_incoherent_impl] #[must_use = "method returns a new number and does not mutate the original value"] #[stable(feature = "rust1", since = "1.0.0")] @@ -321,6 +323,7 @@ impl f64 { /// // limitation due to round-off error /// assert!((-f64::EPSILON).rem_euclid(3.0) != 0.0); /// ``` + #[doc(alias = "modulo", alias = "mod")] #[rustc_allow_incoherent_impl] #[must_use = "method returns a new number and does not mutate the original value"] #[inline] @@ -672,6 +675,7 @@ impl f64 { /// /// assert!(abs_difference < 1e-10); /// ``` + #[doc(alias = "arcsin")] #[rustc_allow_incoherent_impl] #[must_use = "method returns a new number and does not mutate the original value"] #[stable(feature = "rust1", since = "1.0.0")] @@ -694,6 +698,7 @@ impl f64 { /// /// assert!(abs_difference < 1e-10); /// ``` + #[doc(alias = "arccos")] #[rustc_allow_incoherent_impl] #[must_use = "method returns a new number and does not mutate the original value"] #[stable(feature = "rust1", since = "1.0.0")] @@ -715,6 +720,7 @@ impl f64 { /// /// assert!(abs_difference < 1e-10); /// ``` + #[doc(alias = "arctan")] #[rustc_allow_incoherent_impl] #[must_use = "method returns a new number and does not mutate the original value"] #[stable(feature = "rust1", since = "1.0.0")] @@ -772,6 +778,7 @@ impl f64 { /// assert!(abs_difference_0 < 1e-10); /// assert!(abs_difference_1 < 1e-10); /// ``` + #[doc(alias = "sincos")] #[rustc_allow_incoherent_impl] #[stable(feature = "rust1", since = "1.0.0")] #[inline] @@ -815,6 +822,7 @@ impl f64 { /// /// assert!(abs_difference < 1e-20); /// ``` + #[doc(alias = "log1p")] #[rustc_allow_incoherent_impl] #[must_use = "method returns a new number and does not mutate the original value"] #[stable(feature = "rust1", since = "1.0.0")] @@ -904,6 +912,7 @@ impl f64 { /// /// assert!(abs_difference < 1.0e-10); /// ``` + #[doc(alias = "arcsinh")] #[rustc_allow_incoherent_impl] #[must_use = "method returns a new number and does not mutate the original value"] #[stable(feature = "rust1", since = "1.0.0")] @@ -926,6 +935,7 @@ impl f64 { /// /// assert!(abs_difference < 1.0e-10); /// ``` + #[doc(alias = "arccosh")] #[rustc_allow_incoherent_impl] #[must_use = "method returns a new number and does not mutate the original value"] #[stable(feature = "rust1", since = "1.0.0")] @@ -950,6 +960,7 @@ impl f64 { /// /// assert!(abs_difference < 1.0e-10); /// ``` + #[doc(alias = "arctanh")] #[rustc_allow_incoherent_impl] #[must_use = "method returns a new number and does not mutate the original value"] #[stable(feature = "rust1", since = "1.0.0")] diff --git a/library/std/src/io/mod.rs b/library/std/src/io/mod.rs index 5c1d2d8f46c..71d91f21362 100644 --- a/library/std/src/io/mod.rs +++ b/library/std/src/io/mod.rs @@ -1425,9 +1425,9 @@ pub trait Write { /// /// If this method consumed `n > 0` bytes of `buf` it must return [`Ok(n)`]. /// If the return value is `Ok(n)` then `n` must satisfy `n <= buf.len()`. - /// Unless `buf` is empty, this function shouldn’t return `Ok(0)` since the - /// caller may interpret that as an error. To indicate lack of space, - /// implementors should return [`ErrorKind::StorageFull`] error instead. + /// A return value of `Ok(0)` typically means that the underlying object is + /// no longer able to accept bytes and will likely not be able to in the + /// future as well, or that the buffer provided is empty. /// /// # Errors /// diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs index ac4ce222fba..58684ffe500 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -223,6 +223,7 @@ #![cfg_attr(not(bootstrap), allow(internal_features))] #![deny(rustc::existing_doc_keyword)] #![deny(fuzzy_provenance_casts)] +#![cfg_attr(not(bootstrap), allow(rustdoc::redundant_explicit_links))] // Ensure that std can be linked against panic_abort despite compiled with `-C panic=unwind` #![deny(ffi_unwind_calls)] // std may use features in a platform-specific way diff --git a/library/std/src/sys/solid/os.rs b/library/std/src/sys/solid/os.rs index 717c08434a8..9f4e66d628b 100644 --- a/library/std/src/sys/solid/os.rs +++ b/library/std/src/sys/solid/os.rs @@ -81,6 +81,10 @@ pub fn current_exe() -> io::Result<PathBuf> { static ENV_LOCK: RwLock<()> = RwLock::new(()); +pub fn env_read_lock() -> impl Drop { + ENV_LOCK.read().unwrap_or_else(PoisonError::into_inner) +} + pub struct Env { iter: vec::IntoIter<(OsString, OsString)>, } @@ -134,7 +138,7 @@ pub fn env() -> Env { } unsafe { - let _guard = ENV_LOCK.read(); + let _guard = env_read_lock(); let mut result = Vec::new(); if !environ.is_null() { while !(*environ).is_null() { @@ -168,17 +172,21 @@ pub fn env() -> Env { pub fn getenv(k: &OsStr) -> Option<OsString> { // environment variables with a nul byte can't be set, so their value is // always None as well - let s = run_with_cstr(k.as_bytes(), |k| { - let _guard = ENV_LOCK.read(); - Ok(unsafe { libc::getenv(k.as_ptr()) } as *const libc::c_char) - }) - .ok()?; + run_with_cstr(k.as_bytes(), |k| { + let _guard = env_read_lock(); + let v = unsafe { libc::getenv(k.as_ptr()) } as *const libc::c_char; - if s.is_null() { - None - } else { - Some(OsStringExt::from_vec(unsafe { CStr::from_ptr(s) }.to_bytes().to_vec())) - } + if v.is_null() { + Ok(None) + } else { + // SAFETY: `v` cannot be mutated while executing this line since we've a read lock + let bytes = unsafe { CStr::from_ptr(v) }.to_bytes().to_vec(); + + Ok(Some(OsStringExt::from_vec(bytes))) + } + }) + .ok() + .flatten() } pub fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> { diff --git a/library/std/src/sys/unix/os.rs b/library/std/src/sys/unix/os.rs index 215f63d04f7..57e1a36dace 100644 --- a/library/std/src/sys/unix/os.rs +++ b/library/std/src/sys/unix/os.rs @@ -594,16 +594,21 @@ pub fn env() -> Env { pub fn getenv(k: &OsStr) -> Option<OsString> { // environment variables with a nul byte can't be set, so their value is // always None as well - let s = run_with_cstr(k.as_bytes(), |k| { + run_with_cstr(k.as_bytes(), |k| { let _guard = env_read_lock(); - Ok(unsafe { libc::getenv(k.as_ptr()) } as *const libc::c_char) + let v = unsafe { libc::getenv(k.as_ptr()) } as *const libc::c_char; + + if v.is_null() { + Ok(None) + } else { + // SAFETY: `v` cannot be mutated while executing this line since we've a read lock + let bytes = unsafe { CStr::from_ptr(v) }.to_bytes().to_vec(); + + Ok(Some(OsStringExt::from_vec(bytes))) + } }) - .ok()?; - if s.is_null() { - None - } else { - Some(OsStringExt::from_vec(unsafe { CStr::from_ptr(s) }.to_bytes().to_vec())) - } + .ok() + .flatten() } pub fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> { diff --git a/library/std/src/sys/wasi/os.rs b/library/std/src/sys/wasi/os.rs index e0de284c5e2..d53bddd8e9d 100644 --- a/library/std/src/sys/wasi/os.rs +++ b/library/std/src/sys/wasi/os.rs @@ -225,16 +225,23 @@ pub fn env() -> Env { } pub fn getenv(k: &OsStr) -> Option<OsString> { - let s = run_with_cstr(k.as_bytes(), |k| unsafe { + // environment variables with a nul byte can't be set, so their value is + // always None as well + run_with_cstr(k.as_bytes(), |k| { let _guard = env_read_lock(); - Ok(libc::getenv(k.as_ptr()) as *const libc::c_char) + let v = unsafe { libc::getenv(k.as_ptr()) } as *const libc::c_char; + + if v.is_null() { + Ok(None) + } else { + // SAFETY: `v` cannot be mutated while executing this line since we've a read lock + let bytes = unsafe { CStr::from_ptr(v) }.to_bytes().to_vec(); + + Ok(Some(OsStringExt::from_vec(bytes))) + } }) - .ok()?; - if s.is_null() { - None - } else { - Some(OsStringExt::from_vec(unsafe { CStr::from_ptr(s) }.to_bytes().to_vec())) - } + .ok() + .flatten() } pub fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> { diff --git a/library/std/src/sys/windows/os.rs b/library/std/src/sys/windows/os.rs index 2329426ad1d..58afca088ef 100644 --- a/library/std/src/sys/windows/os.rs +++ b/library/std/src/sys/windows/os.rs @@ -25,10 +25,6 @@ pub fn errno() -> i32 { /// Gets a detailed string description for the given error number. pub fn error_string(mut errnum: i32) -> String { - // This value is calculated from the macro - // MAKELANGID(LANG_SYSTEM_DEFAULT, SUBLANG_SYS_DEFAULT) - let langId = 0x0800 as c::DWORD; - let mut buf = [0 as c::WCHAR; 2048]; unsafe { @@ -56,13 +52,13 @@ pub fn error_string(mut errnum: i32) -> String { flags | c::FORMAT_MESSAGE_FROM_SYSTEM | c::FORMAT_MESSAGE_IGNORE_INSERTS, module, errnum as c::DWORD, - langId, + 0, buf.as_mut_ptr(), buf.len() as c::DWORD, ptr::null(), ) as usize; if res == 0 { - // Sometimes FormatMessageW can fail e.g., system doesn't like langId, + // Sometimes FormatMessageW can fail e.g., system doesn't like 0 as langId, let fm_err = errno(); return format!("OS Error {errnum} (FormatMessageW() returned error {fm_err})"); } diff --git a/library/std/src/sys_common/thread_parking/id.rs b/library/std/src/sys_common/thread_parking/id.rs index 15042fc3bee..04667439660 100644 --- a/library/std/src/sys_common/thread_parking/id.rs +++ b/library/std/src/sys_common/thread_parking/id.rs @@ -56,18 +56,14 @@ impl Parker { self.init_tid(); // Changes NOTIFIED to EMPTY and EMPTY to PARKED. - let mut state = self.state.fetch_sub(1, Acquire).wrapping_sub(1); - if state == PARKED { + let state = self.state.fetch_sub(1, Acquire); + if state == EMPTY { // Loop to guard against spurious wakeups. - while state == PARKED { + // The state must be reset with acquire ordering to ensure that all + // calls to `unpark` synchronize with this thread. + while self.state.compare_exchange(NOTIFIED, EMPTY, Acquire, Relaxed).is_err() { park(self.state.as_ptr().addr()); - state = self.state.load(Acquire); } - - // Since the state change has already been observed with acquire - // ordering, the state can be reset with a relaxed store instead - // of a swap. - self.state.store(EMPTY, Relaxed); } } @@ -78,8 +74,7 @@ impl Parker { if state == PARKED { park_timeout(dur, self.state.as_ptr().addr()); // Swap to ensure that we observe all state changes with acquire - // ordering, even if the state has been changed after the timeout - // occurred. + // ordering. self.state.swap(EMPTY, Acquire); } } diff --git a/library/std/tests/env.rs b/library/std/tests/env.rs index 96b4f372b8b..a1ca85c2145 100644 --- a/library/std/tests/env.rs +++ b/library/std/tests/env.rs @@ -5,6 +5,7 @@ use rand::distributions::{Alphanumeric, DistString}; mod common; use common::test_rng; +use std::thread; #[track_caller] fn make_rand_name() -> OsString { @@ -140,3 +141,22 @@ fn env_home_dir() { } } } + +#[test] // miri shouldn't detect any data race in this fn +#[cfg_attr(any(not(miri), target_os = "emscripten"), ignore)] +fn test_env_get_set_multithreaded() { + let getter = thread::spawn(|| { + for _ in 0..100 { + let _ = var_os("foo"); + } + }); + + let setter = thread::spawn(|| { + for _ in 0..100 { + set_var("foo", "bar"); + } + }); + + let _ = getter.join(); + let _ = setter.join(); +} diff --git a/src/bootstrap/bootstrap.py b/src/bootstrap/bootstrap.py index f44a05a6b28..c5c70f2e18a 100644 --- a/src/bootstrap/bootstrap.py +++ b/src/bootstrap/bootstrap.py @@ -644,7 +644,7 @@ class RustBuild(object): return False # If the user has asked binaries to be patched for Nix, then - # don't check for NixOS or `/lib`. + # don't check for NixOS. if self.get_toml("patch-binaries-for-nix", "build") == "true": return True @@ -652,14 +652,9 @@ class RustBuild(object): # The latter one does not exist on NixOS when using tmpfs as root. try: with open("/etc/os-release", "r") as f: - if not any(ln.strip() in ("ID=nixos", "ID='nixos'", 'ID="nixos"') for ln in f): - return False + return any(ln.strip() in ("ID=nixos", "ID='nixos'", 'ID="nixos"') for ln in f) except FileNotFoundError: return False - if os.path.exists("/lib"): - return False - - return True answer = self._should_fix_bins_and_dylibs = get_answer() if answer: diff --git a/src/bootstrap/download.rs b/src/bootstrap/download.rs index a4135b06e9d..52162bf42ea 100644 --- a/src/bootstrap/download.rs +++ b/src/bootstrap/download.rs @@ -105,7 +105,7 @@ impl Config { matches!(l.trim(), "ID=nixos" | "ID='nixos'" | "ID=\"nixos\"") }), }; - is_nixos && !Path::new("/lib").exists() + is_nixos }); if val { eprintln!("info: You seem to be using Nix."); diff --git a/src/bootstrap/test.rs b/src/bootstrap/test.rs index d0d62db0807..db3b7ffbea4 100644 --- a/src/bootstrap/test.rs +++ b/src/bootstrap/test.rs @@ -630,6 +630,10 @@ impl Step for Miri { cargo.env("MIRI_SYSROOT", &miri_sysroot); cargo.env("MIRI_HOST_SYSROOT", sysroot); cargo.env("MIRI", &miri); + if builder.config.locked_deps { + // enforce lockfiles + cargo.env("CARGO_EXTRA_FLAGS", "--locked"); + } // Set the target. cargo.env("MIRI_TEST_TARGET", target.rustc_target_arg()); @@ -675,6 +679,9 @@ impl Step for Miri { ); cargo.add_rustc_lib_path(builder, compiler); cargo.arg("--").arg("miri").arg("test"); + if builder.config.locked_deps { + cargo.arg("--locked"); + } cargo .arg("--manifest-path") .arg(builder.src.join("src/tools/miri/test-cargo-miri/Cargo.toml")); diff --git a/src/bootstrap/tool.rs b/src/bootstrap/tool.rs index e6d27757ac6..07ff3da6b4a 100644 --- a/src/bootstrap/tool.rs +++ b/src/bootstrap/tool.rs @@ -601,7 +601,7 @@ pub struct RustAnalyzer { } impl RustAnalyzer { - pub const ALLOW_FEATURES: &str = + pub const ALLOW_FEATURES: &'static str = "proc_macro_internals,proc_macro_diagnostic,proc_macro_span,proc_macro_span_shrink"; } diff --git a/src/ci/github-actions/ci.yml b/src/ci/github-actions/ci.yml index 2cc0bfd9db9..89b82d59d31 100644 --- a/src/ci/github-actions/ci.yml +++ b/src/ci/github-actions/ci.yml @@ -504,18 +504,6 @@ jobs: NO_OVERFLOW_CHECKS: 1 <<: *job-macos-xl - - name: dist-x86_64-apple-alt - env: - SCRIPT: ./x.py dist bootstrap --include-default-paths - RUST_CONFIGURE_ARGS: --enable-extended --enable-profiler --set rust.jemalloc --set llvm.ninja=false - RUSTC_RETRY_LINKER_ON_SEGFAULT: 1 - MACOSX_DEPLOYMENT_TARGET: 10.7 - SELECT_XCODE: /Applications/Xcode_13.4.1.app - NO_LLVM_ASSERTIONS: 1 - NO_DEBUG_ASSERTIONS: 1 - NO_OVERFLOW_CHECKS: 1 - <<: *job-macos-xl - - name: x86_64-apple-1 env: &env-x86_64-apple-tests SCRIPT: ./x.py --stage 2 test --skip tests/ui --skip tests/rustdoc --skip tests/run-make-fulldeps diff --git a/src/doc/rustc/src/platform-support.md b/src/doc/rustc/src/platform-support.md index 371ee378d1a..7cc17e11bdb 100644 --- a/src/doc/rustc/src/platform-support.md +++ b/src/doc/rustc/src/platform-support.md @@ -176,7 +176,6 @@ target | std | notes `thumbv8m.base-none-eabi` | * | Bare ARMv8-M Baseline `thumbv8m.main-none-eabi` | * | Bare ARMv8-M Mainline `thumbv8m.main-none-eabihf` | * | Bare ARMv8-M Mainline, hardfloat -[`sparc-unknown-none-elf`](./platform-support/sparc-unknown-none-elf.md) | * | Bare 32-bit SPARC V7+ `wasm32-unknown-emscripten` | ✓ | WebAssembly via Emscripten `wasm32-unknown-unknown` | ✓ | WebAssembly `wasm32-wasi` | ✓ | WebAssembly with WASI @@ -271,8 +270,8 @@ target | std | host | notes `i686-uwp-windows-gnu` | ? | | `i686-uwp-windows-msvc` | ? | | `i686-wrs-vxworks` | ? | | -[`loongarch64-unknown-none`](platform-support/loongarch-none.md) | * | LoongArch64 Bare-metal (LP64D ABI) -[`loongarch64-unknown-none-softfloat`](platform-support/loongarch-none.md) | * | LoongArch64 Bare-metal (LP64S ABI) +[`loongarch64-unknown-none`](platform-support/loongarch-none.md) | * | | LoongArch64 Bare-metal (LP64D ABI) +[`loongarch64-unknown-none-softfloat`](platform-support/loongarch-none.md) | * | | LoongArch64 Bare-metal (LP64S ABI) [`m68k-unknown-linux-gnu`](platform-support/m68k-unknown-linux-gnu.md) | ? | | Motorola 680x0 Linux `mips-unknown-linux-uclibc` | ✓ | | MIPS Linux with uClibc [`mips64-openwrt-linux-musl`](platform-support/mips64-openwrt-linux-musl.md) | ? | | MIPS64 for OpenWrt Linux MUSL @@ -314,6 +313,7 @@ target | std | host | notes [`riscv64-linux-android`](platform-support/android.md) | | | RISC-V 64-bit Android `s390x-unknown-linux-musl` | | | S390x Linux (kernel 3.2, MUSL) `sparc-unknown-linux-gnu` | ✓ | | 32-bit SPARC Linux +[`sparc-unknown-none-elf`](./platform-support/sparc-unknown-none-elf.md) | * | Bare 32-bit SPARC V7+ [`sparc64-unknown-netbsd`](platform-support/netbsd.md) | ✓ | ✓ | NetBSD/sparc64 [`sparc64-unknown-openbsd`](platform-support/openbsd.md) | ✓ | ✓ | OpenBSD/sparc64 `thumbv4t-none-eabi` | * | | Thumb-mode Bare ARMv4T @@ -321,7 +321,7 @@ target | std | host | notes `thumbv7a-pc-windows-msvc` | ? | | `thumbv7a-uwp-windows-msvc` | ✓ | | `thumbv7neon-unknown-linux-musleabihf` | ? | | Thumb2-mode ARMv7-A Linux with NEON, MUSL -[`wasm32-wasi-preview1-threads`](platform-support/wasm32-wasi-preview1-threads.md) | ✓ | WebAssembly with WASI Preview 1 and threads +[`wasm32-wasi-preview1-threads`](platform-support/wasm32-wasi-preview1-threads.md) | ✓ | | WebAssembly with WASI Preview 1 and threads [`wasm64-unknown-unknown`](platform-support/wasm64-unknown-unknown.md) | ? | | WebAssembly `x86_64-apple-ios-macabi` | ✓ | | Apple Catalyst on x86_64 [`x86_64-apple-tvos`](platform-support/apple-tvos.md) | ? | | x86 64-bit tvOS diff --git a/src/doc/rustc/src/platform-support/aarch64-unknown-teeos.md b/src/doc/rustc/src/platform-support/aarch64-unknown-teeos.md index 8bc9381342d..f8cd92f9257 100644 --- a/src/doc/rustc/src/platform-support/aarch64-unknown-teeos.md +++ b/src/doc/rustc/src/platform-support/aarch64-unknown-teeos.md @@ -8,6 +8,7 @@ TEEOS is a mini os run in TrustZone, for trusted/security apps. The kernel of TE It's very small that there is no RwLock, no network, no stdin, and no file system for apps in TEEOS. Some abbreviation: + | Abbreviation | The full text | Description | | ---- | ---- | ---- | | TEE | Trusted Execution Environment | ARM TrustZone divides the system into two worlds/modes -- the secure world/mode and the normal world/mode. TEE is in the secure world/mode. | diff --git a/src/doc/rustdoc/src/how-to-write-documentation.md b/src/doc/rustdoc/src/how-to-write-documentation.md index 1fa9f814476..acab1a93690 100644 --- a/src/doc/rustdoc/src/how-to-write-documentation.md +++ b/src/doc/rustdoc/src/how-to-write-documentation.md @@ -254,6 +254,19 @@ characters: So, no need to manually enter those Unicode characters! +### Adding a warning block + +If you want to make a warning or similar note stand out in the documentation, +you can wrap it like this: + +```md +/// documentation +/// +/// <div class="warning">A big warning!</div> +/// +/// more documentation +``` + [`backtrace`]: https://docs.rs/backtrace/0.3.50/backtrace/ [commonmark markdown specification]: https://commonmark.org/ [commonmark quick reference]: https://commonmark.org/help/ diff --git a/src/doc/rustdoc/src/lints.md b/src/doc/rustdoc/src/lints.md index fd57b079644..f15e6e451e7 100644 --- a/src/doc/rustdoc/src/lints.md +++ b/src/doc/rustdoc/src/lints.md @@ -412,3 +412,37 @@ help: if you meant to use a literal backtick, escape it warning: 1 warning emitted ``` + +## `redundant_explicit_links` + +This lint is **warned by default**. It detects explicit links that are same +as computed automatic links. +This usually means the explicit links is removeable. For example: + +```rust +#![warn(rustdoc::redundant_explicit_links)] // note: unnecessary - warns by default. + +/// add takes 2 [`usize`](usize) and performs addition +/// on them, then returns result. +pub fn add(left: usize, right: usize) -> usize { + left + right +} +``` + +Which will give: + +```text +error: redundant explicit rustdoc link + --> src/lib.rs:3:27 + | +3 | /// add takes 2 [`usize`](usize) and performs addition + | ^^^^^ + | + = note: Explicit link does not affect the original link +note: the lint level is defined here + --> src/lib.rs:1:9 + | +1 | #![deny(rustdoc::redundant_explicit_links)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = help: Remove explicit link instead +``` diff --git a/src/doc/unstable-book/src/language-features/lang-items.md b/src/doc/unstable-book/src/language-features/lang-items.md index 1f3d472c3aa..9e20662fff3 100644 --- a/src/doc/unstable-book/src/language-features/lang-items.md +++ b/src/doc/unstable-book/src/language-features/lang-items.md @@ -9,26 +9,60 @@ functionality that isn't hard-coded into the language, but is implemented in libraries, with a special marker to tell the compiler it exists. The marker is the attribute `#[lang = "..."]` and there are various different values of `...`, i.e. various different 'lang -items'. +items'. Most of them can only be defined once. -For example, `Box` pointers require a lang item for allocation. -A freestanding program that uses the `Box` -sugar for dynamic allocations via `malloc` and `free`: +Lang items are loaded lazily by the compiler; e.g. if one never uses `Box` +then there is no need to define a function for `exchange_malloc`. +`rustc` will emit an error when an item is needed but not found in the current +crate or any that it depends on. + +Some features provided by lang items: + +- overloadable operators via traits: the traits corresponding to the + `==`, `<`, dereferencing (`*`) and `+` (etc.) operators are all + marked with lang items; those specific four are `eq`, `partial_ord`, + `deref`/`deref_mut`, and `add` respectively. +- panicking: the `panic` and `panic_impl` lang items, among others. +- stack unwinding: the lang item `eh_personality` is a function used by the + failure mechanisms of the compiler. This is often mapped to GCC's personality + function (see the [`std` implementation][personality] for more information), + but programs which don't trigger a panic can be assured that this function is + never called. Additionally, a `eh_catch_typeinfo` static is needed for certain + targets which implement Rust panics on top of C++ exceptions. +- the traits in `core::marker` used to indicate types of + various kinds; e.g. lang items `sized`, `sync` and `copy`. +- memory allocation, see below. + +Most lang items are defined by `core`, but if you're trying to build +an executable without the `std` crate, you might run into the need +for lang item definitions. + +[personality]: https://github.com/rust-lang/rust/blob/master/library/std/src/personality/gcc.rs + +## Example: Implementing a `Box` + +`Box` pointers require two lang items: one for the type itself and one for +allocation. A freestanding program that uses the `Box` sugar for dynamic +allocations via `malloc` and `free`: ```rust,ignore (libc-is-finicky) -#![feature(lang_items, start, libc, core_intrinsics, rustc_private, rustc_attrs)] +#![feature(lang_items, start, core_intrinsics, rustc_private, panic_unwind, rustc_attrs)] #![allow(internal_features)] #![no_std] + +extern crate libc; +extern crate unwind; + +use core::ffi::c_void; use core::intrinsics; use core::panic::PanicInfo; use core::ptr::NonNull; -extern crate libc; - +pub struct Global; // the global allocator struct Unique<T>(NonNull<T>); #[lang = "owned_box"] -pub struct Box<T>(Unique<T>); +pub struct Box<T, A = Global>(Unique<T>, A); impl<T> Box<T> { pub fn new(x: T) -> Self { @@ -37,24 +71,26 @@ impl<T> Box<T> { } } +impl<T, A> Drop for Box<T, A> { + fn drop(&mut self) { + unsafe { + libc::free(self.0.0.as_ptr() as *mut c_void); + } + } +} + #[lang = "exchange_malloc"] unsafe fn allocate(size: usize, _align: usize) -> *mut u8 { - let p = libc::malloc(size as libc::size_t) as *mut u8; + let p = libc::malloc(size) as *mut u8; // Check if `malloc` failed: - if p as usize == 0 { + if p.is_null() { intrinsics::abort(); } p } -impl<T> Drop for Box<T> { - fn drop(&mut self) { - libc::free(self.0.0.0 as *mut libc::c_void) - } -} - #[start] fn main(_argc: isize, _argv: *const *const u8) -> isize { let _x = Box::new(1); @@ -62,247 +98,18 @@ fn main(_argc: isize, _argv: *const *const u8) -> isize { 0 } -#[lang = "eh_personality"] extern fn rust_eh_personality() {} -#[lang = "panic_impl"] extern fn rust_begin_panic(_info: &PanicInfo) -> ! { intrinsics::abort() } -#[no_mangle] pub extern fn rust_eh_register_frames () {} -#[no_mangle] pub extern fn rust_eh_unregister_frames () {} -``` - -Note the use of `abort`: the `exchange_malloc` lang item is assumed to -return a valid pointer, and so needs to do the check internally. - -Other features provided by lang items include: - -- overloadable operators via traits: the traits corresponding to the - `==`, `<`, dereferencing (`*`) and `+` (etc.) operators are all - marked with lang items; those specific four are `eq`, `ord`, - `deref`, and `add` respectively. -- stack unwinding and general failure; the `eh_personality`, - `panic` and `panic_bounds_check` lang items. -- the traits in `std::marker` used to indicate types of - various kinds; lang items `send`, `sync` and `copy`. -- the marker types and variance indicators found in - `std::marker`; lang items `covariant_type`, - `contravariant_lifetime`, etc. - -Lang items are loaded lazily by the compiler; e.g. if one never uses -`Box` then there is no need to define a function for `exchange_malloc`. -`rustc` will emit an error when an item is needed -but not found in the current crate or any that it depends on. - -Most lang items are defined by `libcore`, but if you're trying to build -an executable without the standard library, you'll run into the need -for lang items. The rest of this page focuses on this use-case, even though -lang items are a bit broader than that. - -### Using libc - -In order to build a `#[no_std]` executable we will need libc as a dependency. -We can specify this using our `Cargo.toml` file: - -```toml -[dependencies] -libc = { version = "0.2.14", default-features = false } -``` - -Note that the default features have been disabled. This is a critical step - -**the default features of libc include the standard library and so must be -disabled.** - -### Writing an executable without stdlib - -Controlling the entry point is possible in two ways: the `#[start]` attribute, -or overriding the default shim for the C `main` function with your own. - -The function marked `#[start]` is passed the command line parameters -in the same format as C: - -```rust,ignore (libc-is-finicky) -#![feature(lang_items, core_intrinsics, rustc_private)] -#![feature(start)] -#![allow(internal_features)] -#![no_std] -use core::intrinsics; -use core::panic::PanicInfo; - -// Pull in the system libc library for what crt0.o likely requires. -extern crate libc; - -// Entry point for this program. -#[start] -fn start(_argc: isize, _argv: *const *const u8) -> isize { - 0 -} - -// These functions are used by the compiler, but not -// for a bare-bones hello world. These are normally -// provided by libstd. -#[lang = "eh_personality"] -#[no_mangle] -pub extern fn rust_eh_personality() { -} - -#[lang = "panic_impl"] -#[no_mangle] -pub extern fn rust_begin_panic(info: &PanicInfo) -> ! { - unsafe { intrinsics::abort() } -} -``` - -To override the compiler-inserted `main` shim, one has to disable it -with `#![no_main]` and then create the appropriate symbol with the -correct ABI and the correct name, which requires overriding the -compiler's name mangling too: - -```rust,ignore (libc-is-finicky) -#![feature(lang_items, core_intrinsics, rustc_private)] -#![feature(start)] -#![allow(internal_features)] -#![no_std] -#![no_main] -use core::intrinsics; -use core::panic::PanicInfo; - -// Pull in the system libc library for what crt0.o likely requires. -extern crate libc; - -// Entry point for this program. -#[no_mangle] // ensure that this symbol is called `main` in the output -pub extern fn main(_argc: i32, _argv: *const *const u8) -> i32 { - 0 -} - -// These functions are used by the compiler, but not -// for a bare-bones hello world. These are normally -// provided by libstd. #[lang = "eh_personality"] -#[no_mangle] -pub extern fn rust_eh_personality() { -} +fn rust_eh_personality() {} -#[lang = "panic_impl"] -#[no_mangle] -pub extern fn rust_begin_panic(info: &PanicInfo) -> ! { - unsafe { intrinsics::abort() } -} +#[panic_handler] +fn panic_handler(_info: &PanicInfo) -> ! { intrinsics::abort() } ``` -In many cases, you may need to manually link to the `compiler_builtins` crate -when building a `no_std` binary. You may observe this via linker error messages -such as "```undefined reference to `__rust_probestack'```". - -## More about the language items - -The compiler currently makes a few assumptions about symbols which are -available in the executable to call. Normally these functions are provided by -the standard library, but without it you must define your own. These symbols -are called "language items", and they each have an internal name, and then a -signature that an implementation must conform to. - -The first of these functions, `rust_eh_personality`, is used by the failure -mechanisms of the compiler. This is often mapped to GCC's personality function -(see the [libstd implementation][unwind] for more information), but crates -which do not trigger a panic can be assured that this function is never -called. The language item's name is `eh_personality`. - -[unwind]: https://github.com/rust-lang/rust/blob/master/library/panic_unwind/src/gcc.rs - -The second function, `rust_begin_panic`, is also used by the failure mechanisms of the -compiler. When a panic happens, this controls the message that's displayed on -the screen. While the language item's name is `panic_impl`, the symbol name is -`rust_begin_panic`. - -Finally, a `eh_catch_typeinfo` static is needed for certain targets which -implement Rust panics on top of C++ exceptions. +Note the use of `abort`: the `exchange_malloc` lang item is assumed to +return a valid pointer, and so needs to do the check internally. ## List of all language items -This is a list of all language items in Rust along with where they are located in -the source code. +An up-to-date list of all language items can be found [here] in the compiler code. -- Primitives - - `i8`: `libcore/num/mod.rs` - - `i16`: `libcore/num/mod.rs` - - `i32`: `libcore/num/mod.rs` - - `i64`: `libcore/num/mod.rs` - - `i128`: `libcore/num/mod.rs` - - `isize`: `libcore/num/mod.rs` - - `u8`: `libcore/num/mod.rs` - - `u16`: `libcore/num/mod.rs` - - `u32`: `libcore/num/mod.rs` - - `u64`: `libcore/num/mod.rs` - - `u128`: `libcore/num/mod.rs` - - `usize`: `libcore/num/mod.rs` - - `f32`: `libstd/f32.rs` - - `f64`: `libstd/f64.rs` - - `char`: `libcore/char.rs` - - `slice`: `liballoc/slice.rs` - - `str`: `liballoc/str.rs` - - `const_ptr`: `libcore/ptr.rs` - - `mut_ptr`: `libcore/ptr.rs` - - `unsafe_cell`: `libcore/cell.rs` -- Runtime - - `start`: `libstd/rt.rs` - - `eh_personality`: `libpanic_unwind/emcc.rs` (EMCC) - - `eh_personality`: `libpanic_unwind/gcc.rs` (GNU) - - `eh_personality`: `libpanic_unwind/seh.rs` (SEH) - - `eh_catch_typeinfo`: `libpanic_unwind/emcc.rs` (EMCC) - - `panic`: `libcore/panicking.rs` - - `panic_bounds_check`: `libcore/panicking.rs` - - `panic_impl`: `libcore/panicking.rs` - - `panic_impl`: `libstd/panicking.rs` -- Allocations - - `owned_box`: `liballoc/boxed.rs` - - `exchange_malloc`: `liballoc/heap.rs` -- Operands - - `not`: `libcore/ops/bit.rs` - - `bitand`: `libcore/ops/bit.rs` - - `bitor`: `libcore/ops/bit.rs` - - `bitxor`: `libcore/ops/bit.rs` - - `shl`: `libcore/ops/bit.rs` - - `shr`: `libcore/ops/bit.rs` - - `bitand_assign`: `libcore/ops/bit.rs` - - `bitor_assign`: `libcore/ops/bit.rs` - - `bitxor_assign`: `libcore/ops/bit.rs` - - `shl_assign`: `libcore/ops/bit.rs` - - `shr_assign`: `libcore/ops/bit.rs` - - `deref`: `libcore/ops/deref.rs` - - `deref_mut`: `libcore/ops/deref.rs` - - `index`: `libcore/ops/index.rs` - - `index_mut`: `libcore/ops/index.rs` - - `add`: `libcore/ops/arith.rs` - - `sub`: `libcore/ops/arith.rs` - - `mul`: `libcore/ops/arith.rs` - - `div`: `libcore/ops/arith.rs` - - `rem`: `libcore/ops/arith.rs` - - `neg`: `libcore/ops/arith.rs` - - `add_assign`: `libcore/ops/arith.rs` - - `sub_assign`: `libcore/ops/arith.rs` - - `mul_assign`: `libcore/ops/arith.rs` - - `div_assign`: `libcore/ops/arith.rs` - - `rem_assign`: `libcore/ops/arith.rs` - - `eq`: `libcore/cmp.rs` - - `ord`: `libcore/cmp.rs` -- Functions - - `fn`: `libcore/ops/function.rs` - - `fn_mut`: `libcore/ops/function.rs` - - `fn_once`: `libcore/ops/function.rs` - - `generator_state`: `libcore/ops/generator.rs` - - `generator`: `libcore/ops/generator.rs` -- Other - - `coerce_unsized`: `libcore/ops/unsize.rs` - - `drop`: `libcore/ops/drop.rs` - - `drop_in_place`: `libcore/ptr.rs` - - `clone`: `libcore/clone.rs` - - `copy`: `libcore/marker.rs` - - `send`: `libcore/marker.rs` - - `sized`: `libcore/marker.rs` - - `unsize`: `libcore/marker.rs` - - `sync`: `libcore/marker.rs` - - `phantom_data`: `libcore/marker.rs` - - `discriminant_kind`: `libcore/marker.rs` - - `freeze`: `libcore/marker.rs` - - `debug_trait`: `libcore/fmt/mod.rs` - - `non_zero`: `libcore/nonzero.rs` - - `arc`: `liballoc/sync.rs` - - `rc`: `liballoc/rc.rs` +[here]: https://github.com/rust-lang/rust/blob/master/compiler/rustc_hir/src/lang_items.rs diff --git a/src/doc/unstable-book/src/language-features/start.md b/src/doc/unstable-book/src/language-features/start.md new file mode 100644 index 00000000000..09e4875a2e4 --- /dev/null +++ b/src/doc/unstable-book/src/language-features/start.md @@ -0,0 +1,59 @@ +# `start` + +The tracking issue for this feature is: [#29633] + +[#29633]: https://github.com/rust-lang/rust/issues/29633 + +------------------------ + +Allows you to mark a function as the entry point of the executable, which is +necessary in `#![no_std]` environments. + +The function marked `#[start]` is passed the command line parameters in the same +format as the C main function (aside from the integer types being used). +It has to be non-generic and have the following signature: + +```rust,ignore (only-for-syntax-highlight) +# let _: +fn(isize, *const *const u8) -> isize +# ; +``` + +This feature should not be confused with the `start` *lang item* which is +defined by the `std` crate and is written `#[lang = "start"]`. + +## Usage together with the `std` crate + +`#[start]` can be used in combination with the `std` crate, in which case the +normal `main` function (which would get called from the `std` crate) won't be +used as an entry point. +The initialization code in `std` will be skipped this way. + +Example: + +```rust +#![feature(start)] + +#[start] +fn start(_argc: isize, _argv: *const *const u8) -> isize { + 0 +} +``` + +Unwinding the stack past the `#[start]` function is currently considered +Undefined Behavior (for any unwinding implementation): + +```rust,ignore (UB) +#![feature(start)] + +#[start] +fn start(_argc: isize, _argv: *const *const u8) -> isize { + std::panic::catch_unwind(|| { + panic!(); // panic safely gets caught or safely aborts execution + }); + + panic!(); // UB! + + 0 +} +``` diff --git a/src/librustdoc/html/markdown.rs b/src/librustdoc/html/markdown.rs index 0ba2d992392..98cc38a10d4 100644 --- a/src/librustdoc/html/markdown.rs +++ b/src/librustdoc/html/markdown.rs @@ -50,7 +50,7 @@ use crate::html::render::small_url_encode; use crate::html::toc::TocBuilder; use pulldown_cmark::{ - html, BrokenLink, CodeBlockKind, CowStr, Event, LinkType, Options, Parser, Tag, + html, BrokenLink, CodeBlockKind, CowStr, Event, LinkType, OffsetIter, Options, Parser, Tag, }; #[cfg(test)] @@ -1240,6 +1240,7 @@ pub(crate) fn plain_text_summary(md: &str, link_names: &[RenderedLink]) -> Strin pub(crate) struct MarkdownLink { pub kind: LinkType, pub link: String, + pub display_text: Option<String>, pub range: MarkdownLinkRange, } @@ -1263,8 +1264,8 @@ impl MarkdownLinkRange { } } -pub(crate) fn markdown_links<R>( - md: &str, +pub(crate) fn markdown_links<'md, R>( + md: &'md str, preprocess_link: impl Fn(MarkdownLink) -> Option<R>, ) -> Vec<R> { if md.is_empty() { @@ -1375,32 +1376,90 @@ pub(crate) fn markdown_links<R>( MarkdownLinkRange::Destination(range.clone()) }; - Parser::new_with_broken_link_callback( + let mut broken_link_callback = |link: BrokenLink<'md>| Some((link.reference, "".into())); + let mut event_iter = Parser::new_with_broken_link_callback( md, main_body_opts(), - Some(&mut |link: BrokenLink<'_>| Some((link.reference, "".into()))), + Some(&mut broken_link_callback), ) - .into_offset_iter() - .filter_map(|(event, span)| match event { - Event::Start(Tag::Link(link_type, dest, _)) if may_be_doc_link(link_type) => { - let range = match link_type { - // Link is pulled from the link itself. - LinkType::ReferenceUnknown | LinkType::ShortcutUnknown => { - span_for_offset_backward(span, b'[', b']') - } - LinkType::CollapsedUnknown => span_for_offset_forward(span, b'[', b']'), - LinkType::Inline => span_for_offset_backward(span, b'(', b')'), - // Link is pulled from elsewhere in the document. - LinkType::Reference | LinkType::Collapsed | LinkType::Shortcut => { - span_for_link(&dest, span) + .into_offset_iter(); + let mut links = Vec::new(); + + while let Some((event, span)) = event_iter.next() { + match event { + Event::Start(Tag::Link(link_type, dest, _)) if may_be_doc_link(link_type) => { + let range = match link_type { + // Link is pulled from the link itself. + LinkType::ReferenceUnknown | LinkType::ShortcutUnknown => { + span_for_offset_backward(span, b'[', b']') + } + LinkType::CollapsedUnknown => span_for_offset_forward(span, b'[', b']'), + LinkType::Inline => span_for_offset_backward(span, b'(', b')'), + // Link is pulled from elsewhere in the document. + LinkType::Reference | LinkType::Collapsed | LinkType::Shortcut => { + span_for_link(&dest, span) + } + LinkType::Autolink | LinkType::Email => unreachable!(), + }; + + let display_text = if matches!( + link_type, + LinkType::Inline + | LinkType::ReferenceUnknown + | LinkType::Reference + | LinkType::Shortcut + | LinkType::ShortcutUnknown + ) { + collect_link_data(&mut event_iter) + } else { + None + }; + + if let Some(link) = preprocess_link(MarkdownLink { + kind: link_type, + link: dest.into_string(), + display_text, + range, + }) { + links.push(link); } - LinkType::Autolink | LinkType::Email => unreachable!(), - }; - preprocess_link(MarkdownLink { kind: link_type, range, link: dest.into_string() }) + } + _ => {} } - _ => None, - }) - .collect() + } + + links +} + +/// Collects additional data of link. +fn collect_link_data<'input, 'callback>( + event_iter: &mut OffsetIter<'input, 'callback>, +) -> Option<String> { + let mut display_text: Option<String> = None; + let mut append_text = |text: CowStr<'_>| { + if let Some(display_text) = &mut display_text { + display_text.push_str(&text); + } else { + display_text = Some(text.to_string()); + } + }; + + while let Some((event, _span)) = event_iter.next() { + match event { + Event::Text(text) => { + append_text(text); + } + Event::Code(code) => { + append_text(code); + } + Event::End(_) => { + break; + } + _ => {} + } + } + + display_text } #[derive(Debug)] diff --git a/src/librustdoc/html/static/css/rustdoc.css b/src/librustdoc/html/static/css/rustdoc.css index b1de8c1529e..da4da50106a 100644 --- a/src/librustdoc/html/static/css/rustdoc.css +++ b/src/librustdoc/html/static/css/rustdoc.css @@ -270,7 +270,7 @@ ul ul, ol ul, ul ol, ol ol { margin-bottom: .625em; } -p { +p, .docblock > .warning { /* Paragraph spacing at least 1.5 times line spacing per Web Content Accessibility Guidelines. Line-height is 1.5rem, so line spacing is .5rem; .75em is 1.5 times that. https://www.w3.org/WAI/WCAG21/Understanding/visual-presentation.html */ @@ -278,7 +278,7 @@ p { } /* For the last child of a div, the margin will be taken care of by the margin-top of the next item. */ -p:last-child { +p:last-child, .docblock > .warning:last-child { margin: 0; } @@ -1096,7 +1096,7 @@ pre.rust .doccomment { } .example-wrap.ignore .tooltip { - color: var(--codeblock-ignore-color); + color: var(--codeblock-ignore-color); } .example-wrap.compile_fail:hover .tooltip, @@ -1124,6 +1124,26 @@ pre.rust .doccomment { font-size: 1.25rem; } +/* This class only exists for users who want to draw attention to a particular element in their +documentation. */ +.content .docblock .warning { + border-left: 2px solid var(--warning-border-color); + padding: 14px; + position: relative; + /* The "!important" part is required because the rule is otherwise overruled in this CSS + selector: ".docblock > :not(.more-examples-toggle):not(.example-wrap)" */ + overflow-x: visible !important; +} +.content .docblock .warning::before { + color: var(--warning-border-color); + content: "ⓘ"; + position: absolute; + left: -25px; + top: 5px; + font-weight: bold; + font-size: 1.25rem; +} + a.test-arrow { visibility: hidden; position: absolute; diff --git a/src/librustdoc/html/static/css/themes/ayu.css b/src/librustdoc/html/static/css/themes/ayu.css index d8dae51eb1b..c81a80eeca0 100644 --- a/src/librustdoc/html/static/css/themes/ayu.css +++ b/src/librustdoc/html/static/css/themes/ayu.css @@ -31,6 +31,7 @@ Original by Dempfi (https://github.com/dempfi/ayu) --codeblock-error-color: rgba(255, 0, 0, .5); --codeblock-ignore-hover-color: rgb(255, 142, 0); --codeblock-ignore-color: rgba(255, 142, 0, .6); + --warning-border-color: rgb(255, 142, 0); --type-link-color: #ffa0a5; --trait-link-color: #39afd7; --assoc-item-link-color: #39afd7; diff --git a/src/librustdoc/html/static/css/themes/dark.css b/src/librustdoc/html/static/css/themes/dark.css index 2b302988734..0f8b1dc24a6 100644 --- a/src/librustdoc/html/static/css/themes/dark.css +++ b/src/librustdoc/html/static/css/themes/dark.css @@ -26,6 +26,7 @@ --codeblock-error-color: rgba(255, 0, 0, .5); --codeblock-ignore-hover-color: rgb(255, 142, 0); --codeblock-ignore-color: rgba(255, 142, 0, .6); + --warning-border-color: rgb(255, 142, 0); --type-link-color: #2dbfb8; --trait-link-color: #b78cf2; --assoc-item-link-color: #d2991d; diff --git a/src/librustdoc/html/static/css/themes/light.css b/src/librustdoc/html/static/css/themes/light.css index 56fd8cbef12..39ea44a1165 100644 --- a/src/librustdoc/html/static/css/themes/light.css +++ b/src/librustdoc/html/static/css/themes/light.css @@ -26,6 +26,7 @@ --codeblock-error-color: rgba(255, 0, 0, .5); --codeblock-ignore-hover-color: rgb(255, 142, 0); --codeblock-ignore-color: rgba(255, 142, 0, .6); + --warning-border-color: rgb(255, 142, 0); --type-link-color: #ad378a; --trait-link-color: #6e4fc9; --assoc-item-link-color: #3873ad; diff --git a/src/librustdoc/lint.rs b/src/librustdoc/lint.rs index 749c1ff51bf..d45040e348a 100644 --- a/src/librustdoc/lint.rs +++ b/src/librustdoc/lint.rs @@ -185,6 +185,17 @@ declare_rustdoc_lint! { "detects unescaped backticks in doc comments" } +declare_rustdoc_lint! { + /// This lint is **warned by default**. It detects explicit links that are same + /// as computed automatic links. This usually means the explicit links is removeable. + /// This is a `rustdoc` only lint, see the documentation in the [rustdoc book]. + /// + /// [rustdoc book]: ../../../rustdoc/lints.html#redundant_explicit_links + REDUNDANT_EXPLICIT_LINKS, + Warn, + "detects redundant explicit links in doc comments" +} + pub(crate) static RUSTDOC_LINTS: Lazy<Vec<&'static Lint>> = Lazy::new(|| { vec![ BROKEN_INTRA_DOC_LINKS, @@ -197,6 +208,7 @@ pub(crate) static RUSTDOC_LINTS: Lazy<Vec<&'static Lint>> = Lazy::new(|| { BARE_URLS, MISSING_CRATE_LEVEL_DOCS, UNESCAPED_BACKTICKS, + REDUNDANT_EXPLICIT_LINKS, ] }); diff --git a/src/librustdoc/passes/collect_intra_doc_links.rs b/src/librustdoc/passes/collect_intra_doc_links.rs index 26ff64f06a3..7b0a7a90d31 100644 --- a/src/librustdoc/passes/collect_intra_doc_links.rs +++ b/src/librustdoc/passes/collect_intra_doc_links.rs @@ -994,7 +994,7 @@ impl LinkCollector<'_, '_> { _ => find_nearest_parent_module(self.cx.tcx, item_id).unwrap(), }; for md_link in preprocessed_markdown_links(&doc) { - let link = self.resolve_link(item, item_id, module_id, &doc, &md_link); + let link = self.resolve_link(&doc, item, item_id, module_id, &md_link); if let Some(link) = link { self.cx.cache.intra_doc_links.entry(item.item_id).or_default().insert(link); } @@ -1007,13 +1007,12 @@ impl LinkCollector<'_, '_> { /// FIXME(jynelson): this is way too many arguments fn resolve_link( &mut self, + dox: &String, item: &Item, item_id: DefId, module_id: DefId, - dox: &str, - link: &PreprocessedMarkdownLink, + PreprocessedMarkdownLink(pp_link, ori_link): &PreprocessedMarkdownLink, ) -> Option<ItemLink> { - let PreprocessedMarkdownLink(pp_link, ori_link) = link; trace!("considering link '{}'", ori_link.link); let diag_info = DiagnosticInfo { @@ -1022,7 +1021,6 @@ impl LinkCollector<'_, '_> { ori_link: &ori_link.link, link_range: ori_link.range.clone(), }; - let PreprocessingInfo { path_str, disambiguator, extra_fragment, link_text } = pp_link.as_ref().map_err(|err| err.report(self.cx, diag_info.clone())).ok()?; let disambiguator = *disambiguator; @@ -1040,8 +1038,24 @@ impl LinkCollector<'_, '_> { // resolutions are cached, for other links we want to report an error every // time so they are not cached. matches!(ori_link.kind, LinkType::Reference | LinkType::Shortcut), + false, )?; + if ori_link.display_text.is_some() { + self.resolve_display_text( + path_str, + ResolutionInfo { + item_id, + module_id, + dis: disambiguator, + path_str: ori_link.display_text.clone()?.into_boxed_str(), + extra_fragment: extra_fragment.clone(), + }, + &ori_link, + &diag_info, + ); + } + // Check for a primitive which might conflict with a module // Report the ambiguity and require that the user specify which one they meant. // FIXME: could there ever be a primitive not in the type namespace? @@ -1221,6 +1235,9 @@ impl LinkCollector<'_, '_> { // If errors are cached then they are only reported on first occurrence // which we want in some cases but not in others. cache_errors: bool, + // If this call is intended to be recoverable, then pass true to silence. + // This is only recoverable when path is failed to resolved. + recoverable: bool, ) -> Option<(Res, Option<UrlFragment>)> { if let Some(res) = self.visited_links.get(&key) { if res.is_some() || cache_errors { @@ -1228,7 +1245,7 @@ impl LinkCollector<'_, '_> { } } - let mut candidates = self.resolve_with_disambiguator(&key, diag.clone()); + let mut candidates = self.resolve_with_disambiguator(&key, diag.clone(), recoverable); // FIXME: it would be nice to check that the feature gate was enabled in the original crate, not just ignore it altogether. // However I'm not sure how to check that across crates. @@ -1279,6 +1296,9 @@ impl LinkCollector<'_, '_> { &mut self, key: &ResolutionInfo, diag: DiagnosticInfo<'_>, + // If this call is intended to be recoverable, then pass true to silence. + // This is only recoverable when path is failed to resolved. + recoverable: bool, ) -> Vec<(Res, Option<DefId>)> { let disambiguator = key.dis; let path_str = &key.path_str; @@ -1308,7 +1328,9 @@ impl LinkCollector<'_, '_> { } } } - resolution_failure(self, diag, path_str, disambiguator, smallvec![err]); + if !recoverable { + resolution_failure(self, diag, path_str, disambiguator, smallvec![err]); + } return vec![]; } } @@ -1345,13 +1367,15 @@ impl LinkCollector<'_, '_> { .fold(0, |acc, res| if let Ok(res) = res { acc + res.len() } else { acc }); if len == 0 { - resolution_failure( - self, - diag, - path_str, - disambiguator, - candidates.into_iter().filter_map(|res| res.err()).collect(), - ); + if !recoverable { + resolution_failure( + self, + diag, + path_str, + disambiguator, + candidates.into_iter().filter_map(|res| res.err()).collect(), + ); + } return vec![]; } else if len == 1 { candidates.into_iter().filter_map(|res| res.ok()).flatten().collect::<Vec<_>>() @@ -1372,6 +1396,58 @@ impl LinkCollector<'_, '_> { } } } + + /// Resolve display text if the provided link has separated parts of links. + /// + /// For example: + /// Inline link `[display_text](dest_link)` and reference link `[display_text][reference_link]` has + /// separated parts of links. + fn resolve_display_text( + &mut self, + explicit_link: &Box<str>, + display_res_info: ResolutionInfo, + ori_link: &MarkdownLink, + diag_info: &DiagnosticInfo<'_>, + ) { + // Check if explicit resolution's path is same as resolution of original link's display text path, see + // tests/rustdoc-ui/lint/redundant_explicit_links.rs for more cases. + // + // To avoid disambiguator from panicking, we check if display text path is possible to be disambiguated + // into explicit path. + if !matches!( + ori_link.kind, + LinkType::Inline | LinkType::Reference | LinkType::ReferenceUnknown + ) { + return; + } + + // Algorithm to check if display text could possibly be the explicit link: + // + // Consider 2 links which are display text and explicit link, pick the shorter + // one as symbol and longer one as full qualified path, and tries to match symbol + // to the full qualified path's last symbol. + // + // Otherwise, check if 2 links are same, if so, skip the resolve process. + // + // Notice that this algorithm is passive, might possibly miss actual redudant cases. + let explicit_link = explicit_link.to_string(); + let display_text = ori_link.display_text.as_ref().unwrap(); + + if display_text.len() == explicit_link.len() { + // Whether they are same or not, skip the resolve process. + return; + } + + if explicit_link.ends_with(&display_text[..]) || display_text.ends_with(&explicit_link[..]) + { + self.resolve_with_disambiguator_cached( + display_res_info, + diag_info.clone(), // this struct should really be Copy, but Range is not :( + false, + true, + ); + } + } } /// Get the section of a link between the backticks, diff --git a/src/librustdoc/passes/lint.rs b/src/librustdoc/passes/lint.rs index e653207b9b6..c6d5b7bd346 100644 --- a/src/librustdoc/passes/lint.rs +++ b/src/librustdoc/passes/lint.rs @@ -4,6 +4,7 @@ mod bare_urls; mod check_code_block_syntax; mod html_tags; +mod redundant_explicit_links; mod unescaped_backticks; use super::Pass; @@ -29,6 +30,7 @@ impl<'a, 'tcx> DocVisitor for Linter<'a, 'tcx> { check_code_block_syntax::visit_item(self.cx, item); html_tags::visit_item(self.cx, item); unescaped_backticks::visit_item(self.cx, item); + redundant_explicit_links::visit_item(self.cx, item); self.visit_item_recur(item) } diff --git a/src/librustdoc/passes/lint/redundant_explicit_links.rs b/src/librustdoc/passes/lint/redundant_explicit_links.rs new file mode 100644 index 00000000000..67cd2cc9732 --- /dev/null +++ b/src/librustdoc/passes/lint/redundant_explicit_links.rs @@ -0,0 +1,347 @@ +use std::ops::Range; + +use pulldown_cmark::{BrokenLink, CowStr, Event, LinkType, OffsetIter, Parser, Tag}; +use rustc_ast::NodeId; +use rustc_errors::SuggestionStyle; +use rustc_hir::def::{DefKind, DocLinkResMap, Namespace, Res}; +use rustc_hir::HirId; +use rustc_lint_defs::Applicability; +use rustc_span::Symbol; + +use crate::clean::utils::find_nearest_parent_module; +use crate::clean::utils::inherits_doc_hidden; +use crate::clean::Item; +use crate::core::DocContext; +use crate::html::markdown::main_body_opts; +use crate::passes::source_span_for_markdown_range; + +#[derive(Debug)] +struct LinkData { + resolvable_link: Option<String>, + resolvable_link_range: Option<Range<usize>>, + display_link: String, +} + +pub(crate) fn visit_item(cx: &DocContext<'_>, item: &Item) { + let Some(hir_id) = DocContext::as_local_hir_id(cx.tcx, item.item_id) else { + // If non-local, no need to check anything. + return; + }; + + let doc = item.doc_value(); + if doc.is_empty() { + return; + } + + if item.link_names(&cx.cache).is_empty() { + // If there's no link names in this item, + // then we skip resolution querying to + // avoid from panicking. + return; + } + + let Some(item_id) = item.def_id() else { + return; + }; + let Some(local_item_id) = item_id.as_local() else { + return; + }; + + let is_hidden = !cx.render_options.document_hidden + && (item.is_doc_hidden() || inherits_doc_hidden(cx.tcx, local_item_id, None)); + if is_hidden { + return; + } + let is_private = !cx.render_options.document_private + && !cx.cache.effective_visibilities.is_directly_public(cx.tcx, item_id); + if is_private { + return; + } + + check_redundant_explicit_link(cx, item, hir_id, &doc); +} + +fn check_redundant_explicit_link<'md>( + cx: &DocContext<'_>, + item: &Item, + hir_id: HirId, + doc: &'md str, +) -> Option<()> { + let mut broken_line_callback = |link: BrokenLink<'md>| Some((link.reference, "".into())); + let mut offset_iter = Parser::new_with_broken_link_callback( + &doc, + main_body_opts(), + Some(&mut broken_line_callback), + ) + .into_offset_iter(); + let item_id = item.def_id()?; + let module_id = match cx.tcx.def_kind(item_id) { + DefKind::Mod if item.inner_docs(cx.tcx) => item_id, + _ => find_nearest_parent_module(cx.tcx, item_id).unwrap(), + }; + let resolutions = cx.tcx.doc_link_resolutions(module_id); + + while let Some((event, link_range)) = offset_iter.next() { + match event { + Event::Start(Tag::Link(link_type, dest, _)) => { + let link_data = collect_link_data(&mut offset_iter); + + if let Some(resolvable_link) = link_data.resolvable_link.as_ref() { + if &link_data.display_link.replace("`", "") != resolvable_link { + // Skips if display link does not match to actual + // resolvable link, usually happens if display link + // has several segments, e.g. + // [this is just an `Option`](Option) + continue; + } + } + + let explicit_link = dest.to_string(); + let display_link = link_data.resolvable_link.clone()?; + + if explicit_link.ends_with(&display_link) || display_link.ends_with(&explicit_link) + { + match link_type { + LinkType::Inline | LinkType::ReferenceUnknown => { + check_inline_or_reference_unknown_redundancy( + cx, + item, + hir_id, + doc, + resolutions, + link_range, + dest.to_string(), + link_data, + if link_type == LinkType::Inline { + (b'(', b')') + } else { + (b'[', b']') + }, + ); + } + LinkType::Reference => { + check_reference_redundancy( + cx, + item, + hir_id, + doc, + resolutions, + link_range, + &dest, + link_data, + ); + } + _ => {} + } + } + } + _ => {} + } + } + + None +} + +/// FIXME(ChAoSUnItY): Too many arguments. +fn check_inline_or_reference_unknown_redundancy( + cx: &DocContext<'_>, + item: &Item, + hir_id: HirId, + doc: &str, + resolutions: &DocLinkResMap, + link_range: Range<usize>, + dest: String, + link_data: LinkData, + (open, close): (u8, u8), +) -> Option<()> { + let (resolvable_link, resolvable_link_range) = + (&link_data.resolvable_link?, &link_data.resolvable_link_range?); + let (dest_res, display_res) = + (find_resolution(resolutions, &dest)?, find_resolution(resolutions, resolvable_link)?); + + if dest_res == display_res { + let link_span = source_span_for_markdown_range(cx.tcx, &doc, &link_range, &item.attrs) + .unwrap_or(item.attr_span(cx.tcx)); + let explicit_span = source_span_for_markdown_range( + cx.tcx, + &doc, + &offset_explicit_range(doc, link_range, open, close), + &item.attrs, + )?; + let display_span = + source_span_for_markdown_range(cx.tcx, &doc, &resolvable_link_range, &item.attrs)?; + + cx.tcx.struct_span_lint_hir(crate::lint::REDUNDANT_EXPLICIT_LINKS, hir_id, explicit_span, "redundant explicit link target", |lint| { + lint.span_label(explicit_span, "explicit target is redundant") + .span_label(display_span, "because label contains path that resolves to same destination") + .note("when a link's destination is not specified,\nthe label is used to resolve intra-doc links") + .span_suggestion_with_style(link_span, "remove explicit link target", format!("[{}]", link_data.display_link), Applicability::MaybeIncorrect, SuggestionStyle::ShowAlways); + + lint + }); + } + + None +} + +/// FIXME(ChAoSUnItY): Too many arguments. +fn check_reference_redundancy( + cx: &DocContext<'_>, + item: &Item, + hir_id: HirId, + doc: &str, + resolutions: &DocLinkResMap, + link_range: Range<usize>, + dest: &CowStr<'_>, + link_data: LinkData, +) -> Option<()> { + let (resolvable_link, resolvable_link_range) = + (&link_data.resolvable_link?, &link_data.resolvable_link_range?); + let (dest_res, display_res) = + (find_resolution(resolutions, &dest)?, find_resolution(resolutions, resolvable_link)?); + + if dest_res == display_res { + let link_span = source_span_for_markdown_range(cx.tcx, &doc, &link_range, &item.attrs) + .unwrap_or(item.attr_span(cx.tcx)); + let explicit_span = source_span_for_markdown_range( + cx.tcx, + &doc, + &offset_explicit_range(doc, link_range.clone(), b'[', b']'), + &item.attrs, + )?; + let display_span = + source_span_for_markdown_range(cx.tcx, &doc, &resolvable_link_range, &item.attrs)?; + let def_span = source_span_for_markdown_range( + cx.tcx, + &doc, + &offset_reference_def_range(doc, dest, link_range), + &item.attrs, + )?; + + cx.tcx.struct_span_lint_hir(crate::lint::REDUNDANT_EXPLICIT_LINKS, hir_id, explicit_span, "redundant explicit link target", |lint| { + lint.span_label(explicit_span, "explicit target is redundant") + .span_label(display_span, "because label contains path that resolves to same destination") + .span_note(def_span, "referenced explicit link target defined here") + .note("when a link's destination is not specified,\nthe label is used to resolve intra-doc links") + .span_suggestion_with_style(link_span, "remove explicit link target", format!("[{}]", link_data.display_link), Applicability::MaybeIncorrect, SuggestionStyle::ShowAlways); + + lint + }); + } + + None +} + +fn find_resolution(resolutions: &DocLinkResMap, path: &str) -> Option<Res<NodeId>> { + [Namespace::TypeNS, Namespace::ValueNS, Namespace::MacroNS] + .into_iter() + .find_map(|ns| resolutions.get(&(Symbol::intern(path), ns)).copied().flatten()) +} + +/// Collects all neccessary data of link. +fn collect_link_data(offset_iter: &mut OffsetIter<'_, '_>) -> LinkData { + let mut resolvable_link = None; + let mut resolvable_link_range = None; + let mut display_link = String::new(); + + while let Some((event, range)) = offset_iter.next() { + match event { + Event::Text(code) => { + let code = code.to_string(); + display_link.push_str(&code); + resolvable_link = Some(code); + resolvable_link_range = Some(range); + } + Event::Code(code) => { + let code = code.to_string(); + display_link.push('`'); + display_link.push_str(&code); + display_link.push('`'); + resolvable_link = Some(code); + resolvable_link_range = Some(range); + } + Event::End(_) => { + break; + } + _ => {} + } + } + + LinkData { resolvable_link, resolvable_link_range, display_link } +} + +fn offset_explicit_range(md: &str, link_range: Range<usize>, open: u8, close: u8) -> Range<usize> { + let mut open_brace = !0; + let mut close_brace = !0; + for (i, b) in md.as_bytes()[link_range.clone()].iter().copied().enumerate().rev() { + let i = i + link_range.start; + if b == close { + close_brace = i; + break; + } + } + + if close_brace < link_range.start || close_brace >= link_range.end { + return link_range; + } + + let mut nesting = 1; + + for (i, b) in md.as_bytes()[link_range.start..close_brace].iter().copied().enumerate().rev() { + let i = i + link_range.start; + if b == close { + nesting += 1; + } + if b == open { + nesting -= 1; + } + if nesting == 0 { + open_brace = i; + break; + } + } + + assert!(open_brace != close_brace); + + if open_brace < link_range.start || open_brace >= link_range.end { + return link_range; + } + // do not actually include braces in the span + (open_brace + 1)..close_brace +} + +fn offset_reference_def_range( + md: &str, + dest: &CowStr<'_>, + link_range: Range<usize>, +) -> Range<usize> { + // For diagnostics, we want to underline the link's definition but `span` will point at + // where the link is used. This is a problem for reference-style links, where the definition + // is separate from the usage. + + match dest { + // `Borrowed` variant means the string (the link's destination) may come directly from + // the markdown text and we can locate the original link destination. + // NOTE: LinkReplacer also provides `Borrowed` but possibly from other sources, + // so `locate()` can fall back to use `span`. + CowStr::Borrowed(s) => { + // FIXME: remove this function once pulldown_cmark can provide spans for link definitions. + unsafe { + let s_start = dest.as_ptr(); + let s_end = s_start.add(s.len()); + let md_start = md.as_ptr(); + let md_end = md_start.add(md.len()); + if md_start <= s_start && s_end <= md_end { + let start = s_start.offset_from(md_start) as usize; + let end = s_end.offset_from(md_start) as usize; + start..end + } else { + link_range + } + } + } + + // For anything else, we can only use the provided range. + CowStr::Boxed(_) | CowStr::Inlined(_) => link_range, + } +} diff --git a/src/tools/cargo b/src/tools/cargo -Subproject 7c3904d6c3ed54e8a413023519b55a536ad44d5 +Subproject 2cc50bc0b63ad20da193e002ba11d391af0104b diff --git a/src/tools/clippy/clippy_lints/src/needless_pass_by_ref_mut.rs b/src/tools/clippy/clippy_lints/src/needless_pass_by_ref_mut.rs index c634de960d1..7b00eabf97b 100644 --- a/src/tools/clippy/clippy_lints/src/needless_pass_by_ref_mut.rs +++ b/src/tools/clippy/clippy_lints/src/needless_pass_by_ref_mut.rs @@ -5,14 +5,16 @@ use clippy_utils::{get_parent_node, inherits_cfg, is_from_proc_macro, is_self}; use rustc_data_structures::fx::{FxHashSet, FxIndexMap}; use rustc_errors::Applicability; use rustc_hir::intravisit::{walk_qpath, FnKind, Visitor}; -use rustc_hir::{Body, ExprKind, FnDecl, HirId, HirIdMap, HirIdSet, Impl, ItemKind, Mutability, Node, PatKind, QPath}; +use rustc_hir::{ + Body, Closure, Expr, ExprKind, FnDecl, HirId, HirIdMap, HirIdSet, Impl, ItemKind, Mutability, Node, PatKind, QPath, +}; use rustc_hir_typeck::expr_use_visitor as euv; use rustc_infer::infer::TyCtxtInferExt; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::hir::map::associated_body; use rustc_middle::hir::nested_filter::OnlyBodies; use rustc_middle::mir::FakeReadCause; -use rustc_middle::ty::{self, Ty, UpvarId, UpvarPath}; +use rustc_middle::ty::{self, Ty, TyCtxt, UpvarId, UpvarPath}; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::def_id::LocalDefId; use rustc_span::symbol::kw; @@ -147,22 +149,36 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByRefMut<'tcx> { // Collect variables mutably used and spans which will need dereferencings from the // function body. let MutablyUsedVariablesCtxt { mutably_used_vars, .. } = { - let mut ctx = MutablyUsedVariablesCtxt::default(); + let mut ctx = MutablyUsedVariablesCtxt { + mutably_used_vars: HirIdSet::default(), + prev_bind: None, + prev_move_to_closure: HirIdSet::default(), + aliases: HirIdMap::default(), + async_closures: FxHashSet::default(), + tcx: cx.tcx, + }; let infcx = cx.tcx.infer_ctxt().build(); euv::ExprUseVisitor::new(&mut ctx, &infcx, fn_def_id, cx.param_env, cx.typeck_results()).consume_body(body); if is_async { - let closures = ctx.async_closures.clone(); - let hir = cx.tcx.hir(); - for closure in closures { - ctx.prev_bind = None; - ctx.prev_move_to_closure.clear(); - if let Some(body) = hir - .find_by_def_id(closure) - .and_then(associated_body) - .map(|(_, body_id)| hir.body(body_id)) - { - euv::ExprUseVisitor::new(&mut ctx, &infcx, closure, cx.param_env, cx.typeck_results()) - .consume_body(body); + let mut checked_closures = FxHashSet::default(); + while !ctx.async_closures.is_empty() { + let closures = ctx.async_closures.clone(); + ctx.async_closures.clear(); + let hir = cx.tcx.hir(); + for closure in closures { + if !checked_closures.insert(closure) { + continue; + } + ctx.prev_bind = None; + ctx.prev_move_to_closure.clear(); + if let Some(body) = hir + .find_by_def_id(closure) + .and_then(associated_body) + .map(|(_, body_id)| hir.body(body_id)) + { + euv::ExprUseVisitor::new(&mut ctx, &infcx, closure, cx.param_env, cx.typeck_results()) + .consume_body(body); + } } } } @@ -225,16 +241,16 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByRefMut<'tcx> { } } -#[derive(Default)] -struct MutablyUsedVariablesCtxt { +struct MutablyUsedVariablesCtxt<'tcx> { mutably_used_vars: HirIdSet, prev_bind: Option<HirId>, prev_move_to_closure: HirIdSet, aliases: HirIdMap<HirId>, async_closures: FxHashSet<LocalDefId>, + tcx: TyCtxt<'tcx>, } -impl MutablyUsedVariablesCtxt { +impl<'tcx> MutablyUsedVariablesCtxt<'tcx> { fn add_mutably_used_var(&mut self, mut used_id: HirId) { while let Some(id) = self.aliases.get(&used_id) { self.mutably_used_vars.insert(used_id); @@ -242,9 +258,27 @@ impl MutablyUsedVariablesCtxt { } self.mutably_used_vars.insert(used_id); } + + fn would_be_alias_cycle(&self, alias: HirId, mut target: HirId) -> bool { + while let Some(id) = self.aliases.get(&target) { + if *id == alias { + return true; + } + target = *id; + } + false + } + + fn add_alias(&mut self, alias: HirId, target: HirId) { + // This is to prevent alias loop. + if alias == target || self.would_be_alias_cycle(alias, target) { + return; + } + self.aliases.insert(alias, target); + } } -impl<'tcx> euv::Delegate<'tcx> for MutablyUsedVariablesCtxt { +impl<'tcx> euv::Delegate<'tcx> for MutablyUsedVariablesCtxt<'tcx> { fn consume(&mut self, cmt: &euv::PlaceWithHirId<'tcx>, _id: HirId) { if let euv::Place { base: @@ -259,7 +293,7 @@ impl<'tcx> euv::Delegate<'tcx> for MutablyUsedVariablesCtxt { { if let Some(bind_id) = self.prev_bind.take() { if bind_id != *vid { - self.aliases.insert(bind_id, *vid); + self.add_alias(bind_id, *vid); } } else if !self.prev_move_to_closure.contains(vid) && matches!(base_ty.ref_mutability(), Some(Mutability::Mut)) @@ -289,6 +323,25 @@ impl<'tcx> euv::Delegate<'tcx> for MutablyUsedVariablesCtxt { { self.add_mutably_used_var(*vid); } + } else if borrow == ty::ImmBorrow { + // If there is an `async block`, it'll contain a call to a closure which we need to + // go into to ensure all "mutate" checks are found. + if let Node::Expr(Expr { + kind: + ExprKind::Call( + _, + [ + Expr { + kind: ExprKind::Closure(Closure { def_id, .. }), + .. + }, + ], + ), + .. + }) = self.tcx.hir().get(cmt.hir_id) + { + self.async_closures.insert(*def_id); + } } } @@ -296,7 +349,12 @@ impl<'tcx> euv::Delegate<'tcx> for MutablyUsedVariablesCtxt { self.prev_bind = None; if let euv::Place { projections, - base: euv::PlaceBase::Local(vid), + base: + euv::PlaceBase::Local(vid) + | euv::PlaceBase::Upvar(UpvarId { + var_path: UpvarPath { hir_id: vid }, + .. + }), .. } = &cmt.place { @@ -329,8 +387,9 @@ impl<'tcx> euv::Delegate<'tcx> for MutablyUsedVariablesCtxt { // Seems like we are inside an async function. We need to store the closure `DefId` // to go through it afterwards. self.async_closures.insert(inner); - self.aliases.insert(cmt.hir_id, *vid); + self.add_alias(cmt.hir_id, *vid); self.prev_move_to_closure.insert(*vid); + self.prev_bind = None; } } } diff --git a/src/tools/clippy/clippy_lints/src/useless_conversion.rs b/src/tools/clippy/clippy_lints/src/useless_conversion.rs index bd4dc07a42b..5ac4f0aa46c 100644 --- a/src/tools/clippy/clippy_lints/src/useless_conversion.rs +++ b/src/tools/clippy/clippy_lints/src/useless_conversion.rs @@ -5,6 +5,7 @@ use clippy_utils::ty::{is_copy, is_type_diagnostic_item, same_type_and_consts}; use clippy_utils::{get_parent_expr, is_trait_method, is_ty_alias, match_def_path, path_to_local, paths}; use if_chain::if_chain; use rustc_errors::Applicability; +use rustc_hir::def::DefKind; use rustc_hir::def_id::DefId; use rustc_hir::{BindingAnnotation, Expr, ExprKind, HirId, MatchSource, Node, PatKind}; use rustc_lint::{LateContext, LateLintPass}; @@ -39,6 +40,7 @@ declare_clippy_lint! { #[derive(Default)] pub struct UselessConversion { try_desugar_arm: Vec<HirId>, + expn_depth: u32, } impl_lint_pass!(UselessConversion => [USELESS_CONVERSION]); @@ -105,6 +107,7 @@ fn into_iter_deep_call<'hir>(cx: &LateContext<'_>, mut expr: &'hir Expr<'hir>) - impl<'tcx> LateLintPass<'tcx> for UselessConversion { fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) { if e.span.from_expansion() { + self.expn_depth += 1; return; } @@ -150,9 +153,14 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion { { if let Some(parent) = get_parent_expr(cx, e) { let parent_fn = match parent.kind { - ExprKind::Call(recv, args) if let ExprKind::Path(ref qpath) = recv.kind => { - cx.qpath_res(qpath, recv.hir_id).opt_def_id() - .map(|did| (did, args, MethodOrFunction::Function)) + ExprKind::Call(recv, args) + if let ExprKind::Path(ref qpath) = recv.kind + && let Some(did) = cx.qpath_res(qpath, recv.hir_id).opt_def_id() + // make sure that the path indeed points to a fn-like item, so that + // `fn_sig` does not ICE. (see #11065) + && cx.tcx.opt_def_kind(did).is_some_and(DefKind::is_fn_like) => + { + Some((did, args, MethodOrFunction::Function)) } ExprKind::MethodCall(.., args, _) => { cx.typeck_results().type_dependent_def_id(parent.hir_id) @@ -168,6 +176,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion { && let Some(&into_iter_param) = sig.inputs().get(kind.param_pos(arg_pos)) && let ty::Param(param) = into_iter_param.kind() && let Some(span) = into_iter_bound(cx, parent_fn_did, into_iter_did, param.index) + && self.expn_depth == 0 { // Get the "innermost" `.into_iter()` call, e.g. given this expression: // `foo.into_iter().into_iter()` @@ -303,5 +312,8 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion { if Some(&e.hir_id) == self.try_desugar_arm.last() { self.try_desugar_arm.pop(); } + if e.span.from_expansion() { + self.expn_depth -= 1; + } } } diff --git a/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs b/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs index 139e31bc528..43523faa236 100644 --- a/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs +++ b/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs @@ -291,8 +291,8 @@ fn check_terminator<'tcx>( | TerminatorKind::FalseUnwind { .. } | TerminatorKind::Goto { .. } | TerminatorKind::Return - | TerminatorKind::Resume - | TerminatorKind::Terminate + | TerminatorKind::UnwindResume + | TerminatorKind::UnwindTerminate | TerminatorKind::Unreachable => Ok(()), TerminatorKind::Drop { place, .. } => { if !is_ty_const_destruct(tcx, place.ty(&body.local_decls, tcx).ty, body) { diff --git a/src/tools/clippy/tests/ui/crashes/ice-11065.rs b/src/tools/clippy/tests/ui/crashes/ice-11065.rs new file mode 100644 index 00000000000..f5cf6b1cd77 --- /dev/null +++ b/src/tools/clippy/tests/ui/crashes/ice-11065.rs @@ -0,0 +1,19 @@ +#![warn(clippy::useless_conversion)] + +use std::iter::FromIterator; +use std::option::IntoIter as OptionIter; + +fn eq<T: Eq>(a: T, b: T) -> bool { + a == b +} + +macro_rules! tests { + ($($expr:expr, $ty:ty, ($($test:expr),*);)+) => (pub fn main() {$({ + const C: $ty = $expr; + assert!(eq(C($($test),*), $expr($($test),*))); + })+}) +} + +tests! { + FromIterator::from_iter, fn(OptionIter<i32>) -> Vec<i32>, (Some(5).into_iter()); +} diff --git a/src/tools/clippy/tests/ui/needless_pass_by_ref_mut.rs b/src/tools/clippy/tests/ui/needless_pass_by_ref_mut.rs index ae7b018d0e2..ec87d447597 100644 --- a/src/tools/clippy/tests/ui/needless_pass_by_ref_mut.rs +++ b/src/tools/clippy/tests/ui/needless_pass_by_ref_mut.rs @@ -196,6 +196,41 @@ mod foo { //~| NOTE: this is cfg-gated and may require further changes } +// Should not warn. +async fn inner_async(x: &mut i32, y: &mut u32) { + async { + *y += 1; + *x += 1; + } + .await; +} + +async fn inner_async2(x: &mut i32, y: &mut u32) { + //~^ ERROR: this argument is a mutable reference, but not used mutably + async { + *x += 1; + } + .await; +} + +async fn inner_async3(x: &mut i32, y: &mut u32) { + //~^ ERROR: this argument is a mutable reference, but not used mutably + async { + *y += 1; + } + .await; +} + +// Should not warn. +async fn async_vec(b: &mut Vec<bool>) { + b.append(&mut vec![]); +} + +// Should not warn. +async fn async_vec2(b: &mut Vec<bool>) { + b.push(true); +} + fn main() { let mut u = 0; let mut v = vec![0]; diff --git a/src/tools/clippy/tests/ui/needless_pass_by_ref_mut.stderr b/src/tools/clippy/tests/ui/needless_pass_by_ref_mut.stderr index 0d426ce32f9..2e06e7252d9 100644 --- a/src/tools/clippy/tests/ui/needless_pass_by_ref_mut.stderr +++ b/src/tools/clippy/tests/ui/needless_pass_by_ref_mut.stderr @@ -94,5 +94,17 @@ LL | fn cfg_warn(s: &mut u32) {} | = note: this is cfg-gated and may require further changes -error: aborting due to 15 previous errors +error: this argument is a mutable reference, but not used mutably + --> $DIR/needless_pass_by_ref_mut.rs:208:39 + | +LL | async fn inner_async2(x: &mut i32, y: &mut u32) { + | ^^^^^^^^ help: consider changing to: `&u32` + +error: this argument is a mutable reference, but not used mutably + --> $DIR/needless_pass_by_ref_mut.rs:216:26 + | +LL | async fn inner_async3(x: &mut i32, y: &mut u32) { + | ^^^^^^^^ help: consider changing to: `&i32` + +error: aborting due to 17 previous errors diff --git a/src/tools/clippy/tests/ui/useless_conversion.fixed b/src/tools/clippy/tests/ui/useless_conversion.fixed index 5d2c5b11658..53d8a5a9ff1 100644 --- a/src/tools/clippy/tests/ui/useless_conversion.fixed +++ b/src/tools/clippy/tests/ui/useless_conversion.fixed @@ -156,6 +156,20 @@ fn main() { } #[allow(dead_code)] +fn issue11065_fp() { + use std::option::IntoIter; + fn takes_into_iter(_: impl IntoIterator<Item = i32>) {} + + macro_rules! x { + ($e:expr) => { + takes_into_iter($e); + let _: IntoIter<i32> = $e; // removing `.into_iter()` leads to a type error here + }; + } + x!(Some(5).into_iter()); +} + +#[allow(dead_code)] fn explicit_into_iter_fn_arg() { fn a<T>(_: T) {} fn b<T: IntoIterator<Item = i32>>(_: T) {} diff --git a/src/tools/clippy/tests/ui/useless_conversion.rs b/src/tools/clippy/tests/ui/useless_conversion.rs index 03a3e3f95ba..51ba4987339 100644 --- a/src/tools/clippy/tests/ui/useless_conversion.rs +++ b/src/tools/clippy/tests/ui/useless_conversion.rs @@ -156,6 +156,20 @@ fn main() { } #[allow(dead_code)] +fn issue11065_fp() { + use std::option::IntoIter; + fn takes_into_iter(_: impl IntoIterator<Item = i32>) {} + + macro_rules! x { + ($e:expr) => { + takes_into_iter($e); + let _: IntoIter<i32> = $e; // removing `.into_iter()` leads to a type error here + }; + } + x!(Some(5).into_iter()); +} + +#[allow(dead_code)] fn explicit_into_iter_fn_arg() { fn a<T>(_: T) {} fn b<T: IntoIterator<Item = i32>>(_: T) {} diff --git a/src/tools/clippy/tests/ui/useless_conversion.stderr b/src/tools/clippy/tests/ui/useless_conversion.stderr index 4957f73a469..6f7dc01d2cd 100644 --- a/src/tools/clippy/tests/ui/useless_conversion.stderr +++ b/src/tools/clippy/tests/ui/useless_conversion.stderr @@ -119,61 +119,61 @@ LL | let _ = vec![s4, s4, s4].into_iter().into_iter(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `.into_iter()`: `vec![s4, s4, s4].into_iter()` error: explicit call to `.into_iter()` in function argument accepting `IntoIterator` - --> $DIR/useless_conversion.rs:171:7 + --> $DIR/useless_conversion.rs:185:7 | LL | b(vec![1, 2].into_iter()); | ^^^^^^^^^^^^^^^^^^^^^^ help: consider removing the `.into_iter()`: `vec![1, 2]` | note: this parameter accepts any `IntoIterator`, so you don't need to call `.into_iter()` - --> $DIR/useless_conversion.rs:161:13 + --> $DIR/useless_conversion.rs:175:13 | LL | fn b<T: IntoIterator<Item = i32>>(_: T) {} | ^^^^^^^^^^^^^^^^^^^^^^^^ error: explicit call to `.into_iter()` in function argument accepting `IntoIterator` - --> $DIR/useless_conversion.rs:172:7 + --> $DIR/useless_conversion.rs:186:7 | LL | c(vec![1, 2].into_iter()); | ^^^^^^^^^^^^^^^^^^^^^^ help: consider removing the `.into_iter()`: `vec![1, 2]` | note: this parameter accepts any `IntoIterator`, so you don't need to call `.into_iter()` - --> $DIR/useless_conversion.rs:162:18 + --> $DIR/useless_conversion.rs:176:18 | LL | fn c(_: impl IntoIterator<Item = i32>) {} | ^^^^^^^^^^^^^^^^^^^^^^^^ error: explicit call to `.into_iter()` in function argument accepting `IntoIterator` - --> $DIR/useless_conversion.rs:173:7 + --> $DIR/useless_conversion.rs:187:7 | LL | d(vec![1, 2].into_iter()); | ^^^^^^^^^^^^^^^^^^^^^^ help: consider removing the `.into_iter()`: `vec![1, 2]` | note: this parameter accepts any `IntoIterator`, so you don't need to call `.into_iter()` - --> $DIR/useless_conversion.rs:165:12 + --> $DIR/useless_conversion.rs:179:12 | LL | T: IntoIterator<Item = i32>, | ^^^^^^^^^^^^^^^^^^^^^^^^ error: explicit call to `.into_iter()` in function argument accepting `IntoIterator` - --> $DIR/useless_conversion.rs:176:7 + --> $DIR/useless_conversion.rs:190:7 | LL | b(vec![1, 2].into_iter().into_iter()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing the `.into_iter()`s: `vec![1, 2]` | note: this parameter accepts any `IntoIterator`, so you don't need to call `.into_iter()` - --> $DIR/useless_conversion.rs:161:13 + --> $DIR/useless_conversion.rs:175:13 | LL | fn b<T: IntoIterator<Item = i32>>(_: T) {} | ^^^^^^^^^^^^^^^^^^^^^^^^ error: explicit call to `.into_iter()` in function argument accepting `IntoIterator` - --> $DIR/useless_conversion.rs:177:7 + --> $DIR/useless_conversion.rs:191:7 | LL | b(vec![1, 2].into_iter().into_iter().into_iter()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing the `.into_iter()`s: `vec![1, 2]` | note: this parameter accepts any `IntoIterator`, so you don't need to call `.into_iter()` - --> $DIR/useless_conversion.rs:161:13 + --> $DIR/useless_conversion.rs:175:13 | LL | fn b<T: IntoIterator<Item = i32>>(_: T) {} | ^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/src/tools/miri/.github/workflows/ci.yml b/src/tools/miri/.github/workflows/ci.yml index 8ced9fa86be..5e2abdde6ac 100644 --- a/src/tools/miri/.github/workflows/ci.yml +++ b/src/tools/miri/.github/workflows/ci.yml @@ -189,8 +189,6 @@ jobs: fetch-depth: 256 # get a bit more of the history - name: install josh-proxy run: cargo +stable install josh-proxy --git https://github.com/josh-project/josh --tag r22.12.06 - - name: start josh-proxy - run: josh-proxy --local=$HOME/.cache/josh --remote=https://github.com --no-background & - name: setup bot git name and email run: | git config --global user.name 'The Miri Conjob Bot' diff --git a/src/tools/miri/CONTRIBUTING.md b/src/tools/miri/CONTRIBUTING.md index b67e7103fd0..4e50b8fae81 100644 --- a/src/tools/miri/CONTRIBUTING.md +++ b/src/tools/miri/CONTRIBUTING.md @@ -165,16 +165,17 @@ to `.vscode/settings.json` in your local Miri clone: { "rust-analyzer.rustc.source": "discover", "rust-analyzer.linkedProjects": [ - "./Cargo.toml", - "./cargo-miri/Cargo.toml" + "Cargo.toml", + "cargo-miri/Cargo.toml", + "miri-script/Cargo.toml", ], - "rust-analyzer.checkOnSave.overrideCommand": [ + "rust-analyzer.check.overrideCommand": [ "env", "MIRI_AUTO_OPS=no", "./miri", "cargo", "clippy", // make this `check` when working with a locally built rustc - "--message-format=json" + "--message-format=json", ], // Contrary to what the name suggests, this also affects proc macros. "rust-analyzer.cargo.buildScripts.overrideCommand": [ @@ -230,25 +231,16 @@ You can also directly run Miri on a Rust source file: ## Advanced topic: Syncing with the rustc repo We use the [`josh` proxy](https://github.com/josh-project/josh) to transmit changes between the -rustc and Miri repositories. +rustc and Miri repositories. You can install it as follows: ```sh cargo +stable install josh-proxy --git https://github.com/josh-project/josh --tag r22.12.06 -josh-proxy --local=$HOME/.cache/josh --remote=https://github.com --no-background ``` -This uses a directory `$HOME/.cache/josh` as a cache, to speed up repeated pulling/pushing. - -To make josh push via ssh instead of https, you can add the following to your `.gitconfig`: - -```toml -[url "git@github.com:"] - pushInsteadOf = https://github.com/ -``` +Josh will automatically be started and stopped by `./miri`. ### Importing changes from the rustc repo -Josh needs to be running, as described above. We assume we start on an up-to-date master branch in the Miri repo. ```sh @@ -267,16 +259,14 @@ needed. ### Exporting changes to the rustc repo -Keep in mind that pushing is the most complicated job that josh has to do -- -pulling just filters the rustc history, but pushing needs to construct a new -rustc history that would filter to the given Miri history! To avoid problems, it -is a good idea to always pull immediately before you push. In particular, you -should never do two josh pushes without an intermediate pull; that can lead to -duplicated commits. +Keep in mind that pushing is the most complicated job that josh has to do -- pulling just filters +the rustc history, but pushing needs to construct a new rustc history that would filter to the given +Miri history! To avoid problems, it is a good idea to always pull immediately before you push. If +you are getting strange errors, chances are you are running into [this josh +bug](https://github.com/josh-project/josh/issues/998). In that case, please get in touch on Zulip. -Josh needs to be running, as described above. We will use the josh proxy to push -to your fork of rustc. Run the following in the Miri repo, assuming we are on an -up-to-date master branch: +We will use the josh proxy to push to your fork of rustc. Run the following in the Miri repo, +assuming we are on an up-to-date master branch: ```sh # Push the Miri changes to your rustc fork (substitute your github handle for YOUR_NAME). @@ -286,3 +276,11 @@ up-to-date master branch: This will create a new branch called 'miri' in your fork, and the output should include a link to create a rustc PR that will integrate those changes into the main repository. + +If this fails due to authentication problems, it can help to make josh push via ssh instead of +https. Add the following to your `.gitconfig`: + +```toml +[url "git@github.com:"] + pushInsteadOf = https://github.com/ +``` diff --git a/src/tools/miri/Cargo.lock b/src/tools/miri/Cargo.lock index 4232d7fda78..ca5b6d22515 100644 --- a/src/tools/miri/Cargo.lock +++ b/src/tools/miri/Cargo.lock @@ -443,6 +443,7 @@ dependencies = [ "rand", "regex", "rustc_version", + "serde", "smallvec", "ui_test", ] @@ -528,18 +529,18 @@ checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" [[package]] name = "proc-macro2" -version = "1.0.60" +version = "1.0.66" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dec2b086b7a862cf4de201096214fa870344cf922b2b30c167badb3af3195406" +checksum = "18fb31db3f9bddb2ea821cde30a9f70117e3f119938b5ee630b7403aa6e2ead9" dependencies = [ "unicode-ident", ] [[package]] name = "quote" -version = "1.0.26" +version = "1.0.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4424af4bf778aae2051a77b60283332f386554255d722233d09fbfc7e30da2fc" +checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" dependencies = [ "proc-macro2", ] @@ -685,18 +686,18 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.162" +version = "1.0.185" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71b2f6e1ab5c2b98c05f0f35b236b22e8df7ead6ffbf51d7808da7f8817e7ab6" +checksum = "be9b6f69f1dfd54c3b568ffa45c310d6973a5e5148fd40cf515acaf38cf5bc31" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.162" +version = "1.0.185" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2a0814352fd64b58489904a44ea8d90cb1a91dcb6b4f5ebabc32c8318e93cb6" +checksum = "dc59dfdcbad1437773485e0367fea4b090a2e0a16d9ffc46af47764536a298ec" dependencies = [ "proc-macro2", "quote", @@ -737,9 +738,9 @@ checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" [[package]] name = "syn" -version = "2.0.15" +version = "2.0.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a34fcf3e8b60f57e6a14301a2e916d323af98b0ea63c599441eec8558660c822" +checksum = "c324c494eba9d92503e6f1ef2e6df781e78f6a7705a0202d9801b198807d518a" dependencies = [ "proc-macro2", "quote", diff --git a/src/tools/miri/Cargo.toml b/src/tools/miri/Cargo.toml index a625e1696e1..67a2aeefa02 100644 --- a/src/tools/miri/Cargo.toml +++ b/src/tools/miri/Cargo.toml @@ -41,6 +41,8 @@ rustc_version = "0.4" # Features chosen to match those required by env_logger, to avoid rebuilds regex = { version = "1.5.5", default-features = false, features = ["perf", "std"] } lazy_static = "1.4.0" +# Require a version of serde without intransparent unreproducible binary blobs. +serde = { version = "1.0.185", features = ["derive"] } [package.metadata.rust-analyzer] # This crate uses #[feature(rustc_private)]. diff --git a/src/tools/miri/README.md b/src/tools/miri/README.md index 89d4b29ebb8..06fe668354a 100644 --- a/src/tools/miri/README.md +++ b/src/tools/miri/README.md @@ -53,7 +53,7 @@ behavior** in your program, and cannot run all programs: positives here, so if your program runs fine in Miri right now that is by no means a guarantee that it is UB-free when these questions get answered. - In particular, Miri does currently not check that references point to valid data. + In particular, Miri does not check that references point to valid data. * If the program relies on unspecified details of how data is laid out, it will still run fine in Miri -- but might break (including causing UB) on different compiler versions or different platforms. @@ -458,8 +458,8 @@ Some native rustc `-Z` flags are also very relevant for Miri: Moreover, Miri recognizes some environment variables: * `MIRI_AUTO_OPS` indicates whether the automatic execution of rustfmt, clippy and toolchain setup - should be skipped. If it is set to any value, they are skipped. This is used for avoiding infinite - recursion in `./miri` and to allow automated IDE actions to avoid the auto ops. + should be skipped. If it is set to `no`, they are skipped. This is used to allow automated IDE + actions to avoid the auto ops. * `MIRI_LOG`, `MIRI_BACKTRACE` control logging and backtrace printing during Miri executions, also [see "Testing the Miri driver" in `CONTRIBUTING.md`][testing-miri]. * `MIRIFLAGS` (recognized by `cargo miri` and the test suite) defines extra diff --git a/src/tools/miri/cargo-miri/Cargo.lock b/src/tools/miri/cargo-miri/Cargo.lock index 47fdc65fa96..1f3a33270e1 100644 --- a/src/tools/miri/cargo-miri/Cargo.lock +++ b/src/tools/miri/cargo-miri/Cargo.lock @@ -178,18 +178,18 @@ checksum = "ece97ea872ece730aed82664c424eb4c8291e1ff2480247ccf7409044bc6479f" [[package]] name = "proc-macro2" -version = "1.0.60" +version = "1.0.66" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dec2b086b7a862cf4de201096214fa870344cf922b2b30c167badb3af3195406" +checksum = "18fb31db3f9bddb2ea821cde30a9f70117e3f119938b5ee630b7403aa6e2ead9" dependencies = [ "unicode-ident", ] [[package]] name = "quote" -version = "1.0.26" +version = "1.0.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4424af4bf778aae2051a77b60283332f386554255d722233d09fbfc7e30da2fc" +checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" dependencies = [ "proc-macro2", ] @@ -280,18 +280,18 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.162" +version = "1.0.185" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71b2f6e1ab5c2b98c05f0f35b236b22e8df7ead6ffbf51d7808da7f8817e7ab6" +checksum = "be9b6f69f1dfd54c3b568ffa45c310d6973a5e5148fd40cf515acaf38cf5bc31" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.162" +version = "1.0.185" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2a0814352fd64b58489904a44ea8d90cb1a91dcb6b4f5ebabc32c8318e93cb6" +checksum = "dc59dfdcbad1437773485e0367fea4b090a2e0a16d9ffc46af47764536a298ec" dependencies = [ "proc-macro2", "quote", @@ -311,9 +311,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.15" +version = "2.0.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a34fcf3e8b60f57e6a14301a2e916d323af98b0ea63c599441eec8558660c822" +checksum = "c324c494eba9d92503e6f1ef2e6df781e78f6a7705a0202d9801b198807d518a" dependencies = [ "proc-macro2", "quote", diff --git a/src/tools/miri/cargo-miri/Cargo.toml b/src/tools/miri/cargo-miri/Cargo.toml index cfa5ac5281d..e118d12897a 100644 --- a/src/tools/miri/cargo-miri/Cargo.toml +++ b/src/tools/miri/cargo-miri/Cargo.toml @@ -22,7 +22,8 @@ rustc-build-sysroot = "0.4.1" # Enable some feature flags that dev-dependencies need but dependencies # do not. This makes `./miri install` after `./miri build` faster. -serde = { version = "*", features = ["derive"] } +# Require a version of serde without intransparent unreproducible binary blobs. +serde = { version = "1.0.185", features = ["derive"] } [build-dependencies] rustc_tools_util = "0.3" diff --git a/src/tools/miri/ci.sh b/src/tools/miri/ci.sh index a8aae524e71..3146ef41a3b 100755 --- a/src/tools/miri/ci.sh +++ b/src/tools/miri/ci.sh @@ -52,7 +52,7 @@ function run_tests { # Also run some many-seeds tests. 64 seeds means this takes around a minute per test. for FILE in tests/many-seeds/*.rs; do - MIRI_SEEDS=64 CARGO_EXTRA_FLAGS="$CARGO_EXTRA_FLAGS -q" ./miri many-seeds ./miri run "$FILE" + MIRI_SEEDS=64 ./miri many-seeds ./miri run "$FILE" done # Check that the benchmarks build and run, but without actually benchmarking. diff --git a/src/tools/miri/miri b/src/tools/miri/miri index c816a4bb06b..938df9799da 100755 --- a/src/tools/miri/miri +++ b/src/tools/miri/miri @@ -2,5 +2,5 @@ set -e # Instead of doing just `cargo run --manifest-path .. $@`, we invoke miri-script binary directly. Invoking `cargo run` goes through # rustup (that sets it's own environmental variables), which is undesirable. -cargo build -q --manifest-path "$(dirname "$0")"/miri-script/Cargo.toml +cargo build $CARGO_EXTRA_FLAGS -q --manifest-path "$(dirname "$0")"/miri-script/Cargo.toml "$(dirname "$0")"/miri-script/target/debug/miri-script "$@" diff --git a/src/tools/miri/miri-script/Cargo.lock b/src/tools/miri/miri-script/Cargo.lock index cf6062d7d7f..1a22596b774 100644 --- a/src/tools/miri/miri-script/Cargo.lock +++ b/src/tools/miri/miri-script/Cargo.lock @@ -9,6 +9,38 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c7d0618f0e0b7e8ff11427422b64564d5fb0be1940354bfe2e0529b18a9d9b8" [[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "directories" +version = "4.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f51c5d4ddabd36886dd3e1438cb358cdcb0d7c499cb99cb4ac2e38e18b5cb210" +dependencies = [ + "dirs-sys", +] + +[[package]] +name = "dirs-sys" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b1d1d91c932ef41c0f2663aa8b0ca0342d444d842c06914aa0a7e352d0bada6" +dependencies = [ + "libc", + "redox_users", + "winapi", +] + +[[package]] name = "dunce" version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -21,6 +53,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91" [[package]] +name = "getrandom" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] name = "itertools" version = "0.10.5" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -40,6 +83,7 @@ name = "miri-script" version = "0.1.0" dependencies = [ "anyhow", + "directories", "dunce", "itertools", "path_macro", @@ -63,6 +107,44 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a6e819bbd49d5939f682638fa54826bf1650abddcd65d000923de8ad63cc7d15" [[package]] +name = "proc-macro2" +version = "1.0.66" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18fb31db3f9bddb2ea821cde30a9f70117e3f119938b5ee630b7403aa6e2ead9" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "redox_syscall" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" +dependencies = [ + "bitflags", +] + +[[package]] +name = "redox_users" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b" +dependencies = [ + "getrandom", + "redox_syscall", + "thiserror", +] + +[[package]] name = "rustc_version" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -93,6 +175,43 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24188a676b6ae68c3b2cb3a01be17fbf7240ce009799bb56d5b1409051e78fde" [[package]] +name = "syn" +version = "2.0.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c324c494eba9d92503e6f1ef2e6df781e78f6a7705a0202d9801b198807d518a" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "thiserror" +version = "1.0.47" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97a802ec30afc17eee47b2855fc72e0c4cd62be9b4efe6591edde0ec5bd68d8f" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.47" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6bb623b56e39ab7dcd4b1b98bb6c8f8d907ed255b18de254088016b27a8ee19b" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "unicode-ident" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "301abaae475aa91687eb82514b328ab47a211a533026cb25fc3e519b86adfc3c" + +[[package]] name = "walkdir" version = "2.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -103,6 +222,12 @@ dependencies = [ ] [[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] name = "which" version = "4.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" diff --git a/src/tools/miri/miri-script/Cargo.toml b/src/tools/miri/miri-script/Cargo.toml index c0414a2fe37..d805a94c8f5 100644 --- a/src/tools/miri/miri-script/Cargo.toml +++ b/src/tools/miri/miri-script/Cargo.toml @@ -20,3 +20,4 @@ anyhow = "1.0" xshell = "0.2" rustc_version = "0.4" dunce = "1.0.4" +directories = "4" diff --git a/src/tools/miri/miri-script/src/commands.rs b/src/tools/miri/miri-script/src/commands.rs index ed78f80c023..2948219ad89 100644 --- a/src/tools/miri/miri-script/src/commands.rs +++ b/src/tools/miri/miri-script/src/commands.rs @@ -2,6 +2,9 @@ use std::env; use std::ffi::OsString; use std::io::Write; use std::ops::Not; +use std::process; +use std::thread; +use std::time; use anyhow::{anyhow, bail, Context, Result}; use path_macro::path; @@ -14,6 +17,7 @@ use crate::Command; /// Used for rustc syncs. const JOSH_FILTER: &str = ":rev(75dd959a3a40eb5b4574f8d2e23aa6efbeb33573:prefix=src/tools/miri):/src/tools/miri"; +const JOSH_PORT: &str = "42042"; impl MiriEnv { fn build_miri_sysroot(&mut self, quiet: bool) -> Result<()> { @@ -57,6 +61,10 @@ impl MiriEnv { impl Command { fn auto_actions() -> Result<()> { + if env::var_os("MIRI_AUTO_OPS").is_some_and(|x| x == "no") { + return Ok(()); + } + let miri_dir = miri_dir()?; let auto_everything = path!(miri_dir / ".auto-everything").exists(); let auto_toolchain = auto_everything || path!(miri_dir / ".auto-toolchain").exists(); @@ -77,7 +85,57 @@ impl Command { Ok(()) } + fn start_josh() -> Result<impl Drop> { + // Determine cache directory. + let local_dir = { + let user_dirs = + directories::ProjectDirs::from("org", "rust-lang", "miri-josh").unwrap(); + user_dirs.cache_dir().to_owned() + }; + + // Start josh, silencing its output. + let mut cmd = process::Command::new("josh-proxy"); + cmd.arg("--local").arg(local_dir); + cmd.arg("--remote").arg("https://github.com"); + cmd.arg("--port").arg(JOSH_PORT); + cmd.arg("--no-background"); + cmd.stdout(process::Stdio::null()); + cmd.stderr(process::Stdio::null()); + let josh = cmd.spawn().context("failed to start josh-proxy, make sure it is installed")?; + // Give it some time so hopefully the port is open. (10ms was not enough.) + thread::sleep(time::Duration::from_millis(100)); + + // Create a wrapper that stops it on drop. + struct Josh(process::Child); + impl Drop for Josh { + fn drop(&mut self) { + #[cfg(unix)] + { + // Try to gracefully shut it down. + process::Command::new("kill") + .args(["-s", "INT", &self.0.id().to_string()]) + .output() + .expect("failed to SIGINT josh-proxy"); + // Sadly there is no "wait with timeout"... so we just give it some time to finish. + thread::sleep(time::Duration::from_millis(100)); + // Now hopefully it is gone. + if self.0.try_wait().expect("failed to wait for josh-proxy").is_some() { + return; + } + } + // If that didn't work (or we're not on Unix), kill it hard. + eprintln!( + "I have to kill josh-proxy the hard way, let's hope this does not break anything." + ); + self.0.kill().expect("failed to SIGKILL josh-proxy"); + } + } + + Ok(Josh(josh)) + } + pub fn exec(self) -> Result<()> { + // First, and crucially only once, run the auto-actions -- but not for all commands. match &self { Command::Install { .. } | Command::Build { .. } @@ -89,10 +147,11 @@ impl Command { | Command::Cargo { .. } => Self::auto_actions()?, | Command::ManySeeds { .. } | Command::Toolchain { .. } - | Command::RustcPull { .. } | Command::Bench { .. } + | Command::RustcPull { .. } | Command::RustcPush { .. } => {} } + // Then run the actual command. match self { Command::Install { flags } => Self::install(flags), Command::Build { flags } => Self::build(flags), @@ -168,6 +227,8 @@ impl Command { if cmd!(sh, "git status --untracked-files=no --porcelain").read()?.is_empty().not() { bail!("working directory must be clean before running `./miri rustc-pull`"); } + // Make sure josh is running. + let josh = Self::start_josh()?; // Update rust-version file. As a separate commit, since making it part of // the merge has confused the heck out of josh in the past. @@ -180,7 +241,7 @@ impl Command { .context("FAILED to commit rust-version file, something went wrong")?; // Fetch given rustc commit. - cmd!(sh, "git fetch http://localhost:8000/rust-lang/rust.git@{commit}{JOSH_FILTER}.git") + cmd!(sh, "git fetch http://localhost:{JOSH_PORT}/rust-lang/rust.git@{commit}{JOSH_FILTER}.git") .run() .map_err(|e| { // Try to un-do the previous `git commit`, to leave the repo in the state we found it it. @@ -196,6 +257,8 @@ impl Command { cmd!(sh, "git merge FETCH_HEAD --no-verify --no-ff -m {MERGE_COMMIT_MESSAGE}") .run() .context("FAILED to merge new commits, something went wrong")?; + + drop(josh); Ok(()) } @@ -207,6 +270,8 @@ impl Command { if cmd!(sh, "git status --untracked-files=no --porcelain").read()?.is_empty().not() { bail!("working directory must be clean before running `./miri rustc-push`"); } + // Make sure josh is running. + let josh = Self::start_josh()?; // Find a repo we can do our preparation in. if let Ok(rustc_git) = env::var("RUSTC_GIT") { @@ -243,6 +308,8 @@ impl Command { } cmd!(sh, "git fetch https://github.com/rust-lang/rust {base}").run()?; cmd!(sh, "git push https://github.com/{github_user}/rust {base}:refs/heads/{branch}") + .ignore_stdout() + .ignore_stderr() // silence the "create GitHub PR" message .run()?; println!(); @@ -251,7 +318,7 @@ impl Command { println!("Pushing miri changes..."); cmd!( sh, - "git push http://localhost:8000/{github_user}/rust.git{JOSH_FILTER}.git HEAD:{branch}" + "git push http://localhost:{JOSH_PORT}/{github_user}/rust.git{JOSH_FILTER}.git HEAD:{branch}" ) .run()?; println!(); @@ -259,7 +326,7 @@ impl Command { // Do a round-trip check to make sure the push worked as expected. cmd!( sh, - "git fetch http://localhost:8000/{github_user}/rust.git{JOSH_FILTER}.git {branch}" + "git fetch http://localhost:{JOSH_PORT}/{github_user}/rust.git{JOSH_FILTER}.git {branch}" ) .ignore_stderr() .read()?; @@ -272,6 +339,8 @@ impl Command { "Confirmed that the push round-trips back to Miri properly. Please create a rustc PR:" ); println!(" https://github.com/{github_user}/rust/pull/new/{branch}"); + + drop(josh); Ok(()) } @@ -289,6 +358,7 @@ impl Command { bail!("expected many-seeds command to be non-empty"); }; let sh = Shell::new()?; + sh.set_var("MIRI_AUTO_OPS", "no"); // just in case we get recursively invoked for seed in seed_start..seed_end { println!("Trying seed: {seed}"); let mut miriflags = env::var_os("MIRIFLAGS").unwrap_or_default(); @@ -313,6 +383,8 @@ impl Command { let Some((program_name, args)) = hyperfine.split_first() else { bail!("expected HYPERFINE environment variable to be non-empty"); }; + // Extra flags to pass to cargo. + let cargo_extra_flags = std::env::var("CARGO_EXTRA_FLAGS").unwrap_or_default(); // Make sure we have an up-to-date Miri installed and selected the right toolchain. Self::install(vec![])?; @@ -335,7 +407,7 @@ impl Command { // That seems to make Windows CI happy. cmd!( sh, - "{program_name} {args...} 'cargo miri run --manifest-path \"'{current_bench}'\"'" + "{program_name} {args...} 'cargo miri run '{cargo_extra_flags}' --manifest-path \"'{current_bench}'\"'" ) .run()?; } diff --git a/src/tools/miri/miri-script/src/main.rs b/src/tools/miri/miri-script/src/main.rs index 849a9168028..41b82cfc472 100644 --- a/src/tools/miri/miri-script/src/main.rs +++ b/src/tools/miri/miri-script/src/main.rs @@ -1,3 +1,5 @@ +#![allow(clippy::needless_question_mark)] + mod commands; mod util; diff --git a/src/tools/miri/rust-version b/src/tools/miri/rust-version index 22a3d439861..d96e7040aaf 100644 --- a/src/tools/miri/rust-version +++ b/src/tools/miri/rust-version @@ -1 +1 @@ -9fa6bdd764a1f7bdf69eccceeace6d13f38cb2e1 +c40cfcf0494ff7506e753e750adb00eeea839f9c diff --git a/src/tools/miri/src/borrow_tracker/tree_borrows/perms.rs b/src/tools/miri/src/borrow_tracker/tree_borrows/perms.rs index 0ce29ac5437..dab216bc5b1 100644 --- a/src/tools/miri/src/borrow_tracker/tree_borrows/perms.rs +++ b/src/tools/miri/src/borrow_tracker/tree_borrows/perms.rs @@ -68,17 +68,31 @@ mod transition { fn foreign_read(state: PermissionPriv, protected: bool) -> Option<PermissionPriv> { use Option::*; Some(match state { + // Non-writeable states just ignore foreign reads. + non_writeable @ (Frozen | Disabled) => non_writeable, + // Writeable states are more tricky, and depend on whether things are protected. // The inner data `ty_is_freeze` of `Reserved` is always irrelevant for Read // accesses, since the data is not being mutated. Hence the `{ .. }` - res @ Reserved { .. } if !protected => res, - Reserved { .. } => Frozen, // protected reserved + res @ Reserved { .. } => + if protected { + // Someone else read, make sure we won't write. + // We could make this `Disabled` but it doesn't look like we get anything out of that extra UB. + Frozen + } else { + // Before activation and without protectors, foreign reads are fine. + // That's the entire point of 2-phase borrows. + res + }, Active => if protected { + // We wrote, someone else reads -- that's bad. + // (If this is initialized, this move-to-protected will mean insta-UB.) Disabled } else { + // We don't want to disable here to allow read-read reordering: it is crucial + // that the foreign read does not invalidate future reads through this tag. Frozen }, - non_writeable @ (Frozen | Disabled) => non_writeable, }) } diff --git a/src/tools/miri/src/diagnostics.rs b/src/tools/miri/src/diagnostics.rs index 6bd4be91e51..f3285ccf917 100644 --- a/src/tools/miri/src/diagnostics.rs +++ b/src/tools/miri/src/diagnostics.rs @@ -304,11 +304,21 @@ pub fn report_error<'tcx, 'mir>( (None, format!("this usually indicates that your program performed an invalid operation and caused Undefined Behavior")), (None, format!("but due to `-Zmiri-symbolic-alignment-check`, alignment errors can also be false positives")), ], - UndefinedBehavior(_) => - vec![ + UndefinedBehavior(info) => { + let mut helps = vec![ (None, format!("this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior")), (None, format!("see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information")), - ], + ]; + if let UndefinedBehaviorInfo::PointerUseAfterFree(alloc_id, _) | UndefinedBehaviorInfo::PointerOutOfBounds { alloc_id, .. } = info { + if let Some(span) = ecx.machine.allocated_span(*alloc_id) { + helps.push((Some(span), format!("{:?} was allocated here:", alloc_id))); + } + if let Some(span) = ecx.machine.deallocated_span(*alloc_id) { + helps.push((Some(span), format!("{:?} was deallocated here:", alloc_id))); + } + } + helps + } InvalidProgram( InvalidProgramInfo::AlreadyReported(_) ) => { diff --git a/src/tools/miri/src/helpers.rs b/src/tools/miri/src/helpers.rs index ea1931f7a18..b5cc5c7e486 100644 --- a/src/tools/miri/src/helpers.rs +++ b/src/tools/miri/src/helpers.rs @@ -13,11 +13,11 @@ use rustc_index::IndexVec; use rustc_middle::mir; use rustc_middle::ty::{ self, - layout::{LayoutOf, TyAndLayout}, - List, TyCtxt, + layout::{IntegerExt as _, LayoutOf, TyAndLayout}, + List, Ty, TyCtxt, }; use rustc_span::{def_id::CrateNum, sym, Span, Symbol}; -use rustc_target::abi::{Align, FieldIdx, FieldsShape, Size, Variants}; +use rustc_target::abi::{Align, FieldIdx, FieldsShape, Integer, Size, Variants}; use rustc_target::spec::abi::Abi; use rand::RngCore; @@ -1011,6 +1011,65 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { None => tcx.item_name(def_id), } } + + /// Converts `f` to integer type `dest_ty` after rounding with mode `round`. + /// Returns `None` if `f` is NaN or out of range. + fn float_to_int_checked<F>( + &self, + f: F, + dest_ty: Ty<'tcx>, + round: rustc_apfloat::Round, + ) -> Option<Scalar<Provenance>> + where + F: rustc_apfloat::Float + Into<Scalar<Provenance>>, + { + let this = self.eval_context_ref(); + + match dest_ty.kind() { + // Unsigned + ty::Uint(t) => { + let size = Integer::from_uint_ty(this, *t).size(); + let res = f.to_u128_r(size.bits_usize(), round, &mut false); + if res.status.intersects( + rustc_apfloat::Status::INVALID_OP + | rustc_apfloat::Status::OVERFLOW + | rustc_apfloat::Status::UNDERFLOW, + ) { + // Floating point value is NaN (flagged with INVALID_OP) or outside the range + // of values of the integer type (flagged with OVERFLOW or UNDERFLOW). + None + } else { + // Floating point value can be represented by the integer type after rounding. + // The INEXACT flag is ignored on purpose to allow rounding. + Some(Scalar::from_uint(res.value, size)) + } + } + // Signed + ty::Int(t) => { + let size = Integer::from_int_ty(this, *t).size(); + let res = f.to_i128_r(size.bits_usize(), round, &mut false); + if res.status.intersects( + rustc_apfloat::Status::INVALID_OP + | rustc_apfloat::Status::OVERFLOW + | rustc_apfloat::Status::UNDERFLOW, + ) { + // Floating point value is NaN (flagged with INVALID_OP) or outside the range + // of values of the integer type (flagged with OVERFLOW or UNDERFLOW). + None + } else { + // Floating point value can be represented by the integer type after rounding. + // The INEXACT flag is ignored on purpose to allow rounding. + Some(Scalar::from_int(res.value, size)) + } + } + // Nothing else + _ => + span_bug!( + this.cur_span(), + "attempted float-to-int conversion with non-int output type {dest_ty:?}" + ), + } + } } impl<'mir, 'tcx> MiriMachine<'mir, 'tcx> { diff --git a/src/tools/miri/src/lib.rs b/src/tools/miri/src/lib.rs index d57da574315..03dd9037808 100644 --- a/src/tools/miri/src/lib.rs +++ b/src/tools/miri/src/lib.rs @@ -32,6 +32,7 @@ clippy::needless_return, clippy::bool_to_int_with_if, clippy::box_default, + clippy::needless_question_mark, // We are not implementing queries here so it's fine rustc::potential_query_instability )] diff --git a/src/tools/miri/src/machine.rs b/src/tools/miri/src/machine.rs index e19be417b22..550c9650fb3 100644 --- a/src/tools/miri/src/machine.rs +++ b/src/tools/miri/src/machine.rs @@ -25,7 +25,7 @@ use rustc_middle::{ }, }; use rustc_span::def_id::{CrateNum, DefId}; -use rustc_span::Symbol; +use rustc_span::{Span, SpanData, Symbol}; use rustc_target::abi::{Align, Size}; use rustc_target::spec::abi::Abi; @@ -135,6 +135,19 @@ impl MayLeak for MiriMemoryKind { } } +impl MiriMemoryKind { + /// Whether we have a useful allocation span for an allocation of this kind. + fn should_save_allocation_span(self) -> bool { + use self::MiriMemoryKind::*; + match self { + // Heap allocations are fine since the `Allocation` is created immediately. + Rust | Miri | C | WinHeap | Mmap => true, + // Everything else is unclear, let's not show potentially confusing spans. + Machine | Global | ExternStatic | Tls | Runtime => false, + } + } +} + impl fmt::Display for MiriMemoryKind { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { use self::MiriMemoryKind::*; @@ -497,6 +510,10 @@ pub struct MiriMachine<'mir, 'tcx> { /// Whether to collect a backtrace when each allocation is created, just in case it leaks. pub(crate) collect_leak_backtraces: bool, + + /// The spans we will use to report where an allocation was created and deallocated in + /// diagnostics. + pub(crate) allocation_spans: RefCell<FxHashMap<AllocId, (Span, Option<Span>)>>, } impl<'mir, 'tcx> MiriMachine<'mir, 'tcx> { @@ -621,6 +638,7 @@ impl<'mir, 'tcx> MiriMachine<'mir, 'tcx> { stack_addr, stack_size, collect_leak_backtraces: config.collect_leak_backtraces, + allocation_spans: RefCell::new(FxHashMap::default()), } } @@ -742,6 +760,21 @@ impl<'mir, 'tcx> MiriMachine<'mir, 'tcx> { pub(crate) fn page_align(&self) -> Align { Align::from_bytes(self.page_size).unwrap() } + + pub(crate) fn allocated_span(&self, alloc_id: AllocId) -> Option<SpanData> { + self.allocation_spans + .borrow() + .get(&alloc_id) + .map(|(allocated, _deallocated)| allocated.data()) + } + + pub(crate) fn deallocated_span(&self, alloc_id: AllocId) -> Option<SpanData> { + self.allocation_spans + .borrow() + .get(&alloc_id) + .and_then(|(_allocated, deallocated)| *deallocated) + .map(Span::data) + } } impl VisitTags for MiriMachine<'_, '_> { @@ -791,6 +824,7 @@ impl VisitTags for MiriMachine<'_, '_> { stack_addr: _, stack_size: _, collect_leak_backtraces: _, + allocation_spans: _, } = self; threads.visit_tags(visit); @@ -937,9 +971,22 @@ impl<'mir, 'tcx> Machine<'mir, 'tcx> for MiriMachine<'mir, 'tcx> { ecx.assert_panic(msg, unwind) } - #[inline(always)] - fn abort(_ecx: &mut MiriInterpCx<'mir, 'tcx>, msg: String) -> InterpResult<'tcx, !> { - throw_machine_stop!(TerminationInfo::Abort(msg)) + fn panic_nounwind(ecx: &mut InterpCx<'mir, 'tcx, Self>, msg: &str) -> InterpResult<'tcx> { + ecx.start_panic_nounwind(msg) + } + + fn unwind_terminate(ecx: &mut InterpCx<'mir, 'tcx, Self>) -> InterpResult<'tcx> { + // Call the lang item. + let panic = ecx.tcx.lang_items().panic_cannot_unwind().unwrap(); + let panic = ty::Instance::mono(ecx.tcx.tcx, panic); + ecx.call_function( + panic, + Abi::Rust, + &[], + None, + StackPopCleanup::Goto { ret: None, unwind: mir::UnwindAction::Unreachable }, + )?; + Ok(()) } #[inline(always)] @@ -1051,6 +1098,14 @@ impl<'mir, 'tcx> Machine<'mir, 'tcx> for MiriMachine<'mir, 'tcx> { }, |ptr| ecx.global_base_pointer(ptr), )?; + + if matches!(kind, MemoryKind::Machine(kind) if kind.should_save_allocation_span()) { + ecx.machine + .allocation_spans + .borrow_mut() + .insert(id, (ecx.machine.current_span(), None)); + } + Ok(Cow::Owned(alloc)) } @@ -1181,6 +1236,10 @@ impl<'mir, 'tcx> Machine<'mir, 'tcx> for MiriMachine<'mir, 'tcx> { if let Some(borrow_tracker) = &mut alloc_extra.borrow_tracker { borrow_tracker.before_memory_deallocation(alloc_id, prove_extra, range, machine)?; } + if let Some((_, deallocated_at)) = machine.allocation_spans.borrow_mut().get_mut(&alloc_id) + { + *deallocated_at = Some(machine.current_span()); + } Ok(()) } diff --git a/src/tools/miri/src/range_map.rs b/src/tools/miri/src/range_map.rs index 146715ddda2..4a3670b76ac 100644 --- a/src/tools/miri/src/range_map.rs +++ b/src/tools/miri/src/range_map.rs @@ -27,35 +27,27 @@ impl<T> RangeMap<T> { #[inline(always)] pub fn new(size: Size, init: T) -> RangeMap<T> { let size = size.bytes(); - let mut map = RangeMap { v: Vec::new() }; - if size > 0 { - map.v.push(Elem { range: 0..size, data: init }); - } - map + let v = if size > 0 { vec![Elem { range: 0..size, data: init }] } else { Vec::new() }; + RangeMap { v } } /// Finds the index containing the given offset. fn find_offset(&self, offset: u64) -> usize { - // We do a binary search. - let mut left = 0usize; // inclusive - let mut right = self.v.len(); // exclusive - loop { - debug_assert!(left < right, "find_offset: offset {offset} is out-of-bounds"); - let candidate = left.checked_add(right).unwrap() / 2; - let elem = &self.v[candidate]; - if offset < elem.range.start { - // We are too far right (offset is further left). - debug_assert!(candidate < right); // we are making progress - right = candidate; - } else if offset >= elem.range.end { - // We are too far left (offset is further right). - debug_assert!(candidate >= left); // we are making progress - left = candidate + 1; - } else { - // This is it! - return candidate; - } - } + self.v + .binary_search_by(|elem| -> std::cmp::Ordering { + if offset < elem.range.start { + // We are too far right (offset is further left). + // (`Greater` means that `elem` is greater than the desired target.) + std::cmp::Ordering::Greater + } else if offset >= elem.range.end { + // We are too far left (offset is further right). + std::cmp::Ordering::Less + } else { + // This is it! + std::cmp::Ordering::Equal + } + }) + .unwrap() } /// Provides read-only iteration over everything in the given range. This does diff --git a/src/tools/miri/src/shims/foreign_items.rs b/src/tools/miri/src/shims/foreign_items.rs index 953677d3290..7996e72615f 100644 --- a/src/tools/miri/src/shims/foreign_items.rs +++ b/src/tools/miri/src/shims/foreign_items.rs @@ -690,6 +690,10 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { let right = this.read_pointer(right)?; let n = Size::from_bytes(this.read_target_usize(n)?); + // C requires that this must always be a valid pointer (C18 §7.1.4). + this.ptr_get_alloc_id(left)?; + this.ptr_get_alloc_id(right)?; + let result = { let left_bytes = this.read_bytes_ptr_strip_provenance(left, n)?; let right_bytes = this.read_bytes_ptr_strip_provenance(right, n)?; @@ -714,6 +718,9 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { #[allow(clippy::cast_sign_loss, clippy::cast_possible_truncation)] let val = val as u8; + // C requires that this must always be a valid pointer (C18 §7.1.4). + this.ptr_get_alloc_id(ptr)?; + if let Some(idx) = this .read_bytes_ptr_strip_provenance(ptr, Size::from_bytes(num))? .iter() @@ -738,6 +745,9 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { #[allow(clippy::cast_sign_loss, clippy::cast_possible_truncation)] let val = val as u8; + // C requires that this must always be a valid pointer (C18 §7.1.4). + this.ptr_get_alloc_id(ptr)?; + let idx = this .read_bytes_ptr_strip_provenance(ptr, Size::from_bytes(num))? .iter() @@ -752,6 +762,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { "strlen" => { let [ptr] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; let ptr = this.read_pointer(ptr)?; + // This reads at least 1 byte, so we are already enforcing that this is a valid pointer. let n = this.read_c_str(ptr)?.len(); this.write_scalar( Scalar::from_target_usize(u64::try_from(n).unwrap(), this), @@ -791,6 +802,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { // pointer provenance is preserved by this implementation of `strcpy`. // That is probably overly cautious, but there also is no fundamental // reason to have `strcpy` destroy pointer provenance. + // This reads at least 1 byte, so we are already enforcing that this is a valid pointer. let n = this.read_c_str(ptr_src)?.len().checked_add(1).unwrap(); this.mem_copy( ptr_src, @@ -942,6 +954,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { this.write_scalar(Scalar::from_u64(res.to_bits()), dest)?; } + // LLVM intrinsics "llvm.prefetch" => { let [p, rw, loc, ty] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; @@ -968,8 +981,6 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { throw_unsup_format!("unsupported `llvm.prefetch` type argument: {}", ty); } } - - // Architecture-specific shims "llvm.x86.addcarry.64" if this.tcx.sess.target.arch == "x86_64" => { // Computes u8+u64+u64, returning tuple (u8,u64) comprising the output carry and truncated sum. let [c_in, a, b] = this.check_shim(abi, Abi::Unadjusted, link_name, args)?; diff --git a/src/tools/miri/src/shims/intrinsics/mod.rs b/src/tools/miri/src/shims/intrinsics/mod.rs index c900ced19cd..8a84b4f51b7 100644 --- a/src/tools/miri/src/shims/intrinsics/mod.rs +++ b/src/tools/miri/src/shims/intrinsics/mod.rs @@ -6,12 +6,12 @@ use std::iter; use log::trace; use rustc_apfloat::{Float, Round}; -use rustc_middle::ty::layout::{IntegerExt, LayoutOf}; +use rustc_middle::ty::layout::LayoutOf; use rustc_middle::{ mir, - ty::{self, FloatTy, Ty}, + ty::{self, FloatTy}, }; -use rustc_target::abi::{Integer, Size}; +use rustc_target::abi::Size; use crate::*; use atomic::EvalContextExt as _; @@ -34,10 +34,20 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { if this.emulate_intrinsic(instance, args, dest, ret)? { return Ok(()); } - - // All remaining supported intrinsics have a return place. let intrinsic_name = this.tcx.item_name(instance.def_id()); let intrinsic_name = intrinsic_name.as_str(); + + // Handle intrinsics without return place. + match intrinsic_name { + "abort" => { + throw_machine_stop!(TerminationInfo::Abort( + "the program aborted execution".to_owned() + )) + } + _ => {} + } + + // All remaining supported intrinsics have a return place. let ret = match ret { None => throw_unsup_format!("unimplemented (diverging) intrinsic: `{intrinsic_name}`"), Some(p) => p, @@ -356,10 +366,28 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { let val = this.read_immediate(val)?; let res = match val.layout.ty.kind() { - ty::Float(FloatTy::F32) => - this.float_to_int_unchecked(val.to_scalar().to_f32()?, dest.layout.ty)?, - ty::Float(FloatTy::F64) => - this.float_to_int_unchecked(val.to_scalar().to_f64()?, dest.layout.ty)?, + ty::Float(FloatTy::F32) => { + let f = val.to_scalar().to_f32()?; + this + .float_to_int_checked(f, dest.layout.ty, Round::TowardZero) + .ok_or_else(|| { + err_ub_format!( + "`float_to_int_unchecked` intrinsic called on {f} which cannot be represented in target type `{:?}`", + dest.layout.ty + ) + })? + } + ty::Float(FloatTy::F64) => { + let f = val.to_scalar().to_f64()?; + this + .float_to_int_checked(f, dest.layout.ty, Round::TowardZero) + .ok_or_else(|| { + err_ub_format!( + "`float_to_int_unchecked` intrinsic called on {f} which cannot be represented in target type `{:?}`", + dest.layout.ty + ) + })? + } _ => span_bug!( this.cur_span(), @@ -375,7 +403,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { "breakpoint" => { let [] = check_arg_count(args)?; // normally this would raise a SIGTRAP, which aborts if no debugger is connected - throw_machine_stop!(TerminationInfo::Abort(format!("Trace/breakpoint trap"))) + throw_machine_stop!(TerminationInfo::Abort(format!("trace/breakpoint trap"))) } name => throw_unsup_format!("unimplemented intrinsic: `{name}`"), @@ -383,57 +411,4 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { Ok(()) } - - fn float_to_int_unchecked<F>( - &self, - f: F, - dest_ty: Ty<'tcx>, - ) -> InterpResult<'tcx, Scalar<Provenance>> - where - F: Float + Into<Scalar<Provenance>>, - { - let this = self.eval_context_ref(); - - // Step 1: cut off the fractional part of `f`. The result of this is - // guaranteed to be precisely representable in IEEE floats. - let f = f.round_to_integral(Round::TowardZero).value; - - // Step 2: Cast the truncated float to the target integer type and see if we lose any information in this step. - Ok(match dest_ty.kind() { - // Unsigned - ty::Uint(t) => { - let size = Integer::from_uint_ty(this, *t).size(); - let res = f.to_u128(size.bits_usize()); - if res.status.is_empty() { - // No status flags means there was no further rounding or other loss of precision. - Scalar::from_uint(res.value, size) - } else { - // `f` was not representable in this integer type. - throw_ub_format!( - "`float_to_int_unchecked` intrinsic called on {f} which cannot be represented in target type `{dest_ty:?}`", - ); - } - } - // Signed - ty::Int(t) => { - let size = Integer::from_int_ty(this, *t).size(); - let res = f.to_i128(size.bits_usize()); - if res.status.is_empty() { - // No status flags means there was no further rounding or other loss of precision. - Scalar::from_int(res.value, size) - } else { - // `f` was not representable in this integer type. - throw_ub_format!( - "`float_to_int_unchecked` intrinsic called on {f} which cannot be represented in target type `{dest_ty:?}`", - ); - } - } - // Nothing else - _ => - span_bug!( - this.cur_span(), - "`float_to_int_unchecked` called with non-int output type {dest_ty:?}" - ), - }) - } } diff --git a/src/tools/miri/src/shims/intrinsics/simd.rs b/src/tools/miri/src/shims/intrinsics/simd.rs index b6225713cd0..dd8c4a4f6ec 100644 --- a/src/tools/miri/src/shims/intrinsics/simd.rs +++ b/src/tools/miri/src/shims/intrinsics/simd.rs @@ -1,4 +1,4 @@ -use rustc_apfloat::Float; +use rustc_apfloat::{Float, Round}; use rustc_middle::ty::layout::{HasParamEnv, LayoutOf}; use rustc_middle::{mir, ty, ty::FloatTy}; use rustc_target::abi::{Endian, HasDataLayout, Size}; @@ -420,7 +420,6 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { } } } - #[rustfmt::skip] "cast" | "as" | "cast_ptr" | "expose_addr" | "from_exposed_addr" => { let [op] = check_arg_count(args)?; let (op, op_len) = this.operand_to_simd(op)?; @@ -440,7 +439,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { let val = match (op.layout.ty.kind(), dest.layout.ty.kind()) { // Int-to-(int|float): always safe - (ty::Int(_) | ty::Uint(_), ty::Int(_) | ty::Uint(_) | ty::Float(_)) if safe_cast || unsafe_cast => + (ty::Int(_) | ty::Uint(_), ty::Int(_) | ty::Uint(_) | ty::Float(_)) + if safe_cast || unsafe_cast => this.int_to_int_or_float(&op, dest.layout.ty)?, // Float-to-float: always safe (ty::Float(_), ty::Float(_)) if safe_cast || unsafe_cast => @@ -449,21 +449,36 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { (ty::Float(_), ty::Int(_) | ty::Uint(_)) if safe_cast => this.float_to_float_or_int(&op, dest.layout.ty)?, // Float-to-int in unchecked mode - (ty::Float(FloatTy::F32), ty::Int(_) | ty::Uint(_)) if unsafe_cast => - this.float_to_int_unchecked(op.to_scalar().to_f32()?, dest.layout.ty)?.into(), - (ty::Float(FloatTy::F64), ty::Int(_) | ty::Uint(_)) if unsafe_cast => - this.float_to_int_unchecked(op.to_scalar().to_f64()?, dest.layout.ty)?.into(), - // Ptr-to-ptr cast - (ty::RawPtr(..), ty::RawPtr(..)) if ptr_cast => { - this.ptr_to_ptr(&op, dest.layout.ty)? - } - // Ptr/Int casts - (ty::RawPtr(..), ty::Int(_) | ty::Uint(_)) if expose_cast => { - this.pointer_expose_address_cast(&op, dest.layout.ty)? + (ty::Float(FloatTy::F32), ty::Int(_) | ty::Uint(_)) if unsafe_cast => { + let f = op.to_scalar().to_f32()?; + this.float_to_int_checked(f, dest.layout.ty, Round::TowardZero) + .ok_or_else(|| { + err_ub_format!( + "`simd_cast` intrinsic called on {f} which cannot be represented in target type `{:?}`", + dest.layout.ty + ) + })? + .into() } - (ty::Int(_) | ty::Uint(_), ty::RawPtr(..)) if from_exposed_cast => { - this.pointer_from_exposed_address_cast(&op, dest.layout.ty)? + (ty::Float(FloatTy::F64), ty::Int(_) | ty::Uint(_)) if unsafe_cast => { + let f = op.to_scalar().to_f64()?; + this.float_to_int_checked(f, dest.layout.ty, Round::TowardZero) + .ok_or_else(|| { + err_ub_format!( + "`simd_cast` intrinsic called on {f} which cannot be represented in target type `{:?}`", + dest.layout.ty + ) + })? + .into() } + // Ptr-to-ptr cast + (ty::RawPtr(..), ty::RawPtr(..)) if ptr_cast => + this.ptr_to_ptr(&op, dest.layout.ty)?, + // Ptr/Int casts + (ty::RawPtr(..), ty::Int(_) | ty::Uint(_)) if expose_cast => + this.pointer_expose_address_cast(&op, dest.layout.ty)?, + (ty::Int(_) | ty::Uint(_), ty::RawPtr(..)) if from_exposed_cast => + this.pointer_from_exposed_address_cast(&op, dest.layout.ty)?, // Error otherwise _ => throw_unsup_format!( diff --git a/src/tools/miri/src/shims/panic.rs b/src/tools/miri/src/shims/panic.rs index 7aefdfcb976..5c0f828e4e6 100644 --- a/src/tools/miri/src/shims/panic.rs +++ b/src/tools/miri/src/shims/panic.rs @@ -188,6 +188,25 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { ) } + /// Start a non-unwinding panic in the interpreter with the given message as payload. + fn start_panic_nounwind(&mut self, msg: &str) -> InterpResult<'tcx> { + let this = self.eval_context_mut(); + + // First arg: message. + let msg = this.allocate_str(msg, MiriMemoryKind::Machine.into(), Mutability::Not)?; + + // Call the lang item. + let panic = this.tcx.lang_items().panic_nounwind().unwrap(); + let panic = ty::Instance::mono(this.tcx.tcx, panic); + this.call_function( + panic, + Abi::Rust, + &[msg.to_ref(this)], + None, + StackPopCleanup::Goto { ret: None, unwind: mir::UnwindAction::Unreachable }, + ) + } + fn assert_panic( &mut self, msg: &mir::AssertMessage<'tcx>, diff --git a/src/tools/miri/src/shims/x86/sse.rs b/src/tools/miri/src/shims/x86/sse.rs index deae0875fb9..b18441bb408 100644 --- a/src/tools/miri/src/shims/x86/sse.rs +++ b/src/tools/miri/src/shims/x86/sse.rs @@ -195,24 +195,12 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { _ => unreachable!(), }; - let mut exact = false; - let cvt = op.to_i128_r(32, rnd, &mut exact); - let res = if cvt.status.intersects( - rustc_apfloat::Status::INVALID_OP - | rustc_apfloat::Status::OVERFLOW - | rustc_apfloat::Status::UNDERFLOW, - ) { - // Input is NaN (flagged with INVALID_OP) or does not fit - // in an i32 (flagged with OVERFLOW or UNDERFLOW), fallback - // to minimum acording to SSE semantics. The INEXACT flag - // is ignored on purpose because rounding can happen during - // float-to-int conversion. - i32::MIN - } else { - i32::try_from(cvt.value).unwrap() - }; + let res = this.float_to_int_checked(op, dest.layout.ty, rnd).unwrap_or_else(|| { + // Fallback to minimum acording to SSE semantics. + Scalar::from_i32(i32::MIN) + }); - this.write_scalar(Scalar::from_i32(res), dest)?; + this.write_scalar(res, dest)?; } // Use to implement _mm_cvtss_si64 and _mm_cvttss_si64. // Converts the first component of `op` from f32 to i64. @@ -232,24 +220,12 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { _ => unreachable!(), }; - let mut exact = false; - let cvt = op.to_i128_r(64, rnd, &mut exact); - let res = if cvt.status.intersects( - rustc_apfloat::Status::INVALID_OP - | rustc_apfloat::Status::OVERFLOW - | rustc_apfloat::Status::UNDERFLOW, - ) { - // Input is NaN (flagged with INVALID_OP) or does not fit - // in an i64 (flagged with OVERFLOW or UNDERFLOW), fallback - // to minimum acording to SSE semantics. The INEXACT flag - // is ignored on purpose because rounding can happen during - // float-to-int conversion. - i64::MIN - } else { - i64::try_from(cvt.value).unwrap() - }; + let res = this.float_to_int_checked(op, dest.layout.ty, rnd).unwrap_or_else(|| { + // Fallback to minimum acording to SSE semantics. + Scalar::from_i64(i64::MIN) + }); - this.write_scalar(Scalar::from_i64(res), dest)?; + this.write_scalar(res, dest)?; } // Used to implement the _mm_cvtsi32_ss function. // Converts `right` from i32 to f32. Returns a SIMD vector with diff --git a/src/tools/miri/test-cargo-miri/Cargo.lock b/src/tools/miri/test-cargo-miri/Cargo.lock index cf5ec2aa883..d5e57a66a8d 100644 --- a/src/tools/miri/test-cargo-miri/Cargo.lock +++ b/src/tools/miri/test-cargo-miri/Cargo.lock @@ -83,27 +83,27 @@ version = "0.1.0" [[package]] name = "proc-macro2" -version = "1.0.60" +version = "1.0.66" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dec2b086b7a862cf4de201096214fa870344cf922b2b30c167badb3af3195406" +checksum = "18fb31db3f9bddb2ea821cde30a9f70117e3f119938b5ee630b7403aa6e2ead9" dependencies = [ "unicode-ident", ] [[package]] name = "quote" -version = "1.0.23" +version = "1.0.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8856d8364d252a14d474036ea1358d63c9e6965c8e5c1885c18f73d70bff9c7b" +checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" dependencies = [ "proc-macro2", ] [[package]] name = "serde_derive" -version = "1.0.152" +version = "1.0.185" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af487d118eecd09402d70a5d72551860e788df87b464af30e5ea6a38c75c541e" +checksum = "dc59dfdcbad1437773485e0367fea4b090a2e0a16d9ffc46af47764536a298ec" dependencies = [ "proc-macro2", "quote", @@ -119,9 +119,9 @@ dependencies = [ [[package]] name = "syn" -version = "1.0.107" +version = "2.0.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f4064b5b16e03ae50984a5a8ed5d4f8803e6bc1fd170a3cda91a1be4b18e3f5" +checksum = "c324c494eba9d92503e6f1ef2e6df781e78f6a7705a0202d9801b198807d518a" dependencies = [ "proc-macro2", "quote", diff --git a/src/tools/miri/test-cargo-miri/Cargo.toml b/src/tools/miri/test-cargo-miri/Cargo.toml index 37c996de662..1688096fd9a 100644 --- a/src/tools/miri/test-cargo-miri/Cargo.toml +++ b/src/tools/miri/test-cargo-miri/Cargo.toml @@ -20,7 +20,8 @@ issue_rust_86261 = { path = "issue-rust-86261" } [dev-dependencies] byteorder_2 = { package = "byteorder", version = "0.5" } # to test dev-dependencies behave as expected, with renaming -serde_derive = "1.0" # not actually used, but exercises some unique code path (`--extern` .so file) +# Not actually used, but exercises some unique code path (`--extern` .so file). +serde_derive = "1.0.185" [build-dependencies] autocfg = "1" diff --git a/src/tools/miri/test-cargo-miri/run-test.py b/src/tools/miri/test-cargo-miri/run-test.py index ca2f69fc8cf..db4341169ea 100755 --- a/src/tools/miri/test-cargo-miri/run-test.py +++ b/src/tools/miri/test-cargo-miri/run-test.py @@ -15,12 +15,14 @@ CGREEN = '\33[32m' CBOLD = '\33[1m' CEND = '\33[0m' +CARGO_EXTRA_FLAGS = os.environ.get("CARGO_EXTRA_FLAGS", "").split() + def fail(msg): print("\nTEST FAIL: {}".format(msg)) sys.exit(1) def cargo_miri(cmd, quiet = True): - args = ["cargo", "miri", cmd] + args = ["cargo", "miri", cmd] + CARGO_EXTRA_FLAGS if quiet: args += ["-q"] if 'MIRI_TEST_TARGET' in os.environ: diff --git a/src/tools/miri/tests/compiletest.rs b/src/tools/miri/tests/compiletest.rs index 8b97c8bb83c..78dd3df01ec 100644 --- a/src/tools/miri/tests/compiletest.rs +++ b/src/tools/miri/tests/compiletest.rs @@ -16,6 +16,11 @@ fn get_host() -> String { .host } +pub fn flagsplit(flags: &str) -> Vec<String> { + // This code is taken from `RUSTFLAGS` handling in cargo. + flags.split(' ').map(str::trim).filter(|s| !s.is_empty()).map(str::to_string).collect() +} + // Build the shared object file for testing external C function calls. fn build_so_for_c_ffi_tests() -> PathBuf { let cc = option_env!("CC").unwrap_or("cc"); @@ -100,14 +105,16 @@ fn test_config(target: &str, path: &str, mode: Mode, with_dependencies: bool) -> if with_dependencies && use_std { config.dependencies_crate_manifest_path = Some(Path::new("test_dependencies").join("Cargo.toml")); - config.dependency_builder.args = vec![ - "run".into(), + let mut builder_args = vec!["run".into()]; + builder_args.extend(flagsplit(&env::var("CARGO_EXTRA_FLAGS").unwrap_or_default())); + builder_args.extend([ "--manifest-path".into(), "cargo-miri/Cargo.toml".into(), "--".into(), "miri".into(), "run".into(), // There is no `cargo miri build` so we just use `cargo miri run`. - ]; + ]); + config.dependency_builder.args = builder_args.into_iter().map(Into::into).collect(); } config } diff --git a/src/tools/miri/tests/fail/alloc/deallocate-twice.stderr b/src/tools/miri/tests/fail/alloc/deallocate-twice.stderr index 23d145e7d30..48d63e59051 100644 --- a/src/tools/miri/tests/fail/alloc/deallocate-twice.stderr +++ b/src/tools/miri/tests/fail/alloc/deallocate-twice.stderr @@ -6,7 +6,17 @@ LL | unsafe { __rust_dealloc(ptr, layout.size(), layout.align()) } | = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information - = note: BACKTRACE: +help: ALLOC was allocated here: + --> $DIR/deallocate-twice.rs:LL:CC + | +LL | let x = alloc(Layout::from_size_align_unchecked(1, 1)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +help: ALLOC was deallocated here: + --> $DIR/deallocate-twice.rs:LL:CC + | +LL | dealloc(x, Layout::from_size_align_unchecked(1, 1)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = note: BACKTRACE (of the first span): = note: inside `std::alloc::dealloc` at RUSTLIB/alloc/src/alloc.rs:LL:CC note: inside `main` --> $DIR/deallocate-twice.rs:LL:CC diff --git a/src/tools/miri/tests/fail/alloc/reallocate-change-alloc.stderr b/src/tools/miri/tests/fail/alloc/reallocate-change-alloc.stderr index 7c7cec211b7..ff4cb399157 100644 --- a/src/tools/miri/tests/fail/alloc/reallocate-change-alloc.stderr +++ b/src/tools/miri/tests/fail/alloc/reallocate-change-alloc.stderr @@ -6,7 +6,17 @@ LL | let _z = *x; | = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information - = note: BACKTRACE: +help: ALLOC was allocated here: + --> $DIR/reallocate-change-alloc.rs:LL:CC + | +LL | let x = alloc(Layout::from_size_align_unchecked(1, 1)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +help: ALLOC was deallocated here: + --> $DIR/reallocate-change-alloc.rs:LL:CC + | +LL | let _y = realloc(x, Layout::from_size_align_unchecked(1, 1), 1); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = note: BACKTRACE (of the first span): = note: inside `main` at $DIR/reallocate-change-alloc.rs:LL:CC note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace diff --git a/src/tools/miri/tests/fail/alloc/reallocate-dangling.stderr b/src/tools/miri/tests/fail/alloc/reallocate-dangling.stderr index 9c222154716..52cc579c1e6 100644 --- a/src/tools/miri/tests/fail/alloc/reallocate-dangling.stderr +++ b/src/tools/miri/tests/fail/alloc/reallocate-dangling.stderr @@ -6,7 +6,17 @@ LL | unsafe { __rust_realloc(ptr, layout.size(), layout.align(), new_size) } | = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information - = note: BACKTRACE: +help: ALLOC was allocated here: + --> $DIR/reallocate-dangling.rs:LL:CC + | +LL | let x = alloc(Layout::from_size_align_unchecked(1, 1)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +help: ALLOC was deallocated here: + --> $DIR/reallocate-dangling.rs:LL:CC + | +LL | dealloc(x, Layout::from_size_align_unchecked(1, 1)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = note: BACKTRACE (of the first span): = note: inside `std::alloc::realloc` at RUSTLIB/alloc/src/alloc.rs:LL:CC note: inside `main` --> $DIR/reallocate-dangling.rs:LL:CC diff --git a/src/tools/miri/tests/fail/both_borrows/issue-miri-1050-1.stack.stderr b/src/tools/miri/tests/fail/both_borrows/issue-miri-1050-1.stack.stderr index c69a3af293c..a5580160c39 100644 --- a/src/tools/miri/tests/fail/both_borrows/issue-miri-1050-1.stack.stderr +++ b/src/tools/miri/tests/fail/both_borrows/issue-miri-1050-1.stack.stderr @@ -6,7 +6,12 @@ LL | Box(unsafe { Unique::new_unchecked(raw) }, alloc) | = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information - = note: BACKTRACE: +help: ALLOC was allocated here: + --> $DIR/issue-miri-1050-1.rs:LL:CC + | +LL | let ptr = Box::into_raw(Box::new(0u16)); + | ^^^^^^^^^^^^^^ + = note: BACKTRACE (of the first span): = note: inside `std::boxed::Box::<u32>::from_raw_in` at RUSTLIB/alloc/src/boxed.rs:LL:CC = note: inside `std::boxed::Box::<u32>::from_raw` at RUSTLIB/alloc/src/boxed.rs:LL:CC note: inside `main` diff --git a/src/tools/miri/tests/fail/both_borrows/issue-miri-1050-1.tree.stderr b/src/tools/miri/tests/fail/both_borrows/issue-miri-1050-1.tree.stderr index c69a3af293c..a5580160c39 100644 --- a/src/tools/miri/tests/fail/both_borrows/issue-miri-1050-1.tree.stderr +++ b/src/tools/miri/tests/fail/both_borrows/issue-miri-1050-1.tree.stderr @@ -6,7 +6,12 @@ LL | Box(unsafe { Unique::new_unchecked(raw) }, alloc) | = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information - = note: BACKTRACE: +help: ALLOC was allocated here: + --> $DIR/issue-miri-1050-1.rs:LL:CC + | +LL | let ptr = Box::into_raw(Box::new(0u16)); + | ^^^^^^^^^^^^^^ + = note: BACKTRACE (of the first span): = note: inside `std::boxed::Box::<u32>::from_raw_in` at RUSTLIB/alloc/src/boxed.rs:LL:CC = note: inside `std::boxed::Box::<u32>::from_raw` at RUSTLIB/alloc/src/boxed.rs:LL:CC note: inside `main` diff --git a/src/tools/miri/tests/fail/breakpoint.rs b/src/tools/miri/tests/fail/breakpoint.rs index fb1d4d958ee..2dd87ea6083 100644 --- a/src/tools/miri/tests/fail/breakpoint.rs +++ b/src/tools/miri/tests/fail/breakpoint.rs @@ -2,6 +2,6 @@ fn main() { unsafe { - core::intrinsics::breakpoint() //~ ERROR: Trace/breakpoint trap + core::intrinsics::breakpoint() //~ ERROR: trace/breakpoint trap }; } diff --git a/src/tools/miri/tests/fail/breakpoint.stderr b/src/tools/miri/tests/fail/breakpoint.stderr index 7b9bbdb3828..8b99c1493b5 100644 --- a/src/tools/miri/tests/fail/breakpoint.stderr +++ b/src/tools/miri/tests/fail/breakpoint.stderr @@ -1,8 +1,8 @@ -error: abnormal termination: Trace/breakpoint trap +error: abnormal termination: trace/breakpoint trap --> $DIR/breakpoint.rs:LL:CC | LL | core::intrinsics::breakpoint() - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Trace/breakpoint trap + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ trace/breakpoint trap | = note: inside `main` at $DIR/breakpoint.rs:LL:CC diff --git a/src/tools/miri/tests/fail/dangling_pointers/dangling_pointer_addr_of.stderr b/src/tools/miri/tests/fail/dangling_pointers/dangling_pointer_addr_of.stderr index 398f216e731..6a3efbdd3dd 100644 --- a/src/tools/miri/tests/fail/dangling_pointers/dangling_pointer_addr_of.stderr +++ b/src/tools/miri/tests/fail/dangling_pointers/dangling_pointer_addr_of.stderr @@ -6,7 +6,17 @@ LL | let x = unsafe { ptr::addr_of!(*p) }; | = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information - = note: BACKTRACE: +help: ALLOC was allocated here: + --> $DIR/dangling_pointer_addr_of.rs:LL:CC + | +LL | let b = Box::new(42); + | ^^^^^^^^^^^^ +help: ALLOC was deallocated here: + --> $DIR/dangling_pointer_addr_of.rs:LL:CC + | +LL | }; + | ^ + = note: BACKTRACE (of the first span): = note: inside `main` at RUSTLIB/core/src/ptr/mod.rs:LL:CC = note: this error originates in the macro `ptr::addr_of` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/src/tools/miri/tests/fail/dangling_pointers/dangling_pointer_deref.stderr b/src/tools/miri/tests/fail/dangling_pointers/dangling_pointer_deref.stderr index cb95d71a605..fad4b4be28c 100644 --- a/src/tools/miri/tests/fail/dangling_pointers/dangling_pointer_deref.stderr +++ b/src/tools/miri/tests/fail/dangling_pointers/dangling_pointer_deref.stderr @@ -6,7 +6,17 @@ LL | let x = unsafe { *p }; | = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information - = note: BACKTRACE: +help: ALLOC was allocated here: + --> $DIR/dangling_pointer_deref.rs:LL:CC + | +LL | let b = Box::new(42); + | ^^^^^^^^^^^^ +help: ALLOC was deallocated here: + --> $DIR/dangling_pointer_deref.rs:LL:CC + | +LL | }; + | ^ + = note: BACKTRACE (of the first span): = note: inside `main` at $DIR/dangling_pointer_deref.rs:LL:CC note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace diff --git a/src/tools/miri/tests/fail/dangling_pointers/dangling_pointer_offset.stderr b/src/tools/miri/tests/fail/dangling_pointers/dangling_pointer_offset.stderr index 85bd2bed9c3..7ef5fd329a4 100644 --- a/src/tools/miri/tests/fail/dangling_pointers/dangling_pointer_offset.stderr +++ b/src/tools/miri/tests/fail/dangling_pointers/dangling_pointer_offset.stderr @@ -6,7 +6,17 @@ LL | let x = unsafe { p.offset(42) }; | = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information - = note: BACKTRACE: +help: ALLOC was allocated here: + --> $DIR/dangling_pointer_offset.rs:LL:CC + | +LL | let b = Box::new(42); + | ^^^^^^^^^^^^ +help: ALLOC was deallocated here: + --> $DIR/dangling_pointer_offset.rs:LL:CC + | +LL | }; + | ^ + = note: BACKTRACE (of the first span): = note: inside `main` at $DIR/dangling_pointer_offset.rs:LL:CC note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace diff --git a/src/tools/miri/tests/fail/dangling_pointers/dangling_pointer_project_underscore.stderr b/src/tools/miri/tests/fail/dangling_pointers/dangling_pointer_project_underscore.stderr index f2d58fe7697..1de6465802b 100644 --- a/src/tools/miri/tests/fail/dangling_pointers/dangling_pointer_project_underscore.stderr +++ b/src/tools/miri/tests/fail/dangling_pointers/dangling_pointer_project_underscore.stderr @@ -6,7 +6,17 @@ LL | let _ = *p; | = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information - = note: BACKTRACE: +help: ALLOC was allocated here: + --> $DIR/dangling_pointer_project_underscore.rs:LL:CC + | +LL | let b = Box::new(42); + | ^^^^^^^^^^^^ +help: ALLOC was deallocated here: + --> $DIR/dangling_pointer_project_underscore.rs:LL:CC + | +LL | }; + | ^ + = note: BACKTRACE (of the first span): = note: inside `main` at $DIR/dangling_pointer_project_underscore.rs:LL:CC note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace diff --git a/src/tools/miri/tests/fail/dangling_pointers/dangling_zst_deref.stderr b/src/tools/miri/tests/fail/dangling_pointers/dangling_zst_deref.stderr index c15f17f3b82..bf6ee775e94 100644 --- a/src/tools/miri/tests/fail/dangling_pointers/dangling_zst_deref.stderr +++ b/src/tools/miri/tests/fail/dangling_pointers/dangling_zst_deref.stderr @@ -6,7 +6,17 @@ LL | let _x = unsafe { *p }; | = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information - = note: BACKTRACE: +help: ALLOC was allocated here: + --> $DIR/dangling_zst_deref.rs:LL:CC + | +LL | let b = Box::new(42); + | ^^^^^^^^^^^^ +help: ALLOC was deallocated here: + --> $DIR/dangling_zst_deref.rs:LL:CC + | +LL | }; + | ^ + = note: BACKTRACE (of the first span): = note: inside `main` at $DIR/dangling_zst_deref.rs:LL:CC note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace diff --git a/src/tools/miri/tests/fail/dangling_pointers/out_of_bounds_read1.stderr b/src/tools/miri/tests/fail/dangling_pointers/out_of_bounds_read1.stderr index b2461ce4230..7d2aed371bd 100644 --- a/src/tools/miri/tests/fail/dangling_pointers/out_of_bounds_read1.stderr +++ b/src/tools/miri/tests/fail/dangling_pointers/out_of_bounds_read1.stderr @@ -6,8 +6,14 @@ LL | let x = unsafe { *v.as_ptr().wrapping_offset(5) }; | = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information - = note: BACKTRACE: +help: ALLOC was allocated here: + --> $DIR/out_of_bounds_read1.rs:LL:CC + | +LL | let v: Vec<u8> = vec![1, 2]; + | ^^^^^^^^^^ + = note: BACKTRACE (of the first span): = note: inside `main` at $DIR/out_of_bounds_read1.rs:LL:CC + = note: this error originates in the macro `vec` (in Nightly builds, run with -Z macro-backtrace for more info) note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace diff --git a/src/tools/miri/tests/fail/dangling_pointers/out_of_bounds_read2.stderr b/src/tools/miri/tests/fail/dangling_pointers/out_of_bounds_read2.stderr index b17058b4062..69a8498f097 100644 --- a/src/tools/miri/tests/fail/dangling_pointers/out_of_bounds_read2.stderr +++ b/src/tools/miri/tests/fail/dangling_pointers/out_of_bounds_read2.stderr @@ -6,8 +6,14 @@ LL | let x = unsafe { *v.as_ptr().wrapping_offset(5) }; | = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information - = note: BACKTRACE: +help: ALLOC was allocated here: + --> $DIR/out_of_bounds_read2.rs:LL:CC + | +LL | let v: Vec<u8> = vec![1, 2]; + | ^^^^^^^^^^ + = note: BACKTRACE (of the first span): = note: inside `main` at $DIR/out_of_bounds_read2.rs:LL:CC + = note: this error originates in the macro `vec` (in Nightly builds, run with -Z macro-backtrace for more info) note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace diff --git a/src/tools/miri/tests/fail/data_race/dealloc_read_race2.stderr b/src/tools/miri/tests/fail/data_race/dealloc_read_race2.stderr index 4efc35c15e2..810e48d59c6 100644 --- a/src/tools/miri/tests/fail/data_race/dealloc_read_race2.stderr +++ b/src/tools/miri/tests/fail/data_race/dealloc_read_race2.stderr @@ -6,7 +6,21 @@ LL | *ptr.0 | = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information - = note: BACKTRACE: +help: ALLOC was allocated here: + --> $DIR/dealloc_read_race2.rs:LL:CC + | +LL | let pointer: *mut usize = Box::into_raw(Box::new(0usize)); + | ^^^^^^^^^^^^^^^^ +help: ALLOC was deallocated here: + --> $DIR/dealloc_read_race2.rs:LL:CC + | +LL | / __rust_dealloc( +LL | | ptr.0 as *mut _, +LL | | std::mem::size_of::<usize>(), +LL | | std::mem::align_of::<usize>(), +LL | | ) + | |_____________^ + = note: BACKTRACE (of the first span): = note: inside closure at $DIR/dealloc_read_race2.rs:LL:CC note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace diff --git a/src/tools/miri/tests/fail/data_race/dealloc_write_race2.stderr b/src/tools/miri/tests/fail/data_race/dealloc_write_race2.stderr index fad525830e6..7d672cd4d62 100644 --- a/src/tools/miri/tests/fail/data_race/dealloc_write_race2.stderr +++ b/src/tools/miri/tests/fail/data_race/dealloc_write_race2.stderr @@ -6,7 +6,21 @@ LL | *ptr.0 = 2; | = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information - = note: BACKTRACE: +help: ALLOC was allocated here: + --> $DIR/dealloc_write_race2.rs:LL:CC + | +LL | let pointer: *mut usize = Box::into_raw(Box::new(0usize)); + | ^^^^^^^^^^^^^^^^ +help: ALLOC was deallocated here: + --> $DIR/dealloc_write_race2.rs:LL:CC + | +LL | / __rust_dealloc( +LL | | ptr.0 as *mut _, +LL | | std::mem::size_of::<usize>(), +LL | | std::mem::align_of::<usize>(), +LL | | ); + | |_____________^ + = note: BACKTRACE (of the first span): = note: inside closure at $DIR/dealloc_write_race2.rs:LL:CC note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace diff --git a/src/tools/miri/tests/fail/function_calls/arg_inplace_mutate.rs b/src/tools/miri/tests/fail/function_calls/arg_inplace_mutate.rs index 625a8bda8af..d47af50d407 100644 --- a/src/tools/miri/tests/fail/function_calls/arg_inplace_mutate.rs +++ b/src/tools/miri/tests/fail/function_calls/arg_inplace_mutate.rs @@ -8,13 +8,13 @@ pub struct S(i32); #[custom_mir(dialect = "runtime", phase = "optimized")] fn main() { mir! { - let unit: (); + let _unit: (); { let non_copy = S(42); let ptr = std::ptr::addr_of_mut!(non_copy); // Inside `callee`, the first argument and `*ptr` are basically // aliasing places! - Call(unit, after_call, callee(Move(*ptr), ptr)) + Call(_unit = callee(Move(*ptr), ptr), after_call) } after_call = { Return() diff --git a/src/tools/miri/tests/fail/function_calls/arg_inplace_mutate.stack.stderr b/src/tools/miri/tests/fail/function_calls/arg_inplace_mutate.stack.stderr index 471dc1dd6dd..381442e69b1 100644 --- a/src/tools/miri/tests/fail/function_calls/arg_inplace_mutate.stack.stderr +++ b/src/tools/miri/tests/fail/function_calls/arg_inplace_mutate.stack.stderr @@ -10,7 +10,7 @@ help: <TAG> was created by a SharedReadWrite retag at offsets [0x0..0x4] --> $DIR/arg_inplace_mutate.rs:LL:CC | LL | / mir! { -LL | | let unit: (); +LL | | let _unit: (); LL | | { LL | | let non_copy = S(42); ... | @@ -27,8 +27,8 @@ LL | unsafe { ptr.write(S(0)) }; note: inside `main` --> $DIR/arg_inplace_mutate.rs:LL:CC | -LL | Call(unit, after_call, callee(Move(*ptr), ptr)) - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | Call(_unit = callee(Move(*ptr), ptr), after_call) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ = note: this error originates in the macro `::core::intrinsics::mir::__internal_remove_let` which comes from the expansion of the macro `mir` (in Nightly builds, run with -Z macro-backtrace for more info) note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace diff --git a/src/tools/miri/tests/fail/function_calls/arg_inplace_mutate.tree.stderr b/src/tools/miri/tests/fail/function_calls/arg_inplace_mutate.tree.stderr index 8393b80f25b..544cd575ada 100644 --- a/src/tools/miri/tests/fail/function_calls/arg_inplace_mutate.tree.stderr +++ b/src/tools/miri/tests/fail/function_calls/arg_inplace_mutate.tree.stderr @@ -12,7 +12,7 @@ help: the accessed tag <TAG> was created here --> $DIR/arg_inplace_mutate.rs:LL:CC | LL | / mir! { -LL | | let unit: (); +LL | | let _unit: (); LL | | { LL | | let non_copy = S(42); ... | @@ -29,8 +29,8 @@ LL | unsafe { ptr.write(S(0)) }; note: inside `main` --> $DIR/arg_inplace_mutate.rs:LL:CC | -LL | Call(unit, after_call, callee(Move(*ptr), ptr)) - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | Call(_unit = callee(Move(*ptr), ptr), after_call) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ = note: this error originates in the macro `::core::intrinsics::mir::__internal_remove_let` which comes from the expansion of the macro `mir` (in Nightly builds, run with -Z macro-backtrace for more info) note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace diff --git a/src/tools/miri/tests/fail/function_calls/arg_inplace_observe_after.rs b/src/tools/miri/tests/fail/function_calls/arg_inplace_observe_after.rs index 093b55759fd..ea773048dd4 100644 --- a/src/tools/miri/tests/fail/function_calls/arg_inplace_observe_after.rs +++ b/src/tools/miri/tests/fail/function_calls/arg_inplace_observe_after.rs @@ -6,12 +6,12 @@ pub struct S(i32); #[custom_mir(dialect = "runtime", phase = "optimized")] fn main() { mir! { - let unit: (); + let _unit: (); let _observe: i32; { let non_copy = S(42); // This could change `non_copy` in-place - Call(unit, after_call, change_arg(Move(non_copy))) + Call(_unit = change_arg(Move(non_copy)), after_call) } after_call = { // So now we must not be allowed to observe non-copy again. diff --git a/src/tools/miri/tests/fail/function_calls/arg_inplace_observe_during.none.stderr b/src/tools/miri/tests/fail/function_calls/arg_inplace_observe_during.none.stderr index baa91484793..cba23c21d12 100644 --- a/src/tools/miri/tests/fail/function_calls/arg_inplace_observe_during.none.stderr +++ b/src/tools/miri/tests/fail/function_calls/arg_inplace_observe_during.none.stderr @@ -11,8 +11,8 @@ LL | unsafe { ptr.read() }; note: inside `main` --> $DIR/arg_inplace_observe_during.rs:LL:CC | -LL | Call(unit, after_call, change_arg(Move(*ptr), ptr)) - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | Call(_unit = change_arg(Move(*ptr), ptr), after_call) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace diff --git a/src/tools/miri/tests/fail/function_calls/arg_inplace_observe_during.rs b/src/tools/miri/tests/fail/function_calls/arg_inplace_observe_during.rs index 2e57872db96..8c6a7df7a6d 100644 --- a/src/tools/miri/tests/fail/function_calls/arg_inplace_observe_during.rs +++ b/src/tools/miri/tests/fail/function_calls/arg_inplace_observe_during.rs @@ -9,12 +9,12 @@ pub struct S(i32); #[custom_mir(dialect = "runtime", phase = "optimized")] fn main() { mir! { - let unit: (); + let _unit: (); { let non_copy = S(42); let ptr = std::ptr::addr_of_mut!(non_copy); // This could change `non_copy` in-place - Call(unit, after_call, change_arg(Move(*ptr), ptr)) + Call(_unit = change_arg(Move(*ptr), ptr), after_call) } after_call = { Return() diff --git a/src/tools/miri/tests/fail/function_calls/arg_inplace_observe_during.stack.stderr b/src/tools/miri/tests/fail/function_calls/arg_inplace_observe_during.stack.stderr index a842d3a8044..f8532186be2 100644 --- a/src/tools/miri/tests/fail/function_calls/arg_inplace_observe_during.stack.stderr +++ b/src/tools/miri/tests/fail/function_calls/arg_inplace_observe_during.stack.stderr @@ -10,7 +10,7 @@ help: <TAG> was created by a SharedReadWrite retag at offsets [0x0..0x4] --> $DIR/arg_inplace_observe_during.rs:LL:CC | LL | / mir! { -LL | | let unit: (); +LL | | let _unit: (); LL | | { LL | | let non_copy = S(42); ... | @@ -27,8 +27,8 @@ LL | x.0 = 0; note: inside `main` --> $DIR/arg_inplace_observe_during.rs:LL:CC | -LL | Call(unit, after_call, change_arg(Move(*ptr), ptr)) - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | Call(_unit = change_arg(Move(*ptr), ptr), after_call) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ = note: this error originates in the macro `::core::intrinsics::mir::__internal_remove_let` which comes from the expansion of the macro `mir` (in Nightly builds, run with -Z macro-backtrace for more info) note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace diff --git a/src/tools/miri/tests/fail/function_calls/arg_inplace_observe_during.tree.stderr b/src/tools/miri/tests/fail/function_calls/arg_inplace_observe_during.tree.stderr index 5af4856bbe3..c33645bdd28 100644 --- a/src/tools/miri/tests/fail/function_calls/arg_inplace_observe_during.tree.stderr +++ b/src/tools/miri/tests/fail/function_calls/arg_inplace_observe_during.tree.stderr @@ -12,7 +12,7 @@ help: the accessed tag <TAG> was created here --> $DIR/arg_inplace_observe_during.rs:LL:CC | LL | / mir! { -LL | | let unit: (); +LL | | let _unit: (); LL | | { LL | | let non_copy = S(42); ... | @@ -29,8 +29,8 @@ LL | x.0 = 0; note: inside `main` --> $DIR/arg_inplace_observe_during.rs:LL:CC | -LL | Call(unit, after_call, change_arg(Move(*ptr), ptr)) - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | Call(_unit = change_arg(Move(*ptr), ptr), after_call) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ = note: this error originates in the macro `::core::intrinsics::mir::__internal_remove_let` which comes from the expansion of the macro `mir` (in Nightly builds, run with -Z macro-backtrace for more info) note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace diff --git a/src/tools/miri/tests/fail/function_calls/exported_symbol_bad_unwind2.both.stderr b/src/tools/miri/tests/fail/function_calls/exported_symbol_bad_unwind2.both.stderr index bf5199307f6..e4821bc0bac 100644 --- a/src/tools/miri/tests/fail/function_calls/exported_symbol_bad_unwind2.both.stderr +++ b/src/tools/miri/tests/fail/function_calls/exported_symbol_bad_unwind2.both.stderr @@ -1,22 +1,35 @@ thread 'main' panicked at $DIR/exported_symbol_bad_unwind2.rs:LL:CC: explicit panic note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace -error: abnormal termination: panic in a function that cannot unwind +thread 'main' panicked at RUSTLIB/core/src/panicking.rs:LL:CC: +panic in a function that cannot unwind +stack backtrace: +thread caused non-unwinding panic. aborting. +error: abnormal termination: the program aborted execution + --> RUSTLIB/std/src/sys/PLATFORM/mod.rs:LL:CC + | +LL | ABORT(); + | ^ the program aborted execution + | + = note: inside `std::sys::PLATFORM::abort_internal` at RUSTLIB/std/src/sys/PLATFORM/mod.rs:LL:CC + = note: inside `std::panicking::rust_panic_with_hook` at RUSTLIB/std/src/panicking.rs:LL:CC + = note: inside closure at RUSTLIB/std/src/panicking.rs:LL:CC + = note: inside `std::sys_common::backtrace::__rust_end_short_backtrace::<[closure@std::panicking::begin_panic_handler::{closure#0}], !>` at RUSTLIB/std/src/sys_common/backtrace.rs:LL:CC + = note: inside `std::panicking::begin_panic_handler` at RUSTLIB/std/src/panicking.rs:LL:CC + = note: inside `core::panicking::panic_nounwind` at RUSTLIB/core/src/panicking.rs:LL:CC + = note: inside `core::panicking::panic_cannot_unwind` at RUSTLIB/core/src/panicking.rs:LL:CC +note: inside `nounwind` --> $DIR/exported_symbol_bad_unwind2.rs:LL:CC | LL | / extern "C-unwind" fn nounwind() { -LL | | -LL | | LL | | panic!(); LL | | } - | |_^ panic in a function that cannot unwind - | - = note: inside `nounwind` at $DIR/exported_symbol_bad_unwind2.rs:LL:CC + | |_^ note: inside `main` --> $DIR/exported_symbol_bad_unwind2.rs:LL:CC | LL | unsafe { nounwind() } - | ^^^^^^^^^^ + | ^ note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace diff --git a/src/tools/miri/tests/fail/function_calls/exported_symbol_bad_unwind2.definition.stderr b/src/tools/miri/tests/fail/function_calls/exported_symbol_bad_unwind2.definition.stderr index bf5199307f6..e4821bc0bac 100644 --- a/src/tools/miri/tests/fail/function_calls/exported_symbol_bad_unwind2.definition.stderr +++ b/src/tools/miri/tests/fail/function_calls/exported_symbol_bad_unwind2.definition.stderr @@ -1,22 +1,35 @@ thread 'main' panicked at $DIR/exported_symbol_bad_unwind2.rs:LL:CC: explicit panic note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace -error: abnormal termination: panic in a function that cannot unwind +thread 'main' panicked at RUSTLIB/core/src/panicking.rs:LL:CC: +panic in a function that cannot unwind +stack backtrace: +thread caused non-unwinding panic. aborting. +error: abnormal termination: the program aborted execution + --> RUSTLIB/std/src/sys/PLATFORM/mod.rs:LL:CC + | +LL | ABORT(); + | ^ the program aborted execution + | + = note: inside `std::sys::PLATFORM::abort_internal` at RUSTLIB/std/src/sys/PLATFORM/mod.rs:LL:CC + = note: inside `std::panicking::rust_panic_with_hook` at RUSTLIB/std/src/panicking.rs:LL:CC + = note: inside closure at RUSTLIB/std/src/panicking.rs:LL:CC + = note: inside `std::sys_common::backtrace::__rust_end_short_backtrace::<[closure@std::panicking::begin_panic_handler::{closure#0}], !>` at RUSTLIB/std/src/sys_common/backtrace.rs:LL:CC + = note: inside `std::panicking::begin_panic_handler` at RUSTLIB/std/src/panicking.rs:LL:CC + = note: inside `core::panicking::panic_nounwind` at RUSTLIB/core/src/panicking.rs:LL:CC + = note: inside `core::panicking::panic_cannot_unwind` at RUSTLIB/core/src/panicking.rs:LL:CC +note: inside `nounwind` --> $DIR/exported_symbol_bad_unwind2.rs:LL:CC | LL | / extern "C-unwind" fn nounwind() { -LL | | -LL | | LL | | panic!(); LL | | } - | |_^ panic in a function that cannot unwind - | - = note: inside `nounwind` at $DIR/exported_symbol_bad_unwind2.rs:LL:CC + | |_^ note: inside `main` --> $DIR/exported_symbol_bad_unwind2.rs:LL:CC | LL | unsafe { nounwind() } - | ^^^^^^^^^^ + | ^ note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace diff --git a/src/tools/miri/tests/fail/function_calls/exported_symbol_bad_unwind2.extern_block.stderr b/src/tools/miri/tests/fail/function_calls/exported_symbol_bad_unwind2.extern_block.stderr index c774bd4dd91..25e13d74754 100644 --- a/src/tools/miri/tests/fail/function_calls/exported_symbol_bad_unwind2.extern_block.stderr +++ b/src/tools/miri/tests/fail/function_calls/exported_symbol_bad_unwind2.extern_block.stderr @@ -5,7 +5,7 @@ error: Undefined Behavior: unwinding past a stack frame that does not allow unwi --> $DIR/exported_symbol_bad_unwind2.rs:LL:CC | LL | unsafe { nounwind() } - | ^^^^^^^^^^ unwinding past a stack frame that does not allow unwinding + | ^ unwinding past a stack frame that does not allow unwinding | = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information diff --git a/src/tools/miri/tests/fail/function_calls/exported_symbol_bad_unwind2.rs b/src/tools/miri/tests/fail/function_calls/exported_symbol_bad_unwind2.rs index 65ba3433c28..10bd5eb7211 100644 --- a/src/tools/miri/tests/fail/function_calls/exported_symbol_bad_unwind2.rs +++ b/src/tools/miri/tests/fail/function_calls/exported_symbol_bad_unwind2.rs @@ -1,11 +1,14 @@ //@revisions: extern_block definition both +//@normalize-stderr-test: "unsafe \{ libc::abort\(\) \}|crate::intrinsics::abort\(\);" -> "ABORT();" +//@normalize-stderr-test: "\| +\^+" -> "| ^" +//@normalize-stderr-test: "\n +[0-9]+:[^\n]+" -> "$1" +//@normalize-stderr-test: "\n at [^\n]+" -> "$1" +//@[definition,both]error-in-other-file: aborted execution #![feature(rustc_attrs, c_unwind)] #[cfg_attr(any(definition, both), rustc_nounwind)] #[no_mangle] extern "C-unwind" fn nounwind() { - //~[definition]^ ERROR: abnormal termination: panic in a function that cannot unwind - //~[both]^^ ERROR: abnormal termination: panic in a function that cannot unwind panic!(); } diff --git a/src/tools/miri/tests/fail/function_calls/return_pointer_aliasing.none.stderr b/src/tools/miri/tests/fail/function_calls/return_pointer_aliasing.none.stderr index 9d9dfc89f89..0a31adabf73 100644 --- a/src/tools/miri/tests/fail/function_calls/return_pointer_aliasing.none.stderr +++ b/src/tools/miri/tests/fail/function_calls/return_pointer_aliasing.none.stderr @@ -11,8 +11,8 @@ LL | unsafe { ptr.read() }; note: inside `main` --> $DIR/return_pointer_aliasing.rs:LL:CC | -LL | Call(*ptr, after_call, myfun(ptr)) - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | Call(*ptr = myfun(ptr), after_call) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace diff --git a/src/tools/miri/tests/fail/function_calls/return_pointer_aliasing.rs b/src/tools/miri/tests/fail/function_calls/return_pointer_aliasing.rs index 829809102fa..3d560af3d5e 100644 --- a/src/tools/miri/tests/fail/function_calls/return_pointer_aliasing.rs +++ b/src/tools/miri/tests/fail/function_calls/return_pointer_aliasing.rs @@ -15,7 +15,7 @@ pub fn main() { let ptr = &raw mut x; // We arrange for `myfun` to have a pointer that aliases // its return place. Even just reading from that pointer is UB. - Call(*ptr, after_call, myfun(ptr)) + Call(*ptr = myfun(ptr), after_call) } after_call = { diff --git a/src/tools/miri/tests/fail/function_calls/return_pointer_aliasing.stack.stderr b/src/tools/miri/tests/fail/function_calls/return_pointer_aliasing.stack.stderr index d486dcb95df..875cc5edad9 100644 --- a/src/tools/miri/tests/fail/function_calls/return_pointer_aliasing.stack.stderr +++ b/src/tools/miri/tests/fail/function_calls/return_pointer_aliasing.stack.stderr @@ -27,8 +27,8 @@ LL | unsafe { ptr.read() }; note: inside `main` --> $DIR/return_pointer_aliasing.rs:LL:CC | -LL | Call(*ptr, after_call, myfun(ptr)) - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | Call(*ptr = myfun(ptr), after_call) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ = note: this error originates in the macro `::core::intrinsics::mir::__internal_remove_let` which comes from the expansion of the macro `mir` (in Nightly builds, run with -Z macro-backtrace for more info) note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace diff --git a/src/tools/miri/tests/fail/function_calls/return_pointer_aliasing.tree.stderr b/src/tools/miri/tests/fail/function_calls/return_pointer_aliasing.tree.stderr index c491a904a10..66c2fb8db19 100644 --- a/src/tools/miri/tests/fail/function_calls/return_pointer_aliasing.tree.stderr +++ b/src/tools/miri/tests/fail/function_calls/return_pointer_aliasing.tree.stderr @@ -29,8 +29,8 @@ LL | unsafe { ptr.read() }; note: inside `main` --> $DIR/return_pointer_aliasing.rs:LL:CC | -LL | Call(*ptr, after_call, myfun(ptr)) - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | Call(*ptr = myfun(ptr), after_call) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ = note: this error originates in the macro `::core::intrinsics::mir::__internal_remove_let` which comes from the expansion of the macro `mir` (in Nightly builds, run with -Z macro-backtrace for more info) note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace diff --git a/src/tools/miri/tests/fail/function_calls/return_pointer_aliasing2.rs b/src/tools/miri/tests/fail/function_calls/return_pointer_aliasing2.rs index 7e9a6320026..9d53faccd1e 100644 --- a/src/tools/miri/tests/fail/function_calls/return_pointer_aliasing2.rs +++ b/src/tools/miri/tests/fail/function_calls/return_pointer_aliasing2.rs @@ -9,11 +9,11 @@ use std::intrinsics::mir::*; pub fn main() { mir! { { - let x = 0; - let ptr = &raw mut x; + let _x = 0; + let ptr = &raw mut _x; // We arrange for `myfun` to have a pointer that aliases // its return place. Even just reading from that pointer is UB. - Call(x, after_call, myfun(ptr)) + Call(_x = myfun(ptr), after_call) } after_call = { diff --git a/src/tools/miri/tests/fail/function_calls/return_pointer_aliasing2.stderr b/src/tools/miri/tests/fail/function_calls/return_pointer_aliasing2.stderr index 33a8a4b46bd..443ee8643fc 100644 --- a/src/tools/miri/tests/fail/function_calls/return_pointer_aliasing2.stderr +++ b/src/tools/miri/tests/fail/function_calls/return_pointer_aliasing2.stderr @@ -13,8 +13,8 @@ help: the accessed tag <TAG> was created here | LL | / mir! { LL | | { -LL | | let x = 0; -LL | | let ptr = &raw mut x; +LL | | let _x = 0; +LL | | let ptr = &raw mut _x; ... | LL | | } LL | | } @@ -29,8 +29,8 @@ LL | unsafe { ptr.write(0) }; note: inside `main` --> $DIR/return_pointer_aliasing2.rs:LL:CC | -LL | Call(x, after_call, myfun(ptr)) - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | Call(_x = myfun(ptr), after_call) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ = note: this error originates in the macro `::core::intrinsics::mir::__internal_remove_let` which comes from the expansion of the macro `mir` (in Nightly builds, run with -Z macro-backtrace for more info) note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace diff --git a/src/tools/miri/tests/fail/generator-pinned-moved.stderr b/src/tools/miri/tests/fail/generator-pinned-moved.stderr index 3eb17f05584..e29e352e64b 100644 --- a/src/tools/miri/tests/fail/generator-pinned-moved.stderr +++ b/src/tools/miri/tests/fail/generator-pinned-moved.stderr @@ -6,7 +6,17 @@ LL | *num += 1; | = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information - = note: BACKTRACE: +help: ALLOC was allocated here: + --> $DIR/generator-pinned-moved.rs:LL:CC + | +LL | let mut generator_iterator = Box::new(GeneratorIteratorAdapter(firstn())); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +help: ALLOC was deallocated here: + --> $DIR/generator-pinned-moved.rs:LL:CC + | +LL | }; // *deallocate* generator_iterator + | ^ + = note: BACKTRACE (of the first span): = note: inside closure at $DIR/generator-pinned-moved.rs:LL:CC note: inside `<GeneratorIteratorAdapter<[static generator@$DIR/generator-pinned-moved.rs:LL:CC]> as std::iter::Iterator>::next` --> $DIR/generator-pinned-moved.rs:LL:CC diff --git a/src/tools/miri/tests/fail/intrinsics/float_to_int_64_neg.stderr b/src/tools/miri/tests/fail/intrinsics/float_to_int_64_neg.stderr index 7e24f45f653..ddf3249d059 100644 --- a/src/tools/miri/tests/fail/intrinsics/float_to_int_64_neg.stderr +++ b/src/tools/miri/tests/fail/intrinsics/float_to_int_64_neg.stderr @@ -1,8 +1,8 @@ -error: Undefined Behavior: `float_to_int_unchecked` intrinsic called on -1 which cannot be represented in target type `u128` +error: Undefined Behavior: `float_to_int_unchecked` intrinsic called on -1.0000000000000999 which cannot be represented in target type `u128` --> $DIR/float_to_int_64_neg.rs:LL:CC | LL | float_to_int_unchecked::<f64, u128>(-1.0000000000001f64); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `float_to_int_unchecked` intrinsic called on -1 which cannot be represented in target type `u128` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `float_to_int_unchecked` intrinsic called on -1.0000000000000999 which cannot be represented in target type `u128` | = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information diff --git a/src/tools/miri/tests/fail/intrinsics/ptr_offset_ptr_plus_0.stderr b/src/tools/miri/tests/fail/intrinsics/ptr_offset_ptr_plus_0.stderr index b18147ce379..7cc4bc184a1 100644 --- a/src/tools/miri/tests/fail/intrinsics/ptr_offset_ptr_plus_0.stderr +++ b/src/tools/miri/tests/fail/intrinsics/ptr_offset_ptr_plus_0.stderr @@ -6,7 +6,12 @@ LL | let _x = unsafe { x.offset(0) }; // UB despite offset 0, the pointer is | = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information - = note: BACKTRACE: +help: ALLOC was allocated here: + --> $DIR/ptr_offset_ptr_plus_0.rs:LL:CC + | +LL | let x = Box::into_raw(Box::new(0u32)); + | ^^^^^^^^^^^^^^ + = note: BACKTRACE (of the first span): = note: inside `main` at $DIR/ptr_offset_ptr_plus_0.rs:LL:CC note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace diff --git a/src/tools/miri/tests/fail/intrinsics/simd-float-to-int.stderr b/src/tools/miri/tests/fail/intrinsics/simd-float-to-int.stderr index ea5ad62aea9..7b2387944af 100644 --- a/src/tools/miri/tests/fail/intrinsics/simd-float-to-int.stderr +++ b/src/tools/miri/tests/fail/intrinsics/simd-float-to-int.stderr @@ -1,8 +1,8 @@ -error: Undefined Behavior: `float_to_int_unchecked` intrinsic called on 3.40282347E+38 which cannot be represented in target type `i32` +error: Undefined Behavior: `simd_cast` intrinsic called on 3.40282347E+38 which cannot be represented in target type `i32` --> $DIR/simd-float-to-int.rs:LL:CC | LL | let _x: i32x2 = f32x2::from_array([f32::MAX, f32::MIN]).to_int_unchecked(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `float_to_int_unchecked` intrinsic called on 3.40282347E+38 which cannot be represented in target type `i32` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `simd_cast` intrinsic called on 3.40282347E+38 which cannot be represented in target type `i32` | = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information diff --git a/src/tools/miri/tests/fail/intrinsics/simd-scatter.stderr b/src/tools/miri/tests/fail/intrinsics/simd-scatter.stderr index a745b61029d..5beee034db2 100644 --- a/src/tools/miri/tests/fail/intrinsics/simd-scatter.stderr +++ b/src/tools/miri/tests/fail/intrinsics/simd-scatter.stderr @@ -11,8 +11,14 @@ LL | | ); | = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information - = note: BACKTRACE: +help: ALLOC was allocated here: + --> $DIR/simd-scatter.rs:LL:CC + | +LL | let mut vec: Vec<i8> = vec![10, 11, 12, 13, 14, 15, 16, 17, 18]; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = note: BACKTRACE (of the first span): = note: inside `main` at $DIR/simd-scatter.rs:LL:CC + = note: this error originates in the macro `vec` (in Nightly builds, run with -Z macro-backtrace for more info) note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace diff --git a/src/tools/miri/tests/fail/intrinsics/uninit_uninhabited_type.rs b/src/tools/miri/tests/fail/intrinsics/uninit_uninhabited_type.rs index 59781f02366..b8fa32c986f 100644 --- a/src/tools/miri/tests/fail/intrinsics/uninit_uninhabited_type.rs +++ b/src/tools/miri/tests/fail/intrinsics/uninit_uninhabited_type.rs @@ -1,7 +1,11 @@ +//@normalize-stderr-test: "unsafe \{ libc::abort\(\) \}|crate::intrinsics::abort\(\);" -> "ABORT();" +//@normalize-stderr-test: "\| +\^+" -> "| ^" +//@normalize-stderr-test: "\n +[0-9]+:[^\n]+" -> "$1" +//@normalize-stderr-test: "\n at [^\n]+" -> "$1" +//@error-in-other-file: aborted execution #![feature(never_type)] #[allow(deprecated, invalid_value)] fn main() { let _ = unsafe { std::mem::uninitialized::<!>() }; - //~^ ERROR: attempted to instantiate uninhabited type `!` } diff --git a/src/tools/miri/tests/fail/intrinsics/uninit_uninhabited_type.stderr b/src/tools/miri/tests/fail/intrinsics/uninit_uninhabited_type.stderr index f2cc3430326..70e10e8dc48 100644 --- a/src/tools/miri/tests/fail/intrinsics/uninit_uninhabited_type.stderr +++ b/src/tools/miri/tests/fail/intrinsics/uninit_uninhabited_type.stderr @@ -1,10 +1,24 @@ -error: abnormal termination: aborted execution: attempted to instantiate uninhabited type `!` +thread 'main' panicked at RUSTLIB/core/src/panicking.rs:LL:CC: +aborted execution: attempted to instantiate uninhabited type `!` +note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace +thread caused non-unwinding panic. aborting. +error: abnormal termination: the program aborted execution + --> RUSTLIB/std/src/sys/PLATFORM/mod.rs:LL:CC + | +LL | ABORT(); + | ^ the program aborted execution + | + = note: inside `std::sys::PLATFORM::abort_internal` at RUSTLIB/std/src/sys/PLATFORM/mod.rs:LL:CC + = note: inside `std::panicking::rust_panic_with_hook` at RUSTLIB/std/src/panicking.rs:LL:CC + = note: inside closure at RUSTLIB/std/src/panicking.rs:LL:CC + = note: inside `std::sys_common::backtrace::__rust_end_short_backtrace::<[closure@std::panicking::begin_panic_handler::{closure#0}], !>` at RUSTLIB/std/src/sys_common/backtrace.rs:LL:CC + = note: inside `std::panicking::begin_panic_handler` at RUSTLIB/std/src/panicking.rs:LL:CC + = note: inside `core::panicking::panic_nounwind` at RUSTLIB/core/src/panicking.rs:LL:CC +note: inside `main` --> $DIR/uninit_uninhabited_type.rs:LL:CC | LL | let _ = unsafe { std::mem::uninitialized::<!>() }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ aborted execution: attempted to instantiate uninhabited type `!` - | - = note: inside `main` at $DIR/uninit_uninhabited_type.rs:LL:CC + | ^ note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace diff --git a/src/tools/miri/tests/fail/intrinsics/zero_fn_ptr.rs b/src/tools/miri/tests/fail/intrinsics/zero_fn_ptr.rs index e9c6e464e88..70ae60593a1 100644 --- a/src/tools/miri/tests/fail/intrinsics/zero_fn_ptr.rs +++ b/src/tools/miri/tests/fail/intrinsics/zero_fn_ptr.rs @@ -1,5 +1,10 @@ +//@normalize-stderr-test: "unsafe \{ libc::abort\(\) \}|crate::intrinsics::abort\(\);" -> "ABORT();" +//@normalize-stderr-test: "\| +\^+" -> "| ^" +//@normalize-stderr-test: "\n +[0-9]+:[^\n]+" -> "$1" +//@normalize-stderr-test: "\n at [^\n]+" -> "$1" +//@error-in-other-file: aborted execution + #[allow(deprecated, invalid_value)] fn main() { let _ = unsafe { std::mem::zeroed::<fn()>() }; - //~^ ERROR: attempted to zero-initialize type `fn()`, which is invalid } diff --git a/src/tools/miri/tests/fail/intrinsics/zero_fn_ptr.stderr b/src/tools/miri/tests/fail/intrinsics/zero_fn_ptr.stderr index 77d58228043..56143b6c205 100644 --- a/src/tools/miri/tests/fail/intrinsics/zero_fn_ptr.stderr +++ b/src/tools/miri/tests/fail/intrinsics/zero_fn_ptr.stderr @@ -1,10 +1,24 @@ -error: abnormal termination: aborted execution: attempted to zero-initialize type `fn()`, which is invalid +thread 'main' panicked at RUSTLIB/core/src/panicking.rs:LL:CC: +aborted execution: attempted to zero-initialize type `fn()`, which is invalid +note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace +thread caused non-unwinding panic. aborting. +error: abnormal termination: the program aborted execution + --> RUSTLIB/std/src/sys/PLATFORM/mod.rs:LL:CC + | +LL | ABORT(); + | ^ the program aborted execution + | + = note: inside `std::sys::PLATFORM::abort_internal` at RUSTLIB/std/src/sys/PLATFORM/mod.rs:LL:CC + = note: inside `std::panicking::rust_panic_with_hook` at RUSTLIB/std/src/panicking.rs:LL:CC + = note: inside closure at RUSTLIB/std/src/panicking.rs:LL:CC + = note: inside `std::sys_common::backtrace::__rust_end_short_backtrace::<[closure@std::panicking::begin_panic_handler::{closure#0}], !>` at RUSTLIB/std/src/sys_common/backtrace.rs:LL:CC + = note: inside `std::panicking::begin_panic_handler` at RUSTLIB/std/src/panicking.rs:LL:CC + = note: inside `core::panicking::panic_nounwind` at RUSTLIB/core/src/panicking.rs:LL:CC +note: inside `main` --> $DIR/zero_fn_ptr.rs:LL:CC | LL | let _ = unsafe { std::mem::zeroed::<fn()>() }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ aborted execution: attempted to zero-initialize type `fn()`, which is invalid - | - = note: inside `main` at $DIR/zero_fn_ptr.rs:LL:CC + | ^ note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace diff --git a/src/tools/miri/tests/fail/panic/double_panic.rs b/src/tools/miri/tests/fail/panic/double_panic.rs index adb30714269..4627e9f1c8e 100644 --- a/src/tools/miri/tests/fail/panic/double_panic.rs +++ b/src/tools/miri/tests/fail/panic/double_panic.rs @@ -1,6 +1,8 @@ +//@normalize-stderr-test: "unsafe \{ libc::abort\(\) \}|crate::intrinsics::abort\(\);" -> "ABORT();" //@normalize-stderr-test: "\| +\^+" -> "| ^" //@normalize-stderr-test: "\n +[0-9]+:[^\n]+" -> "$1" //@normalize-stderr-test: "\n at [^\n]+" -> "$1" +//@error-in-other-file: aborted execution struct Foo; impl Drop for Foo { @@ -9,7 +11,6 @@ impl Drop for Foo { } } fn main() { - //~^ERROR: panic in a function that cannot unwind let _foo = Foo; panic!("first"); } diff --git a/src/tools/miri/tests/fail/panic/double_panic.stderr b/src/tools/miri/tests/fail/panic/double_panic.stderr index 8e008da75ee..25c9a7c44bc 100644 --- a/src/tools/miri/tests/fail/panic/double_panic.stderr +++ b/src/tools/miri/tests/fail/panic/double_panic.stderr @@ -4,17 +4,31 @@ note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace thread 'main' panicked at $DIR/double_panic.rs:LL:CC: second stack backtrace: -error: abnormal termination: panic in a function that cannot unwind +thread 'main' panicked at RUSTLIB/core/src/panicking.rs:LL:CC: +panic in a function that cannot unwind +stack backtrace: +thread caused non-unwinding panic. aborting. +error: abnormal termination: the program aborted execution + --> RUSTLIB/std/src/sys/PLATFORM/mod.rs:LL:CC + | +LL | ABORT(); + | ^ the program aborted execution + | + = note: inside `std::sys::PLATFORM::abort_internal` at RUSTLIB/std/src/sys/PLATFORM/mod.rs:LL:CC + = note: inside `std::panicking::rust_panic_with_hook` at RUSTLIB/std/src/panicking.rs:LL:CC + = note: inside closure at RUSTLIB/std/src/panicking.rs:LL:CC + = note: inside `std::sys_common::backtrace::__rust_end_short_backtrace::<[closure@std::panicking::begin_panic_handler::{closure#0}], !>` at RUSTLIB/std/src/sys_common/backtrace.rs:LL:CC + = note: inside `std::panicking::begin_panic_handler` at RUSTLIB/std/src/panicking.rs:LL:CC + = note: inside `core::panicking::panic_nounwind` at RUSTLIB/core/src/panicking.rs:LL:CC + = note: inside `core::panicking::panic_cannot_unwind` at RUSTLIB/core/src/panicking.rs:LL:CC +note: inside `main` --> $DIR/double_panic.rs:LL:CC | LL | / fn main() { -LL | | LL | | let _foo = Foo; LL | | panic!("first"); LL | | } - | |_^ panic in a function that cannot unwind - | - = note: inside `main` at $DIR/double_panic.rs:LL:CC + | |_^ note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace diff --git a/src/tools/miri/tests/fail/rc_as_ptr.stderr b/src/tools/miri/tests/fail/rc_as_ptr.stderr index 129916ac73c..460ed977137 100644 --- a/src/tools/miri/tests/fail/rc_as_ptr.stderr +++ b/src/tools/miri/tests/fail/rc_as_ptr.stderr @@ -6,7 +6,17 @@ LL | assert_eq!(42, **unsafe { &*Weak::as_ptr(&weak) }); | = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information - = note: BACKTRACE: +help: ALLOC was allocated here: + --> $DIR/rc_as_ptr.rs:LL:CC + | +LL | let strong = Rc::new(Box::new(42)); + | ^^^^^^^^^^^^ +help: ALLOC was deallocated here: + --> $DIR/rc_as_ptr.rs:LL:CC + | +LL | drop(strong); + | ^^^^^^^^^^^^ + = note: BACKTRACE (of the first span): = note: inside `main` at RUSTLIB/core/src/macros/mod.rs:LL:CC = note: this error originates in the macro `assert_eq` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/src/tools/miri/tests/fail/shims/memchr_null.stderr b/src/tools/miri/tests/fail/shims/memchr_null.stderr index d48606f34ad..54b58f22c6c 100644 --- a/src/tools/miri/tests/fail/shims/memchr_null.stderr +++ b/src/tools/miri/tests/fail/shims/memchr_null.stderr @@ -1,8 +1,8 @@ -error: Undefined Behavior: memory access failed: null pointer is a dangling pointer (it has no provenance) +error: Undefined Behavior: out-of-bounds pointer use: null pointer is a dangling pointer (it has no provenance) --> $DIR/memchr_null.rs:LL:CC | LL | libc::memchr(ptr::null(), 0, 0); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ memory access failed: null pointer is a dangling pointer (it has no provenance) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ out-of-bounds pointer use: null pointer is a dangling pointer (it has no provenance) | = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information diff --git a/src/tools/miri/tests/fail/shims/memcmp_null.stderr b/src/tools/miri/tests/fail/shims/memcmp_null.stderr index 7a09c779894..8b2882fc243 100644 --- a/src/tools/miri/tests/fail/shims/memcmp_null.stderr +++ b/src/tools/miri/tests/fail/shims/memcmp_null.stderr @@ -1,8 +1,8 @@ -error: Undefined Behavior: memory access failed: null pointer is a dangling pointer (it has no provenance) +error: Undefined Behavior: out-of-bounds pointer use: null pointer is a dangling pointer (it has no provenance) --> $DIR/memcmp_null.rs:LL:CC | LL | libc::memcmp(ptr::null(), ptr::null(), 0); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ memory access failed: null pointer is a dangling pointer (it has no provenance) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ out-of-bounds pointer use: null pointer is a dangling pointer (it has no provenance) | = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information diff --git a/src/tools/miri/tests/fail/shims/memcmp_zero.rs b/src/tools/miri/tests/fail/shims/memcmp_zero.rs new file mode 100644 index 00000000000..f2ddc200563 --- /dev/null +++ b/src/tools/miri/tests/fail/shims/memcmp_zero.rs @@ -0,0 +1,13 @@ +//@ignore-target-windows: No libc on Windows +//@compile-flags: -Zmiri-permissive-provenance + +// C says that passing "invalid" pointers is UB for all string functions. +// It is unclear whether `(int*)42` is "invalid", but there is no actually +// a `char` living at that address, so arguably it cannot be a valid pointer. +// Hence this is UB. +fn main() { + let ptr = 42 as *const u8; + unsafe { + libc::memcmp(ptr.cast(), ptr.cast(), 0); //~ERROR: dangling + } +} diff --git a/src/tools/miri/tests/fail/shims/memcmp_zero.stderr b/src/tools/miri/tests/fail/shims/memcmp_zero.stderr new file mode 100644 index 00000000000..e21b9b06008 --- /dev/null +++ b/src/tools/miri/tests/fail/shims/memcmp_zero.stderr @@ -0,0 +1,15 @@ +error: Undefined Behavior: out-of-bounds pointer use: 0x2a[noalloc] is a dangling pointer (it has no provenance) + --> $DIR/memcmp_zero.rs:LL:CC + | +LL | libc::memcmp(ptr.cast(), ptr.cast(), 0); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ out-of-bounds pointer use: 0x2a[noalloc] is a dangling pointer (it has no provenance) + | + = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior + = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information + = note: BACKTRACE: + = note: inside `main` at $DIR/memcmp_zero.rs:LL:CC + +note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace + +error: aborting due to previous error + diff --git a/src/tools/miri/tests/fail/shims/memrchr_null.stderr b/src/tools/miri/tests/fail/shims/memrchr_null.stderr index b5b7630e7fd..cc11ba89f8f 100644 --- a/src/tools/miri/tests/fail/shims/memrchr_null.stderr +++ b/src/tools/miri/tests/fail/shims/memrchr_null.stderr @@ -1,8 +1,8 @@ -error: Undefined Behavior: memory access failed: null pointer is a dangling pointer (it has no provenance) +error: Undefined Behavior: out-of-bounds pointer use: null pointer is a dangling pointer (it has no provenance) --> $DIR/memrchr_null.rs:LL:CC | LL | libc::memrchr(ptr::null(), 0, 0); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ memory access failed: null pointer is a dangling pointer (it has no provenance) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ out-of-bounds pointer use: null pointer is a dangling pointer (it has no provenance) | = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information diff --git a/src/tools/miri/tests/fail/shims/mmap_use_after_munmap.stderr b/src/tools/miri/tests/fail/shims/mmap_use_after_munmap.stderr index 8b9969da8fd..44e122330bc 100644 --- a/src/tools/miri/tests/fail/shims/mmap_use_after_munmap.stderr +++ b/src/tools/miri/tests/fail/shims/mmap_use_after_munmap.stderr @@ -21,7 +21,24 @@ LL | let _x = *(ptr as *mut u8); | = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information - = note: BACKTRACE: +help: ALLOC was allocated here: + --> $DIR/mmap_use_after_munmap.rs:LL:CC + | +LL | let ptr = libc::mmap( + | ___________________^ +LL | | std::ptr::null_mut(), +LL | | 4096, +LL | | libc::PROT_READ | libc::PROT_WRITE, +... | +LL | | 0, +LL | | ); + | |_________^ +help: ALLOC was deallocated here: + --> $DIR/mmap_use_after_munmap.rs:LL:CC + | +LL | libc::munmap(ptr, 4096); + | ^^^^^^^^^^^^^^^^^^^^^^^ + = note: BACKTRACE (of the first span): = note: inside `main` at $DIR/mmap_use_after_munmap.rs:LL:CC note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace diff --git a/src/tools/miri/tests/fail/terminate-terminator.rs b/src/tools/miri/tests/fail/terminate-terminator.rs index bd6cd69215a..5373b5f70c0 100644 --- a/src/tools/miri/tests/fail/terminate-terminator.rs +++ b/src/tools/miri/tests/fail/terminate-terminator.rs @@ -1,5 +1,10 @@ //@compile-flags: -Zmir-opt-level=3 -Zinline-mir-hint-threshold=1000 -// Enable MIR inlining to ensure that `TerminatorKind::Terminate` is generated +//@normalize-stderr-test: "unsafe \{ libc::abort\(\) \}|crate::intrinsics::abort\(\);" -> "ABORT();" +//@normalize-stderr-test: "\| +\^+" -> "| ^" +//@normalize-stderr-test: "\n +[0-9]+:[^\n]+" -> "$1" +//@normalize-stderr-test: "\n at [^\n]+" -> "$1" +//@error-in-other-file: aborted execution +// Enable MIR inlining to ensure that `TerminatorKind::UnwindTerminate` is generated // instead of just `UnwindAction::Terminate`. #![feature(c_unwind)] @@ -12,7 +17,6 @@ impl Drop for Foo { #[inline(always)] fn has_cleanup() { - //~^ ERROR: panic in a function that cannot unwind let _f = Foo; panic!(); } diff --git a/src/tools/miri/tests/fail/terminate-terminator.stderr b/src/tools/miri/tests/fail/terminate-terminator.stderr index 4f73b599a3f..e9277ebdbfd 100644 --- a/src/tools/miri/tests/fail/terminate-terminator.stderr +++ b/src/tools/miri/tests/fail/terminate-terminator.stderr @@ -3,27 +3,41 @@ warning: You have explicitly enabled MIR optimizations, overriding Miri's defaul thread 'main' panicked at $DIR/terminate-terminator.rs:LL:CC: explicit panic note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace -error: abnormal termination: panic in a function that cannot unwind +thread 'main' panicked at RUSTLIB/core/src/panicking.rs:LL:CC: +panic in a function that cannot unwind +stack backtrace: +thread caused non-unwinding panic. aborting. +error: abnormal termination: the program aborted execution + --> RUSTLIB/std/src/sys/PLATFORM/mod.rs:LL:CC + | +LL | ABORT(); + | ^ the program aborted execution + | + = note: inside `std::sys::PLATFORM::abort_internal` at RUSTLIB/std/src/sys/PLATFORM/mod.rs:LL:CC + = note: inside `std::panicking::rust_panic_with_hook` at RUSTLIB/std/src/panicking.rs:LL:CC + = note: inside closure at RUSTLIB/std/src/panicking.rs:LL:CC + = note: inside `std::sys_common::backtrace::__rust_end_short_backtrace::<[closure@std::panicking::begin_panic_handler::{closure#0}], !>` at RUSTLIB/std/src/sys_common/backtrace.rs:LL:CC + = note: inside `std::panicking::begin_panic_handler` at RUSTLIB/std/src/panicking.rs:LL:CC + = note: inside `core::panicking::panic_nounwind` at RUSTLIB/core/src/panicking.rs:LL:CC + = note: inside `core::panicking::panic_cannot_unwind` at RUSTLIB/core/src/panicking.rs:LL:CC +note: inside `has_cleanup` --> $DIR/terminate-terminator.rs:LL:CC | LL | / fn has_cleanup() { -LL | | LL | | let _f = Foo; LL | | panic!(); LL | | } - | |_^ panic in a function that cannot unwind - | - = note: inside `has_cleanup` at $DIR/terminate-terminator.rs:LL:CC + | |_^ note: inside `panic_abort` --> $DIR/terminate-terminator.rs:LL:CC | LL | has_cleanup(); - | ^^^^^^^^^^^^^ + | ^ note: inside `main` --> $DIR/terminate-terminator.rs:LL:CC | LL | panic_abort(); - | ^^^^^^^^^^^^^ + | ^ note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace diff --git a/src/tools/miri/tests/fail/unwind-action-terminate.rs b/src/tools/miri/tests/fail/unwind-action-terminate.rs index 876b9a9ab0a..77844844483 100644 --- a/src/tools/miri/tests/fail/unwind-action-terminate.rs +++ b/src/tools/miri/tests/fail/unwind-action-terminate.rs @@ -1,7 +1,11 @@ +//@error-in-other-file: aborted execution +//@normalize-stderr-test: "unsafe \{ libc::abort\(\) \}|crate::intrinsics::abort\(\);" -> "ABORT();" +//@normalize-stderr-test: "\| +\^+" -> "| ^" +//@normalize-stderr-test: "\n +[0-9]+:[^\n]+" -> "$1" +//@normalize-stderr-test: "\n at [^\n]+" -> "$1" #![feature(c_unwind)] extern "C" fn panic_abort() { - //~^ ERROR: panic in a function that cannot unwind panic!() } diff --git a/src/tools/miri/tests/fail/unwind-action-terminate.stderr b/src/tools/miri/tests/fail/unwind-action-terminate.stderr index daa4a808df9..cf94dbb9909 100644 --- a/src/tools/miri/tests/fail/unwind-action-terminate.stderr +++ b/src/tools/miri/tests/fail/unwind-action-terminate.stderr @@ -1,21 +1,35 @@ thread 'main' panicked at $DIR/unwind-action-terminate.rs:LL:CC: explicit panic note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace -error: abnormal termination: panic in a function that cannot unwind +thread 'main' panicked at RUSTLIB/core/src/panicking.rs:LL:CC: +panic in a function that cannot unwind +stack backtrace: +thread caused non-unwinding panic. aborting. +error: abnormal termination: the program aborted execution + --> RUSTLIB/std/src/sys/PLATFORM/mod.rs:LL:CC + | +LL | ABORT(); + | ^ the program aborted execution + | + = note: inside `std::sys::PLATFORM::abort_internal` at RUSTLIB/std/src/sys/PLATFORM/mod.rs:LL:CC + = note: inside `std::panicking::rust_panic_with_hook` at RUSTLIB/std/src/panicking.rs:LL:CC + = note: inside closure at RUSTLIB/std/src/panicking.rs:LL:CC + = note: inside `std::sys_common::backtrace::__rust_end_short_backtrace::<[closure@std::panicking::begin_panic_handler::{closure#0}], !>` at RUSTLIB/std/src/sys_common/backtrace.rs:LL:CC + = note: inside `std::panicking::begin_panic_handler` at RUSTLIB/std/src/panicking.rs:LL:CC + = note: inside `core::panicking::panic_nounwind` at RUSTLIB/core/src/panicking.rs:LL:CC + = note: inside `core::panicking::panic_cannot_unwind` at RUSTLIB/core/src/panicking.rs:LL:CC +note: inside `panic_abort` --> $DIR/unwind-action-terminate.rs:LL:CC | LL | / extern "C" fn panic_abort() { -LL | | LL | | panic!() LL | | } - | |_^ panic in a function that cannot unwind - | - = note: inside `panic_abort` at $DIR/unwind-action-terminate.rs:LL:CC + | |_^ note: inside `main` --> $DIR/unwind-action-terminate.rs:LL:CC | LL | panic_abort(); - | ^^^^^^^^^^^^^ + | ^ note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace diff --git a/src/tools/miri/tests/fail/zst2.stderr b/src/tools/miri/tests/fail/zst2.stderr index 63f40ed2067..49954b1fd14 100644 --- a/src/tools/miri/tests/fail/zst2.stderr +++ b/src/tools/miri/tests/fail/zst2.stderr @@ -6,7 +6,17 @@ LL | unsafe { *x = zst_val }; | = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information - = note: BACKTRACE: +help: ALLOC was allocated here: + --> $DIR/zst2.rs:LL:CC + | +LL | let mut x_box = Box::new(1u8); + | ^^^^^^^^^^^^^ +help: ALLOC was deallocated here: + --> $DIR/zst2.rs:LL:CC + | +LL | drop(x_box); + | ^^^^^^^^^^^ + = note: BACKTRACE (of the first span): = note: inside `main` at $DIR/zst2.rs:LL:CC note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace diff --git a/src/tools/miri/tests/fail/zst3.stderr b/src/tools/miri/tests/fail/zst3.stderr index c9accf2c8fb..b62aef675d2 100644 --- a/src/tools/miri/tests/fail/zst3.stderr +++ b/src/tools/miri/tests/fail/zst3.stderr @@ -6,7 +6,12 @@ LL | unsafe { *(x as *mut [u8; 0]) = zst_val }; | = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information - = note: BACKTRACE: +help: ALLOC was allocated here: + --> $DIR/zst3.rs:LL:CC + | +LL | let mut x_box = Box::new(1u8); + | ^^^^^^^^^^^^^ + = note: BACKTRACE (of the first span): = note: inside `main` at $DIR/zst3.rs:LL:CC note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace diff --git a/src/tools/miri/tests/pass/function_calls/return_place_on_heap.rs b/src/tools/miri/tests/pass/function_calls/return_place_on_heap.rs index dcfebd0f82b..d410a875b1b 100644 --- a/src/tools/miri/tests/pass/function_calls/return_place_on_heap.rs +++ b/src/tools/miri/tests/pass/function_calls/return_place_on_heap.rs @@ -11,7 +11,7 @@ pub fn main() { { let x = 0; let ptr = &raw mut x; - Call(*ptr, after_call, myfun()) + Call(*ptr = myfun(), after_call) } after_call = { diff --git a/src/tools/rust-analyzer/Cargo.lock b/src/tools/rust-analyzer/Cargo.lock index a2b263cf2d8..49e96236c51 100644 --- a/src/tools/rust-analyzer/Cargo.lock +++ b/src/tools/rust-analyzer/Cargo.lock @@ -999,23 +999,23 @@ checksum = "b06a4cde4c0f271a446782e3eff8de789548ce57dbc8eca9292c27f4a42004b4" [[package]] name = "lsp-server" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3711e4d6f491dc9edc0f1df80e204f38206775ac92c1241e89b79229a850bc00" +version = "0.7.3" dependencies = [ "crossbeam-channel", "log", + "lsp-types", "serde", "serde_json", ] [[package]] name = "lsp-server" -version = "0.7.2" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72417faa455bfb4e5bf14b157d8e2ca2ed74b4e89b8cf42ea2d864825ae5c8a2" dependencies = [ "crossbeam-channel", "log", - "lsp-types", "serde", "serde_json", ] @@ -1555,7 +1555,7 @@ dependencies = [ "ide-ssr", "itertools", "load-cargo", - "lsp-server 0.7.1", + "lsp-server 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", "lsp-types", "mbe", "mimalloc", diff --git a/src/tools/rust-analyzer/Cargo.toml b/src/tools/rust-analyzer/Cargo.toml index f6a50bfa6b2..5eb59d6db11 100644 --- a/src/tools/rust-analyzer/Cargo.toml +++ b/src/tools/rust-analyzer/Cargo.toml @@ -86,7 +86,7 @@ proc-macro-test = { path = "./crates/proc-macro-test" } # In-tree crates that are published separately and follow semver. See lib/README.md line-index = { version = "0.1.0-pre.1" } la-arena = { version = "0.3.1" } -lsp-server = { version = "0.7.1" } +lsp-server = { version = "0.7.3" } # non-local crates smallvec = { version = "1.10.0", features = [ @@ -97,7 +97,8 @@ smallvec = { version = "1.10.0", features = [ smol_str = "0.2.0" nohash-hasher = "0.2.0" text-size = "1.1.0" -serde = { version = "1.0.156", features = ["derive"] } +# See https://github.com/serde-rs/serde/issues/2538#issuecomment-1684517372 for why we pin serde +serde = { version = "1.0.156, < 1.0.172", features = ["derive"] } serde_json = "1.0.96" triomphe = { version = "0.1.8", default-features = false, features = ["std"] } # can't upgrade due to dashmap depending on 0.12.3 currently diff --git a/src/tools/rust-analyzer/crates/base-db/src/fixture.rs b/src/tools/rust-analyzer/crates/base-db/src/fixture.rs index 323ee4260e4..aaac0fc3790 100644 --- a/src/tools/rust-analyzer/crates/base-db/src/fixture.rs +++ b/src/tools/rust-analyzer/crates/base-db/src/fixture.rs @@ -130,6 +130,7 @@ impl ChangeFixture { let mut default_crate_root: Option<FileId> = None; let mut default_target_data_layout: Option<String> = None; let mut default_cfg = CfgOptions::default(); + let mut default_env = Env::new_for_test_fixture(); let mut file_set = FileSet::default(); let mut current_source_root_kind = SourceRootKind::Local; @@ -200,6 +201,7 @@ impl ChangeFixture { assert!(default_crate_root.is_none()); default_crate_root = Some(file_id); default_cfg = meta.cfg; + default_env.extend(meta.env.iter().map(|(x, y)| (x.to_owned(), y.to_owned()))); default_target_data_layout = meta.target_data_layout; } @@ -220,7 +222,7 @@ impl ChangeFixture { None, default_cfg, Default::default(), - Env::new_for_test_fixture(), + default_env, false, CrateOrigin::Local { repo: None, name: None }, default_target_data_layout diff --git a/src/tools/rust-analyzer/crates/base-db/src/input.rs b/src/tools/rust-analyzer/crates/base-db/src/input.rs index c47799f1320..b75c7079be7 100644 --- a/src/tools/rust-analyzer/crates/base-db/src/input.rs +++ b/src/tools/rust-analyzer/crates/base-db/src/input.rs @@ -686,6 +686,12 @@ impl fmt::Display for Edition { } } +impl Extend<(String, String)> for Env { + fn extend<T: IntoIterator<Item = (String, String)>>(&mut self, iter: T) { + self.entries.extend(iter); + } +} + impl FromIterator<(String, String)> for Env { fn from_iter<T: IntoIterator<Item = (String, String)>>(iter: T) -> Self { Env { entries: FromIterator::from_iter(iter) } diff --git a/src/tools/rust-analyzer/crates/hir-def/src/attr.rs b/src/tools/rust-analyzer/crates/hir-def/src/attr.rs index fae07111806..a5db75a91eb 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/attr.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/attr.rs @@ -431,12 +431,10 @@ impl AttrsWithOwner { .item_tree(db) .raw_attrs(AttrOwner::ModItem(definition_tree_id.value.into())) .clone(), - ModuleOrigin::BlockExpr { block } => RawAttrs::from_attrs_owner( - db.upcast(), - InFile::new(block.file_id, block.to_node(db.upcast())) - .as_ref() - .map(|it| it as &dyn ast::HasAttrs), - ), + ModuleOrigin::BlockExpr { id, .. } => { + let tree = db.block_item_tree_query(id); + tree.raw_attrs(AttrOwner::TopLevel).clone() + } } } AttrDefId::FieldId(it) => { diff --git a/src/tools/rust-analyzer/crates/hir-def/src/body/lower.rs b/src/tools/rust-analyzer/crates/hir-def/src/body/lower.rs index 3853a6ab3a5..7071fcb9394 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/body/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/body/lower.rs @@ -505,6 +505,9 @@ impl ExprCollector<'_> { let mut args = Vec::new(); let mut arg_types = Vec::new(); if let Some(pl) = e.param_list() { + let num_params = pl.params().count(); + args.reserve_exact(num_params); + arg_types.reserve_exact(num_params); for param in pl.params() { let pat = this.collect_pat_top(param.pat()); let type_ref = @@ -1100,7 +1103,9 @@ impl ExprCollector<'_> { ast::Stmt::ExprStmt(es) => matches!(es.expr(), Some(ast::Expr::MacroExpr(_))), _ => false, }); - statement_has_item || matches!(block.tail_expr(), Some(ast::Expr::MacroExpr(_))) + statement_has_item + || matches!(block.tail_expr(), Some(ast::Expr::MacroExpr(_))) + || (block.may_carry_attributes() && block.attrs().next().is_some()) }; let block_id = if block_has_items { diff --git a/src/tools/rust-analyzer/crates/hir-def/src/body/tests/block.rs b/src/tools/rust-analyzer/crates/hir-def/src/body/tests/block.rs index 4e015a7fbbb..44eeed9e3fb 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/body/tests/block.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/body/tests/block.rs @@ -38,9 +38,9 @@ fn outer() { "#, expect![[r#" block scope - CrateStruct: t - PlainStruct: t v - SelfStruct: t + CrateStruct: ti + PlainStruct: ti vi + SelfStruct: ti Struct: v SuperStruct: _ @@ -66,7 +66,7 @@ fn outer() { "#, expect![[r#" block scope - imported: t v + imported: ti vi name: v crate @@ -92,9 +92,9 @@ fn outer() { "#, expect![[r#" block scope - inner1: t + inner1: ti inner2: v - outer: v + outer: vi block scope inner: v @@ -121,7 +121,7 @@ struct Struct {} "#, expect![[r#" block scope - Struct: t + Struct: ti crate Struct: t @@ -153,7 +153,7 @@ fn outer() { "#, expect![[r#" block scope - ResolveMe: t + ResolveMe: ti block scope m2: t @@ -214,7 +214,7 @@ fn f() { "#, expect![[r#" block scope - ResolveMe: t + ResolveMe: ti block scope h: v @@ -292,7 +292,7 @@ pub mod cov_mark { nested: v crate - cov_mark: t + cov_mark: ti f: v "#]], ); diff --git a/src/tools/rust-analyzer/crates/hir-def/src/data.rs b/src/tools/rust-analyzer/crates/hir-def/src/data.rs index 91db68058b0..68defa3858f 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/data.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/data.rs @@ -487,7 +487,7 @@ impl ExternCrateDeclData { db.crate_def_map(loc.container.krate()) .extern_prelude() .find(|&(prelude_name, ..)| *prelude_name == name) - .map(|(_, root)| root.krate()) + .map(|(_, (root, _))| root.krate()) }; Arc::new(Self { diff --git a/src/tools/rust-analyzer/crates/hir-def/src/db.rs b/src/tools/rust-analyzer/crates/hir-def/src/db.rs index e34a6768f28..31c1a713031 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/db.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/db.rs @@ -82,6 +82,9 @@ pub trait DefDatabase: InternDatabase + ExpandDatabase + Upcast<dyn ExpandDataba #[salsa::invoke(ItemTree::file_item_tree_query)] fn file_item_tree(&self, file_id: HirFileId) -> Arc<ItemTree>; + #[salsa::invoke(ItemTree::block_item_tree_query)] + fn block_item_tree_query(&self, block_id: BlockId) -> Arc<ItemTree>; + #[salsa::invoke(crate_def_map_wait)] #[salsa::transparent] fn crate_def_map(&self, krate: CrateId) -> Arc<DefMap>; diff --git a/src/tools/rust-analyzer/crates/hir-def/src/find_path.rs b/src/tools/rust-analyzer/crates/hir-def/src/find_path.rs index df2af4c89b0..b60e7909105 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/find_path.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/find_path.rs @@ -11,7 +11,7 @@ use crate::{ nameres::DefMap, path::{ModPath, PathKind}, visibility::Visibility, - ModuleDefId, ModuleId, + CrateRootModuleId, ModuleDefId, ModuleId, }; /// Find a path that can be used to refer to a certain item. This can depend on @@ -81,7 +81,7 @@ fn find_path_inner( } let def_map = from.def_map(db); - let crate_root = def_map.crate_root().into(); + let crate_root = def_map.crate_root(); // - if the item is a module, jump straight to module search if let ItemInNs::Types(ModuleDefId::ModuleId(module_id)) = item { let mut visited_modules = FxHashSet::default(); @@ -149,7 +149,7 @@ fn find_path_for_module( db: &dyn DefDatabase, def_map: &DefMap, visited_modules: &mut FxHashSet<ModuleId>, - crate_root: ModuleId, + crate_root: CrateRootModuleId, from: ModuleId, module_id: ModuleId, max_len: usize, @@ -183,7 +183,7 @@ fn find_path_for_module( // - if the item is the crate root of a dependency crate, return the name from the extern prelude let root_def_map = crate_root.def_map(db); - for (name, def_id) in root_def_map.extern_prelude() { + for (name, (def_id, _extern_crate)) in root_def_map.extern_prelude() { if module_id == def_id { let name = scope_name.unwrap_or_else(|| name.clone()); @@ -192,7 +192,7 @@ fn find_path_for_module( def_map[local_id] .scope .type_(&name) - .filter(|&(id, _)| id != ModuleDefId::ModuleId(def_id)) + .filter(|&(id, _)| id != ModuleDefId::ModuleId(def_id.into())) }) .is_some(); let kind = if name_already_occupied_in_type_ns { @@ -224,6 +224,7 @@ fn find_path_for_module( ) } +// FIXME: Do we still need this now that we record import origins, and hence aliases? fn find_in_scope( db: &dyn DefDatabase, def_map: &DefMap, @@ -244,7 +245,7 @@ fn find_in_prelude( item: ItemInNs, from: ModuleId, ) -> Option<ModPath> { - let prelude_module = root_def_map.prelude()?; + let (prelude_module, _) = root_def_map.prelude()?; // Preludes in block DefMaps are ignored, only the crate DefMap is searched let prelude_def_map = prelude_module.def_map(db); let prelude_scope = &prelude_def_map[prelude_module.local_id].scope; @@ -293,7 +294,7 @@ fn calculate_best_path( db: &dyn DefDatabase, def_map: &DefMap, visited_modules: &mut FxHashSet<ModuleId>, - crate_root: ModuleId, + crate_root: CrateRootModuleId, max_len: usize, item: ItemInNs, from: ModuleId, @@ -346,6 +347,11 @@ fn calculate_best_path( let extern_paths = crate_graph[from.krate].dependencies.iter().filter_map(|dep| { let import_map = db.import_map(dep.crate_id); import_map.import_info_for(item).and_then(|info| { + if info.is_doc_hidden { + // the item or import is `#[doc(hidden)]`, so skip it as it is in an external crate + return None; + } + // Determine best path for containing module and append last segment from `info`. // FIXME: we should guide this to look up the path locally, or from the same crate again? let mut path = find_path_for_module( @@ -1293,4 +1299,65 @@ pub mod prelude { "None", ); } + + #[test] + fn different_crate_renamed_through_dep() { + check_found_path( + r#" +//- /main.rs crate:main deps:intermediate +$0 +//- /intermediate.rs crate:intermediate deps:std +pub extern crate std as std_renamed; +//- /std.rs crate:std +pub struct S; + "#, + "intermediate::std_renamed::S", + "intermediate::std_renamed::S", + "intermediate::std_renamed::S", + "intermediate::std_renamed::S", + ); + } + + #[test] + fn different_crate_doc_hidden() { + check_found_path( + r#" +//- /main.rs crate:main deps:intermediate +$0 +//- /intermediate.rs crate:intermediate deps:std +#[doc(hidden)] +pub extern crate std; +pub extern crate std as longer; +//- /std.rs crate:std +pub struct S; + "#, + "intermediate::longer::S", + "intermediate::longer::S", + "intermediate::longer::S", + "intermediate::longer::S", + ); + } + + #[test] + fn respect_doc_hidden() { + check_found_path( + r#" +//- /main.rs crate:main deps:std,lazy_static +$0 +//- /lazy_static.rs crate:lazy_static deps:core +#[doc(hidden)] +pub use core::ops::Deref as __Deref; +//- /std.rs crate:std deps:core +pub use core::ops; +//- /core.rs crate:core +pub mod ops { + pub trait Deref {} +} + "#, + "std::ops::Deref", + "std::ops::Deref", + "std::ops::Deref", + "std::ops::Deref", + ); + } } diff --git a/src/tools/rust-analyzer/crates/hir-def/src/generics.rs b/src/tools/rust-analyzer/crates/hir-def/src/generics.rs index d7d44e41388..1e2535a8a99 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/generics.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/generics.rs @@ -21,10 +21,11 @@ use crate::{ db::DefDatabase, dyn_map::{keys, DynMap}, expander::Expander, + item_tree::{AttrOwner, ItemTree}, lower::LowerCtx, nameres::{DefMap, MacroSubNs}, src::{HasChildSource, HasSource}, - type_ref::{LifetimeRef, TypeBound, TypeRef}, + type_ref::{ConstRef, LifetimeRef, TypeBound, TypeRef}, AdtId, ConstParamId, GenericDefId, HasModule, LifetimeParamId, LocalLifetimeParamId, LocalTypeOrConstParamId, Lookup, TypeOrConstParamId, TypeParamId, }; @@ -48,7 +49,7 @@ pub struct LifetimeParamData { pub struct ConstParamData { pub name: Name, pub ty: Interned<TypeRef>, - pub has_default: bool, + pub default: Option<ConstRef>, } #[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)] @@ -75,7 +76,7 @@ impl TypeOrConstParamData { pub fn has_default(&self) -> bool { match self { TypeOrConstParamData::TypeParamData(it) => it.default.is_some(), - TypeOrConstParamData::ConstParamData(it) => it.has_default, + TypeOrConstParamData::ConstParamData(it) => it.default.is_some(), } } @@ -154,12 +155,58 @@ impl GenericParams { def: GenericDefId, ) -> Interned<GenericParams> { let _p = profile::span("generic_params_query"); + + let krate = def.module(db).krate; + let cfg_options = db.crate_graph(); + let cfg_options = &cfg_options[krate].cfg_options; + + // Returns the generic parameters that are enabled under the current `#[cfg]` options + let enabled_params = |params: &Interned<GenericParams>, item_tree: &ItemTree| { + let enabled = |param| item_tree.attrs(db, krate, param).is_cfg_enabled(cfg_options); + + // In the common case, no parameters will by disabled by `#[cfg]` attributes. + // Therefore, make a first pass to check if all parameters are enabled and, if so, + // clone the `Interned<GenericParams>` instead of recreating an identical copy. + let all_type_or_consts_enabled = + params.type_or_consts.iter().all(|(idx, _)| enabled(idx.into())); + let all_lifetimes_enabled = params.lifetimes.iter().all(|(idx, _)| enabled(idx.into())); + + if all_type_or_consts_enabled && all_lifetimes_enabled { + params.clone() + } else { + Interned::new(GenericParams { + type_or_consts: all_type_or_consts_enabled + .then(|| params.type_or_consts.clone()) + .unwrap_or_else(|| { + params + .type_or_consts + .iter() + .filter_map(|(idx, param)| { + enabled(idx.into()).then(|| param.clone()) + }) + .collect() + }), + lifetimes: all_lifetimes_enabled + .then(|| params.lifetimes.clone()) + .unwrap_or_else(|| { + params + .lifetimes + .iter() + .filter_map(|(idx, param)| { + enabled(idx.into()).then(|| param.clone()) + }) + .collect() + }), + where_predicates: params.where_predicates.clone(), + }) + } + }; macro_rules! id_to_generics { ($id:ident) => {{ let id = $id.lookup(db).id; let tree = id.item_tree(db); let item = &tree[id.value]; - item.generic_params.clone() + enabled_params(&item.generic_params, &tree) }}; } @@ -169,7 +216,8 @@ impl GenericParams { let tree = loc.id.item_tree(db); let item = &tree[loc.id.value]; - let mut generic_params = GenericParams::clone(&item.explicit_generic_params); + let enabled_params = enabled_params(&item.explicit_generic_params, &tree); + let mut generic_params = GenericParams::clone(&enabled_params); let module = loc.container.module(db); let func_data = db.function_data(id); @@ -198,9 +246,14 @@ impl GenericParams { } } - pub(crate) fn fill(&mut self, lower_ctx: &LowerCtx<'_>, node: &dyn HasGenericParams) { + pub(crate) fn fill( + &mut self, + lower_ctx: &LowerCtx<'_>, + node: &dyn HasGenericParams, + add_param_attrs: impl FnMut(AttrOwner, ast::GenericParam), + ) { if let Some(params) = node.generic_param_list() { - self.fill_params(lower_ctx, params) + self.fill_params(lower_ctx, params, add_param_attrs) } if let Some(where_clause) = node.where_clause() { self.fill_where_predicates(lower_ctx, where_clause); @@ -218,7 +271,12 @@ impl GenericParams { } } - fn fill_params(&mut self, lower_ctx: &LowerCtx<'_>, params: ast::GenericParamList) { + fn fill_params( + &mut self, + lower_ctx: &LowerCtx<'_>, + params: ast::GenericParamList, + mut add_param_attrs: impl FnMut(AttrOwner, ast::GenericParam), + ) { for type_or_const_param in params.type_or_const_params() { match type_or_const_param { ast::TypeOrConstParam::Type(type_param) => { @@ -232,13 +290,14 @@ impl GenericParams { default, provenance: TypeParamProvenance::TypeParamList, }; - self.type_or_consts.alloc(param.into()); + let idx = self.type_or_consts.alloc(param.into()); let type_ref = TypeRef::Path(name.into()); self.fill_bounds( lower_ctx, type_param.type_bound_list(), Either::Left(type_ref), ); + add_param_attrs(idx.into(), ast::GenericParam::TypeParam(type_param)); } ast::TypeOrConstParam::Const(const_param) => { let name = const_param.name().map_or_else(Name::missing, |it| it.as_name()); @@ -248,9 +307,10 @@ impl GenericParams { let param = ConstParamData { name, ty: Interned::new(ty), - has_default: const_param.default_val().is_some(), + default: ConstRef::from_const_param(lower_ctx, &const_param), }; - self.type_or_consts.alloc(param.into()); + let idx = self.type_or_consts.alloc(param.into()); + add_param_attrs(idx.into(), ast::GenericParam::ConstParam(const_param)); } } } @@ -258,13 +318,14 @@ impl GenericParams { let name = lifetime_param.lifetime().map_or_else(Name::missing, |lt| Name::new_lifetime(<)); let param = LifetimeParamData { name: name.clone() }; - self.lifetimes.alloc(param); + let idx = self.lifetimes.alloc(param); let lifetime_ref = LifetimeRef::new_name(name); self.fill_bounds( lower_ctx, lifetime_param.type_bound_list(), Either::Right(lifetime_ref), ); + add_param_attrs(idx.into(), ast::GenericParam::LifetimeParam(lifetime_param)); } } diff --git a/src/tools/rust-analyzer/crates/hir-def/src/hir/type_ref.rs b/src/tools/rust-analyzer/crates/hir-def/src/hir/type_ref.rs index 57f023ef35d..75adf21abdc 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/hir/type_ref.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/hir/type_ref.rs @@ -393,6 +393,17 @@ impl ConstRef { Self::Scalar(LiteralConstRef::Unknown) } + pub(crate) fn from_const_param( + lower_ctx: &LowerCtx<'_>, + param: &ast::ConstParam, + ) -> Option<Self> { + let default = param.default_val(); + match default { + Some(_) => Some(Self::from_const_arg(lower_ctx, default)), + None => None, + } + } + pub fn display<'a>(&'a self, db: &'a dyn ExpandDatabase) -> impl fmt::Display + 'a { struct Display<'a>(&'a dyn ExpandDatabase, &'a ConstRef); impl fmt::Display for Display<'_> { diff --git a/src/tools/rust-analyzer/crates/hir-def/src/import_map.rs b/src/tools/rust-analyzer/crates/hir-def/src/import_map.rs index 4b2e5041a12..6038caaf8fc 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/import_map.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/import_map.rs @@ -11,6 +11,7 @@ use itertools::Itertools; use rustc_hash::{FxHashMap, FxHashSet, FxHasher}; use triomphe::Arc; +use crate::item_scope::ImportOrExternCrate; use crate::{ db::DefDatabase, item_scope::ItemInNs, nameres::DefMap, visibility::Visibility, AssocItemId, ModuleDefId, ModuleId, TraitId, @@ -29,6 +30,8 @@ pub struct ImportInfo { pub container: ModuleId, /// Whether the import is a trait associated item or not. pub is_trait_assoc_item: bool, + /// Whether this item is annotated with `#[doc(hidden)]`. + pub is_doc_hidden: bool, } /// A map from publicly exported items to its name. @@ -109,23 +112,41 @@ fn collect_import_map(db: &dyn DefDatabase, krate: CrateId) -> FxIndexMap<ItemIn }); for (name, per_ns) in visible_items { - for item in per_ns.iter_items() { + for (item, import) in per_ns.iter_items() { + // FIXME: Not yet used, but will be once we handle doc(hidden) import sources + let attr_id = if let Some(import) = import { + match import { + ImportOrExternCrate::ExternCrate(id) => Some(id.into()), + ImportOrExternCrate::Import(id) => Some(id.import.into()), + } + } else { + match item { + ItemInNs::Types(id) | ItemInNs::Values(id) => id.try_into().ok(), + ItemInNs::Macros(id) => Some(id.into()), + } + }; + let is_doc_hidden = + attr_id.map_or(false, |attr_id| db.attrs(attr_id).has_doc_hidden()); + let import_info = ImportInfo { name: name.clone(), container: module, is_trait_assoc_item: false, + is_doc_hidden, }; match depth_map.entry(item) { - Entry::Vacant(entry) => { - entry.insert(depth); - } + Entry::Vacant(entry) => _ = entry.insert((depth, is_doc_hidden)), Entry::Occupied(mut entry) => { - if depth < *entry.get() { - entry.insert(depth); - } else { + let &(occ_depth, occ_is_doc_hidden) = entry.get(); + // Prefer the one that is not doc(hidden), + // Otherwise, if both have the same doc(hidden)-ness and the new path is shorter, prefer that one. + let overwrite_entry = occ_is_doc_hidden && !is_doc_hidden + || occ_is_doc_hidden == is_doc_hidden && depth < occ_depth; + if !overwrite_entry { continue; } + entry.insert((depth, is_doc_hidden)); } } @@ -162,10 +183,10 @@ fn collect_trait_assoc_items( trait_import_info: &ImportInfo, ) { let _p = profile::span("collect_trait_assoc_items"); - for (assoc_item_name, item) in &db.trait_data(tr).items { + for &(ref assoc_item_name, item) in &db.trait_data(tr).items { let module_def_id = match item { - AssocItemId::FunctionId(f) => ModuleDefId::from(*f), - AssocItemId::ConstId(c) => ModuleDefId::from(*c), + AssocItemId::FunctionId(f) => ModuleDefId::from(f), + AssocItemId::ConstId(c) => ModuleDefId::from(c), // cannot use associated type aliases directly: need a `<Struct as Trait>::TypeAlias` // qualifier, ergo no need to store it for imports in import_map AssocItemId::TypeAliasId(_) => { @@ -183,6 +204,7 @@ fn collect_trait_assoc_items( container: trait_import_info.container, name: assoc_item_name.clone(), is_trait_assoc_item: true, + is_doc_hidden: db.attrs(item.into()).has_doc_hidden(), }; map.insert(assoc_item, assoc_item_info); } diff --git a/src/tools/rust-analyzer/crates/hir-def/src/item_scope.rs b/src/tools/rust-analyzer/crates/hir-def/src/item_scope.rs index 873accafb43..7c11fb9d136 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/item_scope.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/item_scope.rs @@ -6,6 +6,7 @@ use std::collections::hash_map::Entry; use base_db::CrateId; use hir_expand::{attrs::AttrId, db::ExpandDatabase, name::Name, AstId, MacroCallId}; use itertools::Itertools; +use la_arena::Idx; use once_cell::sync::Lazy; use profile::Count; use rustc_hash::{FxHashMap, FxHashSet}; @@ -15,16 +16,10 @@ use syntax::ast; use crate::{ db::DefDatabase, per_ns::PerNs, visibility::Visibility, AdtId, BuiltinType, ConstId, - ExternCrateId, HasModule, ImplId, LocalModuleId, MacroId, ModuleDefId, ModuleId, TraitId, - UseId, + ExternCrateId, HasModule, ImplId, LocalModuleId, Lookup, MacroId, ModuleDefId, ModuleId, + TraitId, UseId, }; -#[derive(Copy, Clone, Debug)] -pub(crate) enum ImportType { - Glob, - Named, -} - #[derive(Debug, Default)] pub struct PerNsGlobImports { types: FxHashSet<(LocalModuleId, Name)>, @@ -32,15 +27,50 @@ pub struct PerNsGlobImports { macros: FxHashSet<(LocalModuleId, Name)>, } +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] +pub enum ImportOrExternCrate { + Import(ImportId), + ExternCrate(ExternCrateId), +} + +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] +pub(crate) enum ImportType { + Import(ImportId), + Glob(UseId), + ExternCrate(ExternCrateId), +} + +impl ImportOrExternCrate { + pub fn into_import(self) -> Option<ImportId> { + match self { + ImportOrExternCrate::Import(it) => Some(it), + _ => None, + } + } +} + +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] +pub enum ImportOrDef { + Import(ImportId), + ExternCrate(ExternCrateId), + Def(ModuleDefId), +} +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd)] +pub struct ImportId { + pub import: UseId, + pub idx: Idx<ast::UseTree>, +} + #[derive(Debug, Default, PartialEq, Eq)] pub struct ItemScope { _c: Count<Self>, /// Defs visible in this scope. This includes `declarations`, but also - /// imports. - types: FxHashMap<Name, (ModuleDefId, Visibility)>, - values: FxHashMap<Name, (ModuleDefId, Visibility)>, - macros: FxHashMap<Name, (MacroId, Visibility)>, + /// imports. The imports belong to this module and can be resolved by using them on + /// the `use_imports_*` fields. + types: FxHashMap<Name, (ModuleDefId, Visibility, Option<ImportOrExternCrate>)>, + values: FxHashMap<Name, (ModuleDefId, Visibility, Option<ImportId>)>, + macros: FxHashMap<Name, (MacroId, Visibility, Option<ImportId>)>, unresolved: FxHashSet<Name>, /// The defs declared in this scope. Each def has a single scope where it is @@ -50,7 +80,14 @@ pub struct ItemScope { impls: Vec<ImplId>, unnamed_consts: Vec<ConstId>, /// Traits imported via `use Trait as _;`. - unnamed_trait_imports: FxHashMap<TraitId, Visibility>, + unnamed_trait_imports: FxHashMap<TraitId, (Visibility, Option<ImportId>)>, + + // the resolutions of the imports of this scope + use_imports_types: FxHashMap<ImportOrExternCrate, ImportOrDef>, + use_imports_values: FxHashMap<ImportId, ImportOrDef>, + use_imports_macros: FxHashMap<ImportId, ImportOrDef>, + + use_decls: Vec<UseId>, extern_crate_decls: Vec<ExternCrateId>, /// Macros visible in current module in legacy textual scope /// @@ -82,7 +119,7 @@ struct DeriveMacroInvocation { pub(crate) static BUILTIN_SCOPE: Lazy<FxHashMap<Name, PerNs>> = Lazy::new(|| { BuiltinType::ALL .iter() - .map(|(name, ty)| (name.clone(), PerNs::types((*ty).into(), Visibility::Public))) + .map(|(name, ty)| (name.clone(), PerNs::types((*ty).into(), Visibility::Public, None))) .collect() }); @@ -105,11 +142,77 @@ impl ItemScope { .chain(self.values.keys()) .chain(self.macros.keys()) .chain(self.unresolved.iter()) - .sorted() .unique() + .sorted() .map(move |name| (name, self.get(name))) } + pub fn imports(&self) -> impl Iterator<Item = ImportId> + '_ { + self.use_imports_types + .keys() + .copied() + .filter_map(ImportOrExternCrate::into_import) + .chain(self.use_imports_values.keys().copied()) + .chain(self.use_imports_macros.keys().copied()) + .unique() + .sorted() + } + + pub fn fully_resolve_import(&self, db: &dyn DefDatabase, mut import: ImportId) -> PerNs { + let mut res = PerNs::none(); + + let mut def_map; + let mut scope = self; + while let Some(&m) = scope.use_imports_macros.get(&import) { + match m { + ImportOrDef::Import(i) => { + let module_id = i.import.lookup(db).container; + def_map = module_id.def_map(db); + scope = &def_map[module_id.local_id].scope; + import = i; + } + ImportOrDef::Def(ModuleDefId::MacroId(def)) => { + res.macros = Some((def, Visibility::Public, None)); + break; + } + _ => break, + } + } + let mut scope = self; + while let Some(&m) = scope.use_imports_types.get(&ImportOrExternCrate::Import(import)) { + match m { + ImportOrDef::Import(i) => { + let module_id = i.import.lookup(db).container; + def_map = module_id.def_map(db); + scope = &def_map[module_id.local_id].scope; + import = i; + } + ImportOrDef::Def(def) => { + res.types = Some((def, Visibility::Public, None)); + break; + } + _ => break, + } + } + let mut scope = self; + while let Some(&m) = scope.use_imports_values.get(&import) { + match m { + ImportOrDef::Import(i) => { + let module_id = i.import.lookup(db).container; + def_map = module_id.def_map(db); + scope = &def_map[module_id.local_id].scope; + import = i; + } + ImportOrDef::Def(def) => { + res.values = Some((def, Visibility::Public, None)); + break; + } + _ => break, + } + } + res + } + pub fn declarations(&self) -> impl Iterator<Item = ModuleDefId> + '_ { self.declarations.iter().copied() } @@ -121,8 +224,7 @@ impl ItemScope { } pub fn use_decls(&self) -> impl Iterator<Item = UseId> + ExactSizeIterator + '_ { - // FIXME: to be implemented - std::iter::empty() + self.use_decls.iter().copied() } pub fn impls(&self) -> impl Iterator<Item = ImplId> + ExactSizeIterator + '_ { @@ -132,13 +234,13 @@ impl ItemScope { pub fn values( &self, ) -> impl Iterator<Item = (ModuleDefId, Visibility)> + ExactSizeIterator + '_ { - self.values.values().copied() + self.values.values().copied().map(|(a, b, _)| (a, b)) } - pub fn types( + pub(crate) fn types( &self, ) -> impl Iterator<Item = (ModuleDefId, Visibility)> + ExactSizeIterator + '_ { - self.types.values().copied() + self.types.values().copied().map(|(def, vis, _)| (def, vis)) } pub fn unnamed_consts(&self) -> impl Iterator<Item = ConstId> + '_ { @@ -165,33 +267,55 @@ impl ItemScope { } pub(crate) fn type_(&self, name: &Name) -> Option<(ModuleDefId, Visibility)> { - self.types.get(name).copied() + self.types.get(name).copied().map(|(a, b, _)| (a, b)) } /// XXX: this is O(N) rather than O(1), try to not introduce new usages. pub(crate) fn name_of(&self, item: ItemInNs) -> Option<(&Name, Visibility)> { - let (def, mut iter) = match item { - ItemInNs::Macros(def) => { - return self.macros.iter().find_map(|(name, &(other_def, vis))| { - (other_def == def).then_some((name, vis)) - }); - } - ItemInNs::Types(def) => (def, self.types.iter()), - ItemInNs::Values(def) => (def, self.values.iter()), - }; - iter.find_map(|(name, &(other_def, vis))| (other_def == def).then_some((name, vis))) + match item { + ItemInNs::Macros(def) => self + .macros + .iter() + .find_map(|(name, &(other_def, vis, _))| (other_def == def).then_some((name, vis))), + ItemInNs::Types(def) => self + .types + .iter() + .find_map(|(name, &(other_def, vis, _))| (other_def == def).then_some((name, vis))), + + ItemInNs::Values(def) => self + .values + .iter() + .find_map(|(name, &(other_def, vis, _))| (other_def == def).then_some((name, vis))), + } } pub(crate) fn traits(&self) -> impl Iterator<Item = TraitId> + '_ { self.types .values() - .filter_map(|&(def, _)| match def { + .filter_map(|&(def, _, _)| match def { ModuleDefId::TraitId(t) => Some(t), _ => None, }) .chain(self.unnamed_trait_imports.keys().copied()) } + pub(crate) fn resolutions(&self) -> impl Iterator<Item = (Option<Name>, PerNs)> + '_ { + self.entries().map(|(name, res)| (Some(name.clone()), res)).chain( + self.unnamed_trait_imports.iter().map(|(tr, (vis, i))| { + ( + None, + PerNs::types( + ModuleDefId::TraitId(*tr), + *vis, + i.map(ImportOrExternCrate::Import), + ), + ) + }), + ) + } +} + +impl ItemScope { pub(crate) fn declare(&mut self, def: ModuleDefId) { self.declarations.push(def) } @@ -277,12 +401,14 @@ impl ItemScope { }) } + // FIXME: This is only used in collection, we should move the relevant parts of it out of ItemScope pub(crate) fn unnamed_trait_vis(&self, tr: TraitId) -> Option<Visibility> { - self.unnamed_trait_imports.get(&tr).copied() + self.unnamed_trait_imports.get(&tr).copied().map(|(a, _)| a) } pub(crate) fn push_unnamed_trait(&mut self, tr: TraitId, vis: Visibility) { - self.unnamed_trait_imports.insert(tr, vis); + // FIXME: import + self.unnamed_trait_imports.insert(tr, (vis, None)); } pub(crate) fn push_res_with_import( @@ -290,51 +416,187 @@ impl ItemScope { glob_imports: &mut PerNsGlobImports, lookup: (LocalModuleId, Name), def: PerNs, - def_import_type: ImportType, + import: Option<ImportType>, ) -> bool { let mut changed = false; - macro_rules! check_changed { - ( - $changed:ident, - ( $this:ident / $def:ident ) . $field:ident, - $glob_imports:ident [ $lookup:ident ], - $def_import_type:ident - ) => {{ - if let Some(fld) = $def.$field { - let existing = $this.$field.entry($lookup.1.clone()); - match existing { - Entry::Vacant(entry) => { - match $def_import_type { - ImportType::Glob => { - $glob_imports.$field.insert($lookup.clone()); + // FIXME: Document and simplify this + + if let Some(mut fld) = def.types { + let existing = self.types.entry(lookup.1.clone()); + match existing { + Entry::Vacant(entry) => { + match import { + Some(ImportType::Glob(_)) => { + glob_imports.types.insert(lookup.clone()); + } + _ => _ = glob_imports.types.remove(&lookup), + } + let import = match import { + Some(ImportType::ExternCrate(extern_crate)) => { + Some(ImportOrExternCrate::ExternCrate(extern_crate)) + } + Some(ImportType::Import(import)) => { + Some(ImportOrExternCrate::Import(import)) + } + None | Some(ImportType::Glob(_)) => None, + }; + let prev = std::mem::replace(&mut fld.2, import); + if let Some(import) = import { + self.use_imports_types.insert( + import, + match prev { + Some(ImportOrExternCrate::Import(import)) => { + ImportOrDef::Import(import) } - ImportType::Named => { - $glob_imports.$field.remove(&$lookup); + Some(ImportOrExternCrate::ExternCrate(import)) => { + ImportOrDef::ExternCrate(import) } + None => ImportOrDef::Def(fld.0), + }, + ); + } + entry.insert(fld); + changed = true; + } + Entry::Occupied(mut entry) if !matches!(import, Some(ImportType::Glob(..))) => { + if glob_imports.types.remove(&lookup) { + let import = match import { + Some(ImportType::ExternCrate(extern_crate)) => { + Some(ImportOrExternCrate::ExternCrate(extern_crate)) + } + Some(ImportType::Import(import)) => { + Some(ImportOrExternCrate::Import(import)) } + None | Some(ImportType::Glob(_)) => None, + }; + let prev = std::mem::replace(&mut fld.2, import); + if let Some(import) = import { + self.use_imports_types.insert( + import, + match prev { + Some(ImportOrExternCrate::Import(import)) => { + ImportOrDef::Import(import) + } + Some(ImportOrExternCrate::ExternCrate(import)) => { + ImportOrDef::ExternCrate(import) + } + None => ImportOrDef::Def(fld.0), + }, + ); + } + cov_mark::hit!(import_shadowed); + entry.insert(fld); + changed = true; + } + } + _ => {} + } + } - entry.insert(fld); - $changed = true; + if let Some(mut fld) = def.values { + let existing = self.values.entry(lookup.1.clone()); + match existing { + Entry::Vacant(entry) => { + match import { + Some(ImportType::Glob(_)) => { + glob_imports.values.insert(lookup.clone()); } - Entry::Occupied(mut entry) - if matches!($def_import_type, ImportType::Named) => - { - if $glob_imports.$field.remove(&$lookup) { - cov_mark::hit!(import_shadowed); - entry.insert(fld); - $changed = true; - } + _ => _ = glob_imports.values.remove(&lookup), + } + let import = match import { + Some(ImportType::Import(import)) => Some(import), + _ => None, + }; + let prev = std::mem::replace(&mut fld.2, import); + if let Some(import) = import { + self.use_imports_values.insert( + import, + match prev { + Some(import) => ImportOrDef::Import(import), + None => ImportOrDef::Def(fld.0), + }, + ); + } + entry.insert(fld); + changed = true; + } + Entry::Occupied(mut entry) if !matches!(import, Some(ImportType::Glob(..))) => { + if glob_imports.values.remove(&lookup) { + cov_mark::hit!(import_shadowed); + let import = match import { + Some(ImportType::Import(import)) => Some(import), + _ => None, + }; + let prev = std::mem::replace(&mut fld.2, import); + if let Some(import) = import { + self.use_imports_values.insert( + import, + match prev { + Some(import) => ImportOrDef::Import(import), + None => ImportOrDef::Def(fld.0), + }, + ); } - _ => {} + entry.insert(fld); + changed = true; } } - }}; + _ => {} + } } - check_changed!(changed, (self / def).types, glob_imports[lookup], def_import_type); - check_changed!(changed, (self / def).values, glob_imports[lookup], def_import_type); - check_changed!(changed, (self / def).macros, glob_imports[lookup], def_import_type); + if let Some(mut fld) = def.macros { + let existing = self.macros.entry(lookup.1.clone()); + match existing { + Entry::Vacant(entry) => { + match import { + Some(ImportType::Glob(_)) => { + glob_imports.macros.insert(lookup.clone()); + } + _ => _ = glob_imports.macros.remove(&lookup), + } + let import = match import { + Some(ImportType::Import(import)) => Some(import), + _ => None, + }; + let prev = std::mem::replace(&mut fld.2, import); + if let Some(import) = import { + self.use_imports_macros.insert( + import, + match prev { + Some(import) => ImportOrDef::Import(import), + None => ImportOrDef::Def(fld.0.into()), + }, + ); + } + entry.insert(fld); + changed = true; + } + Entry::Occupied(mut entry) if !matches!(import, Some(ImportType::Glob(..))) => { + if glob_imports.macros.remove(&lookup) { + cov_mark::hit!(import_shadowed); + let import = match import { + Some(ImportType::Import(import)) => Some(import), + _ => None, + }; + let prev = std::mem::replace(&mut fld.2, import); + if let Some(import) = import { + self.use_imports_macros.insert( + import, + match prev { + Some(import) => ImportOrDef::Import(import), + None => ImportOrDef::Def(fld.0.into()), + }, + ); + } + entry.insert(fld); + changed = true; + } + } + _ => {} + } + } if def.is_none() && self.unresolved.insert(lookup.1) { changed = true; @@ -343,27 +605,18 @@ impl ItemScope { changed } - pub(crate) fn resolutions(&self) -> impl Iterator<Item = (Option<Name>, PerNs)> + '_ { - self.entries().map(|(name, res)| (Some(name.clone()), res)).chain( - self.unnamed_trait_imports - .iter() - .map(|(tr, vis)| (None, PerNs::types(ModuleDefId::TraitId(*tr), *vis))), - ) - } - /// Marks everything that is not a procedural macro as private to `this_module`. pub(crate) fn censor_non_proc_macros(&mut self, this_module: ModuleId) { self.types .values_mut() - .chain(self.values.values_mut()) + .map(|(def, vis, _)| (def, vis)) + .chain(self.values.values_mut().map(|(def, vis, _)| (def, vis))) .map(|(_, v)| v) - .chain(self.unnamed_trait_imports.values_mut()) + .chain(self.unnamed_trait_imports.values_mut().map(|(vis, _)| vis)) .for_each(|vis| *vis = Visibility::Module(this_module)); - for (mac, vis) in self.macros.values_mut() { - if let MacroId::ProcMacroId(_) = mac { - // FIXME: Technically this is insufficient since reexports of proc macros are also - // forbidden. Practically nobody does that. + for (mac, vis, import) in self.macros.values_mut() { + if matches!(mac, MacroId::ProcMacroId(_) if import.is_none()) { continue; } @@ -382,14 +635,25 @@ impl ItemScope { name.map_or("_".to_string(), |name| name.display(db).to_string()) ); - if def.types.is_some() { + if let Some((.., i)) = def.types { buf.push_str(" t"); + match i { + Some(ImportOrExternCrate::Import(_)) => buf.push('i'), + Some(ImportOrExternCrate::ExternCrate(_)) => buf.push('e'), + None => (), + } } - if def.values.is_some() { + if let Some((.., i)) = def.values { buf.push_str(" v"); + if i.is_some() { + buf.push('i'); + } } - if def.macros.is_some() { + if let Some((.., i)) = def.macros { buf.push_str(" m"); + if i.is_some() { + buf.push('i'); + } } if def.is_none() { buf.push_str(" _"); @@ -415,10 +679,17 @@ impl ItemScope { attr_macros, derive_macros, extern_crate_decls, + use_decls, + use_imports_values, + use_imports_types, + use_imports_macros, } = self; types.shrink_to_fit(); values.shrink_to_fit(); macros.shrink_to_fit(); + use_imports_types.shrink_to_fit(); + use_imports_values.shrink_to_fit(); + use_imports_macros.shrink_to_fit(); unresolved.shrink_to_fit(); declarations.shrink_to_fit(); impls.shrink_to_fit(); @@ -428,32 +699,44 @@ impl ItemScope { attr_macros.shrink_to_fit(); derive_macros.shrink_to_fit(); extern_crate_decls.shrink_to_fit(); + use_decls.shrink_to_fit(); } } impl PerNs { - pub(crate) fn from_def(def: ModuleDefId, v: Visibility, has_constructor: bool) -> PerNs { + pub(crate) fn from_def( + def: ModuleDefId, + v: Visibility, + has_constructor: bool, + import: Option<ImportOrExternCrate>, + ) -> PerNs { match def { - ModuleDefId::ModuleId(_) => PerNs::types(def, v), - ModuleDefId::FunctionId(_) => PerNs::values(def, v), + ModuleDefId::ModuleId(_) => PerNs::types(def, v, import), + ModuleDefId::FunctionId(_) => { + PerNs::values(def, v, import.and_then(ImportOrExternCrate::into_import)) + } ModuleDefId::AdtId(adt) => match adt { - AdtId::UnionId(_) => PerNs::types(def, v), - AdtId::EnumId(_) => PerNs::types(def, v), + AdtId::UnionId(_) => PerNs::types(def, v, import), + AdtId::EnumId(_) => PerNs::types(def, v, import), AdtId::StructId(_) => { if has_constructor { - PerNs::both(def, def, v) + PerNs::both(def, def, v, import) } else { - PerNs::types(def, v) + PerNs::types(def, v, import) } } }, - ModuleDefId::EnumVariantId(_) => PerNs::both(def, def, v), - ModuleDefId::ConstId(_) | ModuleDefId::StaticId(_) => PerNs::values(def, v), - ModuleDefId::TraitId(_) => PerNs::types(def, v), - ModuleDefId::TraitAliasId(_) => PerNs::types(def, v), - ModuleDefId::TypeAliasId(_) => PerNs::types(def, v), - ModuleDefId::BuiltinType(_) => PerNs::types(def, v), - ModuleDefId::MacroId(mac) => PerNs::macros(mac, v), + ModuleDefId::EnumVariantId(_) => PerNs::both(def, def, v, import), + ModuleDefId::ConstId(_) | ModuleDefId::StaticId(_) => { + PerNs::values(def, v, import.and_then(ImportOrExternCrate::into_import)) + } + ModuleDefId::TraitId(_) => PerNs::types(def, v, import), + ModuleDefId::TraitAliasId(_) => PerNs::types(def, v, import), + ModuleDefId::TypeAliasId(_) => PerNs::types(def, v, import), + ModuleDefId::BuiltinType(_) => PerNs::types(def, v, import), + ModuleDefId::MacroId(mac) => { + PerNs::macros(mac, v, import.and_then(ImportOrExternCrate::into_import)) + } } } } diff --git a/src/tools/rust-analyzer/crates/hir-def/src/item_tree.rs b/src/tools/rust-analyzer/crates/hir-def/src/item_tree.rs index c9b0f75f1a8..3e1922750b9 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/item_tree.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/item_tree.rs @@ -64,11 +64,11 @@ use triomphe::Arc; use crate::{ attr::Attrs, db::DefDatabase, - generics::GenericParams, + generics::{GenericParams, LifetimeParamData, TypeOrConstParamData}, path::{path, AssociatedTypeBinding, GenericArgs, ImportAlias, ModPath, Path, PathKind}, type_ref::{Mutability, TraitRef, TypeBound, TypeRef}, visibility::RawVisibility, - BlockId, + BlockId, Lookup, }; #[derive(Copy, Clone, Eq, PartialEq)] @@ -143,6 +143,16 @@ impl ItemTree { Arc::new(item_tree) } + pub(crate) fn block_item_tree_query(db: &dyn DefDatabase, block: BlockId) -> Arc<ItemTree> { + let loc = block.lookup(db); + let block = loc.ast_id.to_node(db.upcast()); + + let ctx = lower::Ctx::new(db, loc.ast_id.file_id); + let mut item_tree = ctx.lower_block(&block); + item_tree.shrink_to_fit(); + Arc::new(item_tree) + } + /// Returns an iterator over all items located at the top level of the `HirFileId` this /// `ItemTree` was created from. pub fn top_level_items(&self) -> &[ModItem] { @@ -178,13 +188,6 @@ impl ItemTree { self.data.get_or_insert_with(Box::default) } - fn block_item_tree(db: &dyn DefDatabase, block: BlockId) -> Arc<ItemTree> { - let loc = db.lookup_intern_block(block); - let block = loc.ast_id.to_node(db.upcast()); - let ctx = lower::Ctx::new(db, loc.ast_id.file_id); - Arc::new(ctx.lower_block(&block)) - } - fn shrink_to_fit(&mut self) { if let Some(data) = &mut self.data { let ItemTreeData { @@ -296,10 +299,12 @@ pub enum AttrOwner { Variant(Idx<Variant>), Field(Idx<Field>), Param(Idx<Param>), + TypeOrConstParamData(Idx<TypeOrConstParamData>), + LifetimeParamData(Idx<LifetimeParamData>), } macro_rules! from_attrs { - ( $( $var:ident($t:ty) ),+ ) => { + ( $( $var:ident($t:ty) ),+ $(,)? ) => { $( impl From<$t> for AttrOwner { fn from(t: $t) -> AttrOwner { @@ -310,7 +315,14 @@ macro_rules! from_attrs { }; } -from_attrs!(ModItem(ModItem), Variant(Idx<Variant>), Field(Idx<Field>), Param(Idx<Param>)); +from_attrs!( + ModItem(ModItem), + Variant(Idx<Variant>), + Field(Idx<Field>), + Param(Idx<Param>), + TypeOrConstParamData(Idx<TypeOrConstParamData>), + LifetimeParamData(Idx<LifetimeParamData>), +); /// Trait implemented by all item nodes in the item tree. pub trait ItemTreeNode: Clone { @@ -373,7 +385,7 @@ impl TreeId { pub(crate) fn item_tree(&self, db: &dyn DefDatabase) -> Arc<ItemTree> { match self.block { - Some(block) => ItemTree::block_item_tree(db, block), + Some(block) => db.block_item_tree_query(block), None => db.file_item_tree(self.file), } } @@ -761,6 +773,19 @@ impl Use { lower::lower_use_tree(db, &hygiene, ast_use_tree).expect("failed to lower use tree"); source_map[index].clone() } + /// Maps a `UseTree` contained in this import back to its AST node. + pub fn use_tree_source_map( + &self, + db: &dyn DefDatabase, + file_id: HirFileId, + ) -> Arena<ast::UseTree> { + // Re-lower the AST item and get the source map. + // Note: The AST unwraps are fine, since if they fail we should have never obtained `index`. + let ast = InFile::new(file_id, self.ast_id).to_node(db.upcast()); + let ast_use_tree = ast.use_tree().expect("missing `use_tree`"); + let hygiene = Hygiene::new(db.upcast(), file_id); + lower::lower_use_tree(db, &hygiene, ast_use_tree).expect("failed to lower use tree").1 + } } #[derive(Clone, Copy, Debug, Eq, PartialEq)] @@ -785,7 +810,7 @@ impl UseTree { fn expand_impl( &self, prefix: Option<ModPath>, - cb: &mut dyn FnMut(Idx<ast::UseTree>, ModPath, ImportKind, Option<ImportAlias>), + cb: &mut impl FnMut(Idx<ast::UseTree>, ModPath, ImportKind, Option<ImportAlias>), ) { fn concat_mod_paths( prefix: Option<ModPath>, diff --git a/src/tools/rust-analyzer/crates/hir-def/src/item_tree/lower.rs b/src/tools/rust-analyzer/crates/hir-def/src/item_tree/lower.rs index 7b898e62dba..e4702c113b8 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/item_tree/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/item_tree/lower.rs @@ -77,6 +77,9 @@ impl<'a> Ctx<'a> { } pub(super) fn lower_block(mut self, block: &ast::BlockExpr) -> ItemTree { + self.tree + .attrs + .insert(AttrOwner::TopLevel, RawAttrs::new(self.db.upcast(), block, self.hygiene())); self.tree.top_level = block .statements() .filter_map(|stmt| match stmt { @@ -602,7 +605,21 @@ impl<'a> Ctx<'a> { generics.fill_bounds(&self.body_ctx, bounds, Either::Left(self_param)); } - generics.fill(&self.body_ctx, node); + let add_param_attrs = |item, param| { + let attrs = RawAttrs::new(self.db.upcast(), ¶m, self.body_ctx.hygiene()); + // This is identical to the body of `Ctx::add_attrs()` but we can't call that here + // because it requires `&mut self` and the call to `generics.fill()` below also + // references `self`. + match self.tree.attrs.entry(item) { + Entry::Occupied(mut entry) => { + *entry.get_mut() = entry.get().merge(attrs); + } + Entry::Vacant(entry) => { + entry.insert(attrs); + } + } + }; + generics.fill(&self.body_ctx, node, add_param_attrs); generics.shrink_to_fit(); Interned::new(generics) @@ -763,7 +780,7 @@ impl UseTreeLowering<'_> { } } -pub(super) fn lower_use_tree( +pub(crate) fn lower_use_tree( db: &dyn DefDatabase, hygiene: &Hygiene, tree: ast::UseTree, diff --git a/src/tools/rust-analyzer/crates/hir-def/src/item_tree/pretty.rs b/src/tools/rust-analyzer/crates/hir-def/src/item_tree/pretty.rs index da30830fe45..4b852dd613e 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/item_tree/pretty.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/item_tree/pretty.rs @@ -16,7 +16,7 @@ pub(super) fn print_item_tree(db: &dyn ExpandDatabase, tree: &ItemTree) -> Strin let mut p = Printer { db, tree, buf: String::new(), indent_level: 0, needs_indent: true }; if let Some(attrs) = tree.attrs.get(&AttrOwner::TopLevel) { - p.print_attrs(attrs, true); + p.print_attrs(attrs, true, "\n"); } p.blank(); @@ -84,22 +84,23 @@ impl Printer<'_> { } } - fn print_attrs(&mut self, attrs: &RawAttrs, inner: bool) { + fn print_attrs(&mut self, attrs: &RawAttrs, inner: bool, separated_by: &str) { let inner = if inner { "!" } else { "" }; for attr in &**attrs { - wln!( + w!( self, - "#{}[{}{}]", + "#{}[{}{}]{}", inner, attr.path.display(self.db), attr.input.as_ref().map(|it| it.to_string()).unwrap_or_default(), + separated_by, ); } } - fn print_attrs_of(&mut self, of: impl Into<AttrOwner>) { + fn print_attrs_of(&mut self, of: impl Into<AttrOwner>, separated_by: &str) { if let Some(attrs) = self.tree.attrs.get(&of.into()) { - self.print_attrs(attrs, false); + self.print_attrs(attrs, false, separated_by); } } @@ -118,7 +119,7 @@ impl Printer<'_> { self.indented(|this| { for field in fields.clone() { let Field { visibility, name, type_ref, ast_id: _ } = &this.tree[field]; - this.print_attrs_of(field); + this.print_attrs_of(field, "\n"); this.print_visibility(*visibility); w!(this, "{}: ", name.display(self.db)); this.print_type_ref(type_ref); @@ -132,7 +133,7 @@ impl Printer<'_> { self.indented(|this| { for field in fields.clone() { let Field { visibility, name, type_ref, ast_id: _ } = &this.tree[field]; - this.print_attrs_of(field); + this.print_attrs_of(field, "\n"); this.print_visibility(*visibility); w!(this, "{}: ", name.display(self.db)); this.print_type_ref(type_ref); @@ -195,7 +196,7 @@ impl Printer<'_> { } fn print_mod_item(&mut self, item: ModItem) { - self.print_attrs_of(item); + self.print_attrs_of(item, "\n"); match item { ModItem::Use(it) => { @@ -261,7 +262,7 @@ impl Printer<'_> { if !params.is_empty() { self.indented(|this| { for param in params.clone() { - this.print_attrs_of(param); + this.print_attrs_of(param, "\n"); match &this.tree[param] { Param::Normal(ty) => { if flags.contains(FnFlags::HAS_SELF_PARAM) { @@ -319,7 +320,7 @@ impl Printer<'_> { self.indented(|this| { for variant in variants.clone() { let Variant { name, fields, ast_id: _ } = &this.tree[variant]; - this.print_attrs_of(variant); + this.print_attrs_of(variant, "\n"); w!(this, "{}", name.display(self.db)); this.print_fields(fields); wln!(this, ","); @@ -484,11 +485,12 @@ impl Printer<'_> { w!(self, "<"); let mut first = true; - for (_, lt) in params.lifetimes.iter() { + for (idx, lt) in params.lifetimes.iter() { if !first { w!(self, ", "); } first = false; + self.print_attrs_of(idx, " "); w!(self, "{}", lt.name.display(self.db)); } for (idx, x) in params.type_or_consts.iter() { @@ -496,6 +498,7 @@ impl Printer<'_> { w!(self, ", "); } first = false; + self.print_attrs_of(idx, " "); match x { TypeOrConstParamData::TypeParamData(ty) => match &ty.name { Some(name) => w!(self, "{}", name.display(self.db)), diff --git a/src/tools/rust-analyzer/crates/hir-def/src/item_tree/tests.rs b/src/tools/rust-analyzer/crates/hir-def/src/item_tree/tests.rs index 5ded4b6b273..4180f817209 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/item_tree/tests.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/item_tree/tests.rs @@ -358,3 +358,15 @@ trait Tr<'a, T: 'a>: Super where Self: for<'a> Tr<'a, T> {} "#]], ) } + +#[test] +fn generics_with_attributes() { + check( + r#" +struct S<#[cfg(never)] T>; + "#, + expect![[r#" + pub(self) struct S<#[cfg(never)] T>; + "#]], + ) +} diff --git a/src/tools/rust-analyzer/crates/hir-def/src/lib.rs b/src/tools/rust-analyzer/crates/hir-def/src/lib.rs index 1901db8a0f9..3f87fe62b83 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/lib.rs @@ -109,6 +109,17 @@ impl CrateRootModuleId { } } +impl PartialEq<ModuleId> for CrateRootModuleId { + fn eq(&self, other: &ModuleId) -> bool { + other.block.is_none() && other.local_id == DefMap::ROOT && self.krate == other.krate + } +} +impl PartialEq<CrateRootModuleId> for ModuleId { + fn eq(&self, other: &CrateRootModuleId) -> bool { + other == self + } +} + impl From<CrateRootModuleId> for ModuleId { fn from(CrateRootModuleId { krate }: CrateRootModuleId) -> Self { ModuleId { krate, block: None, local_id: DefMap::ROOT } @@ -854,14 +865,36 @@ impl_from!( ConstId, FunctionId, TraitId, + TraitAliasId, TypeAliasId, MacroId(Macro2Id, MacroRulesId, ProcMacroId), ImplId, GenericParamId, - ExternCrateId + ExternCrateId, + UseId for AttrDefId ); +impl TryFrom<ModuleDefId> for AttrDefId { + type Error = (); + + fn try_from(value: ModuleDefId) -> Result<Self, Self::Error> { + match value { + ModuleDefId::ModuleId(it) => Ok(it.into()), + ModuleDefId::FunctionId(it) => Ok(it.into()), + ModuleDefId::AdtId(it) => Ok(it.into()), + ModuleDefId::EnumVariantId(it) => Ok(it.into()), + ModuleDefId::ConstId(it) => Ok(it.into()), + ModuleDefId::StaticId(it) => Ok(it.into()), + ModuleDefId::TraitId(it) => Ok(it.into()), + ModuleDefId::TypeAliasId(it) => Ok(it.into()), + ModuleDefId::TraitAliasId(id) => Ok(id.into()), + ModuleDefId::MacroId(id) => Ok(id.into()), + ModuleDefId::BuiltinType(_) => Err(()), + } + } +} + impl From<ItemContainerId> for AttrDefId { fn from(acid: ItemContainerId) -> Self { match acid { @@ -872,6 +905,15 @@ impl From<ItemContainerId> for AttrDefId { } } } +impl From<AssocItemId> for AttrDefId { + fn from(assoc: AssocItemId) -> Self { + match assoc { + AssocItemId::FunctionId(it) => AttrDefId::FunctionId(it), + AssocItemId::ConstId(it) => AttrDefId::ConstId(it), + AssocItemId::TypeAliasId(it) => AttrDefId::TypeAliasId(it), + } + } +} #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub enum VariantId { diff --git a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/builtin_fn_macro.rs b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/builtin_fn_macro.rs index 1250cbb742c..b232651db96 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/builtin_fn_macro.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/builtin_fn_macro.rs @@ -238,7 +238,7 @@ fn main() { /* error: expected expression */; /* error: expected expression, expected COMMA */; /* error: expected expression */::core::fmt::Arguments::new_v1(&["", ], &[::core::fmt::ArgumentV1::new(&(), ::core::fmt::Display::fmt), ]); - /* error: expected expression, expected expression */; + /* error: expected expression, expected R_PAREN */; ::core::fmt::Arguments::new_v1(&["", ], &[::core::fmt::ArgumentV1::new(&(5), ::core::fmt::Display::fmt), ]); } "##]], diff --git a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe/regression.rs b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe/regression.rs index d8e4a4dcc7c..97554f93f1c 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe/regression.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe/regression.rs @@ -909,3 +909,68 @@ macro_rules! with_std { "##]], ) } + +#[test] +fn eager_regression_15403() { + check( + r#" +#[rustc_builtin_macro] +#[macro_export] +macro_rules! format_args {} + +fn main() { + format_args /* +errors */ !("{}", line.1.); +} + +"#, + expect![[r##" +#[rustc_builtin_macro] +#[macro_export] +macro_rules! format_args {} + +fn main() { + /* error: expected field name or number *//* parse error: expected field name or number */ +::core::fmt::Arguments::new_v1(&["", ], &[::core::fmt::ArgumentV1::new(&(line.1.), ::core::fmt::Display::fmt), ]); +} + +"##]], + ); +} + +#[test] +fn eager_regression_154032() { + check( + r#" +#[rustc_builtin_macro] +#[macro_export] +macro_rules! format_args {} + +fn main() { + format_args /* +errors */ !("{}", &[0 2]); +} + +"#, + expect![[r##" +#[rustc_builtin_macro] +#[macro_export] +macro_rules! format_args {} + +fn main() { + /* error: expected COMMA, expected R_BRACK, expected COMMA, expected COMMA, expected expression, expected R_PAREN *//* parse error: expected COMMA */ +/* parse error: expected R_BRACK */ +/* parse error: expected COMMA */ +/* parse error: expected COMMA */ +/* parse error: expected expression */ +/* parse error: expected R_PAREN */ +/* parse error: expected R_PAREN */ +/* parse error: expected expression, item or let statement */ +/* parse error: expected expression, item or let statement */ +/* parse error: expected expression, item or let statement */ +/* parse error: expected expression, item or let statement */ +/* parse error: expected expression, item or let statement */ +::core::fmt::Arguments::new_v1(&["", ], &[::core::fmt::ArgumentV1::new(&(&[0 2]), ::core::fmt::Display::fmt), ]); +} + +"##]], + ); +} diff --git a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mod.rs b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mod.rs index 7a87e61c693..8adced4e082 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mod.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mod.rs @@ -131,7 +131,7 @@ pub fn identity_when_valid(_attr: TokenStream, item: TokenStream) -> TokenStream .as_call_id_with_errors(&db, krate, |path| { resolver .resolve_path_as_macro(&db, &path, Some(MacroSubNs::Bang)) - .map(|it| macro_id_to_def_id(&db, it)) + .map(|(it, _)| macro_id_to_def_id(&db, it)) }) .unwrap(); let macro_call_id = res.value.unwrap(); diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres.rs index 86818ce26dd..f2110410980 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/nameres.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres.rs @@ -60,7 +60,7 @@ mod tests; use std::{cmp::Ord, ops::Deref}; use base_db::{CrateId, Edition, FileId, ProcMacroKind}; -use hir_expand::{name::Name, HirFileId, InFile, MacroCallId, MacroDefId}; +use hir_expand::{ast_id_map::FileAstId, name::Name, HirFileId, InFile, MacroCallId, MacroDefId}; use itertools::Itertools; use la_arena::Arena; use profile::Count; @@ -77,8 +77,8 @@ use crate::{ path::ModPath, per_ns::PerNs, visibility::Visibility, - AstId, BlockId, BlockLoc, CrateRootModuleId, FunctionId, LocalModuleId, Lookup, MacroExpander, - MacroId, ModuleId, ProcMacroId, + AstId, BlockId, BlockLoc, CrateRootModuleId, ExternCrateId, FunctionId, LocalModuleId, Lookup, + MacroExpander, MacroId, ModuleId, ProcMacroId, UseId, }; /// Contains the results of (early) name resolution. @@ -105,10 +105,11 @@ pub struct DefMap { /// The prelude is empty for non-block DefMaps (unless `#[prelude_import]` was used, /// but that attribute is nightly and when used in a block, it affects resolution globally /// so we aren't handling this correctly anyways). - prelude: Option<ModuleId>, + prelude: Option<(ModuleId, Option<UseId>)>, /// `macro_use` prelude that contains macros from `#[macro_use]`'d external crates. Note that /// this contains all kinds of macro, not just `macro_rules!` macro. - macro_use_prelude: FxHashMap<Name, MacroId>, + /// ExternCrateId being None implies it being imported from the general prelude import. + macro_use_prelude: FxHashMap<Name, (MacroId, Option<ExternCrateId>)>, /// Tracks which custom derives are in scope for an item, to allow resolution of derive helper /// attributes. @@ -125,7 +126,7 @@ pub struct DefMap { #[derive(Clone, Debug, PartialEq, Eq)] struct DefMapCrateData { /// The extern prelude which contains all root modules of external crates that are in scope. - extern_prelude: FxHashMap<Name, CrateRootModuleId>, + extern_prelude: FxHashMap<Name, (CrateRootModuleId, Option<ExternCrateId>)>, /// Side table for resolving derive helpers. exported_derives: FxHashMap<MacroDefId, Box<[Name]>>, @@ -217,16 +218,17 @@ pub enum ModuleOrigin { /// Note that non-inline modules, by definition, live inside non-macro file. File { is_mod_rs: bool, - declaration: AstId<ast::Module>, + declaration: FileAstId<ast::Module>, declaration_tree_id: ItemTreeId<Mod>, definition: FileId, }, Inline { definition_tree_id: ItemTreeId<Mod>, - definition: AstId<ast::Module>, + definition: FileAstId<ast::Module>, }, /// Pseudo-module introduced by a block scope (contains only inner items). BlockExpr { + id: BlockId, block: AstId<ast::BlockExpr>, }, } @@ -234,8 +236,12 @@ pub enum ModuleOrigin { impl ModuleOrigin { pub fn declaration(&self) -> Option<AstId<ast::Module>> { match self { - ModuleOrigin::File { declaration: module, .. } - | ModuleOrigin::Inline { definition: module, .. } => Some(*module), + &ModuleOrigin::File { declaration, declaration_tree_id, .. } => { + Some(AstId::new(declaration_tree_id.file_id(), declaration)) + } + &ModuleOrigin::Inline { definition, definition_tree_id } => { + Some(AstId::new(definition_tree_id.file_id(), definition)) + } ModuleOrigin::CrateRoot { .. } | ModuleOrigin::BlockExpr { .. } => None, } } @@ -260,16 +266,17 @@ impl ModuleOrigin { /// That is, a file or a `mod foo {}` with items. fn definition_source(&self, db: &dyn DefDatabase) -> InFile<ModuleSource> { match self { - ModuleOrigin::File { definition, .. } | ModuleOrigin::CrateRoot { definition } => { - let file_id = *definition; - let sf = db.parse(file_id).tree(); - InFile::new(file_id.into(), ModuleSource::SourceFile(sf)) + &ModuleOrigin::File { definition, .. } | &ModuleOrigin::CrateRoot { definition } => { + let sf = db.parse(definition).tree(); + InFile::new(definition.into(), ModuleSource::SourceFile(sf)) } - ModuleOrigin::Inline { definition, .. } => InFile::new( - definition.file_id, - ModuleSource::Module(definition.to_node(db.upcast())), + &ModuleOrigin::Inline { definition, definition_tree_id } => InFile::new( + definition_tree_id.file_id(), + ModuleSource::Module( + AstId::new(definition_tree_id.file_id(), definition).to_node(db.upcast()), + ), ), - ModuleOrigin::BlockExpr { block } => { + ModuleOrigin::BlockExpr { block, .. } => { InFile::new(block.file_id, ModuleSource::BlockExpr(block.to_node(db.upcast()))) } } @@ -314,9 +321,7 @@ impl DefMap { } pub(crate) fn block_def_map_query(db: &dyn DefDatabase, block_id: BlockId) -> Arc<DefMap> { - let block: BlockLoc = db.lookup_intern_block(block_id); - - let tree_id = TreeId::new(block.ast_id.file_id, Some(block_id)); + let block: BlockLoc = block_id.lookup(db); let parent_map = block.module.def_map(db); let krate = block.module.krate; @@ -325,8 +330,10 @@ impl DefMap { // modules declared by blocks with items. At the moment, we don't use // this visibility for anything outside IDE, so that's probably OK. let visibility = Visibility::Module(ModuleId { krate, local_id, block: None }); - let module_data = - ModuleData::new(ModuleOrigin::BlockExpr { block: block.ast_id }, visibility); + let module_data = ModuleData::new( + ModuleOrigin::BlockExpr { block: block.ast_id, id: block_id }, + visibility, + ); let mut def_map = DefMap::empty(krate, parent_map.data.edition, module_data); def_map.data = parent_map.data.clone(); @@ -338,7 +345,8 @@ impl DefMap { }, }); - let def_map = collector::collect_defs(db, def_map, tree_id); + let def_map = + collector::collect_defs(db, def_map, TreeId::new(block.ast_id.file_id, Some(block_id))); Arc::new(def_map) } @@ -427,15 +435,19 @@ impl DefMap { self.block.map(|block| block.block) } - pub(crate) fn prelude(&self) -> Option<ModuleId> { + pub(crate) fn prelude(&self) -> Option<(ModuleId, Option<UseId>)> { self.prelude } - pub(crate) fn extern_prelude(&self) -> impl Iterator<Item = (&Name, ModuleId)> + '_ { - self.data.extern_prelude.iter().map(|(name, &def)| (name, def.into())) + pub(crate) fn extern_prelude( + &self, + ) -> impl Iterator<Item = (&Name, (CrateRootModuleId, Option<ExternCrateId>))> + '_ { + self.data.extern_prelude.iter().map(|(name, &def)| (name, def)) } - pub(crate) fn macro_use_prelude(&self) -> impl Iterator<Item = (&Name, MacroId)> + '_ { + pub(crate) fn macro_use_prelude( + &self, + ) -> impl Iterator<Item = (&Name, (MacroId, Option<ExternCrateId>))> + '_ { self.macro_use_prelude.iter().map(|(name, &def)| (name, def)) } @@ -638,8 +650,8 @@ impl ModuleData { ModuleOrigin::File { definition, .. } | ModuleOrigin::CrateRoot { definition } => { definition.into() } - ModuleOrigin::Inline { definition, .. } => definition.file_id, - ModuleOrigin::BlockExpr { block } => block.file_id, + ModuleOrigin::Inline { definition_tree_id, .. } => definition_tree_id.file_id(), + ModuleOrigin::BlockExpr { block, .. } => block.file_id, } } diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs index eef54fc492e..e9e71a8747f 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs @@ -33,7 +33,7 @@ use crate::{ attr_macro_as_call_id, db::DefDatabase, derive_macro_as_call_id, - item_scope::{ImportType, PerNsGlobImports}, + item_scope::{ImportId, ImportOrExternCrate, ImportType, PerNsGlobImports}, item_tree::{ self, ExternCrate, Fields, FileItemTreeId, ImportKind, ItemTree, ItemTreeId, ItemTreeNode, MacroCall, MacroDef, MacroRules, Mod, ModItem, ModKind, TreeId, @@ -52,10 +52,10 @@ use crate::{ tt, visibility::{RawVisibility, Visibility}, AdtId, AstId, AstIdWithPath, ConstLoc, CrateRootModuleId, EnumLoc, EnumVariantId, - ExternBlockLoc, ExternCrateLoc, FunctionId, FunctionLoc, ImplLoc, Intern, ItemContainerId, - LocalModuleId, Macro2Id, Macro2Loc, MacroExpander, MacroId, MacroRulesId, MacroRulesLoc, - ModuleDefId, ModuleId, ProcMacroId, ProcMacroLoc, StaticLoc, StructLoc, TraitAliasLoc, - TraitLoc, TypeAliasLoc, UnionLoc, UnresolvedMacro, UseLoc, + ExternBlockLoc, ExternCrateId, ExternCrateLoc, FunctionId, FunctionLoc, ImplLoc, Intern, + ItemContainerId, LocalModuleId, Lookup, Macro2Id, Macro2Loc, MacroExpander, MacroId, + MacroRulesId, MacroRulesLoc, ModuleDefId, ModuleId, ProcMacroId, ProcMacroLoc, StaticLoc, + StructLoc, TraitAliasLoc, TraitLoc, TypeAliasLoc, UnionLoc, UnresolvedMacro, UseId, UseLoc, }; static GLOB_RECURSION_LIMIT: Limit = Limit::new(100); @@ -146,8 +146,8 @@ impl PartialResolvedImport { #[derive(Clone, Debug, Eq, PartialEq)] enum ImportSource { - Use { id: ItemTreeId<item_tree::Use>, use_tree: Idx<ast::UseTree> }, - ExternCrate(ItemTreeId<item_tree::ExternCrate>), + Use { use_tree: Idx<ast::UseTree>, id: UseId, is_prelude: bool, kind: ImportKind }, + ExternCrate { id: ExternCrateId }, } #[derive(Debug, Eq, PartialEq)] @@ -155,54 +155,41 @@ struct Import { path: ModPath, alias: Option<ImportAlias>, visibility: RawVisibility, - kind: ImportKind, source: ImportSource, - is_prelude: bool, - is_macro_use: bool, } impl Import { fn from_use( - db: &dyn DefDatabase, - krate: CrateId, tree: &ItemTree, - id: ItemTreeId<item_tree::Use>, + item_tree_id: ItemTreeId<item_tree::Use>, + id: UseId, + is_prelude: bool, mut cb: impl FnMut(Self), ) { - let it = &tree[id.value]; - let attrs = &tree.attrs(db, krate, ModItem::from(id.value).into()); + let it = &tree[item_tree_id.value]; let visibility = &tree[it.visibility]; - let is_prelude = attrs.by_key("prelude_import").exists(); it.use_tree.expand(|idx, path, kind, alias| { cb(Self { path, alias, visibility: visibility.clone(), - kind, - is_prelude, - is_macro_use: false, - source: ImportSource::Use { id, use_tree: idx }, + source: ImportSource::Use { use_tree: idx, id, is_prelude, kind }, }); }); } fn from_extern_crate( - db: &dyn DefDatabase, - krate: CrateId, tree: &ItemTree, - id: ItemTreeId<item_tree::ExternCrate>, + item_tree_id: ItemTreeId<item_tree::ExternCrate>, + id: ExternCrateId, ) -> Self { - let it = &tree[id.value]; - let attrs = &tree.attrs(db, krate, ModItem::from(id.value).into()); + let it = &tree[item_tree_id.value]; let visibility = &tree[it.visibility]; Self { path: ModPath::from_segments(PathKind::Plain, iter::once(it.name.clone())), alias: it.alias.clone(), visibility: visibility.clone(), - kind: ImportKind::Plain, - is_prelude: false, - is_macro_use: attrs.by_key("macro_use").exists(), - source: ImportSource::ExternCrate(id), + source: ImportSource::ExternCrate { id }, } } } @@ -235,7 +222,7 @@ struct DefCollector<'a> { db: &'a dyn DefDatabase, def_map: DefMap, deps: FxHashMap<Name, Dependency>, - glob_imports: FxHashMap<LocalModuleId, Vec<(LocalModuleId, Visibility)>>, + glob_imports: FxHashMap<LocalModuleId, Vec<(LocalModuleId, Visibility, UseId)>>, unresolved_imports: Vec<ImportDirective>, indeterminate_imports: Vec<ImportDirective>, unresolved_macros: Vec<MacroDirective>, @@ -280,7 +267,7 @@ impl DefCollector<'_> { if dep.is_prelude() { crate_data .extern_prelude - .insert(name.clone(), CrateRootModuleId { krate: dep.crate_id }); + .insert(name.clone(), (CrateRootModuleId { krate: dep.crate_id }, None)); } } @@ -556,8 +543,12 @@ impl DefCollector<'_> { self.def_map.resolve_path(self.db, DefMap::ROOT, &path, BuiltinShadowMode::Other, None); match per_ns.types { - Some((ModuleDefId::ModuleId(m), _)) => { - self.def_map.prelude = Some(m); + Some((ModuleDefId::ModuleId(m), _, import)) => { + // FIXME: This should specifically look for a glob import somehow and record that here + self.def_map.prelude = Some(( + m, + import.and_then(ImportOrExternCrate::into_import).map(|it| it.import), + )); } types => { tracing::debug!( @@ -657,9 +648,9 @@ impl DefCollector<'_> { self.def_map.modules[module_id].scope.declare(macro_.into()); self.update( module_id, - &[(Some(name), PerNs::macros(macro_.into(), Visibility::Public))], + &[(Some(name), PerNs::macros(macro_.into(), Visibility::Public, None))], Visibility::Public, - ImportType::Named, + None, ); } } @@ -693,9 +684,9 @@ impl DefCollector<'_> { self.def_map.modules[module_id].scope.declare(macro_.into()); self.update( module_id, - &[(Some(name), PerNs::macros(macro_.into(), Visibility::Public))], + &[(Some(name), PerNs::macros(macro_.into(), Visibility::Public, None))], vis, - ImportType::Named, + None, ); } @@ -708,9 +699,9 @@ impl DefCollector<'_> { self.def_map.modules[module_id].scope.declare(macro_.into()); self.update( module_id, - &[(Some(name), PerNs::macros(macro_.into(), Visibility::Public))], + &[(Some(name), PerNs::macros(macro_.into(), Visibility::Public, None))], Visibility::Public, - ImportType::Named, + None, ); } @@ -720,21 +711,29 @@ impl DefCollector<'_> { /// Exported macros are just all macros in the root module scope. /// Note that it contains not only all `#[macro_export]` macros, but also all aliases /// created by `use` in the root module, ignoring the visibility of `use`. - fn import_macros_from_extern_crate(&mut self, krate: CrateId, names: Option<Vec<Name>>) { + fn import_macros_from_extern_crate( + &mut self, + krate: CrateId, + names: Option<Vec<Name>>, + extern_crate: Option<ExternCrateId>, + ) { let def_map = self.db.crate_def_map(krate); // `#[macro_use]` brings macros into macro_use prelude. Yes, even non-`macro_rules!` // macros. let root_scope = &def_map[DefMap::ROOT].scope; - if let Some(names) = names { - for name in names { - // FIXME: Report diagnostic on 404. - if let Some(def) = root_scope.get(&name).take_macros() { - self.def_map.macro_use_prelude.insert(name, def); + match names { + Some(names) => { + for name in names { + // FIXME: Report diagnostic on 404. + if let Some(def) = root_scope.get(&name).take_macros() { + self.def_map.macro_use_prelude.insert(name, (def, extern_crate)); + } } } - } else { - for (name, def) in root_scope.macros() { - self.def_map.macro_use_prelude.insert(name.clone(), def); + None => { + for (name, def) in root_scope.macros() { + self.def_map.macro_use_prelude.insert(name.clone(), (def, extern_crate)); + } } } } @@ -771,48 +770,53 @@ impl DefCollector<'_> { let _p = profile::span("resolve_import") .detail(|| format!("{}", import.path.display(self.db.upcast()))); tracing::debug!("resolving import: {:?} ({:?})", import, self.def_map.data.edition); - if matches!(import.source, ImportSource::ExternCrate { .. }) { - let name = import - .path - .as_ident() - .expect("extern crate should have been desugared to one-element path"); - - let res = self.resolve_extern_crate(name); - - match res { - Some(res) => { - PartialResolvedImport::Resolved(PerNs::types(res.into(), Visibility::Public)) + match import.source { + ImportSource::ExternCrate { .. } => { + let name = import + .path + .as_ident() + .expect("extern crate should have been desugared to one-element path"); + + let res = self.resolve_extern_crate(name); + + match res { + Some(res) => PartialResolvedImport::Resolved(PerNs::types( + res.into(), + Visibility::Public, + None, + )), + None => PartialResolvedImport::Unresolved, } - None => PartialResolvedImport::Unresolved, } - } else { - let res = self.def_map.resolve_path_fp_with_macro( - self.db, - ResolveMode::Import, - module_id, - &import.path, - BuiltinShadowMode::Module, - None, // An import may resolve to any kind of macro. - ); + ImportSource::Use { .. } => { + let res = self.def_map.resolve_path_fp_with_macro( + self.db, + ResolveMode::Import, + module_id, + &import.path, + BuiltinShadowMode::Module, + None, // An import may resolve to any kind of macro. + ); - let def = res.resolved_def; - if res.reached_fixedpoint == ReachedFixedPoint::No || def.is_none() { - return PartialResolvedImport::Unresolved; - } + let def = res.resolved_def; + if res.reached_fixedpoint == ReachedFixedPoint::No || def.is_none() { + return PartialResolvedImport::Unresolved; + } - if let Some(krate) = res.krate { - if krate != self.def_map.krate { - return PartialResolvedImport::Resolved( - def.filter_visibility(|v| matches!(v, Visibility::Public)), - ); + if let Some(krate) = res.krate { + if krate != self.def_map.krate { + return PartialResolvedImport::Resolved( + def.filter_visibility(|v| matches!(v, Visibility::Public)), + ); + } } - } - // Check whether all namespaces are resolved. - if def.is_full() { - PartialResolvedImport::Resolved(def) - } else { - PartialResolvedImport::Indeterminate(def) + // Check whether all namespaces are resolved. + if def.is_full() { + PartialResolvedImport::Resolved(def) + } else { + PartialResolvedImport::Indeterminate(def) + } } } } @@ -837,8 +841,9 @@ impl DefCollector<'_> { .resolve_visibility(self.db, module_id, &directive.import.visibility, false) .unwrap_or(Visibility::Public); - match import.kind { - ImportKind::Plain | ImportKind::TypeOnly => { + match import.source { + ImportSource::ExternCrate { .. } + | ImportSource::Use { kind: ImportKind::Plain | ImportKind::TypeOnly, .. } => { let name = match &import.alias { Some(ImportAlias::Alias(name)) => Some(name), Some(ImportAlias::Underscore) => None, @@ -851,40 +856,44 @@ impl DefCollector<'_> { }, }; - if import.kind == ImportKind::TypeOnly { - def.values = None; - def.macros = None; - } - - tracing::debug!("resolved import {:?} ({:?}) to {:?}", name, import, def); - - // extern crates in the crate root are special-cased to insert entries into the extern prelude: rust-lang/rust#54658 - if matches!(import.source, ImportSource::ExternCrate { .. }) - && self.def_map.block.is_none() - && module_id == DefMap::ROOT - { - if let (Some(ModuleDefId::ModuleId(def)), Some(name)) = (def.take_types(), name) - { - if let Ok(def) = def.try_into() { - Arc::get_mut(&mut self.def_map.data) - .unwrap() - .extern_prelude - .insert(name.clone(), def); + let imp = match import.source { + // extern crates in the crate root are special-cased to insert entries into the extern prelude: rust-lang/rust#54658 + ImportSource::ExternCrate { id, .. } => { + if self.def_map.block.is_none() && module_id == DefMap::ROOT { + if let (Some(ModuleDefId::ModuleId(def)), Some(name)) = + (def.take_types(), name) + { + if let Ok(def) = def.try_into() { + Arc::get_mut(&mut self.def_map.data) + .unwrap() + .extern_prelude + .insert(name.clone(), (def, Some(id))); + } + } } + ImportType::ExternCrate(id) } - } + ImportSource::Use { kind, id, use_tree, .. } => { + if kind == ImportKind::TypeOnly { + def.values = None; + def.macros = None; + } + ImportType::Import(ImportId { import: id, idx: use_tree }) + } + }; + tracing::debug!("resolved import {:?} ({:?}) to {:?}", name, import, def); - self.update(module_id, &[(name.cloned(), def)], vis, ImportType::Named); + self.update(module_id, &[(name.cloned(), def)], vis, Some(imp)); } - ImportKind::Glob => { + ImportSource::Use { kind: ImportKind::Glob, id, .. } => { tracing::debug!("glob import: {:?}", import); match def.take_types() { Some(ModuleDefId::ModuleId(m)) => { - if import.is_prelude { + if let ImportSource::Use { id, is_prelude: true, .. } = import.source { // Note: This dodgily overrides the injected prelude. The rustc // implementation seems to work the same though. cov_mark::hit!(std_prelude); - self.def_map.prelude = Some(m); + self.def_map.prelude = Some((m, Some(id))); } else if m.krate != self.def_map.krate { cov_mark::hit!(glob_across_crates); // glob import from other crate => we can just import everything once @@ -901,7 +910,7 @@ impl DefCollector<'_> { .filter(|(_, res)| !res.is_none()) .collect::<Vec<_>>(); - self.update(module_id, &items, vis, ImportType::Glob); + self.update(module_id, &items, vis, Some(ImportType::Glob(id))); } else { // glob import from same crate => we do an initial // import, and then need to propagate any further @@ -933,11 +942,11 @@ impl DefCollector<'_> { .filter(|(_, res)| !res.is_none()) .collect::<Vec<_>>(); - self.update(module_id, &items, vis, ImportType::Glob); + self.update(module_id, &items, vis, Some(ImportType::Glob(id))); // record the glob import in case we add further items let glob = self.glob_imports.entry(m.local_id).or_default(); - if !glob.iter().any(|(mid, _)| *mid == module_id) { - glob.push((module_id, vis)); + if !glob.iter().any(|(mid, _, _)| *mid == module_id) { + glob.push((module_id, vis, id)); } } } @@ -959,11 +968,11 @@ impl DefCollector<'_> { .map(|(local_id, variant_data)| { let name = variant_data.name.clone(); let variant = EnumVariantId { parent: e, local_id }; - let res = PerNs::both(variant.into(), variant.into(), vis); + let res = PerNs::both(variant.into(), variant.into(), vis, None); (Some(name), res) }) .collect::<Vec<_>>(); - self.update(module_id, &resolutions, vis, ImportType::Glob); + self.update(module_id, &resolutions, vis, Some(ImportType::Glob(id))); } Some(d) => { tracing::debug!("glob import {:?} from non-module/enum {:?}", import, d); @@ -983,10 +992,10 @@ impl DefCollector<'_> { resolutions: &[(Option<Name>, PerNs)], // Visibility this import will have vis: Visibility, - import_type: ImportType, + import: Option<ImportType>, ) { self.db.unwind_if_cancelled(); - self.update_recursive(module_id, resolutions, vis, import_type, 0) + self.update_recursive(module_id, resolutions, vis, import, 0) } fn update_recursive( @@ -997,7 +1006,7 @@ impl DefCollector<'_> { // All resolutions are imported with this visibility; the visibilities in // the `PerNs` values are ignored and overwritten vis: Visibility, - import_type: ImportType, + import: Option<ImportType>, depth: usize, ) { if GLOB_RECURSION_LIMIT.check(depth).is_err() { @@ -1014,7 +1023,7 @@ impl DefCollector<'_> { &mut self.from_glob_import, (module_id, name.clone()), res.with_visibility(vis), - import_type, + import, ); } None => { @@ -1059,7 +1068,7 @@ impl DefCollector<'_> { .get(&module_id) .into_iter() .flatten() - .filter(|(glob_importing_module, _)| { + .filter(|(glob_importing_module, _, _)| { // we know all resolutions have the same visibility (`vis`), so we // just need to check that once vis.is_visible_from_def_map(self.db, &self.def_map, *glob_importing_module) @@ -1067,12 +1076,12 @@ impl DefCollector<'_> { .cloned() .collect::<Vec<_>>(); - for (glob_importing_module, glob_import_vis) in glob_imports { + for (glob_importing_module, glob_import_vis, use_) in glob_imports { self.update_recursive( glob_importing_module, resolutions, glob_import_vis, - ImportType::Glob, + Some(ImportType::Glob(use_)), depth + 1, ); } @@ -1460,31 +1469,34 @@ impl DefCollector<'_> { // heuristic, but it works in practice. let mut diagnosed_extern_crates = FxHashSet::default(); for directive in &self.unresolved_imports { - if let ImportSource::ExternCrate(krate) = directive.import.source { - let item_tree = krate.item_tree(self.db); - let extern_crate = &item_tree[krate.value]; + if let ImportSource::ExternCrate { id } = directive.import.source { + let item_tree_id = id.lookup(self.db).id; + let item_tree = item_tree_id.item_tree(self.db); + let extern_crate = &item_tree[item_tree_id.value]; diagnosed_extern_crates.insert(extern_crate.name.clone()); self.def_map.diagnostics.push(DefDiagnostic::unresolved_extern_crate( directive.module_id, - InFile::new(krate.file_id(), extern_crate.ast_id), + InFile::new(item_tree_id.file_id(), extern_crate.ast_id), )); } } for directive in &self.unresolved_imports { - if let ImportSource::Use { id: import, use_tree } = directive.import.source { + if let ImportSource::Use { use_tree, id, is_prelude: _, kind: _ } = + directive.import.source + { if matches!( (directive.import.path.segments().first(), &directive.import.path.kind), (Some(krate), PathKind::Plain | PathKind::Abs) if diagnosed_extern_crates.contains(krate) ) { continue; } - + let item_tree_id = id.lookup(self.db).id; self.def_map.diagnostics.push(DefDiagnostic::unresolved_import( directive.module_id, - import, + item_tree_id, use_tree, )); } @@ -1519,72 +1531,66 @@ impl ModCollector<'_, '_> { self.def_collector.mod_dirs.insert(self.module_id, self.mod_dir.clone()); // Prelude module is always considered to be `#[macro_use]`. - if let Some(prelude_module) = self.def_collector.def_map.prelude { + if let Some((prelude_module, _use)) = self.def_collector.def_map.prelude { if prelude_module.krate != krate && is_crate_root { cov_mark::hit!(prelude_is_macro_use); - self.def_collector.import_macros_from_extern_crate(prelude_module.krate, None); - } - } - - // This should be processed eagerly instead of deferred to resolving. - // `#[macro_use] extern crate` is hoisted to imports macros before collecting - // any other items. - // - // If we're not at the crate root, `macro_use`d extern crates are an error so let's just - // ignore them. - if is_crate_root { - for &item in items { - if let ModItem::ExternCrate(id) = item { - self.process_macro_use_extern_crate(id); - } + self.def_collector.import_macros_from_extern_crate( + prelude_module.krate, + None, + None, + ); } } + let db = self.def_collector.db; + let module_id = self.module_id; + let update_def = + |def_collector: &mut DefCollector<'_>, id, name: &Name, vis, has_constructor| { + def_collector.def_map.modules[module_id].scope.declare(id); + def_collector.update( + module_id, + &[(Some(name.clone()), PerNs::from_def(id, vis, has_constructor, None))], + vis, + None, + ) + }; + let resolve_vis = |def_map: &DefMap, visibility| { + def_map + .resolve_visibility(db, module_id, visibility, false) + .unwrap_or(Visibility::Public) + }; - for &item in items { - let attrs = self.item_tree.attrs(self.def_collector.db, krate, item.into()); + let mut process_mod_item = |item: ModItem| { + let attrs = self.item_tree.attrs(db, krate, item.into()); if let Some(cfg) = attrs.cfg() { if !self.is_cfg_enabled(&cfg) { self.emit_unconfigured_diagnostic(item, &cfg); - continue; + return; } } if let Err(()) = self.resolve_attributes(&attrs, item, container) { // Do not process the item. It has at least one non-builtin attribute, so the // fixed-point algorithm is required to resolve the rest of them. - continue; + return; } - let db = self.def_collector.db; - let module = self.def_collector.def_map.module_id(self.module_id); + let module = self.def_collector.def_map.module_id(module_id); let def_map = &mut self.def_collector.def_map; - let update_def = - |def_collector: &mut DefCollector<'_>, id, name: &Name, vis, has_constructor| { - def_collector.def_map.modules[self.module_id].scope.declare(id); - def_collector.update( - self.module_id, - &[(Some(name.clone()), PerNs::from_def(id, vis, has_constructor))], - vis, - ImportType::Named, - ) - }; - let resolve_vis = |def_map: &DefMap, visibility| { - def_map - .resolve_visibility(db, self.module_id, visibility, false) - .unwrap_or(Visibility::Public) - }; match item { ModItem::Mod(m) => self.collect_module(m, &attrs), - ModItem::Use(import_id) => { - let _import_id = - UseLoc { container: module, id: ItemTreeId::new(self.tree_id, import_id) } - .intern(db); + ModItem::Use(item_tree_id) => { + let id = UseLoc { + container: module, + id: ItemTreeId::new(self.tree_id, item_tree_id), + } + .intern(db); + let is_prelude = attrs.by_key("prelude_import").exists(); Import::from_use( - db, - krate, self.item_tree, - ItemTreeId::new(self.tree_id, import_id), + ItemTreeId::new(self.tree_id, item_tree_id), + id, + is_prelude, |import| { self.def_collector.unresolved_imports.push(ImportDirective { module_id: self.module_id, @@ -1594,22 +1600,29 @@ impl ModCollector<'_, '_> { }, ) } - ModItem::ExternCrate(import_id) => { - let extern_crate_id = ExternCrateLoc { + ModItem::ExternCrate(item_tree_id) => { + let id = ExternCrateLoc { container: module, - id: ItemTreeId::new(self.tree_id, import_id), + id: ItemTreeId::new(self.tree_id, item_tree_id), } .intern(db); + if is_crate_root { + self.process_macro_use_extern_crate( + item_tree_id, + id, + attrs.by_key("macro_use").attrs(), + ); + } + self.def_collector.def_map.modules[self.module_id] .scope - .define_extern_crate_decl(extern_crate_id); + .define_extern_crate_decl(id); self.def_collector.unresolved_imports.push(ImportDirective { module_id: self.module_id, import: Import::from_extern_crate( - db, - krate, self.item_tree, - ItemTreeId::new(self.tree_id, import_id), + ItemTreeId::new(self.tree_id, item_tree_id), + id, ), status: PartialResolvedImport::Unresolved, }) @@ -1768,21 +1781,34 @@ impl ModCollector<'_, '_> { ); } } + }; + + // extern crates should be processed eagerly instead of deferred to resolving. + // `#[macro_use] extern crate` is hoisted to imports macros before collecting + // any other items. + if is_crate_root { + items + .iter() + .filter(|it| matches!(it, ModItem::ExternCrate(..))) + .copied() + .for_each(&mut process_mod_item); + items + .iter() + .filter(|it| !matches!(it, ModItem::ExternCrate(..))) + .copied() + .for_each(process_mod_item); + } else { + items.iter().copied().for_each(process_mod_item); } } - fn process_macro_use_extern_crate(&mut self, extern_crate: FileItemTreeId<ExternCrate>) { + fn process_macro_use_extern_crate<'a>( + &mut self, + extern_crate: FileItemTreeId<ExternCrate>, + extern_crate_id: ExternCrateId, + macro_use_attrs: impl Iterator<Item = &'a Attr>, + ) { let db = self.def_collector.db; - let attrs = self.item_tree.attrs( - db, - self.def_collector.def_map.krate, - ModItem::from(extern_crate).into(), - ); - if let Some(cfg) = attrs.cfg() { - if !self.is_cfg_enabled(&cfg) { - return; - } - } let target_crate = match self.def_collector.resolve_extern_crate(&self.item_tree[extern_crate].name) { @@ -1798,11 +1824,11 @@ impl ModCollector<'_, '_> { let mut single_imports = Vec::new(); let hygiene = Hygiene::new_unhygienic(); - for attr in attrs.by_key("macro_use").attrs() { + for attr in macro_use_attrs { let Some(paths) = attr.parse_path_comma_token_tree(db.upcast(), &hygiene) else { // `#[macro_use]` (without any paths) found, forget collected names and just import // all visible macros. - self.def_collector.import_macros_from_extern_crate(target_crate, None); + self.def_collector.import_macros_from_extern_crate(target_crate, None, Some(extern_crate_id)); return; }; for path in paths { @@ -1812,7 +1838,11 @@ impl ModCollector<'_, '_> { } } - self.def_collector.import_macros_from_extern_crate(target_crate, Some(single_imports)); + self.def_collector.import_macros_from_extern_crate( + target_crate, + Some(single_imports), + Some(extern_crate_id), + ); } fn collect_module(&mut self, module_id: FileItemTreeId<Mod>, attrs: &Attrs) { @@ -1824,7 +1854,7 @@ impl ModCollector<'_, '_> { ModKind::Inline { items } => { let module_id = self.push_child_module( module.name.clone(), - AstId::new(self.file_id(), module.ast_id), + module.ast_id, None, &self.item_tree[module.visibility], module_id, @@ -1862,7 +1892,7 @@ impl ModCollector<'_, '_> { if is_enabled { let module_id = self.push_child_module( module.name.clone(), - ast_id, + ast_id.value, Some((file_id, is_mod_rs)), &self.item_tree[module.visibility], module_id, @@ -1889,7 +1919,7 @@ impl ModCollector<'_, '_> { Err(candidates) => { self.push_child_module( module.name.clone(), - ast_id, + ast_id.value, None, &self.item_tree[module.visibility], module_id, @@ -1906,7 +1936,7 @@ impl ModCollector<'_, '_> { fn push_child_module( &mut self, name: Name, - declaration: AstId<ast::Module>, + declaration: FileAstId<ast::Module>, definition: Option<(FileId, bool)>, visibility: &crate::visibility::RawVisibility, mod_tree_id: FileItemTreeId<Mod>, @@ -1948,9 +1978,9 @@ impl ModCollector<'_, '_> { def_map.modules[self.module_id].scope.declare(def); self.def_collector.update( self.module_id, - &[(Some(name), PerNs::from_def(def, vis, false))], + &[(Some(name), PerNs::from_def(def, vis, false, None))], vis, - ImportType::Named, + None, ); res } @@ -2198,7 +2228,7 @@ impl ModCollector<'_, '_> { map[module].scope.get_legacy_macro(name)?.last().copied() }) .or_else(|| def_map[self.module_id].scope.get(name).take_macros()) - .or_else(|| def_map.macro_use_prelude.get(name).copied()) + .or_else(|| Some(def_map.macro_use_prelude.get(name).copied()?.0)) .filter(|&id| { sub_namespace_match( Some(MacroSubNs::from_id(db, id)), diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres/path_resolution.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres/path_resolution.rs index de22ea10146..460a908b6db 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/nameres/path_resolution.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres/path_resolution.rs @@ -15,8 +15,9 @@ use hir_expand::name::Name; use triomphe::Arc; use crate::{ + data::adt::VariantData, db::DefDatabase, - item_scope::BUILTIN_SCOPE, + item_scope::{ImportOrExternCrate, BUILTIN_SCOPE}, nameres::{sub_namespace_match, BlockInfo, BuiltinShadowMode, DefMap, MacroSubNs}, path::{ModPath, PathKind}, per_ns::PerNs, @@ -65,7 +66,7 @@ impl PerNs { db: &dyn DefDatabase, expected: Option<MacroSubNs>, ) -> Self { - self.macros = self.macros.filter(|&(id, _)| { + self.macros = self.macros.filter(|&(id, _, _)| { let this = MacroSubNs::from_id(db, id); sub_namespace_match(Some(this), expected) }); @@ -196,15 +197,15 @@ impl DefMap { PathKind::DollarCrate(krate) => { if krate == self.krate { cov_mark::hit!(macro_dollar_crate_self); - PerNs::types(self.crate_root().into(), Visibility::Public) + PerNs::types(self.crate_root().into(), Visibility::Public, None) } else { let def_map = db.crate_def_map(krate); let module = def_map.module_id(Self::ROOT); cov_mark::hit!(macro_dollar_crate_other); - PerNs::types(module.into(), Visibility::Public) + PerNs::types(module.into(), Visibility::Public, None) } } - PathKind::Crate => PerNs::types(self.crate_root().into(), Visibility::Public), + PathKind::Crate => PerNs::types(self.crate_root().into(), Visibility::Public, None), // plain import or absolute path in 2015: crate-relative with // fallback to extern prelude (with the simplification in // rust-lang/rust#57745) @@ -291,7 +292,7 @@ impl DefMap { ); } - PerNs::types(module.into(), Visibility::Public) + PerNs::types(module.into(), Visibility::Public, None) } PathKind::Abs => { // 2018-style absolute path -- only extern prelude @@ -299,9 +300,13 @@ impl DefMap { Some((_, segment)) => segment, None => return ResolvePathResult::empty(ReachedFixedPoint::Yes), }; - if let Some(&def) = self.data.extern_prelude.get(segment) { + if let Some(&(def, extern_crate)) = self.data.extern_prelude.get(segment) { tracing::debug!("absolute path {:?} resolved to crate {:?}", path, def); - PerNs::types(def.into(), Visibility::Public) + PerNs::types( + def.into(), + Visibility::Public, + extern_crate.map(ImportOrExternCrate::ExternCrate), + ) } else { return ResolvePathResult::empty(ReachedFixedPoint::No); // extern crate declarations can add to the extern prelude } @@ -309,7 +314,7 @@ impl DefMap { }; for (i, segment) in segments { - let (curr, vis) = match curr_per_ns.take_types_vis() { + let (curr, vis, imp) = match curr_per_ns.take_types_full() { Some(r) => r, None => { // we still have path segments left, but the path so far @@ -364,18 +369,20 @@ impl DefMap { Some(local_id) => { let variant = EnumVariantId { parent: e, local_id }; match &*enum_data.variants[local_id].variant_data { - crate::data::adt::VariantData::Record(_) => { - PerNs::types(variant.into(), Visibility::Public) - } - crate::data::adt::VariantData::Tuple(_) - | crate::data::adt::VariantData::Unit => { - PerNs::both(variant.into(), variant.into(), Visibility::Public) + VariantData::Record(_) => { + PerNs::types(variant.into(), Visibility::Public, None) } + VariantData::Tuple(_) | VariantData::Unit => PerNs::both( + variant.into(), + variant.into(), + Visibility::Public, + None, + ), } } None => { return ResolvePathResult::with( - PerNs::types(e.into(), vis), + PerNs::types(e.into(), vis, imp), ReachedFixedPoint::Yes, Some(i), Some(self.krate), @@ -393,7 +400,7 @@ impl DefMap { ); return ResolvePathResult::with( - PerNs::types(s, vis), + PerNs::types(s, vis, imp), ReachedFixedPoint::Yes, Some(i), Some(self.krate), @@ -430,7 +437,7 @@ impl DefMap { .filter(|&id| { sub_namespace_match(Some(MacroSubNs::from_id(db, id)), expected_macro_subns) }) - .map_or_else(PerNs::none, |m| PerNs::macros(m, Visibility::Public)); + .map_or_else(PerNs::none, |m| PerNs::macros(m, Visibility::Public, None)); let from_scope = self[module].scope.get(name).filter_macro(db, expected_macro_subns); let from_builtin = match self.block { Some(_) => { @@ -449,18 +456,27 @@ impl DefMap { let extern_prelude = || { if self.block.is_some() { - // Don't resolve extern prelude in block `DefMap`s. + // Don't resolve extern prelude in block `DefMap`s, defer it to the crate def map so + // that blocks can properly shadow them return PerNs::none(); } - self.data - .extern_prelude - .get(name) - .map_or(PerNs::none(), |&it| PerNs::types(it.into(), Visibility::Public)) + self.data.extern_prelude.get(name).map_or(PerNs::none(), |&(it, extern_crate)| { + PerNs::types( + it.into(), + Visibility::Public, + extern_crate.map(ImportOrExternCrate::ExternCrate), + ) + }) }; let macro_use_prelude = || { - self.macro_use_prelude - .get(name) - .map_or(PerNs::none(), |&it| PerNs::macros(it.into(), Visibility::Public)) + self.macro_use_prelude.get(name).map_or(PerNs::none(), |&(it, _extern_crate)| { + PerNs::macros( + it.into(), + Visibility::Public, + // FIXME? + None, // extern_crate.map(ImportOrExternCrate::ExternCrate), + ) + }) }; let prelude = || self.resolve_in_prelude(db, name); @@ -488,18 +504,23 @@ impl DefMap { // Don't resolve extern prelude in block `DefMap`s. return PerNs::none(); } - self.data - .extern_prelude - .get(name) - .copied() - .map_or(PerNs::none(), |it| PerNs::types(it.into(), Visibility::Public)) + self.data.extern_prelude.get(name).copied().map_or( + PerNs::none(), + |(it, extern_crate)| { + PerNs::types( + it.into(), + Visibility::Public, + extern_crate.map(ImportOrExternCrate::ExternCrate), + ) + }, + ) }; from_crate_root.or_else(from_extern_prelude) } fn resolve_in_prelude(&self, db: &dyn DefDatabase, name: &Name) -> PerNs { - if let Some(prelude) = self.prelude { + if let Some((prelude, _use)) = self.prelude { let keep; let def_map = if prelude.krate == self.krate { self diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests.rs index dd7c3c36306..e7cc44b04da 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests.rs @@ -168,7 +168,7 @@ pub struct Baz; "#, expect![[r#" crate - Foo: t v + Foo: ti vi foo: t crate::foo @@ -194,8 +194,8 @@ pub enum Quux {}; "#, expect![[r#" crate - Baz: t v - Quux: t + Baz: ti vi + Quux: ti foo: t crate::foo @@ -225,11 +225,11 @@ pub struct Baz; "#, expect![[r#" crate - Baz: t v + Baz: ti vi foo: t crate::foo - Baz: t v + Baz: ti vi bar: t crate::foo::bar @@ -274,7 +274,7 @@ use self::E::V; expect![[r#" crate E: t - V: t v + V: ti vi "#]], ); } @@ -307,7 +307,7 @@ pub struct FromLib; crate::foo Bar: _ - FromLib: t v + FromLib: ti vi "#]], ); } @@ -328,7 +328,7 @@ pub struct Baz; "#, expect![[r#" crate - Baz: t + Baz: ti foo: t crate::foo @@ -352,7 +352,7 @@ pub struct Baz; "#, expect![[r#" crate - Baz: t v + Baz: ti vi "#]], ); } @@ -375,13 +375,13 @@ pub struct Arc; expect![[r#" crate alloc: t - alloc_crate: t + alloc_crate: te sync: t crate::alloc crate::sync - Arc: t v + Arc: ti vi "#]], ); } @@ -404,13 +404,13 @@ pub struct Arc; expect![[r#" crate alloc: t - alloc_crate: t + alloc_crate: te sync: t crate::alloc crate::sync - Arc: t v + Arc: ti vi "#]], ); } @@ -426,7 +426,7 @@ extern crate self as bla; "#, expect![[r#" crate - bla: t + bla: te "#]], ); } @@ -447,7 +447,7 @@ pub struct Baz; "#, expect![[r#" crate - Baz: t v + Baz: ti vi "#]], ); } @@ -465,7 +465,7 @@ pub struct Bar; "#, expect![[r#" crate - Bar: t v + Bar: ti vi foo: v "#]], ); @@ -492,9 +492,9 @@ fn no_std_prelude() { } "#, expect![[r#" - crate - Rust: t v - "#]], + crate + Rust: ti vi + "#]], ); } @@ -516,9 +516,9 @@ fn edition_specific_preludes() { } "#, expect![[r#" - crate - Rust2018: t v - "#]], + crate + Rust2018: ti vi + "#]], ); check( r#" @@ -533,9 +533,9 @@ fn edition_specific_preludes() { } "#, expect![[r#" - crate - Rust2021: t v - "#]], + crate + Rust2021: ti vi + "#]], ); } @@ -563,8 +563,8 @@ pub mod prelude { "#, expect![[r#" crate - Bar: t v - Foo: t v + Bar: ti vi + Foo: ti vi "#]], ); } @@ -590,7 +590,7 @@ pub mod prelude { "#, expect![[r#" crate - Bar: t v + Bar: ti vi Baz: _ Foo: _ "#]], @@ -619,8 +619,8 @@ pub mod prelude { expect![[r#" crate Bar: _ - Baz: t v - Foo: t v + Baz: ti vi + Foo: ti vi "#]], ); } @@ -643,7 +643,7 @@ mod b { "#, expect![[r#" crate - T: t v + T: ti vi a: t b: t @@ -816,8 +816,8 @@ fn bar() {} expect![[r#" crate bar: v - baz: v - foo: t + baz: vi + foo: ti "#]], ); } @@ -836,7 +836,7 @@ use self::m::S::{self}; "#, expect![[r#" crate - S: t + S: ti m: t crate::m @@ -860,8 +860,8 @@ pub const settings: () = (); "#, expect![[r#" crate - Settings: t v - settings: v + Settings: ti vi + settings: vi "#]], ) } @@ -890,8 +890,8 @@ pub struct Struct; "#, expect![[r#" crate - Struct: t v - dep: t + Struct: ti vi + dep: te "#]], ); } @@ -917,13 +917,13 @@ use some_module::unknown_func; crate other_module: t some_module: t - unknown_func: v + unknown_func: vi crate::other_module some_submodule: t crate::other_module::some_submodule - unknown_func: v + unknown_func: vi crate::some_module unknown_func: v diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/globs.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/globs.rs index 88a3c76393f..1ca74b5da6b 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/globs.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/globs.rs @@ -24,7 +24,7 @@ pub struct Baz; foo: t crate::foo - Baz: t v + Baz: ti vi Foo: t v bar: t @@ -237,9 +237,9 @@ pub mod baz { pub struct Bar; } "#, expect![[r#" crate - Bar: t v + Bar: ti vi bar: t - baz: t + baz: ti foo: t crate::bar @@ -276,9 +276,9 @@ pub mod baz { pub struct Bar; } "#, expect![[r#" crate - Bar: t v + Bar: ti vi bar: t - baz: t + baz: ti foo: t crate::bar @@ -323,7 +323,7 @@ mod d { X: t v crate::b - foo: t + foo: ti crate::c foo: t @@ -332,8 +332,8 @@ mod d { Y: t v crate::d - Y: t v - foo: t + Y: ti vi + foo: ti "#]], ); } @@ -355,7 +355,7 @@ use event::Event; "#, expect![[r#" crate - Event: t + Event: ti event: t crate::event diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/incremental.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/incremental.rs index 40d3a16540d..4a86f88e57a 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/incremental.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/incremental.rs @@ -212,7 +212,7 @@ pub type Ty = (); } for (_, res) in module_data.scope.resolutions() { - match res.values.or(res.types).unwrap().0 { + match res.values.map(|(a, _, _)| a).or(res.types.map(|(a, _, _)| a)).unwrap() { ModuleDefId::FunctionId(f) => _ = db.function_data(f), ModuleDefId::AdtId(adt) => match adt { AdtId::StructId(it) => _ = db.struct_data(it), diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/macros.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/macros.rs index f4cca8d68d0..e64fa0b46f1 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/macros.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/macros.rs @@ -203,8 +203,8 @@ macro_rules! bar { expect![[r#" crate Foo: t - bar: m - foo: m + bar: mi + foo: mi "#]], ); } @@ -251,7 +251,7 @@ mod priv_mod { Bar: t v Foo: t v bar: t - foo: t + foo: te crate::bar Baz: t v @@ -318,9 +318,9 @@ macro_rules! baz3 { () => { struct OkBaz3; } } OkBaz1: t v OkBaz2: t v OkBaz3: t v - all: t - empty: t - multiple: t + all: te + empty: te + multiple: te "#]], ); } @@ -551,8 +551,8 @@ fn baz() {} "#, expect![[r#" crate - bar: t m - baz: t v m + bar: ti mi + baz: ti v mi foo: t m "#]], ); @@ -583,7 +583,7 @@ mod m { crate Alias: t v Direct: t v - foo: t + foo: te "#]], ); } @@ -628,9 +628,9 @@ mod m { m: t crate::m - alias1: m - alias2: m - alias3: m + alias1: mi + alias2: mi + alias3: mi not_found: _ "#]], ); @@ -682,11 +682,11 @@ pub struct Baz; "#, expect![[r#" crate - Bar: t v - Baz: t v + Bar: ti vi + Baz: ti vi Foo: t v - FooSelf: t v - foo: t + FooSelf: ti vi + foo: te m: t crate::m @@ -725,7 +725,7 @@ pub struct bar; "#, expect![[r#" crate - bar: t v + bar: ti vi "#]], ); } @@ -1340,7 +1340,7 @@ pub mod prelude { crate Ok: t v bar: m - dep: t + dep: te foo: m ok: v "#]], @@ -1370,13 +1370,13 @@ macro_rules! mk_foo { } "#, expect![[r#" - crate - a: t - lib: t + crate + a: t + lib: te - crate::a - Ok: t v - "#]], + crate::a + Ok: t v + "#]], ); } @@ -1427,8 +1427,8 @@ pub mod prelude { expect![[r#" crate Ok: t v - bar: m - foo: m + bar: mi + foo: mi ok: v "#]], ); diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/mod_resolution.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/mod_resolution.rs index 81bc0ff91e3..1327d9aa62e 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/mod_resolution.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/mod_resolution.rs @@ -80,18 +80,18 @@ pub trait Iterator; prelude: t crate::iter - Iterator: t + Iterator: ti traits: t crate::iter::traits - Iterator: t + Iterator: ti iterator: t crate::iter::traits::iterator Iterator: t crate::prelude - Iterator: t + Iterator: ti "#]], ); } @@ -109,7 +109,7 @@ pub struct Bar; "#, expect![[r#" crate - Bar: t v + Bar: ti vi foo: t crate::foo @@ -139,7 +139,7 @@ pub struct Baz; "#, expect![[r#" crate - Bar: t v + Bar: ti vi r#async: t crate::r#async @@ -176,8 +176,8 @@ pub struct Bar; "#, expect![[r#" crate - Bar: t v - Foo: t v + Bar: ti vi + Foo: ti vi r#async: t crate::r#async @@ -207,7 +207,7 @@ pub struct Bar; "#, expect![[r#" crate - Bar: t v + Bar: ti vi foo: t crate::foo @@ -236,7 +236,7 @@ pub struct Baz; foo: t crate::foo - Baz: t v + Baz: ti vi bar: t crate::foo::bar @@ -265,7 +265,7 @@ pub struct Baz; foo: t crate::foo - Baz: t v + Baz: ti vi bar: t crate::foo::bar @@ -292,7 +292,7 @@ use super::Baz; foo: t crate::foo - Baz: t v + Baz: ti vi "#]], ); } @@ -626,7 +626,7 @@ pub struct Baz; "#, expect![[r#" crate - Baz: t v + Baz: ti vi foo: t crate::foo @@ -660,7 +660,7 @@ pub struct Baz; foo: t crate::foo - Baz: t v + Baz: ti vi bar: t crate::foo::bar @@ -694,7 +694,7 @@ pub struct Baz; foo: t crate::foo - Baz: t v + Baz: ti vi bar: t crate::foo::bar @@ -728,7 +728,7 @@ pub struct Baz; foo: t crate::foo - Baz: t v + Baz: ti vi bar: t crate::foo::bar @@ -868,7 +868,7 @@ pub mod hash { pub trait Hash {} } "#, expect![[r#" crate - Hash: t + Hash: ti core: t crate::core diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/primitives.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/primitives.rs index 215e8952d90..271eb1c79b1 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/primitives.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/primitives.rs @@ -14,10 +14,10 @@ pub use i32 as int; expect![[r#" crate foo: t - int: t + int: ti crate::foo - int: t + int: ti "#]], ); } diff --git a/src/tools/rust-analyzer/crates/hir-def/src/per_ns.rs b/src/tools/rust-analyzer/crates/hir-def/src/per_ns.rs index 2bc1f8e926e..14890364d0b 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/per_ns.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/per_ns.rs @@ -3,13 +3,24 @@ //! //! `PerNs` (per namespace) captures this. -use crate::{item_scope::ItemInNs, visibility::Visibility, MacroId, ModuleDefId}; +use crate::{ + item_scope::{ImportId, ImportOrExternCrate, ItemInNs}, + visibility::Visibility, + MacroId, ModuleDefId, +}; + +#[derive(PartialEq, Eq, Hash, Copy, Clone, Debug)] +pub enum Namespace { + Types, + Values, + Macros, +} #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] pub struct PerNs { - pub types: Option<(ModuleDefId, Visibility)>, - pub values: Option<(ModuleDefId, Visibility)>, - pub macros: Option<(MacroId, Visibility)>, + pub types: Option<(ModuleDefId, Visibility, Option<ImportOrExternCrate>)>, + pub values: Option<(ModuleDefId, Visibility, Option<ImportId>)>, + pub macros: Option<(MacroId, Visibility, Option<ImportId>)>, } impl Default for PerNs { @@ -23,20 +34,29 @@ impl PerNs { PerNs { types: None, values: None, macros: None } } - pub fn values(t: ModuleDefId, v: Visibility) -> PerNs { - PerNs { types: None, values: Some((t, v)), macros: None } + pub fn values(t: ModuleDefId, v: Visibility, i: Option<ImportId>) -> PerNs { + PerNs { types: None, values: Some((t, v, i)), macros: None } } - pub fn types(t: ModuleDefId, v: Visibility) -> PerNs { - PerNs { types: Some((t, v)), values: None, macros: None } + pub fn types(t: ModuleDefId, v: Visibility, i: Option<ImportOrExternCrate>) -> PerNs { + PerNs { types: Some((t, v, i)), values: None, macros: None } } - pub fn both(types: ModuleDefId, values: ModuleDefId, v: Visibility) -> PerNs { - PerNs { types: Some((types, v)), values: Some((values, v)), macros: None } + pub fn both( + types: ModuleDefId, + values: ModuleDefId, + v: Visibility, + i: Option<ImportOrExternCrate>, + ) -> PerNs { + PerNs { + types: Some((types, v, i)), + values: Some((values, v, i.and_then(ImportOrExternCrate::into_import))), + macros: None, + } } - pub fn macros(macro_: MacroId, v: Visibility) -> PerNs { - PerNs { types: None, values: None, macros: Some((macro_, v)) } + pub fn macros(macro_: MacroId, v: Visibility, i: Option<ImportId>) -> PerNs { + PerNs { types: None, values: None, macros: Some((macro_, v, i)) } } pub fn is_none(&self) -> bool { @@ -51,7 +71,7 @@ impl PerNs { self.types.map(|it| it.0) } - pub fn take_types_vis(self) -> Option<(ModuleDefId, Visibility)> { + pub fn take_types_full(self) -> Option<(ModuleDefId, Visibility, Option<ImportOrExternCrate>)> { self.types } @@ -59,24 +79,32 @@ impl PerNs { self.values.map(|it| it.0) } + pub fn take_values_import(self) -> Option<(ModuleDefId, Option<ImportId>)> { + self.values.map(|it| (it.0, it.2)) + } + pub fn take_macros(self) -> Option<MacroId> { self.macros.map(|it| it.0) } + pub fn take_macros_import(self) -> Option<(MacroId, Option<ImportId>)> { + self.macros.map(|it| (it.0, it.2)) + } + pub fn filter_visibility(self, mut f: impl FnMut(Visibility) -> bool) -> PerNs { let _p = profile::span("PerNs::filter_visibility"); PerNs { - types: self.types.filter(|(_, v)| f(*v)), - values: self.values.filter(|(_, v)| f(*v)), - macros: self.macros.filter(|(_, v)| f(*v)), + types: self.types.filter(|&(_, v, _)| f(v)), + values: self.values.filter(|&(_, v, _)| f(v)), + macros: self.macros.filter(|&(_, v, _)| f(v)), } } pub fn with_visibility(self, vis: Visibility) -> PerNs { PerNs { - types: self.types.map(|(it, _)| (it, vis)), - values: self.values.map(|(it, _)| (it, vis)), - macros: self.macros.map(|(it, _)| (it, vis)), + types: self.types.map(|(it, _, c)| (it, vis, c)), + values: self.values.map(|(it, _, c)| (it, vis, c)), + macros: self.macros.map(|(it, _, import)| (it, vis, import)), } } @@ -96,12 +124,20 @@ impl PerNs { } } - pub fn iter_items(self) -> impl Iterator<Item = ItemInNs> { + pub fn iter_items(self) -> impl Iterator<Item = (ItemInNs, Option<ImportOrExternCrate>)> { let _p = profile::span("PerNs::iter_items"); self.types - .map(|it| ItemInNs::Types(it.0)) + .map(|it| (ItemInNs::Types(it.0), it.2)) .into_iter() - .chain(self.values.map(|it| ItemInNs::Values(it.0)).into_iter()) - .chain(self.macros.map(|it| ItemInNs::Macros(it.0)).into_iter()) + .chain( + self.values + .map(|it| (ItemInNs::Values(it.0), it.2.map(ImportOrExternCrate::Import))) + .into_iter(), + ) + .chain( + self.macros + .map(|it| (ItemInNs::Macros(it.0), it.2.map(ImportOrExternCrate::Import))) + .into_iter(), + ) } } diff --git a/src/tools/rust-analyzer/crates/hir-def/src/resolver.rs b/src/tools/rust-analyzer/crates/hir-def/src/resolver.rs index b112c1070d4..2f9187009e2 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/resolver.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/resolver.rs @@ -12,20 +12,21 @@ use triomphe::Arc; use crate::{ body::scope::{ExprScopes, ScopeId}, builtin_type::BuiltinType, + data::ExternCrateDeclData, db::DefDatabase, generics::{GenericParams, TypeOrConstParamData}, hir::{BindingId, ExprId, LabelId}, - item_scope::{BuiltinShadowMode, BUILTIN_SCOPE}, + item_scope::{BuiltinShadowMode, ImportId, ImportOrExternCrate, BUILTIN_SCOPE}, lang_item::LangItemTarget, nameres::{DefMap, MacroSubNs}, path::{ModPath, Path, PathKind}, per_ns::PerNs, visibility::{RawVisibility, Visibility}, - AdtId, AssocItemId, ConstId, ConstParamId, CrateRootModuleId, DefWithBodyId, EnumId, - EnumVariantId, ExternBlockId, ExternCrateId, FunctionId, GenericDefId, GenericParamId, - HasModule, ImplId, ItemContainerId, LifetimeParamId, LocalModuleId, Lookup, Macro2Id, MacroId, - MacroRulesId, ModuleDefId, ModuleId, ProcMacroId, StaticId, StructId, TraitAliasId, TraitId, - TypeAliasId, TypeOrConstParamId, TypeOwnerId, TypeParamId, UseId, VariantId, + AdtId, ConstId, ConstParamId, CrateRootModuleId, DefWithBodyId, EnumId, EnumVariantId, + ExternBlockId, ExternCrateId, FunctionId, GenericDefId, GenericParamId, HasModule, ImplId, + ItemContainerId, LifetimeParamId, LocalModuleId, Lookup, Macro2Id, MacroId, MacroRulesId, + ModuleDefId, ModuleId, ProcMacroId, StaticId, StructId, TraitAliasId, TraitId, TypeAliasId, + TypeOrConstParamId, TypeOwnerId, TypeParamId, UseId, VariantId, }; #[derive(Debug, Clone)] @@ -100,8 +101,8 @@ pub enum TypeNs { #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub enum ResolveValueResult { - ValueNs(ValueNs), - Partial(TypeNs, usize), + ValueNs(ValueNs, Option<ImportId>), + Partial(TypeNs, usize, Option<ImportOrExternCrate>), } #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] @@ -148,39 +149,11 @@ impl Resolver { self.resolve_module_path(db, path, BuiltinShadowMode::Module) } - // FIXME: This shouldn't exist - pub fn resolve_module_path_in_trait_assoc_items( - &self, - db: &dyn DefDatabase, - path: &ModPath, - ) -> Option<PerNs> { - let (item_map, module) = self.item_scope(); - let (module_res, idx) = - item_map.resolve_path(db, module, path, BuiltinShadowMode::Module, None); - match module_res.take_types()? { - ModuleDefId::TraitId(it) => { - let idx = idx?; - let unresolved = &path.segments()[idx..]; - let assoc = match unresolved { - [it] => it, - _ => return None, - }; - let &(_, assoc) = db.trait_data(it).items.iter().find(|(n, _)| n == assoc)?; - Some(match assoc { - AssocItemId::FunctionId(it) => PerNs::values(it.into(), Visibility::Public), - AssocItemId::ConstId(it) => PerNs::values(it.into(), Visibility::Public), - AssocItemId::TypeAliasId(it) => PerNs::types(it.into(), Visibility::Public), - }) - } - _ => None, - } - } - pub fn resolve_path_in_type_ns( &self, db: &dyn DefDatabase, path: &Path, - ) -> Option<(TypeNs, Option<usize>)> { + ) -> Option<(TypeNs, Option<usize>, Option<ImportOrExternCrate>)> { let path = match path { Path::Normal { mod_path, .. } => mod_path, Path::LangItem(l) => { @@ -197,6 +170,7 @@ impl Resolver { | LangItemTarget::Static(_) => return None, }, None, + None, )) } }; @@ -213,17 +187,17 @@ impl Resolver { Scope::ExprScope(_) => continue, Scope::GenericParams { params, def } => { if let Some(id) = params.find_type_by_name(first_name, *def) { - return Some((TypeNs::GenericParam(id), remaining_idx())); + return Some((TypeNs::GenericParam(id), remaining_idx(), None)); } } &Scope::ImplDefScope(impl_) => { if first_name == &name![Self] { - return Some((TypeNs::SelfType(impl_), remaining_idx())); + return Some((TypeNs::SelfType(impl_), remaining_idx(), None)); } } &Scope::AdtScope(adt) => { if first_name == &name![Self] { - return Some((TypeNs::AdtSelfType(adt), remaining_idx())); + return Some((TypeNs::AdtSelfType(adt), remaining_idx(), None)); } } Scope::BlockScope(m) => { @@ -236,12 +210,24 @@ impl Resolver { self.module_scope.resolve_path_in_type_ns(db, path) } + pub fn resolve_path_in_type_ns_fully_with_imports( + &self, + db: &dyn DefDatabase, + path: &Path, + ) -> Option<(TypeNs, Option<ImportOrExternCrate>)> { + let (res, unresolved, imp) = self.resolve_path_in_type_ns(db, path)?; + if unresolved.is_some() { + return None; + } + Some((res, imp)) + } + pub fn resolve_path_in_type_ns_fully( &self, db: &dyn DefDatabase, path: &Path, ) -> Option<TypeNs> { - let (res, unresolved) = self.resolve_path_in_type_ns(db, path)?; + let (res, unresolved, _) = self.resolve_path_in_type_ns(db, path)?; if unresolved.is_some() { return None; } @@ -263,7 +249,6 @@ impl Resolver { RawVisibility::Public => Some(Visibility::Public), } } - pub fn resolve_path_in_value_ns( &self, db: &dyn DefDatabase, @@ -272,17 +257,20 @@ impl Resolver { let path = match path { Path::Normal { mod_path, .. } => mod_path, Path::LangItem(l) => { - return Some(ResolveValueResult::ValueNs(match *l { - LangItemTarget::Function(it) => ValueNs::FunctionId(it), - LangItemTarget::Static(it) => ValueNs::StaticId(it), - LangItemTarget::Struct(it) => ValueNs::StructId(it), - LangItemTarget::EnumVariant(it) => ValueNs::EnumVariantId(it), - LangItemTarget::Union(_) - | LangItemTarget::ImplDef(_) - | LangItemTarget::TypeAlias(_) - | LangItemTarget::Trait(_) - | LangItemTarget::EnumId(_) => return None, - })) + return Some(ResolveValueResult::ValueNs( + match *l { + LangItemTarget::Function(it) => ValueNs::FunctionId(it), + LangItemTarget::Static(it) => ValueNs::StaticId(it), + LangItemTarget::Struct(it) => ValueNs::StructId(it), + LangItemTarget::EnumVariant(it) => ValueNs::EnumVariantId(it), + LangItemTarget::Union(_) + | LangItemTarget::ImplDef(_) + | LangItemTarget::TypeAlias(_) + | LangItemTarget::Trait(_) + | LangItemTarget::EnumId(_) => return None, + }, + None, + )) } }; let n_segments = path.segments().len(); @@ -304,20 +292,24 @@ impl Resolver { .find(|entry| entry.name() == first_name); if let Some(e) = entry { - return Some(ResolveValueResult::ValueNs(ValueNs::LocalBinding( - e.binding(), - ))); + return Some(ResolveValueResult::ValueNs( + ValueNs::LocalBinding(e.binding()), + None, + )); } } Scope::GenericParams { params, def } => { if let Some(id) = params.find_const_by_name(first_name, *def) { let val = ValueNs::GenericParam(id); - return Some(ResolveValueResult::ValueNs(val)); + return Some(ResolveValueResult::ValueNs(val, None)); } } &Scope::ImplDefScope(impl_) => { if first_name == &name![Self] { - return Some(ResolveValueResult::ValueNs(ValueNs::ImplSelf(impl_))); + return Some(ResolveValueResult::ValueNs( + ValueNs::ImplSelf(impl_), + None, + )); } } // bare `Self` doesn't work in the value namespace in a struct/enum definition @@ -336,18 +328,22 @@ impl Resolver { Scope::GenericParams { params, def } => { if let Some(id) = params.find_type_by_name(first_name, *def) { let ty = TypeNs::GenericParam(id); - return Some(ResolveValueResult::Partial(ty, 1)); + return Some(ResolveValueResult::Partial(ty, 1, None)); } } &Scope::ImplDefScope(impl_) => { if first_name == &name![Self] { - return Some(ResolveValueResult::Partial(TypeNs::SelfType(impl_), 1)); + return Some(ResolveValueResult::Partial( + TypeNs::SelfType(impl_), + 1, + None, + )); } } Scope::AdtScope(adt) => { if first_name == &name![Self] { let ty = TypeNs::AdtSelfType(*adt); - return Some(ResolveValueResult::Partial(ty, 1)); + return Some(ResolveValueResult::Partial(ty, 1, None)); } } Scope::BlockScope(m) => { @@ -368,7 +364,7 @@ impl Resolver { // `use core::u16;`. if path.kind == PathKind::Plain && n_segments > 1 { if let Some(builtin) = BuiltinType::by_name(first_name) { - return Some(ResolveValueResult::Partial(TypeNs::BuiltinType(builtin), 1)); + return Some(ResolveValueResult::Partial(TypeNs::BuiltinType(builtin), 1, None)); } } @@ -381,7 +377,7 @@ impl Resolver { path: &Path, ) -> Option<ValueNs> { match self.resolve_path_in_value_ns(db, path)? { - ResolveValueResult::ValueNs(it) => Some(it), + ResolveValueResult::ValueNs(it, _) => Some(it), ResolveValueResult::Partial(..) => None, } } @@ -391,12 +387,12 @@ impl Resolver { db: &dyn DefDatabase, path: &ModPath, expected_macro_kind: Option<MacroSubNs>, - ) -> Option<MacroId> { + ) -> Option<(MacroId, Option<ImportId>)> { let (item_map, module) = self.item_scope(); item_map .resolve_path(db, module, path, BuiltinShadowMode::Other, expected_macro_kind) .0 - .take_macros() + .take_macros_import() } /// Returns a set of names available in the current scope. @@ -456,21 +452,22 @@ impl Resolver { def_map[module_id].scope.entries().for_each(|(name, def)| { res.add_per_ns(name, def); }); + def_map[module_id].scope.legacy_macros().for_each(|(name, macs)| { macs.iter().for_each(|&mac| { res.add(name, ScopeDef::ModuleDef(ModuleDefId::MacroId(mac))); }) }); - def_map.macro_use_prelude().for_each(|(name, def)| { + def_map.macro_use_prelude().for_each(|(name, (def, _extern_crate))| { res.add(name, ScopeDef::ModuleDef(def.into())); }); - def_map.extern_prelude().for_each(|(name, def)| { - res.add(name, ScopeDef::ModuleDef(ModuleDefId::ModuleId(def))); + def_map.extern_prelude().for_each(|(name, (def, _extern_crate))| { + res.add(name, ScopeDef::ModuleDef(ModuleDefId::ModuleId(def.into()))); }); BUILTIN_SCOPE.iter().for_each(|(name, &def)| { res.add_per_ns(name, def); }); - if let Some(prelude) = def_map.prelude() { + if let Some((prelude, _use)) = def_map.prelude() { let prelude_def_map = prelude.def_map(db); for (name, def) in prelude_def_map[prelude.local_id].scope.entries() { res.add_per_ns(name, def) @@ -479,6 +476,23 @@ impl Resolver { res.map } + pub fn extern_crate_decls_in_scope<'a>( + &'a self, + db: &'a dyn DefDatabase, + ) -> impl Iterator<Item = Name> + 'a { + self.module_scope.def_map[self.module_scope.module_id] + .scope + .extern_crate_decls() + .map(|id| ExternCrateDeclData::extern_crate_decl_data_query(db, id).name.clone()) + } + + pub fn extern_crates_in_scope<'a>(&'a self) -> impl Iterator<Item = (Name, ModuleId)> + 'a { + self.module_scope + .def_map + .extern_prelude() + .map(|(name, module_id)| (name.clone(), module_id.0.into())) + } + pub fn traits_in_scope(&self, db: &dyn DefDatabase) -> FxHashSet<TraitId> { // FIXME(trait_alias): Trait alias brings aliased traits in scope! Note that supertraits of // aliased traits are NOT brought in scope (unless also aliased). @@ -501,7 +515,7 @@ impl Resolver { } // Fill in the prelude traits - if let Some(prelude) = self.module_scope.def_map.prelude() { + if let Some((prelude, _use)) = self.module_scope.def_map.prelude() { let prelude_def_map = prelude.def_map(db); traits.extend(prelude_def_map[prelude.local_id].scope.traits()); } @@ -804,11 +818,12 @@ impl ModuleItemMap { self.def_map.resolve_path_locally(db, self.module_id, path, BuiltinShadowMode::Other); match idx { None => { - let value = to_value_ns(module_def)?; - Some(ResolveValueResult::ValueNs(value)) + let (value, import) = to_value_ns(module_def)?; + Some(ResolveValueResult::ValueNs(value, import)) } Some(idx) => { - let ty = match module_def.take_types()? { + let (def, _, import) = module_def.take_types_full()?; + let ty = match def { ModuleDefId::AdtId(it) => TypeNs::AdtId(it), ModuleDefId::TraitId(it) => TypeNs::TraitId(it), ModuleDefId::TraitAliasId(it) => TypeNs::TraitAliasId(it), @@ -822,7 +837,7 @@ impl ModuleItemMap { | ModuleDefId::MacroId(_) | ModuleDefId::StaticId(_) => return None, }; - Some(ResolveValueResult::Partial(ty, idx)) + Some(ResolveValueResult::Partial(ty, idx, import)) } } } @@ -831,16 +846,17 @@ impl ModuleItemMap { &self, db: &dyn DefDatabase, path: &ModPath, - ) -> Option<(TypeNs, Option<usize>)> { + ) -> Option<(TypeNs, Option<usize>, Option<ImportOrExternCrate>)> { let (module_def, idx) = self.def_map.resolve_path_locally(db, self.module_id, path, BuiltinShadowMode::Other); - let res = to_type_ns(module_def)?; - Some((res, idx)) + let (res, import) = to_type_ns(module_def)?; + Some((res, idx, import)) } } -fn to_value_ns(per_ns: PerNs) -> Option<ValueNs> { - let res = match per_ns.take_values()? { +fn to_value_ns(per_ns: PerNs) -> Option<(ValueNs, Option<ImportId>)> { + let (def, import) = per_ns.take_values_import()?; + let res = match def { ModuleDefId::FunctionId(it) => ValueNs::FunctionId(it), ModuleDefId::AdtId(AdtId::StructId(it)) => ValueNs::StructId(it), ModuleDefId::EnumVariantId(it) => ValueNs::EnumVariantId(it), @@ -855,11 +871,12 @@ fn to_value_ns(per_ns: PerNs) -> Option<ValueNs> { | ModuleDefId::MacroId(_) | ModuleDefId::ModuleId(_) => return None, }; - Some(res) + Some((res, import)) } -fn to_type_ns(per_ns: PerNs) -> Option<TypeNs> { - let res = match per_ns.take_types()? { +fn to_type_ns(per_ns: PerNs) -> Option<(TypeNs, Option<ImportOrExternCrate>)> { + let (def, _, import) = per_ns.take_types_full()?; + let res = match def { ModuleDefId::AdtId(it) => TypeNs::AdtId(it), ModuleDefId::EnumVariantId(it) => TypeNs::EnumVariantId(it), @@ -875,7 +892,7 @@ fn to_type_ns(per_ns: PerNs) -> Option<TypeNs> { | ModuleDefId::StaticId(_) | ModuleDefId::ModuleId(_) => return None, }; - Some(res) + Some((res, import)) } type FxIndexMap<K, V> = IndexMap<K, V, BuildHasherDefault<rustc_hash::FxHasher>>; @@ -892,13 +909,13 @@ impl ScopeNames { } } fn add_per_ns(&mut self, name: &Name, def: PerNs) { - if let &Some((ty, _)) = &def.types { + if let &Some((ty, _, _)) = &def.types { self.add(name, ScopeDef::ModuleDef(ty)) } - if let &Some((def, _)) = &def.values { + if let &Some((def, _, _)) = &def.values { self.add(name, ScopeDef::ModuleDef(def)) } - if let &Some((mac, _)) = &def.macros { + if let &Some((mac, _, _)) = &def.macros { self.add(name, ScopeDef::ModuleDef(ModuleDefId::MacroId(mac))) } if def.is_none() { diff --git a/src/tools/rust-analyzer/crates/hir-def/src/src.rs b/src/tools/rust-analyzer/crates/hir-def/src/src.rs index 6047f770d4d..3770103cda5 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/src.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/src.rs @@ -5,8 +5,8 @@ use la_arena::ArenaMap; use syntax::ast; use crate::{ - db::DefDatabase, item_tree::ItemTreeNode, AssocItemLoc, ItemLoc, Macro2Loc, MacroRulesLoc, - ProcMacroLoc, + db::DefDatabase, item_tree::ItemTreeNode, AssocItemLoc, ItemLoc, Lookup, Macro2Loc, + MacroRulesLoc, ProcMacroLoc, UseId, }; pub trait HasSource { @@ -83,3 +83,18 @@ pub trait HasChildSource<ChildId> { type Value; fn child_source(&self, db: &dyn DefDatabase) -> InFile<ArenaMap<ChildId, Self::Value>>; } + +impl HasChildSource<la_arena::Idx<ast::UseTree>> for UseId { + type Value = ast::UseTree; + fn child_source( + &self, + db: &dyn DefDatabase, + ) -> InFile<ArenaMap<la_arena::Idx<ast::UseTree>, Self::Value>> { + let loc = &self.lookup(db); + let use_ = &loc.id.item_tree(db)[loc.id.value]; + InFile::new( + loc.id.file_id(), + use_.use_tree_source_map(db, loc.id.file_id()).into_iter().collect(), + ) + } +} diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/attrs.rs b/src/tools/rust-analyzer/crates/hir-expand/src/attrs.rs index 4c918e55b92..0ec2422b30c 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/attrs.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/attrs.rs @@ -342,14 +342,7 @@ fn inner_attributes( ast::Impl(it) => it.assoc_item_list()?.syntax().clone(), ast::Module(it) => it.item_list()?.syntax().clone(), ast::BlockExpr(it) => { - use syntax::SyntaxKind::{BLOCK_EXPR , EXPR_STMT}; - // Block expressions accept outer and inner attributes, but only when they are the outer - // expression of an expression statement or the final expression of another block expression. - let may_carry_attributes = matches!( - it.syntax().parent().map(|it| it.kind()), - Some(BLOCK_EXPR | EXPR_STMT) - ); - if !may_carry_attributes { + if !it.may_carry_attributes() { return None } syntax.clone() diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/lib.rs b/src/tools/rust-analyzer/crates/hir-expand/src/lib.rs index 1f1e20f49e3..4be55126b86 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/lib.rs @@ -37,7 +37,7 @@ use either::Either; use syntax::{ algo::{self, skip_trivia_token}, ast::{self, AstNode, HasDocComments}, - AstPtr, Direction, SyntaxNode, SyntaxNodePtr, SyntaxToken, + AstPtr, Direction, SyntaxNode, SyntaxNodePtr, SyntaxToken, TextSize, }; use crate::{ @@ -544,7 +544,7 @@ impl MacroCallKind { }; let range = match kind { - MacroCallKind::FnLike { ast_id, .. } => ast_id.to_node(db).syntax().text_range(), + MacroCallKind::FnLike { ast_id, .. } => ast_id.to_ptr(db).text_range(), MacroCallKind::Derive { ast_id, derive_attr_index, .. } => { // FIXME: should be the range of the macro name, not the whole derive // FIXME: handle `cfg_attr` @@ -642,6 +642,8 @@ impl ExpansionInfo { db: &dyn db::ExpandDatabase, item: Option<ast::Item>, token: InFile<&SyntaxToken>, + // FIXME: use this for range mapping, so that we can resolve inline format args + _relative_token_offset: Option<TextSize>, ) -> Option<impl Iterator<Item = InFile<SyntaxToken>> + '_> { assert_eq!(token.file_id, self.arg.file_id); let token_id_in_attr_input = if let Some(item) = item { @@ -840,9 +842,6 @@ impl<N: AstIdNode> AstId<N> { pub type ErasedAstId = InFile<ErasedFileAstId>; impl ErasedAstId { - pub fn to_node(&self, db: &dyn db::ExpandDatabase) -> SyntaxNode { - self.to_ptr(db).to_node(&db.parse_or_expand(self.file_id)) - } pub fn to_ptr(&self, db: &dyn db::ExpandDatabase) -> SyntaxNodePtr { db.ast_id_map(self.file_id).get_raw(self.value) } @@ -1054,16 +1053,6 @@ impl InFile<SyntaxToken> { } } } - - pub fn ancestors_with_macros( - self, - db: &dyn db::ExpandDatabase, - ) -> impl Iterator<Item = InFile<SyntaxNode>> + '_ { - self.value.parent().into_iter().flat_map({ - let file_id = self.file_id; - move |parent| InFile::new(file_id, &parent).ancestors_with_macros(db) - }) - } } #[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)] diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/builder.rs b/src/tools/rust-analyzer/crates/hir-ty/src/builder.rs index eec57ba3f80..967e028bfb1 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/builder.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/builder.rs @@ -17,7 +17,8 @@ use smallvec::SmallVec; use crate::{ consteval::unknown_const_as_generic, db::HirDatabase, infer::unify::InferenceTable, primitive, to_assoc_type_id, to_chalk_trait_id, utils::generics, Binders, BoundVar, CallableSig, - GenericArg, Interner, ProjectionTy, Substitution, TraitRef, Ty, TyDefId, TyExt, TyKind, + GenericArg, GenericArgData, Interner, ProjectionTy, Substitution, TraitRef, Ty, TyDefId, TyExt, + TyKind, }; #[derive(Debug, Clone, PartialEq, Eq)] @@ -79,9 +80,9 @@ impl<D> TyBuilder<D> { let expected_kind = &self.param_kinds[self.vec.len()]; let arg_kind = match arg.data(Interner) { - chalk_ir::GenericArgData::Ty(_) => ParamKind::Type, - chalk_ir::GenericArgData::Lifetime(_) => panic!("Got lifetime in TyBuilder::push"), - chalk_ir::GenericArgData::Const(c) => { + GenericArgData::Ty(_) => ParamKind::Type, + GenericArgData::Lifetime(_) => panic!("Got lifetime in TyBuilder::push"), + GenericArgData::Const(c) => { let c = c.data(Interner); ParamKind::Const(c.ty.clone()) } @@ -139,8 +140,8 @@ impl<D> TyBuilder<D> { fn assert_match_kind(&self, a: &chalk_ir::GenericArg<Interner>, e: &ParamKind) { match (a.data(Interner), e) { - (chalk_ir::GenericArgData::Ty(_), ParamKind::Type) - | (chalk_ir::GenericArgData::Const(_), ParamKind::Const(_)) => (), + (GenericArgData::Ty(_), ParamKind::Type) + | (GenericArgData::Const(_), ParamKind::Const(_)) => (), _ => panic!("Mismatched kinds: {a:?}, {:?}, {:?}", self.vec, self.param_kinds), } } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/consteval.rs b/src/tools/rust-analyzer/crates/hir-ty/src/consteval.rs index 1c0f7b08da8..0348680e5da 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/consteval.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/consteval.rs @@ -1,7 +1,7 @@ //! Constant evaluation details use base_db::CrateId; -use chalk_ir::{BoundVar, DebruijnIndex, GenericArgData}; +use chalk_ir::{cast::Cast, BoundVar, DebruijnIndex}; use hir_def::{ hir::Expr, path::Path, @@ -120,7 +120,7 @@ pub fn unknown_const(ty: Ty) -> Const { } pub fn unknown_const_as_generic(ty: Ty) -> GenericArg { - GenericArgData::Const(unknown_const(ty)).intern(Interner) + unknown_const(ty).cast(Interner) } /// Interns a constant scalar with the given type diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/consteval/tests.rs b/src/tools/rust-analyzer/crates/hir-ty/src/consteval/tests.rs index 666955fa1c3..7ad3659a4f6 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/consteval/tests.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/consteval/tests.rs @@ -1203,6 +1203,27 @@ fn destructing_assignment() { "#, 5, ); + check_number( + r#" + const GOAL: u8 = { + let (mut a, mut b) = (2, 5); + (a, b) = (b, a); + a * 10 + b + }; + "#, + 52, + ); + check_number( + r#" + struct Point { x: i32, y: i32 } + const GOAL: i32 = { + let mut p = Point { x: 5, y: 6 }; + (p.x, _) = (p.y, p.x); + p.x * 10 + p.y + }; + "#, + 66, + ); } #[test] @@ -1433,6 +1454,30 @@ fn from_trait() { } #[test] +fn closure_clone() { + check_number( + r#" +//- minicore: clone, fn +struct S(u8); + +impl Clone for S(u8) { + fn clone(&self) -> S { + S(self.0 + 5) + } +} + +const GOAL: u8 = { + let s = S(3); + let cl = move || s; + let cl = cl.clone(); + cl().0 +} + "#, + 8, + ); +} + +#[test] fn builtin_derive_macro() { check_number( r#" @@ -2396,14 +2441,14 @@ fn const_loop() { fn const_transfer_memory() { check_number( r#" - //- minicore: slice, index, coerce_unsized + //- minicore: slice, index, coerce_unsized, option const A1: &i32 = &1; const A2: &i32 = &10; const A3: [&i32; 3] = [&1, &2, &100]; - const A4: (i32, &i32) = (1, &1000); - const GOAL: i32 = *A1 + *A2 + *A3[2] + *A4.1; + const A4: (i32, &i32, Option<&i32>) = (1, &1000, Some(&10000)); + const GOAL: i32 = *A1 + *A2 + *A3[2] + *A4.1 + *A4.2.unwrap_or(&5); "#, - 1111, + 11111, ); } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/unsafe_check.rs b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/unsafe_check.rs index 9f9a56ffab0..cbca0e801d4 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/unsafe_check.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/unsafe_check.rs @@ -75,7 +75,7 @@ fn walk_unsafe( Expr::Path(path) => { let resolver = resolver_for_expr(db.upcast(), def, current); let value_or_partial = resolver.resolve_path_in_value_ns(db.upcast(), path); - if let Some(ResolveValueResult::ValueNs(ValueNs::StaticId(id))) = value_or_partial { + if let Some(ResolveValueResult::ValueNs(ValueNs::StaticId(id), _)) = value_or_partial { if db.static_data(id).mutable { unsafe_expr_cb(UnsafeExpr { expr: current, inside_unsafe_block }); } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/display.rs b/src/tools/rust-analyzer/crates/hir-ty/src/display.rs index 1b4ee4613d6..f6d6b00d740 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/display.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/display.rs @@ -1809,6 +1809,25 @@ impl HirDisplay for Path { } } + // Convert trait's `Self` bound back to the surface syntax. Note there is no associated + // trait, so there can only be one path segment that `has_self_type`. The `Self` type + // itself can contain further qualified path through, which will be handled by recursive + // `hir_fmt`s. + // + // `trait_mod::Trait<Self = type_mod::Type, Args>::Assoc` + // => + // `<type_mod::Type as trait_mod::Trait<Args>>::Assoc` + let trait_self_ty = self.segments().iter().find_map(|seg| { + let generic_args = seg.args_and_bindings?; + generic_args.has_self_type.then(|| &generic_args.args[0]) + }); + if let Some(ty) = trait_self_ty { + write!(f, "<")?; + ty.hir_fmt(f)?; + write!(f, " as ")?; + // Now format the path of the trait... + } + for (seg_idx, segment) in self.segments().iter().enumerate() { if !matches!(self.kind(), PathKind::Plain) || seg_idx > 0 { write!(f, "::")?; @@ -1840,15 +1859,12 @@ impl HirDisplay for Path { return Ok(()); } - write!(f, "<")?; let mut first = true; - for arg in generic_args.args.iter() { + // Skip the `Self` bound if exists. It's handled outside the loop. + for arg in &generic_args.args[generic_args.has_self_type as usize..] { if first { first = false; - if generic_args.has_self_type { - // FIXME: Convert to `<Ty as Trait>` form. - write!(f, "Self = ")?; - } + write!(f, "<")?; } else { write!(f, ", ")?; } @@ -1857,6 +1873,7 @@ impl HirDisplay for Path { for binding in generic_args.bindings.iter() { if first { first = false; + write!(f, "<")?; } else { write!(f, ", ")?; } @@ -1872,9 +1889,20 @@ impl HirDisplay for Path { } } } - write!(f, ">")?; + + // There may be no generic arguments to print, in case of a trait having only a + // single `Self` bound which is converted to `<Ty as Trait>::Assoc`. + if !first { + write!(f, ">")?; + } + + // Current position: `<Ty as Trait<Args>|` + if generic_args.has_self_type { + write!(f, ">")?; + } } } + Ok(()) } } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs index b4915dbf0f9..0fb4934444b 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs @@ -1017,7 +1017,7 @@ impl<'a> InferenceContext<'a> { let ctx = crate::lower::TyLoweringContext::new(self.db, &self.resolver, self.owner.into()); let (resolution, unresolved) = if value_ns { match self.resolver.resolve_path_in_value_ns(self.db.upcast(), path) { - Some(ResolveValueResult::ValueNs(value)) => match value { + Some(ResolveValueResult::ValueNs(value, _)) => match value { ValueNs::EnumVariantId(var) => { let substs = ctx.substs_from_path(path, var.into(), true); let ty = self.db.ty(var.parent.into()); @@ -1033,12 +1033,14 @@ impl<'a> InferenceContext<'a> { ValueNs::ImplSelf(impl_id) => (TypeNs::SelfType(impl_id), None), _ => return (self.err_ty(), None), }, - Some(ResolveValueResult::Partial(typens, unresolved)) => (typens, Some(unresolved)), + Some(ResolveValueResult::Partial(typens, unresolved, _)) => { + (typens, Some(unresolved)) + } None => return (self.err_ty(), None), } } else { match self.resolver.resolve_path_in_type_ns(self.db.upcast(), path) { - Some(it) => it, + Some((it, idx, _)) => (it, idx), None => return (self.err_ty(), None), } }; diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs index 1781f6c58f1..23efe616f4f 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs @@ -322,7 +322,7 @@ impl InferenceContext<'_> { Expr::Path(p) => { let resolver = resolver_for_expr(self.db.upcast(), self.owner, tgt_expr); if let Some(r) = resolver.resolve_path_in_value_ns(self.db.upcast(), p) { - if let ResolveValueResult::ValueNs(v) = r { + if let ResolveValueResult::ValueNs(v, _) = r { if let ValueNs::LocalBinding(b) = v { return Some(HirPlace { local: b, projections: vec![] }); } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs index 8cbdae62526..8b352141084 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs @@ -5,9 +5,7 @@ use std::{ mem, }; -use chalk_ir::{ - cast::Cast, fold::Shift, DebruijnIndex, GenericArgData, Mutability, TyVariableKind, -}; +use chalk_ir::{cast::Cast, fold::Shift, DebruijnIndex, Mutability, TyVariableKind}; use hir_def::{ generics::TypeOrConstParamData, hir::{ @@ -750,7 +748,7 @@ impl InferenceContext<'_> { self.resolve_associated_type_with_params( self_ty, self.resolve_ops_index_output(), - &[GenericArgData::Ty(index_ty).intern(Interner)], + &[index_ty.cast(Interner)], ) } else { self.err_ty() @@ -1721,16 +1719,13 @@ impl InferenceContext<'_> { for (id, data) in def_generics.iter().skip(substs.len()) { match data { TypeOrConstParamData::TypeParamData(_) => { - substs.push(GenericArgData::Ty(self.table.new_type_var()).intern(Interner)) - } - TypeOrConstParamData::ConstParamData(_) => { - substs.push( - GenericArgData::Const(self.table.new_const_var( - self.db.const_param_ty(ConstParamId::from_unchecked(id)), - )) - .intern(Interner), - ) + substs.push(self.table.new_type_var().cast(Interner)) } + TypeOrConstParamData::ConstParamData(_) => substs.push( + self.table + .new_const_var(self.db.const_param_ty(ConstParamId::from_unchecked(id))) + .cast(Interner), + ), } } assert_eq!(substs.len(), total_len); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/path.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/path.rs index 79d9e21e797..2a51c84db3a 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/path.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/path.rs @@ -61,8 +61,8 @@ impl InferenceContext<'_> { self.resolver.resolve_path_in_value_ns(self.db.upcast(), path)?; match value_or_partial { - ResolveValueResult::ValueNs(it) => (it, None), - ResolveValueResult::Partial(def, remaining_index) => self + ResolveValueResult::ValueNs(it, _) => (it, None), + ResolveValueResult::Partial(def, remaining_index, _) => self .resolve_assoc_item(def, path, remaining_index, id) .map(|(it, substs)| (it, Some(substs)))?, } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs index 0fb71135b4d..0a68a9f3b58 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs @@ -10,7 +10,6 @@ use chalk_solve::infer::ParameterEnaVariableExt; use either::Either; use ena::unify::UnifyKey; use hir_expand::name; -use stdx::never; use triomphe::Arc; use super::{InferOk, InferResult, InferenceContext, TypeError}; @@ -92,15 +91,10 @@ pub(crate) fn unify( let vars = Substitution::from_iter( Interner, tys.binders.iter(Interner).map(|it| match &it.kind { - chalk_ir::VariableKind::Ty(_) => { - GenericArgData::Ty(table.new_type_var()).intern(Interner) - } - chalk_ir::VariableKind::Lifetime => { - GenericArgData::Ty(table.new_type_var()).intern(Interner) - } // FIXME: maybe wrong? - chalk_ir::VariableKind::Const(ty) => { - GenericArgData::Const(table.new_const_var(ty.clone())).intern(Interner) - } + chalk_ir::VariableKind::Ty(_) => table.new_type_var().cast(Interner), + // FIXME: maybe wrong? + chalk_ir::VariableKind::Lifetime => table.new_type_var().cast(Interner), + chalk_ir::VariableKind::Const(ty) => table.new_const_var(ty.clone()).cast(Interner), }), ); let ty1_with_vars = vars.apply(tys.value.0.clone(), Interner); @@ -111,10 +105,10 @@ pub(crate) fn unify( // default any type vars that weren't unified back to their original bound vars // (kind of hacky) let find_var = |iv| { - vars.iter(Interner).position(|v| match v.interned() { - chalk_ir::GenericArgData::Ty(ty) => ty.inference_var(Interner), - chalk_ir::GenericArgData::Lifetime(lt) => lt.inference_var(Interner), - chalk_ir::GenericArgData::Const(c) => c.inference_var(Interner), + vars.iter(Interner).position(|v| match v.data(Interner) { + GenericArgData::Ty(ty) => ty.inference_var(Interner), + GenericArgData::Lifetime(lt) => lt.inference_var(Interner), + GenericArgData::Const(c) => c.inference_var(Interner), } == Some(iv)) }; let fallback = |iv, kind, default, binder| match kind { @@ -149,6 +143,9 @@ pub(crate) struct InferenceTable<'a> { var_unification_table: ChalkInferenceTable, type_variable_table: Vec<TypeVariableFlags>, pending_obligations: Vec<Canonicalized<InEnvironment<Goal>>>, + /// Double buffer used in [`Self::resolve_obligations_as_possible`] to cut down on + /// temporary allocations. + resolve_obligations_buffer: Vec<Canonicalized<InEnvironment<Goal>>>, } pub(crate) struct InferenceTableSnapshot { @@ -165,6 +162,7 @@ impl<'a> InferenceTable<'a> { var_unification_table: ChalkInferenceTable::new(), type_variable_table: Vec::new(), pending_obligations: Vec::new(), + resolve_obligations_buffer: Vec::new(), } } @@ -516,10 +514,10 @@ impl<'a> InferenceTable<'a> { pub(crate) fn resolve_obligations_as_possible(&mut self) { let _span = profile::span("resolve_obligations_as_possible"); let mut changed = true; - let mut obligations = Vec::new(); - while changed { - changed = false; + let mut obligations = mem::take(&mut self.resolve_obligations_buffer); + while mem::take(&mut changed) { mem::swap(&mut self.pending_obligations, &mut obligations); + for canonicalized in obligations.drain(..) { if !self.check_changed(&canonicalized) { self.pending_obligations.push(canonicalized); @@ -534,6 +532,8 @@ impl<'a> InferenceTable<'a> { self.register_obligation_in_env(uncanonical); } } + self.resolve_obligations_buffer = obligations; + self.resolve_obligations_buffer.clear(); } pub(crate) fn fudge_inference<T: TypeFoldable<Interner>>( @@ -611,9 +611,9 @@ impl<'a> InferenceTable<'a> { fn check_changed(&mut self, canonicalized: &Canonicalized<InEnvironment<Goal>>) -> bool { canonicalized.free_vars.iter().any(|var| { let iv = match var.data(Interner) { - chalk_ir::GenericArgData::Ty(ty) => ty.inference_var(Interner), - chalk_ir::GenericArgData::Lifetime(lt) => lt.inference_var(Interner), - chalk_ir::GenericArgData::Const(c) => c.inference_var(Interner), + GenericArgData::Ty(ty) => ty.inference_var(Interner), + GenericArgData::Lifetime(lt) => lt.inference_var(Interner), + GenericArgData::Const(c) => c.inference_var(Interner), } .expect("free var is not inference var"); if self.var_unification_table.probe_var(iv).is_some() { @@ -690,14 +690,10 @@ impl<'a> InferenceTable<'a> { .fill(|it| { let arg = match it { ParamKind::Type => self.new_type_var(), - ParamKind::Const(ty) => { - never!("Tuple with const parameter"); - return GenericArgData::Const(self.new_const_var(ty.clone())) - .intern(Interner); - } + ParamKind::Const(_) => unreachable!("Tuple with const parameter"), }; arg_tys.push(arg.clone()); - GenericArgData::Ty(arg).intern(Interner) + arg.cast(Interner) }) .build(); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs index b3ca2a22258..405bb001b5d 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs @@ -52,12 +52,14 @@ use hir_expand::name; use la_arena::{Arena, Idx}; use mir::{MirEvalError, VTableMap}; use rustc_hash::FxHashSet; +use syntax::ast::{make, ConstArg}; use traits::FnTrait; use triomphe::Arc; use utils::Generics; use crate::{ - consteval::unknown_const, db::HirDatabase, infer::unify::InferenceTable, utils::generics, + consteval::unknown_const, db::HirDatabase, display::HirDisplay, infer::unify::InferenceTable, + utils::generics, }; pub use autoderef::autoderef; @@ -719,3 +721,16 @@ where value.visit_with(&mut collector, DebruijnIndex::INNERMOST); collector.placeholders.into_iter().collect() } + +pub fn known_const_to_ast(konst: &Const, db: &dyn HirDatabase) -> Option<ConstArg> { + if let ConstValue::Concrete(c) = &konst.interned().value { + match c.interned { + ConstScalar::UnevaluatedConst(GeneralConstId::InTypeConstId(cid), _) => { + return Some(cid.source(db.upcast())); + } + ConstScalar::Unknown => return None, + _ => (), + } + } + Some(make::expr_const_value(konst.display(db).to_string().as_str())) +} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs index 2837f400bce..9a61f153599 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs @@ -58,10 +58,9 @@ use crate::{ InTypeConstIdMetadata, }, AliasEq, AliasTy, Binders, BoundVar, CallableSig, Const, ConstScalar, DebruijnIndex, DynTy, - FnPointer, FnSig, FnSubst, GenericArgData, ImplTraitId, Interner, ParamKind, PolyFnSig, - ProjectionTy, QuantifiedWhereClause, QuantifiedWhereClauses, ReturnTypeImplTrait, - ReturnTypeImplTraits, Substitution, TraitEnvironment, TraitRef, TraitRefExt, Ty, TyBuilder, - TyKind, WhereClause, + FnPointer, FnSig, FnSubst, ImplTraitId, Interner, ParamKind, PolyFnSig, ProjectionTy, + QuantifiedWhereClause, QuantifiedWhereClauses, ReturnTypeImplTrait, ReturnTypeImplTraits, + Substitution, TraitEnvironment, TraitRef, TraitRefExt, Ty, TyBuilder, TyKind, WhereClause, }; #[derive(Debug)] @@ -213,6 +212,19 @@ impl<'a> TyLoweringContext<'a> { self.lower_ty_ext(type_ref).0 } + pub fn lower_const(&self, const_ref: &ConstRef, const_type: Ty) -> Const { + const_or_path_to_chalk( + self.db, + self.resolver, + self.owner, + const_type, + const_ref, + self.type_param_mode, + || self.generics(), + self.in_binders, + ) + } + fn generics(&self) -> Generics { generics( self.db.upcast(), @@ -242,17 +254,7 @@ impl<'a> TyLoweringContext<'a> { } TypeRef::Array(inner, len) => { let inner_ty = self.lower_ty(inner); - let const_len = const_or_path_to_chalk( - self.db, - self.resolver, - self.owner, - TyBuilder::usize(), - len, - self.type_param_mode, - || self.generics(), - self.in_binders, - ); - + let const_len = self.lower_const(len, TyBuilder::usize()); TyKind::Array(inner_ty, const_len).intern(Interner) } TypeRef::Slice(inner) => { @@ -391,11 +393,9 @@ impl<'a> TyLoweringContext<'a> { let ty = { let macro_call = macro_call.to_node(self.db.upcast()); let resolver = |path| { - self.resolver.resolve_path_as_macro( - self.db.upcast(), - &path, - Some(MacroSubNs::Bang), - ) + self.resolver + .resolve_path_as_macro(self.db.upcast(), &path, Some(MacroSubNs::Bang)) + .map(|(it, _)| it) }; match expander.enter_expand::<ast::Type>(self.db.upcast(), macro_call, resolver) { @@ -447,7 +447,7 @@ impl<'a> TyLoweringContext<'a> { return None; } let resolution = match self.resolver.resolve_path_in_type_ns(self.db.upcast(), path) { - Some((it, None)) => it, + Some((it, None, _)) => it, _ => return None, }; match resolution { @@ -627,7 +627,7 @@ impl<'a> TyLoweringContext<'a> { return self.lower_ty_relative_path(ty, res, path.segments()); } - let (resolution, remaining_index) = + let (resolution, remaining_index, _) = match self.resolver.resolve_path_in_type_ns(self.db.upcast(), path) { Some(it) => it, None => return (TyKind::Error.intern(Interner), None), @@ -847,18 +847,7 @@ impl<'a> TyLoweringContext<'a> { arg, &mut (), |_, type_ref| self.lower_ty(type_ref), - |_, c, ty| { - const_or_path_to_chalk( - self.db, - self.resolver, - self.owner, - ty, - c, - self.type_param_mode, - || self.generics(), - self.in_binders, - ) - }, + |_, const_ref, ty| self.lower_const(const_ref, ty), ) { had_explicit_args = true; substs.push(x); @@ -1604,24 +1593,35 @@ pub(crate) fn generic_defaults_query( .iter() .enumerate() .map(|(idx, (id, p))| { - let p = match p { - TypeOrConstParamData::TypeParamData(p) => p, - TypeOrConstParamData::ConstParamData(_) => { - // FIXME: implement const generic defaults - let val = unknown_const_as_generic( - db.const_param_ty(ConstParamId::from_unchecked(id)), + match p { + TypeOrConstParamData::TypeParamData(p) => { + let mut ty = p + .default + .as_ref() + .map_or(TyKind::Error.intern(Interner), |t| ctx.lower_ty(t)); + // Each default can only refer to previous parameters. + // Type variable default referring to parameter coming + // after it is forbidden (FIXME: report diagnostic) + ty = fallback_bound_vars(ty, idx, parent_start_idx); + crate::make_binders(db, &generic_params, ty.cast(Interner)) + } + TypeOrConstParamData::ConstParamData(p) => { + let mut val = p.default.as_ref().map_or_else( + || { + unknown_const_as_generic( + db.const_param_ty(ConstParamId::from_unchecked(id)), + ) + }, + |c| { + let c = ctx.lower_const(c, ctx.lower_ty(&p.ty)); + c.cast(Interner) + }, ); - return make_binders(db, &generic_params, val); + // Each default can only refer to previous parameters, see above. + val = fallback_bound_vars(val, idx, parent_start_idx); + make_binders(db, &generic_params, val) } - }; - let mut ty = - p.default.as_ref().map_or(TyKind::Error.intern(Interner), |t| ctx.lower_ty(t)); - - // Each default can only refer to previous parameters. - // Type variable default referring to parameter coming - // after it is forbidden (FIXME: report diagnostic) - ty = fallback_bound_vars(ty, idx, parent_start_idx); - crate::make_binders(db, &generic_params, ty.cast(Interner)) + } }) // FIXME: use `Arc::from_iter` when it becomes available .collect::<Vec<_>>(), @@ -1643,9 +1643,7 @@ pub(crate) fn generic_defaults_recover( .iter_id() .map(|id| { let val = match id { - Either::Left(_) => { - GenericArgData::Ty(TyKind::Error.intern(Interner)).intern(Interner) - } + Either::Left(_) => TyKind::Error.intern(Interner).cast(Interner), Either::Right(id) => unknown_const_as_generic(db.const_param_ty(id)), }; crate::make_binders(db, &generic_params, val) @@ -1991,16 +1989,9 @@ pub(crate) fn generic_arg_to_chalk<'a, T>( } }; Some(match (arg, kind) { - (GenericArg::Type(type_ref), ParamKind::Type) => { - let ty = for_type(this, type_ref); - GenericArgData::Ty(ty).intern(Interner) - } - (GenericArg::Const(c), ParamKind::Const(c_ty)) => { - GenericArgData::Const(for_const(this, c, c_ty)).intern(Interner) - } - (GenericArg::Const(_), ParamKind::Type) => { - GenericArgData::Ty(TyKind::Error.intern(Interner)).intern(Interner) - } + (GenericArg::Type(type_ref), ParamKind::Type) => for_type(this, type_ref).cast(Interner), + (GenericArg::Const(c), ParamKind::Const(c_ty)) => for_const(this, c, c_ty).cast(Interner), + (GenericArg::Const(_), ParamKind::Type) => TyKind::Error.intern(Interner).cast(Interner), (GenericArg::Type(t), ParamKind::Const(c_ty)) => { // We want to recover simple idents, which parser detects them // as types. Maybe here is not the best place to do it, but @@ -2010,9 +2001,7 @@ pub(crate) fn generic_arg_to_chalk<'a, T>( if p.kind == PathKind::Plain { if let [n] = p.segments() { let c = ConstRef::Path(n.clone()); - return Some( - GenericArgData::Const(for_const(this, &c, c_ty)).intern(Interner), - ); + return Some(for_const(this, &c, c_ty).cast(Interner)); } } } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir.rs index 4723c25ed08..9be083d0117 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir.rs @@ -368,7 +368,7 @@ pub enum TerminatorKind { /// /// Only permitted in cleanup blocks. `Resume` is not permitted with `-C unwind=abort` after /// deaggregation runs. - Resume, + UnwindResume, /// Indicates that the landing pad is finished and that the process should abort. /// @@ -1057,7 +1057,7 @@ impl MirBody { TerminatorKind::FalseEdge { .. } | TerminatorKind::FalseUnwind { .. } | TerminatorKind::Goto { .. } - | TerminatorKind::Resume + | TerminatorKind::UnwindResume | TerminatorKind::GeneratorDrop | TerminatorKind::Abort | TerminatorKind::Return diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/borrowck.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/borrowck.rs index ad98e8fa181..c70d7f63fd8 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/borrowck.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/borrowck.rs @@ -160,7 +160,7 @@ fn moved_out_of_ref(db: &dyn HirDatabase, body: &MirBody) -> Vec<MovedOutOfRef> TerminatorKind::FalseEdge { .. } | TerminatorKind::FalseUnwind { .. } | TerminatorKind::Goto { .. } - | TerminatorKind::Resume + | TerminatorKind::UnwindResume | TerminatorKind::GeneratorDrop | TerminatorKind::Abort | TerminatorKind::Return @@ -280,7 +280,7 @@ fn ever_initialized_map( let targets = match &terminator.kind { TerminatorKind::Goto { target } => vec![*target], TerminatorKind::SwitchInt { targets, .. } => targets.all_targets().to_vec(), - TerminatorKind::Resume + TerminatorKind::UnwindResume | TerminatorKind::Abort | TerminatorKind::Return | TerminatorKind::Unreachable => vec![], @@ -371,7 +371,7 @@ fn mutability_of_locals( }; match &terminator.kind { TerminatorKind::Goto { .. } - | TerminatorKind::Resume + | TerminatorKind::UnwindResume | TerminatorKind::Abort | TerminatorKind::Return | TerminatorKind::Unreachable diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs index 9e30eed56f3..3944feb128c 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs @@ -10,7 +10,7 @@ use std::{ }; use base_db::{CrateId, FileId}; -use chalk_ir::Mutability; +use chalk_ir::{cast::Cast, Mutability}; use either::Either; use hir_def::{ builtin_type::BuiltinType, @@ -40,8 +40,8 @@ use crate::{ name, static_lifetime, traits::FnTrait, utils::{detect_variant_from_bytes, ClosureSubst}, - CallableDefId, ClosureId, Const, ConstScalar, FnDefId, GenericArgData, Interner, MemoryMap, - Substitution, TraitEnvironment, Ty, TyBuilder, TyExt, TyKind, + CallableDefId, ClosureId, Const, ConstScalar, FnDefId, Interner, MemoryMap, Substitution, + TraitEnvironment, Ty, TyBuilder, TyExt, TyKind, }; use super::{ @@ -2007,7 +2007,28 @@ impl Evaluator<'_> { } } AdtId::UnionId(_) => (), - AdtId::EnumId(_) => (), + AdtId::EnumId(e) => { + if let Some((variant, layout)) = detect_variant_from_bytes( + &layout, + self.db, + self.trait_env.clone(), + self.read_memory(addr, layout.size.bytes_usize())?, + e, + ) { + let ev = EnumVariantId { parent: e, local_id: variant }; + for (i, (_, ty)) in self.db.field_types(ev.into()).iter().enumerate() { + let offset = layout.fields.offset(i).bytes_usize(); + let ty = ty.clone().substitute(Interner, subst); + self.patch_addresses( + patch_map, + old_vtable, + addr.offset(offset), + &ty, + locals, + )?; + } + } + } }, TyKind::Tuple(_, subst) => { for (id, ty) in subst.iter(Interner).enumerate() { @@ -2248,7 +2269,7 @@ impl Evaluator<'_> { interval: args_for_target[0].interval.slice(0..self.ptr_size()), ty: ty.clone(), }; - let ty = GenericArgData::Ty(ty.clone()).intern(Interner); + let ty = ty.clone().cast(Interner); let generics_for_target = Substitution::from_iter( Interner, generic_args.iter(Interner).enumerate().map(|(i, it)| { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/shim.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/shim.rs index b2e29fd34b5..52943e97ac0 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/shim.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/shim.rs @@ -136,7 +136,10 @@ impl Evaluator<'_> { not_supported!("wrong generic arg kind for clone"); }; // Clone has special impls for tuples and function pointers - if matches!(self_ty.kind(Interner), TyKind::Function(_) | TyKind::Tuple(..)) { + if matches!( + self_ty.kind(Interner), + TyKind::Function(_) | TyKind::Tuple(..) | TyKind::Closure(..) + ) { self.exec_clone(def, args, self_ty.clone(), locals, destination, span)?; return Ok(true); } @@ -167,32 +170,26 @@ impl Evaluator<'_> { return destination .write_from_interval(self, Interval { addr, size: destination.size }); } + TyKind::Closure(id, subst) => { + let [arg] = args else { + not_supported!("wrong arg count for clone"); + }; + let addr = Address::from_bytes(arg.get(self)?)?; + let (closure_owner, _) = self.db.lookup_intern_closure((*id).into()); + let infer = self.db.infer(closure_owner); + let (captures, _) = infer.closure_info(id); + let layout = self.layout(&self_ty)?; + let ty_iter = captures.iter().map(|c| c.ty(subst)); + self.exec_clone_for_fields(ty_iter, layout, addr, def, locals, destination, span)?; + } TyKind::Tuple(_, subst) => { let [arg] = args else { not_supported!("wrong arg count for clone"); }; let addr = Address::from_bytes(arg.get(self)?)?; let layout = self.layout(&self_ty)?; - for (i, ty) in subst.iter(Interner).enumerate() { - let ty = ty.assert_ty_ref(Interner); - let size = self.layout(ty)?.size.bytes_usize(); - let tmp = self.heap_allocate(self.ptr_size(), self.ptr_size())?; - let arg = IntervalAndTy { - interval: Interval { addr: tmp, size: self.ptr_size() }, - ty: TyKind::Ref(Mutability::Not, static_lifetime(), ty.clone()) - .intern(Interner), - }; - let offset = layout.fields.offset(i).bytes_usize(); - self.write_memory(tmp, &addr.offset(offset).to_bytes())?; - self.exec_clone( - def, - &[arg], - ty.clone(), - locals, - destination.slice(offset..offset + size), - span, - )?; - } + let ty_iter = subst.iter(Interner).map(|ga| ga.assert_ty_ref(Interner).clone()); + self.exec_clone_for_fields(ty_iter, layout, addr, def, locals, destination, span)?; } _ => { self.exec_fn_with_args( @@ -209,6 +206,37 @@ impl Evaluator<'_> { Ok(()) } + fn exec_clone_for_fields( + &mut self, + ty_iter: impl Iterator<Item = Ty>, + layout: Arc<Layout>, + addr: Address, + def: FunctionId, + locals: &Locals, + destination: Interval, + span: MirSpan, + ) -> Result<()> { + for (i, ty) in ty_iter.enumerate() { + let size = self.layout(&ty)?.size.bytes_usize(); + let tmp = self.heap_allocate(self.ptr_size(), self.ptr_size())?; + let arg = IntervalAndTy { + interval: Interval { addr: tmp, size: self.ptr_size() }, + ty: TyKind::Ref(Mutability::Not, static_lifetime(), ty.clone()).intern(Interner), + }; + let offset = layout.fields.offset(i).bytes_usize(); + self.write_memory(tmp, &addr.offset(offset).to_bytes())?; + self.exec_clone( + def, + &[arg], + ty, + locals, + destination.slice(offset..offset + size), + span, + )?; + } + Ok(()) + } + fn exec_alloc_fn( &mut self, alloc_fn: &str, @@ -473,6 +501,38 @@ impl Evaluator<'_> { self.write_memory_using_ref(destination.addr, destination.size)?.fill(0); Ok(()) } + "getenv" => { + let [name] = args else { + return Err(MirEvalError::TypeError("libc::write args are not provided")); + }; + let mut name_buf = vec![]; + let name = { + let mut index = Address::from_bytes(name.get(self)?)?; + loop { + let byte = self.read_memory(index, 1)?[0]; + index = index.offset(1); + if byte == 0 { + break; + } + name_buf.push(byte); + } + String::from_utf8_lossy(&name_buf) + }; + let value = self.db.crate_graph()[self.crate_id].env.get(&name); + match value { + None => { + // Write null as fail + self.write_memory_using_ref(destination.addr, destination.size)?.fill(0); + } + Some(mut value) => { + value.push('\0'); + let addr = self.heap_allocate(value.len(), 1)?; + self.write_memory(addr, value.as_bytes())?; + self.write_memory(destination.addr, &addr.to_bytes())?; + } + } + Ok(()) + } _ => not_supported!("unknown external function {as_str}"), } } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/tests.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/tests.rs index 46165cf3d69..ff30dc6dade 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/tests.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/tests.rs @@ -730,6 +730,48 @@ fn main() { } #[test] +fn posix_getenv() { + check_pass( + r#" +//- /main.rs env:foo=bar + +type c_char = u8; + +extern "C" { + pub fn getenv(s: *const c_char) -> *mut c_char; +} + +fn should_not_reach() { + _ // FIXME: replace this function with panic when that works +} + +fn main() { + let result = getenv(b"foo\0" as *const _); + if *result != b'b' { + should_not_reach(); + } + let result = (result as usize + 1) as *const c_char; + if *result != b'a' { + should_not_reach(); + } + let result = (result as usize + 1) as *const c_char; + if *result != b'r' { + should_not_reach(); + } + let result = (result as usize + 1) as *const c_char; + if *result != 0 { + should_not_reach(); + } + let result = getenv(b"not found\0" as *const _); + if result as usize != 0 { + should_not_reach(); + } +} +"#, + ); +} + +#[test] fn posix_tls() { check_pass( r#" diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs index 718df8331e2..51cf882d053 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs @@ -15,7 +15,7 @@ use hir_def::{ path::Path, resolver::{resolver_for_expr, HasResolver, ResolveValueResult, ValueNs}, AdtId, DefWithBodyId, EnumVariantId, GeneralConstId, HasModule, ItemContainerId, LocalFieldId, - TraitId, TypeOrConstParamId, + Lookup, TraitId, TypeOrConstParamId, }; use hir_expand::name::Name; use la_arena::ArenaMap; @@ -372,7 +372,7 @@ impl<'ctx> MirLowerCtx<'ctx> { match &self.body.exprs[expr_id] { Expr::Missing => { if let DefWithBodyId::FunctionId(f) = self.owner { - let assoc = self.db.lookup_intern_function(f); + let assoc = f.lookup(self.db.upcast()); if let ItemContainerId::TraitId(t) = assoc.container { let name = &self.db.function_data(f).name; return Err(MirLowerError::TraitFunctionDefinition(t, name.clone())); @@ -1244,6 +1244,41 @@ impl<'ctx> MirLowerCtx<'ctx> { } } + fn lower_destructing_assignment( + &mut self, + mut current: BasicBlockId, + lhs: ExprId, + rhs: Place, + span: MirSpan, + ) -> Result<Option<BasicBlockId>> { + match &self.body.exprs[lhs] { + Expr::Tuple { exprs, is_assignee_expr: _ } => { + for (i, expr) in exprs.iter().enumerate() { + let Some(c) = self.lower_destructing_assignment( + current, + *expr, + rhs.project(ProjectionElem::TupleOrClosureField(i)), + span, + )? else { + return Ok(None); + }; + current = c; + } + Ok(Some(current)) + } + Expr::Underscore => Ok(Some(current)), + _ => { + let Some((lhs_place, current)) = + self.lower_expr_as_place(current, lhs, false)? + else { + return Ok(None); + }; + self.push_assignment(current, lhs_place, Operand::Copy(rhs).into(), span); + Ok(Some(current)) + } + } + } + fn lower_assignment( &mut self, current: BasicBlockId, @@ -1259,6 +1294,15 @@ impl<'ctx> MirLowerCtx<'ctx> { if matches!(&self.body.exprs[lhs], Expr::Underscore) { return Ok(Some(current)); } + if matches!( + &self.body.exprs[lhs], + Expr::Tuple { .. } | Expr::RecordLit { .. } | Expr::Call { .. } + ) { + let temp = self.temp(self.expr_ty_after_adjustments(rhs), current, rhs.into())?; + let temp = Place::from(temp); + self.push_assignment(current, temp.clone(), rhs_op.into(), span); + return self.lower_destructing_assignment(current, lhs, temp, span); + } let Some((lhs_place, current)) = self.lower_expr_as_place(current, lhs, false)? else { @@ -1308,14 +1352,14 @@ impl<'ctx> MirLowerCtx<'ctx> { .resolve_path_in_value_ns(self.db.upcast(), c) .ok_or_else(unresolved_name)?; match pr { - ResolveValueResult::ValueNs(v) => { + ResolveValueResult::ValueNs(v, _) => { if let ValueNs::ConstId(c) = v { self.lower_const_to_operand(Substitution::empty(Interner), c.into(), ty) } else { not_supported!("bad path in range pattern"); } } - ResolveValueResult::Partial(_, _) => { + ResolveValueResult::Partial(_, _, _) => { not_supported!("associated constants in range pattern") } } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/pattern_matching.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/pattern_matching.rs index 3354cbd76a0..1cdfd919742 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/pattern_matching.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/pattern_matching.rs @@ -323,7 +323,7 @@ impl MirLowerCtx<'_> { break 'b (c, x.1); } } - if let ResolveValueResult::ValueNs(v) = pr { + if let ResolveValueResult::ValueNs(v, _) = pr { if let ValueNs::ConstId(c) = v { break 'b (c, Substitution::empty(Interner)); } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/monomorphization.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/monomorphization.rs index c565228d91e..df16d0d8201 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/monomorphization.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/monomorphization.rs @@ -265,7 +265,7 @@ impl Filler<'_> { self.fill_operand(discr)?; } TerminatorKind::Goto { .. } - | TerminatorKind::Resume + | TerminatorKind::UnwindResume | TerminatorKind::Abort | TerminatorKind::Return | TerminatorKind::Unreachable diff --git a/src/tools/rust-analyzer/crates/hir/src/attrs.rs b/src/tools/rust-analyzer/crates/hir/src/attrs.rs index 0f2fb2c8118..121e5a0a249 100644 --- a/src/tools/rust-analyzer/crates/hir/src/attrs.rs +++ b/src/tools/rust-analyzer/crates/hir/src/attrs.rs @@ -3,18 +3,19 @@ use hir_def::{ attr::{AttrsWithOwner, Documentation}, item_scope::ItemInNs, - path::ModPath, - resolver::HasResolver, - AttrDefId, GenericParamId, ModuleDefId, + path::{ModPath, Path}, + per_ns::Namespace, + resolver::{HasResolver, Resolver, TypeNs}, + AssocItemId, AttrDefId, GenericParamId, ModuleDefId, }; -use hir_expand::hygiene::Hygiene; +use hir_expand::{hygiene::Hygiene, name::Name}; use hir_ty::db::HirDatabase; use syntax::{ast, AstNode}; use crate::{ - Adt, AssocItem, Const, ConstParam, Enum, ExternCrateDecl, Field, Function, GenericParam, Impl, - LifetimeParam, Macro, Module, ModuleDef, Static, Struct, Trait, TraitAlias, TypeAlias, - TypeParam, Union, Variant, + Adt, AsAssocItem, AssocItem, BuiltinType, Const, ConstParam, Enum, ExternCrateDecl, Field, + Function, GenericParam, Impl, LifetimeParam, Macro, Module, ModuleDef, Static, Struct, Trait, + TraitAlias, TypeAlias, TypeParam, Union, Variant, VariantDef, }; pub trait HasAttrs { @@ -25,14 +26,14 @@ pub trait HasAttrs { db: &dyn HirDatabase, link: &str, ns: Option<Namespace>, - ) -> Option<ModuleDef>; + ) -> Option<DocLinkDef>; } -#[derive(PartialEq, Eq, Hash, Copy, Clone, Debug)] -pub enum Namespace { - Types, - Values, - Macros, +/// Subset of `ide_db::Definition` that doc links can resolve to. +pub enum DocLinkDef { + ModuleDef(ModuleDef), + Field(Field), + SelfType(Trait), } macro_rules! impl_has_attrs { @@ -46,9 +47,14 @@ macro_rules! impl_has_attrs { let def = AttrDefId::$def_id(self.into()); db.attrs(def).docs() } - fn resolve_doc_path(self, db: &dyn HirDatabase, link: &str, ns: Option<Namespace>) -> Option<ModuleDef> { + fn resolve_doc_path( + self, + db: &dyn HirDatabase, + link: &str, + ns: Option<Namespace> + ) -> Option<DocLinkDef> { let def = AttrDefId::$def_id(self.into()); - resolve_doc_path(db, def, link, ns).map(ModuleDef::from) + resolve_doc_path(db, def, link, ns) } } )*}; @@ -79,7 +85,12 @@ macro_rules! impl_has_attrs_enum { fn docs(self, db: &dyn HirDatabase) -> Option<Documentation> { $enum::$variant(self).docs(db) } - fn resolve_doc_path(self, db: &dyn HirDatabase, link: &str, ns: Option<Namespace>) -> Option<ModuleDef> { + fn resolve_doc_path( + self, + db: &dyn HirDatabase, + link: &str, + ns: Option<Namespace> + ) -> Option<DocLinkDef> { $enum::$variant(self).resolve_doc_path(db, link, ns) } } @@ -111,7 +122,7 @@ impl HasAttrs for AssocItem { db: &dyn HirDatabase, link: &str, ns: Option<Namespace>, - ) -> Option<ModuleDef> { + ) -> Option<DocLinkDef> { match self { AssocItem::Function(it) => it.resolve_doc_path(db, link, ns), AssocItem::Const(it) => it.resolve_doc_path(db, link, ns), @@ -147,9 +158,9 @@ impl HasAttrs for ExternCrateDecl { db: &dyn HirDatabase, link: &str, ns: Option<Namespace>, - ) -> Option<ModuleDef> { + ) -> Option<DocLinkDef> { let def = AttrDefId::ExternCrateId(self.into()); - resolve_doc_path(db, def, link, ns).map(ModuleDef::from) + resolve_doc_path(db, def, link, ns) } } @@ -159,7 +170,7 @@ fn resolve_doc_path( def: AttrDefId, link: &str, ns: Option<Namespace>, -) -> Option<ModuleDefId> { +) -> Option<DocLinkDef> { let resolver = match def { AttrDefId::ModuleId(it) => it.resolver(db.upcast()), AttrDefId::FieldId(it) => it.parent.resolver(db.upcast()), @@ -184,8 +195,107 @@ fn resolve_doc_path( .resolver(db.upcast()), }; - let modpath = { - // FIXME: this is not how we should get a mod path here + let mut modpath = modpath_from_str(db, link)?; + + let resolved = resolver.resolve_module_path_in_items(db.upcast(), &modpath); + if resolved.is_none() { + let last_name = modpath.pop_segment()?; + resolve_assoc_or_field(db, resolver, modpath, last_name, ns) + } else { + let def = match ns { + Some(Namespace::Types) => resolved.take_types(), + Some(Namespace::Values) => resolved.take_values(), + Some(Namespace::Macros) => resolved.take_macros().map(ModuleDefId::MacroId), + None => resolved.iter_items().next().map(|(it, _)| match it { + ItemInNs::Types(it) => it, + ItemInNs::Values(it) => it, + ItemInNs::Macros(it) => ModuleDefId::MacroId(it), + }), + }; + Some(DocLinkDef::ModuleDef(def?.into())) + } +} + +fn resolve_assoc_or_field( + db: &dyn HirDatabase, + resolver: Resolver, + path: ModPath, + name: Name, + ns: Option<Namespace>, +) -> Option<DocLinkDef> { + let path = Path::from_known_path_with_no_generic(path); + // FIXME: This does not handle `Self` on trait definitions, which we should resolve to the + // trait itself. + let base_def = resolver.resolve_path_in_type_ns_fully(db.upcast(), &path)?; + + let ty = match base_def { + TypeNs::SelfType(id) => Impl::from(id).self_ty(db), + TypeNs::GenericParam(_) => { + // Even if this generic parameter has some trait bounds, rustdoc doesn't + // resolve `name` to trait items. + return None; + } + TypeNs::AdtId(id) | TypeNs::AdtSelfType(id) => Adt::from(id).ty(db), + TypeNs::EnumVariantId(id) => { + // Enum variants don't have path candidates. + let variant = Variant::from(id); + return resolve_field(db, variant.into(), name, ns); + } + TypeNs::TypeAliasId(id) => { + let alias = TypeAlias::from(id); + if alias.as_assoc_item(db).is_some() { + // We don't normalize associated type aliases, so we have nothing to + // resolve `name` to. + return None; + } + alias.ty(db) + } + TypeNs::BuiltinType(id) => BuiltinType::from(id).ty(db), + TypeNs::TraitId(id) => { + // Doc paths in this context may only resolve to an item of this trait + // (i.e. no items of its supertraits), so we need to handle them here + // independently of others. + return db.trait_data(id).items.iter().find(|it| it.0 == name).map(|(_, assoc_id)| { + let def = match *assoc_id { + AssocItemId::FunctionId(it) => ModuleDef::Function(it.into()), + AssocItemId::ConstId(it) => ModuleDef::Const(it.into()), + AssocItemId::TypeAliasId(it) => ModuleDef::TypeAlias(it.into()), + }; + DocLinkDef::ModuleDef(def) + }); + } + TypeNs::TraitAliasId(_) => { + // XXX: Do these get resolved? + return None; + } + }; + + // FIXME: Resolve associated items here, e.g. `Option::map`. Note that associated items take + // precedence over fields. + + let variant_def = match ty.as_adt()? { + Adt::Struct(it) => it.into(), + Adt::Union(it) => it.into(), + Adt::Enum(_) => return None, + }; + resolve_field(db, variant_def, name, ns) +} + +fn resolve_field( + db: &dyn HirDatabase, + def: VariantDef, + name: Name, + ns: Option<Namespace>, +) -> Option<DocLinkDef> { + if let Some(Namespace::Types | Namespace::Macros) = ns { + return None; + } + def.fields(db).into_iter().find(|f| f.name(db) == name).map(DocLinkDef::Field) +} + +fn modpath_from_str(db: &dyn HirDatabase, link: &str) -> Option<ModPath> { + // FIXME: this is not how we should get a mod path here. + let try_get_modpath = |link: &str| { let ast_path = ast::SourceFile::parse(&format!("type T = {link};")) .syntax_node() .descendants() @@ -193,23 +303,20 @@ fn resolve_doc_path( if ast_path.syntax().text() != link { return None; } - ModPath::from_src(db.upcast(), ast_path, &Hygiene::new_unhygienic())? + ModPath::from_src(db.upcast(), ast_path, &Hygiene::new_unhygienic()) }; - let resolved = resolver.resolve_module_path_in_items(db.upcast(), &modpath); - let resolved = if resolved.is_none() { - resolver.resolve_module_path_in_trait_assoc_items(db.upcast(), &modpath)? - } else { - resolved - }; - match ns { - Some(Namespace::Types) => resolved.take_types(), - Some(Namespace::Values) => resolved.take_values(), - Some(Namespace::Macros) => resolved.take_macros().map(ModuleDefId::MacroId), - None => resolved.iter_items().next().map(|it| match it { - ItemInNs::Types(it) => it, - ItemInNs::Values(it) => it, - ItemInNs::Macros(it) => ModuleDefId::MacroId(it), - }), + let full = try_get_modpath(link); + if full.is_some() { + return full; } + + // Tuple field names cannot be a part of `ModPath` usually, but rustdoc can + // resolve doc paths like `TupleStruct::0`. + // FIXME: Find a better way to handle these. + let (base, maybe_tuple_field) = link.rsplit_once("::")?; + let tuple_field = Name::new_tuple_field(maybe_tuple_field.parse().ok()?); + let mut modpath = try_get_modpath(base)?; + modpath.push_segment(tuple_field); + Some(modpath) } diff --git a/src/tools/rust-analyzer/crates/hir/src/display.rs b/src/tools/rust-analyzer/crates/hir/src/display.rs index 9dfb98e459b..ac171026d5d 100644 --- a/src/tools/rust-analyzer/crates/hir/src/display.rs +++ b/src/tools/rust-analyzer/crates/hir/src/display.rs @@ -8,7 +8,6 @@ use hir_def::{ type_ref::{TypeBound, TypeRef}, AdtId, GenericDefId, }; -use hir_expand::name; use hir_ty::{ display::{ write_bounds_like_dyn_trait_with_prefix, write_visibility, HirDisplay, HirDisplayError, @@ -19,8 +18,9 @@ use hir_ty::{ use crate::{ Adt, AsAssocItem, AssocItemContainer, Const, ConstParam, Enum, ExternCrateDecl, Field, - Function, GenericParam, HasCrate, HasVisibility, LifetimeParam, Macro, Module, Static, Struct, - Trait, TraitAlias, TyBuilder, Type, TypeAlias, TypeOrConstParam, TypeParam, Union, Variant, + Function, GenericParam, HasCrate, HasVisibility, LifetimeParam, Macro, Module, SelfParam, + Static, Struct, Trait, TraitAlias, TyBuilder, Type, TypeAlias, TypeOrConstParam, TypeParam, + Union, Variant, }; impl HirDisplay for Function { @@ -57,37 +57,21 @@ impl HirDisplay for Function { f.write_char('(')?; - let write_self_param = |ty: &TypeRef, f: &mut HirFormatter<'_>| match ty { - TypeRef::Path(p) if p.is_self_type() => f.write_str("self"), - TypeRef::Reference(inner, lifetime, mut_) if matches!(&**inner, TypeRef::Path(p) if p.is_self_type()) => - { - f.write_char('&')?; - if let Some(lifetime) = lifetime { - write!(f, "{} ", lifetime.name.display(f.db.upcast()))?; - } - if let hir_def::type_ref::Mutability::Mut = mut_ { - f.write_str("mut ")?; - } - f.write_str("self") - } - _ => { - f.write_str("self: ")?; - ty.hir_fmt(f) - } - }; - let mut first = true; + let mut skip_self = 0; + if let Some(self_param) = self.self_param(db) { + self_param.hir_fmt(f)?; + first = false; + skip_self = 1; + } + // FIXME: Use resolved `param.ty` once we no longer discard lifetimes - for (type_ref, param) in data.params.iter().zip(self.assoc_fn_params(db)) { + for (type_ref, param) in data.params.iter().zip(self.assoc_fn_params(db)).skip(skip_self) { let local = param.as_local(db).map(|it| it.name(db)); if !first { f.write_str(", ")?; } else { first = false; - if local == Some(name!(self)) { - write_self_param(type_ref, f)?; - continue; - } } match local { Some(name) => write!(f, "{}: ", name.display(f.db.upcast()))?, @@ -137,6 +121,31 @@ impl HirDisplay for Function { } } +impl HirDisplay for SelfParam { + fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> { + let data = f.db.function_data(self.func); + let param = data.params.first().unwrap(); + match &**param { + TypeRef::Path(p) if p.is_self_type() => f.write_str("self"), + TypeRef::Reference(inner, lifetime, mut_) if matches!(&**inner, TypeRef::Path(p) if p.is_self_type()) => + { + f.write_char('&')?; + if let Some(lifetime) = lifetime { + write!(f, "{} ", lifetime.name.display(f.db.upcast()))?; + } + if let hir_def::type_ref::Mutability::Mut = mut_ { + f.write_str("mut ")?; + } + f.write_str("self") + } + ty => { + f.write_str("self: ")?; + ty.hir_fmt(f) + } + } + } +} + impl HirDisplay for Adt { fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> { match self { @@ -357,6 +366,11 @@ fn write_generic_params( delim(f)?; write!(f, "const {}: ", name.display(f.db.upcast()))?; c.ty.hir_fmt(f)?; + + if let Some(default) = &c.default { + f.write_str(" = ")?; + write!(f, "{}", default.display(f.db.upcast()))?; + } } } } diff --git a/src/tools/rust-analyzer/crates/hir/src/lib.rs b/src/tools/rust-analyzer/crates/hir/src/lib.rs index bf041b61f2f..512fe7e0428 100644 --- a/src/tools/rust-analyzer/crates/hir/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir/src/lib.rs @@ -63,12 +63,13 @@ use hir_ty::{ all_super_traits, autoderef, consteval::{try_const_usize, unknown_const_as_generic, ConstEvalError, ConstExt}, diagnostics::BodyValidationDiagnostic, + known_const_to_ast, layout::{Layout as TyLayout, RustcEnumVariantIdx, TagEncoding}, method_resolution::{self, TyFingerprint}, mir::{self, interpret_mir}, primitive::UintTy, traits::FnTrait, - AliasTy, CallableDefId, CallableSig, Canonical, CanonicalVarKinds, Cast, ClosureId, + AliasTy, CallableDefId, CallableSig, Canonical, CanonicalVarKinds, Cast, ClosureId, GenericArg, GenericArgData, Interner, ParamKind, QuantifiedWhereClause, Scalar, Substitution, TraitEnvironment, TraitRefExt, Ty, TyBuilder, TyDefId, TyExt, TyKind, ValueTyDefId, WhereClause, @@ -87,7 +88,7 @@ use triomphe::Arc; use crate::db::{DefDatabase, HirDatabase}; pub use crate::{ - attrs::{HasAttrs, Namespace}, + attrs::{DocLinkDef, HasAttrs}, diagnostics::{ AnyDiagnostic, BreakOutsideOfLoop, CaseType, ExpectedFunction, InactiveCode, IncoherentImpl, IncorrectCase, InvalidDeriveTarget, MacroDefError, MacroError, @@ -121,6 +122,7 @@ pub use { lang_item::LangItem, nameres::{DefMap, ModuleSource}, path::{ModPath, PathKind}, + per_ns::Namespace, type_ref::{Mutability, TypeRef}, visibility::Visibility, // FIXME: This is here since some queries take it as input that are used @@ -719,20 +721,18 @@ fn emit_def_diagnostic_( ) { match diag { DefDiagnosticKind::UnresolvedModule { ast: declaration, candidates } => { - let decl = declaration.to_node(db.upcast()); + let decl = declaration.to_ptr(db.upcast()); acc.push( UnresolvedModule { - decl: InFile::new(declaration.file_id, AstPtr::new(&decl)), + decl: InFile::new(declaration.file_id, decl), candidates: candidates.clone(), } .into(), ) } DefDiagnosticKind::UnresolvedExternCrate { ast } => { - let item = ast.to_node(db.upcast()); - acc.push( - UnresolvedExternCrate { decl: InFile::new(ast.file_id, AstPtr::new(&item)) }.into(), - ); + let item = ast.to_ptr(db.upcast()); + acc.push(UnresolvedExternCrate { decl: InFile::new(ast.file_id, item) }.into()); } DefDiagnosticKind::UnresolvedImport { id, index } => { @@ -747,14 +747,10 @@ fn emit_def_diagnostic_( } DefDiagnosticKind::UnconfiguredCode { ast, cfg, opts } => { - let item = ast.to_node(db.upcast()); + let item = ast.to_ptr(db.upcast()); acc.push( - InactiveCode { - node: ast.with_value(SyntaxNodePtr::new(&item).into()), - cfg: cfg.clone(), - opts: opts.clone(), - } - .into(), + InactiveCode { node: ast.with_value(item), cfg: cfg.clone(), opts: opts.clone() } + .into(), ); } DefDiagnosticKind::UnresolvedProcMacro { ast, krate } => { @@ -1273,7 +1269,7 @@ impl Adt { .fill(|x| { let r = it.next().unwrap_or_else(|| TyKind::Error.intern(Interner)); match x { - ParamKind::Type => GenericArgData::Ty(r).intern(Interner), + ParamKind::Type => r.cast(Interner), ParamKind::Const(ty) => unknown_const_as_generic(ty.clone()), } }) @@ -2096,14 +2092,6 @@ impl SelfParam { .unwrap_or(Access::Owned) } - pub fn display(self, db: &dyn HirDatabase) -> &'static str { - match self.access(db) { - Access::Shared => "&self", - Access::Exclusive => "&mut self", - Access::Owned => "self", - } - } - pub fn source(&self, db: &dyn HirDatabase) -> Option<InFile<ast::SelfParam>> { let InFile { file_id, value } = Function::from(self.func).source(db)?; value @@ -3142,12 +3130,8 @@ impl TypeParam { } pub fn default(self, db: &dyn HirDatabase) -> Option<Type> { - let params = db.generic_defaults(self.id.parent()); - let local_idx = hir_ty::param_idx(db, self.id.into())?; + let ty = generic_arg_from_param(db, self.id.into())?; let resolver = self.id.parent().resolver(db.upcast()); - let ty = params.get(local_idx)?.clone(); - let subst = TyBuilder::placeholder_subst(db, self.id.parent()); - let ty = ty.substitute(Interner, &subst); match ty.data(Interner) { GenericArgData::Ty(it) => { Some(Type::new_with_resolver_inner(db, &resolver, it.clone())) @@ -3209,6 +3193,19 @@ impl ConstParam { pub fn ty(self, db: &dyn HirDatabase) -> Type { Type::new(db, self.id.parent(), db.const_param_ty(self.id)) } + + pub fn default(self, db: &dyn HirDatabase) -> Option<ast::ConstArg> { + let arg = generic_arg_from_param(db, self.id.into())?; + known_const_to_ast(arg.constant(Interner)?, db) + } +} + +fn generic_arg_from_param(db: &dyn HirDatabase, id: TypeOrConstParamId) -> Option<GenericArg> { + let params = db.generic_defaults(id.parent); + let local_idx = hir_ty::param_idx(db, id)?; + let ty = params.get(local_idx)?.clone(); + let subst = TyBuilder::placeholder_subst(db, id.parent); + Some(ty.substitute(Interner, &subst)) } #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] @@ -3716,7 +3713,7 @@ impl Type { .fill(|x| { let r = it.next().unwrap(); match x { - ParamKind::Type => GenericArgData::Ty(r).intern(Interner), + ParamKind::Type => r.cast(Interner), ParamKind::Const(ty) => { // FIXME: this code is not covered in tests. unknown_const_as_generic(ty.clone()) @@ -3749,9 +3746,7 @@ impl Type { .fill(|it| { // FIXME: this code is not covered in tests. match it { - ParamKind::Type => { - GenericArgData::Ty(args.next().unwrap().ty.clone()).intern(Interner) - } + ParamKind::Type => args.next().unwrap().ty.clone().cast(Interner), ParamKind::Const(ty) => unknown_const_as_generic(ty.clone()), } }) @@ -4414,14 +4409,13 @@ impl Callable { Other => CallableKind::Other, } } - pub fn receiver_param(&self, db: &dyn HirDatabase) -> Option<(ast::SelfParam, Type)> { + pub fn receiver_param(&self, db: &dyn HirDatabase) -> Option<(SelfParam, Type)> { let func = match self.callee { Callee::Def(CallableDefId::FunctionId(it)) if self.is_bound_method => it, _ => return None, }; - let src = func.lookup(db.upcast()).source(db.upcast()); - let param_list = src.value.param_list()?; - Some((param_list.self_param()?, self.ty.derived(self.sig.params()[0].clone()))) + let func = Function { id: func }; + Some((func.self_param(db)?, self.ty.derived(self.sig.params()[0].clone()))) } pub fn n_params(&self) -> usize { self.sig.params().len() - if self.is_bound_method { 1 } else { 0 } diff --git a/src/tools/rust-analyzer/crates/hir/src/semantics.rs b/src/tools/rust-analyzer/crates/hir/src/semantics.rs index e99d2984c36..b8d4ecd4414 100644 --- a/src/tools/rust-analyzer/crates/hir/src/semantics.rs +++ b/src/tools/rust-analyzer/crates/hir/src/semantics.rs @@ -170,6 +170,8 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> { self.imp.is_derive_annotated(item) } + /// Expand the macro call with a different token tree, mapping the `token_to_map` down into the + /// expansion. `token_to_map` should be a token from the `speculative args` node. pub fn speculative_expand( &self, actual_macro_call: &ast::MacroCall, @@ -179,6 +181,8 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> { self.imp.speculative_expand(actual_macro_call, speculative_args, token_to_map) } + /// Expand the macro call with a different item as the input, mapping the `token_to_map` down into the + /// expansion. `token_to_map` should be a token from the `speculative args` node. pub fn speculative_expand_attr_macro( &self, actual_macro_call: &ast::Item, @@ -201,14 +205,22 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> { ) } - /// Descend the token into macrocalls to its first mapped counterpart. - pub fn descend_into_macros_single(&self, token: SyntaxToken) -> SyntaxToken { - self.imp.descend_into_macros_single(token) + /// Descend the token into its macro call if it is part of one, returning the token in the + /// expansion that it is associated with. If `offset` points into the token's range, it will + /// be considered for the mapping in case of inline format args. + pub fn descend_into_macros_single(&self, token: SyntaxToken, offset: TextSize) -> SyntaxToken { + self.imp.descend_into_macros_single(token, offset) } - /// Descend the token into macrocalls to all its mapped counterparts. - pub fn descend_into_macros(&self, token: SyntaxToken) -> SmallVec<[SyntaxToken; 1]> { - self.imp.descend_into_macros(token) + /// Descend the token into its macro call if it is part of one, returning the tokens in the + /// expansion that it is associated with. If `offset` points into the token's range, it will + /// be considered for the mapping in case of inline format args. + pub fn descend_into_macros( + &self, + token: SyntaxToken, + offset: TextSize, + ) -> SmallVec<[SyntaxToken; 1]> { + self.imp.descend_into_macros(token, offset) } /// Descend the token into macrocalls to all its mapped counterparts that have the same text as the input token. @@ -217,12 +229,17 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> { pub fn descend_into_macros_with_same_text( &self, token: SyntaxToken, + offset: TextSize, ) -> SmallVec<[SyntaxToken; 1]> { - self.imp.descend_into_macros_with_same_text(token) + self.imp.descend_into_macros_with_same_text(token, offset) } - pub fn descend_into_macros_with_kind_preference(&self, token: SyntaxToken) -> SyntaxToken { - self.imp.descend_into_macros_with_kind_preference(token) + pub fn descend_into_macros_with_kind_preference( + &self, + token: SyntaxToken, + offset: TextSize, + ) -> SyntaxToken { + self.imp.descend_into_macros_with_kind_preference(token, offset) } /// Maps a node down by mapping its first and last token down. @@ -606,7 +623,7 @@ impl<'db> SemanticsImpl<'db> { let macro_call_id = macro_call.as_call_id(self.db.upcast(), krate, |path| { resolver .resolve_path_as_macro(self.db.upcast(), &path, Some(MacroSubNs::Bang)) - .map(|it| macro_id_to_def_id(self.db.upcast(), it)) + .map(|(it, _)| macro_id_to_def_id(self.db.upcast(), it)) })?; hir_expand::db::expand_speculative( self.db.upcast(), @@ -665,7 +682,7 @@ impl<'db> SemanticsImpl<'db> { }; if first == last { - self.descend_into_macros_impl(first, &mut |InFile { value, .. }| { + self.descend_into_macros_impl(first, 0.into(), &mut |InFile { value, .. }| { if let Some(node) = value.parent_ancestors().find_map(N::cast) { res.push(node) } @@ -674,7 +691,7 @@ impl<'db> SemanticsImpl<'db> { } else { // Descend first and last token, then zip them to look for the node they belong to let mut scratch: SmallVec<[_; 1]> = smallvec![]; - self.descend_into_macros_impl(first, &mut |token| { + self.descend_into_macros_impl(first, 0.into(), &mut |token| { scratch.push(token); false }); @@ -682,6 +699,7 @@ impl<'db> SemanticsImpl<'db> { let mut scratch = scratch.into_iter(); self.descend_into_macros_impl( last, + 0.into(), &mut |InFile { value: last, file_id: last_fid }| { if let Some(InFile { value: first, file_id: first_fid }) = scratch.next() { if first_fid == last_fid { @@ -705,19 +723,27 @@ impl<'db> SemanticsImpl<'db> { res } - fn descend_into_macros(&self, token: SyntaxToken) -> SmallVec<[SyntaxToken; 1]> { + fn descend_into_macros( + &self, + token: SyntaxToken, + offset: TextSize, + ) -> SmallVec<[SyntaxToken; 1]> { let mut res = smallvec![]; - self.descend_into_macros_impl(token, &mut |InFile { value, .. }| { + self.descend_into_macros_impl(token, offset, &mut |InFile { value, .. }| { res.push(value); false }); res } - fn descend_into_macros_with_same_text(&self, token: SyntaxToken) -> SmallVec<[SyntaxToken; 1]> { + fn descend_into_macros_with_same_text( + &self, + token: SyntaxToken, + offset: TextSize, + ) -> SmallVec<[SyntaxToken; 1]> { let text = token.text(); let mut res = smallvec![]; - self.descend_into_macros_impl(token.clone(), &mut |InFile { value, .. }| { + self.descend_into_macros_impl(token.clone(), offset, &mut |InFile { value, .. }| { if value.text() == text { res.push(value); } @@ -729,7 +755,11 @@ impl<'db> SemanticsImpl<'db> { res } - fn descend_into_macros_with_kind_preference(&self, token: SyntaxToken) -> SyntaxToken { + fn descend_into_macros_with_kind_preference( + &self, + token: SyntaxToken, + offset: TextSize, + ) -> SyntaxToken { let fetch_kind = |token: &SyntaxToken| match token.parent() { Some(node) => match node.kind() { kind @ (SyntaxKind::NAME | SyntaxKind::NAME_REF) => { @@ -741,7 +771,7 @@ impl<'db> SemanticsImpl<'db> { }; let preferred_kind = fetch_kind(&token); let mut res = None; - self.descend_into_macros_impl(token.clone(), &mut |InFile { value, .. }| { + self.descend_into_macros_impl(token.clone(), offset, &mut |InFile { value, .. }| { if fetch_kind(&value) == preferred_kind { res = Some(value); true @@ -755,9 +785,9 @@ impl<'db> SemanticsImpl<'db> { res.unwrap_or(token) } - fn descend_into_macros_single(&self, token: SyntaxToken) -> SyntaxToken { + fn descend_into_macros_single(&self, token: SyntaxToken, offset: TextSize) -> SyntaxToken { let mut res = token.clone(); - self.descend_into_macros_impl(token, &mut |InFile { value, .. }| { + self.descend_into_macros_impl(token, offset, &mut |InFile { value, .. }| { res = value; true }); @@ -767,9 +797,13 @@ impl<'db> SemanticsImpl<'db> { fn descend_into_macros_impl( &self, token: SyntaxToken, + // FIXME: We might want this to be Option<TextSize> to be able to opt out of subrange + // mapping, specifically for node downmapping + offset: TextSize, f: &mut dyn FnMut(InFile<SyntaxToken>) -> bool, ) { let _p = profile::span("descend_into_macros"); + let relative_token_offset = token.text_range().start().checked_sub(offset); let parent = match token.parent() { Some(it) => it, None => return, @@ -796,7 +830,12 @@ impl<'db> SemanticsImpl<'db> { self.cache(value, file_id); } - let mapped_tokens = expansion_info.map_token_down(self.db.upcast(), item, token)?; + let mapped_tokens = expansion_info.map_token_down( + self.db.upcast(), + item, + token, + relative_token_offset, + )?; let len = stack.len(); // requeue the tokens we got from mapping our current token down @@ -943,7 +982,7 @@ impl<'db> SemanticsImpl<'db> { offset: TextSize, ) -> impl Iterator<Item = impl Iterator<Item = SyntaxNode> + '_> + '_ { node.token_at_offset(offset) - .map(move |token| self.descend_into_macros(token)) + .map(move |token| self.descend_into_macros(token, offset)) .map(|descendants| { descendants.into_iter().map(move |it| self.token_ancestors_with_macros(it)) }) @@ -1683,6 +1722,14 @@ impl SemanticsScope<'_> { |name, id| cb(name, id.into()), ) } + + pub fn extern_crates(&self) -> impl Iterator<Item = (Name, Module)> + '_ { + self.resolver.extern_crates_in_scope().map(|(name, id)| (name, Module { id })) + } + + pub fn extern_crate_decls(&self) -> impl Iterator<Item = Name> + '_ { + self.resolver.extern_crate_decls_in_scope(self.db.upcast()) + } } #[derive(Debug)] diff --git a/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs b/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs index 3499daf1140..f29fb1edf00 100644 --- a/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs +++ b/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs @@ -487,7 +487,7 @@ impl SourceAnalyzer { let path = macro_call.value.path().and_then(|ast| Path::from_src(ast, &ctx))?; self.resolver .resolve_path_as_macro(db.upcast(), path.mod_path()?, Some(MacroSubNs::Bang)) - .map(|it| it.into()) + .map(|(it, _)| it.into()) } pub(crate) fn resolve_bind_pat_to_const( @@ -760,7 +760,7 @@ impl SourceAnalyzer { let macro_call_id = macro_call.as_call_id(db.upcast(), krate, |path| { self.resolver .resolve_path_as_macro(db.upcast(), &path, Some(MacroSubNs::Bang)) - .map(|it| macro_id_to_def_id(db.upcast(), it)) + .map(|(it, _)| macro_id_to_def_id(db.upcast(), it)) })?; Some(macro_call_id.as_file()).filter(|it| it.expansion_level(db.upcast()) < 64) } @@ -966,6 +966,7 @@ pub(crate) fn resolve_hir_path_as_attr_macro( ) -> Option<Macro> { resolver .resolve_path_as_macro(db.upcast(), path.mod_path()?, Some(MacroSubNs::Attr)) + .map(|(it, _)| it) .map(Into::into) } @@ -983,7 +984,7 @@ fn resolve_hir_path_( res.map(|ty_ns| (ty_ns, path.segments().first())) } None => { - let (ty, remaining_idx) = resolver.resolve_path_in_type_ns(db.upcast(), path)?; + let (ty, remaining_idx, _) = resolver.resolve_path_in_type_ns(db.upcast(), path)?; match remaining_idx { Some(remaining_idx) => { if remaining_idx + 1 == path.segments().len() { @@ -1067,7 +1068,7 @@ fn resolve_hir_path_( let macros = || { resolver .resolve_path_as_macro(db.upcast(), path.mod_path()?, None) - .map(|def| PathResolution::Def(ModuleDef::Macro(def.into()))) + .map(|(def, _)| PathResolution::Def(ModuleDef::Macro(def.into()))) }; if prefer_value_ns { values().or_else(types) } else { types().or_else(values) } diff --git a/src/tools/rust-analyzer/crates/hir/src/symbols.rs b/src/tools/rust-analyzer/crates/hir/src/symbols.rs index 43d957412bc..ca7874c3683 100644 --- a/src/tools/rust-analyzer/crates/hir/src/symbols.rs +++ b/src/tools/rust-analyzer/crates/hir/src/symbols.rs @@ -2,8 +2,10 @@ use base_db::FileRange; use hir_def::{ - src::HasSource, AdtId, AssocItemId, DefWithBodyId, HasModule, ImplId, Lookup, MacroId, - ModuleDefId, ModuleId, TraitId, + item_scope::ItemInNs, + src::{HasChildSource, HasSource}, + AdtId, AssocItemId, DefWithBodyId, HasModule, ImplId, Lookup, MacroId, ModuleDefId, ModuleId, + TraitId, }; use hir_expand::{HirFileId, InFile}; use hir_ty::db::HirDatabase; @@ -167,6 +169,40 @@ impl<'a> SymbolCollector<'a> { self.collect_from_impl(impl_id); } + // Record renamed imports. + // In case it imports multiple items under different namespaces we just pick one arbitrarily + // for now. + for id in scope.imports() { + let loc = id.import.lookup(self.db.upcast()); + loc.id.item_tree(self.db.upcast()); + let source = id.import.child_source(self.db.upcast()); + let Some(use_tree_src) = source.value.get(id.idx) else { continue }; + let Some(rename) = use_tree_src.rename() else { continue }; + let Some(name) = rename.name() else { continue }; + + let res = scope.fully_resolve_import(self.db.upcast(), id); + res.iter_items().for_each(|(item, _)| { + let def = match item { + ItemInNs::Types(def) | ItemInNs::Values(def) => def, + ItemInNs::Macros(def) => ModuleDefId::from(def), + } + .into(); + let dec_loc = DeclarationLocation { + hir_file_id: source.file_id, + ptr: SyntaxNodePtr::new(use_tree_src.syntax()), + name_ptr: SyntaxNodePtr::new(name.syntax()), + }; + + self.symbols.push(FileSymbol { + name: name.text().into(), + def, + container_name: self.current_container_name.clone(), + loc: dec_loc, + is_alias: false, + }); + }); + } + for const_id in scope.unnamed_consts() { self.collect_from_body(const_id); } diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_missing_impl_members.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_missing_impl_members.rs index 6aca716bb60..c0e5429a22c 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_missing_impl_members.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_missing_impl_members.rs @@ -422,7 +422,7 @@ impl<'x, 'y, T, V, U: Default> Trait<'x, 'y, T, V, U> for () { check_assist( add_missing_default_members, r#" -struct Bar<const: N: bool> { +struct Bar<const N: usize> { bar: [i32, N] } @@ -439,7 +439,7 @@ impl<const X: usize, Y, Z> Foo<X, Z> for S<Y> { $0 }"#, r#" -struct Bar<const: N: bool> { +struct Bar<const N: usize> { bar: [i32, N] } @@ -484,6 +484,107 @@ impl<X> Foo<42, {20 + 22}, X> for () { } #[test] + fn test_const_substitution_with_defaults() { + check_assist( + add_missing_default_members, + r#" +trait Foo<T, const N: usize = 42, const M: bool = false, const P: char = 'a'> { + fn get_n(&self) -> usize { N } + fn get_m(&self) -> bool { M } + fn get_p(&self) -> char { P } + fn get_array(&self, arg: &T) -> [bool; N] { [M; N] } +} + +impl<X> Foo<X> for () { + $0 +}"#, + r#" +trait Foo<T, const N: usize = 42, const M: bool = false, const P: char = 'a'> { + fn get_n(&self) -> usize { N } + fn get_m(&self) -> bool { M } + fn get_p(&self) -> char { P } + fn get_array(&self, arg: &T) -> [bool; N] { [M; N] } +} + +impl<X> Foo<X> for () { + $0fn get_n(&self) -> usize { 42 } + + fn get_m(&self) -> bool { false } + + fn get_p(&self) -> char { 'a' } + + fn get_array(&self, arg: &X) -> [bool; 42] { [false; 42] } +}"#, + ); + } + + #[test] + fn test_const_substitution_with_defaults_2() { + check_assist( + add_missing_impl_members, + r#" +mod m { + pub const LEN: usize = 42; + pub trait Foo<const M: usize = LEN, const N: usize = M, T = [bool; N]> { + fn get_t(&self) -> T; + } +} + +impl m::Foo for () { + $0 +}"#, + r#" +mod m { + pub const LEN: usize = 42; + pub trait Foo<const M: usize = LEN, const N: usize = M, T = [bool; N]> { + fn get_t(&self) -> T; + } +} + +impl m::Foo for () { + fn get_t(&self) -> [bool; m::LEN] { + ${0:todo!()} + } +}"#, + ) + } + + #[test] + fn test_const_substitution_with_defaults_3() { + check_assist( + add_missing_default_members, + r#" +mod m { + pub const VAL: usize = 0; + + pub trait Foo<const N: usize = {40 + 2}, const M: usize = {VAL + 1}> { + fn get_n(&self) -> usize { N } + fn get_m(&self) -> usize { M } + } +} + +impl m::Foo for () { + $0 +}"#, + r#" +mod m { + pub const VAL: usize = 0; + + pub trait Foo<const N: usize = {40 + 2}, const M: usize = {VAL + 1}> { + fn get_n(&self) -> usize { N } + fn get_m(&self) -> usize { M } + } +} + +impl m::Foo for () { + $0fn get_n(&self) -> usize { {40 + 2} } + + fn get_m(&self) -> usize { {m::VAL + 1} } +}"#, + ) + } + + #[test] fn test_cursor_after_empty_impl_def() { check_assist( add_missing_impl_members, diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/apply_demorgan.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/apply_demorgan.rs index 57cfa17cc8e..66bc2f6dadc 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/apply_demorgan.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/apply_demorgan.rs @@ -1,6 +1,10 @@ use std::collections::VecDeque; -use syntax::ast::{self, AstNode}; +use syntax::{ + ast::{self, AstNode, Expr::BinExpr}, + ted::{self, Position}, + SyntaxKind, +}; use crate::{utils::invert_boolean_expression, AssistContext, AssistId, AssistKind, Assists}; @@ -23,121 +27,117 @@ use crate::{utils::invert_boolean_expression, AssistContext, AssistId, AssistKin // } // ``` pub(crate) fn apply_demorgan(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { - let expr = ctx.find_node_at_offset::<ast::BinExpr>()?; - let op = expr.op_kind()?; - let op_range = expr.op_token()?.text_range(); + let mut bin_expr = ctx.find_node_at_offset::<ast::BinExpr>()?; + let op = bin_expr.op_kind()?; + let op_range = bin_expr.op_token()?.text_range(); - let opposite_op = match op { - ast::BinaryOp::LogicOp(ast::LogicOp::And) => "||", - ast::BinaryOp::LogicOp(ast::LogicOp::Or) => "&&", - _ => return None, - }; - - let cursor_in_range = op_range.contains_range(ctx.selection_trimmed()); - if !cursor_in_range { + // Is the cursor on the expression's logical operator? + if !op_range.contains_range(ctx.selection_trimmed()) { return None; } - let mut expr = expr; - // Walk up the tree while we have the same binary operator - while let Some(parent_expr) = expr.syntax().parent().and_then(ast::BinExpr::cast) { - match expr.op_kind() { + while let Some(parent_expr) = bin_expr.syntax().parent().and_then(ast::BinExpr::cast) { + match parent_expr.op_kind() { Some(parent_op) if parent_op == op => { - expr = parent_expr; + bin_expr = parent_expr; } _ => break, } } - let mut expr_stack = vec![expr.clone()]; - let mut terms = Vec::new(); - let mut op_ranges = Vec::new(); - - // Find all the children with the same binary operator - while let Some(expr) = expr_stack.pop() { - let mut traverse_bin_expr_arm = |expr| { - if let ast::Expr::BinExpr(bin_expr) = expr { - if let Some(expr_op) = bin_expr.op_kind() { - if expr_op == op { - expr_stack.push(bin_expr); - } else { - terms.push(ast::Expr::BinExpr(bin_expr)); - } + let op = bin_expr.op_kind()?; + let inv_token = match op { + ast::BinaryOp::LogicOp(ast::LogicOp::And) => SyntaxKind::PIPE2, + ast::BinaryOp::LogicOp(ast::LogicOp::Or) => SyntaxKind::AMP2, + _ => return None, + }; + + let demorganed = bin_expr.clone_subtree().clone_for_update(); + + ted::replace(demorganed.op_token()?, ast::make::token(inv_token)); + let mut exprs = VecDeque::from(vec![ + (bin_expr.lhs()?, demorganed.lhs()?), + (bin_expr.rhs()?, demorganed.rhs()?), + ]); + + while let Some((expr, dm)) = exprs.pop_front() { + if let BinExpr(bin_expr) = &expr { + if let BinExpr(cbin_expr) = &dm { + if op == bin_expr.op_kind()? { + ted::replace(cbin_expr.op_token()?, ast::make::token(inv_token)); + exprs.push_back((bin_expr.lhs()?, cbin_expr.lhs()?)); + exprs.push_back((bin_expr.rhs()?, cbin_expr.rhs()?)); } else { - terms.push(ast::Expr::BinExpr(bin_expr)); + let mut inv = invert_boolean_expression(expr); + if inv.needs_parens_in(dm.syntax().parent()?) { + inv = ast::make::expr_paren(inv).clone_for_update(); + } + ted::replace(dm.syntax(), inv.syntax()); } } else { - terms.push(expr); + return None; } - }; - - op_ranges.extend(expr.op_token().map(|t| t.text_range())); - traverse_bin_expr_arm(expr.lhs()?); - traverse_bin_expr_arm(expr.rhs()?); + } else { + let mut inv = invert_boolean_expression(dm.clone_subtree()).clone_for_update(); + if inv.needs_parens_in(dm.syntax().parent()?) { + inv = ast::make::expr_paren(inv).clone_for_update(); + } + ted::replace(dm.syntax(), inv.syntax()); + } } + let dm_lhs = demorganed.lhs()?; + acc.add( AssistId("apply_demorgan", AssistKind::RefactorRewrite), "Apply De Morgan's law", op_range, |edit| { - terms.sort_by_key(|t| t.syntax().text_range().start()); - let mut terms = VecDeque::from(terms); - - let paren_expr = expr.syntax().parent().and_then(ast::ParenExpr::cast); - + let paren_expr = bin_expr.syntax().parent().and_then(ast::ParenExpr::cast); let neg_expr = paren_expr .clone() .and_then(|paren_expr| paren_expr.syntax().parent()) .and_then(ast::PrefixExpr::cast) .and_then(|prefix_expr| { - if prefix_expr.op_kind().unwrap() == ast::UnaryOp::Not { + if prefix_expr.op_kind()? == ast::UnaryOp::Not { Some(prefix_expr) } else { None } }); - for op_range in op_ranges { - edit.replace(op_range, opposite_op); - } - if let Some(paren_expr) = paren_expr { - for term in terms { - let range = term.syntax().text_range(); - let not_term = invert_boolean_expression(term); - - edit.replace(range, not_term.syntax().text()); - } - if let Some(neg_expr) = neg_expr { cov_mark::hit!(demorgan_double_negation); - edit.replace(neg_expr.op_token().unwrap().text_range(), ""); + edit.replace_ast(ast::Expr::PrefixExpr(neg_expr), demorganed.into()); } else { cov_mark::hit!(demorgan_double_parens); - edit.replace(paren_expr.l_paren_token().unwrap().text_range(), "!("); + ted::insert_all_raw( + Position::before(dm_lhs.syntax()), + vec![ + syntax::NodeOrToken::Token(ast::make::token(SyntaxKind::BANG)), + syntax::NodeOrToken::Token(ast::make::token(SyntaxKind::L_PAREN)), + ], + ); + + ted::append_child_raw( + demorganed.syntax(), + syntax::NodeOrToken::Token(ast::make::token(SyntaxKind::R_PAREN)), + ); + + edit.replace_ast(ast::Expr::ParenExpr(paren_expr), demorganed.into()); } } else { - if let Some(lhs) = terms.pop_front() { - let lhs_range = lhs.syntax().text_range(); - let not_lhs = invert_boolean_expression(lhs); - - edit.replace(lhs_range, format!("!({not_lhs}")); - } - - if let Some(rhs) = terms.pop_back() { - let rhs_range = rhs.syntax().text_range(); - let not_rhs = invert_boolean_expression(rhs); - - edit.replace(rhs_range, format!("{not_rhs})")); - } - - for term in terms { - let term_range = term.syntax().text_range(); - let not_term = invert_boolean_expression(term); - edit.replace(term_range, not_term.to_string()); - } + ted::insert_all_raw( + Position::before(dm_lhs.syntax()), + vec![ + syntax::NodeOrToken::Token(ast::make::token(SyntaxKind::BANG)), + syntax::NodeOrToken::Token(ast::make::token(SyntaxKind::L_PAREN)), + ], + ); + ted::append_child_raw(demorganed.syntax(), ast::make::token(SyntaxKind::R_PAREN)); + edit.replace_ast(bin_expr, demorganed); } }, ) @@ -145,9 +145,8 @@ pub(crate) fn apply_demorgan(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opti #[cfg(test)] mod tests { - use crate::tests::{check_assist, check_assist_not_applicable}; - use super::*; + use crate::tests::{check_assist, check_assist_not_applicable}; #[test] fn demorgan_handles_leq() { @@ -213,7 +212,7 @@ fn f() { !(S <= S || S < S) } #[test] fn demorgan_doesnt_double_negation() { cov_mark::check!(demorgan_double_negation); - check_assist(apply_demorgan, "fn f() { !(x ||$0 x) }", "fn f() { (!x && !x) }") + check_assist(apply_demorgan, "fn f() { !(x ||$0 x) }", "fn f() { !x && !x }") } #[test] @@ -222,13 +221,38 @@ fn f() { !(S <= S || S < S) } check_assist(apply_demorgan, "fn f() { (x ||$0 x) }", "fn f() { !(!x && !x) }") } - // https://github.com/rust-lang/rust-analyzer/issues/10963 + // FIXME : This needs to go. + // // https://github.com/rust-lang/rust-analyzer/issues/10963 + // #[test] + // fn demorgan_doesnt_hang() { + // check_assist( + // apply_demorgan, + // "fn f() { 1 || 3 &&$0 4 || 5 }", + // "fn f() { !(!1 || !3 || !4) || 5 }", + // ) + // } + + #[test] + fn demorgan_keep_pars_for_op_precedence() { + check_assist( + apply_demorgan, + "fn main() { + let _ = !(!a ||$0 !(b || c)); +} +", + "fn main() { + let _ = a && (b || c); +} +", + ); + } + #[test] - fn demorgan_doesnt_hang() { + fn demorgan_removes_pars_in_eq_precedence() { check_assist( apply_demorgan, - "fn f() { 1 || 3 &&$0 4 || 5 }", - "fn f() { !(!1 || !3 || !4) || 5 }", + "fn() { let x = a && !(!b |$0| !c); }", + "fn() { let x = a && b && c; }", ) } } diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_named_struct_to_tuple_struct.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_named_struct_to_tuple_struct.rs index fe1cb6fce36..76f021ed912 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_named_struct_to_tuple_struct.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_named_struct_to_tuple_struct.rs @@ -161,9 +161,9 @@ fn process_struct_name_reference( let path_segment = name_ref.syntax().parent().and_then(ast::PathSegment::cast)?; // A `PathSegment` always belongs to a `Path`, so there's at least one `Path` at this point. let full_path = - path_segment.syntax().parent()?.ancestors().map_while(ast::Path::cast).last().unwrap(); + path_segment.syntax().parent()?.ancestors().map_while(ast::Path::cast).last()?; - if full_path.segment().unwrap().name_ref()? != *name_ref { + if full_path.segment()?.name_ref()? != *name_ref { // `name_ref` isn't the last segment of the path, so `full_path` doesn't point to the // struct we want to edit. return None; diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_to_guarded_return.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_to_guarded_return.rs index dcb96ab8af4..7d0e424769e 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_to_guarded_return.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_to_guarded_return.rs @@ -58,7 +58,7 @@ pub(crate) fn convert_to_guarded_return(acc: &mut Assists, ctx: &AssistContext<' return None; } - let bound_ident = pat.fields().next().unwrap(); + let bound_ident = pat.fields().next()?; if !ast::IdentPat::can_cast(bound_ident.syntax().kind()) { return None; } @@ -108,6 +108,15 @@ pub(crate) fn convert_to_guarded_return(acc: &mut Assists, ctx: &AssistContext<' then_block.syntax().last_child_or_token().filter(|t| t.kind() == T!['}'])?; + let then_block_items = then_block.dedent(IndentLevel(1)).clone_for_update(); + + let end_of_then = then_block_items.syntax().last_child_or_token()?; + let end_of_then = if end_of_then.prev_sibling_or_token().map(|n| n.kind()) == Some(WHITESPACE) { + end_of_then.prev_sibling_or_token()? + } else { + end_of_then + }; + let target = if_expr.syntax().text_range(); acc.add( AssistId("convert_to_guarded_return", AssistKind::RefactorRewrite), @@ -141,16 +150,6 @@ pub(crate) fn convert_to_guarded_return(acc: &mut Assists, ctx: &AssistContext<' } }; - let then_block_items = then_block.dedent(IndentLevel(1)).clone_for_update(); - - let end_of_then = then_block_items.syntax().last_child_or_token().unwrap(); - let end_of_then = - if end_of_then.prev_sibling_or_token().map(|n| n.kind()) == Some(WHITESPACE) { - end_of_then.prev_sibling_or_token().unwrap() - } else { - end_of_then - }; - let then_statements = replacement .children_with_tokens() .chain( diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_expressions_from_format_string.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_expressions_from_format_string.rs index 4f3b6e0c287..c3d925cb26c 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_expressions_from_format_string.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_expressions_from_format_string.rs @@ -48,7 +48,7 @@ pub(crate) fn extract_expressions_from_format_string( let tt = fmt_string.syntax().parent().and_then(ast::TokenTree::cast)?; let expanded_t = ast::String::cast( - ctx.sema.descend_into_macros_with_kind_preference(fmt_string.syntax().clone()), + ctx.sema.descend_into_macros_with_kind_preference(fmt_string.syntax().clone(), 0.into()), )?; if !is_format_string(&expanded_t) { return None; diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_function.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_function.rs index b8b781ea48d..ea7a21e77a4 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_function.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_function.rs @@ -750,7 +750,7 @@ impl FunctionBody { .descendants_with_tokens() .filter_map(SyntaxElement::into_token) .filter(|it| matches!(it.kind(), SyntaxKind::IDENT | T![self])) - .flat_map(|t| sema.descend_into_macros(t)) + .flat_map(|t| sema.descend_into_macros(t, 0.into())) .for_each(|t| add_name_if_local(t.parent().and_then(ast::NameRef::cast))); } } @@ -810,7 +810,7 @@ impl FunctionBody { (true, konst.body(), Some(sema.to_def(&konst)?.ty(sema.db))) }, ast::ConstParam(cp) => { - (true, cp.default_val(), Some(sema.to_def(&cp)?.ty(sema.db))) + (true, cp.default_val()?.expr(), Some(sema.to_def(&cp)?.ty(sema.db))) }, ast::ConstBlockPat(cbp) => { let expr = cbp.block_expr().map(ast::Expr::BlockExpr); @@ -1385,31 +1385,30 @@ enum FlowHandler { impl FlowHandler { fn from_ret_ty(fun: &Function, ret_ty: &FunType) -> FlowHandler { - match &fun.control_flow.kind { - None => FlowHandler::None, - Some(flow_kind) => { - let action = flow_kind.clone(); - if let FunType::Unit = ret_ty { - match flow_kind { - FlowKind::Return(None) - | FlowKind::Break(_, None) - | FlowKind::Continue(_) => FlowHandler::If { action }, - FlowKind::Return(_) | FlowKind::Break(_, _) => { - FlowHandler::IfOption { action } - } - FlowKind::Try { kind } => FlowHandler::Try { kind: kind.clone() }, - } - } else { - match flow_kind { - FlowKind::Return(None) - | FlowKind::Break(_, None) - | FlowKind::Continue(_) => FlowHandler::MatchOption { none: action }, - FlowKind::Return(_) | FlowKind::Break(_, _) => { - FlowHandler::MatchResult { err: action } - } - FlowKind::Try { kind } => FlowHandler::Try { kind: kind.clone() }, - } + if fun.contains_tail_expr { + return FlowHandler::None; + } + let Some(action) = fun.control_flow.kind.clone() else { + return FlowHandler::None; + }; + + if let FunType::Unit = ret_ty { + match action { + FlowKind::Return(None) | FlowKind::Break(_, None) | FlowKind::Continue(_) => { + FlowHandler::If { action } + } + FlowKind::Return(_) | FlowKind::Break(_, _) => FlowHandler::IfOption { action }, + FlowKind::Try { kind } => FlowHandler::Try { kind }, + } + } else { + match action { + FlowKind::Return(None) | FlowKind::Break(_, None) | FlowKind::Continue(_) => { + FlowHandler::MatchOption { none: action } + } + FlowKind::Return(_) | FlowKind::Break(_, _) => { + FlowHandler::MatchResult { err: action } } + FlowKind::Try { kind } => FlowHandler::Try { kind }, } } } @@ -1654,11 +1653,7 @@ impl Function { fn make_ret_ty(&self, ctx: &AssistContext<'_>, module: hir::Module) -> Option<ast::RetType> { let fun_ty = self.return_type(ctx); - let handler = if self.contains_tail_expr { - FlowHandler::None - } else { - FlowHandler::from_ret_ty(self, &fun_ty) - }; + let handler = FlowHandler::from_ret_ty(self, &fun_ty); let ret_ty = match &handler { FlowHandler::None => { if matches!(fun_ty, FunType::Unit) { @@ -1728,11 +1723,7 @@ fn make_body( fun: &Function, ) -> ast::BlockExpr { let ret_ty = fun.return_type(ctx); - let handler = if fun.contains_tail_expr { - FlowHandler::None - } else { - FlowHandler::from_ret_ty(fun, &ret_ty) - }; + let handler = FlowHandler::from_ret_ty(fun, &ret_ty); let block = match &fun.body { FunctionBody::Expr(expr) => { @@ -4471,7 +4462,7 @@ async fn foo() -> Result<(), ()> { "#, r#" async fn foo() -> Result<(), ()> { - fun_name().await? + fun_name().await } async fn $0fun_name() -> Result<(), ()> { @@ -4690,7 +4681,7 @@ fn $0fun_name() { check_assist( extract_function, r#" -//- minicore: result +//- minicore: result, try fn foo() -> Result<(), i64> { $0Result::<i32, i64>::Ok(0)?; Ok(())$0 @@ -4698,7 +4689,7 @@ fn foo() -> Result<(), i64> { "#, r#" fn foo() -> Result<(), i64> { - fun_name()? + fun_name() } fn $0fun_name() -> Result<(), i64> { @@ -5754,6 +5745,34 @@ fn $0fun_name<T, V>(t: T, v: V) -> i32 where T: Into<i32> + Copy, V: Into<i32> { } #[test] + fn tail_expr_no_extra_control_flow() { + check_assist( + extract_function, + r#" +//- minicore: result +fn fallible() -> Result<(), ()> { + $0if true { + return Err(()); + } + Ok(())$0 +} +"#, + r#" +fn fallible() -> Result<(), ()> { + fun_name() +} + +fn $0fun_name() -> Result<(), ()> { + if true { + return Err(()); + } + Ok(()) +} +"#, + ); + } + + #[test] fn non_tail_expr_of_tail_expr_loop() { check_assist( extract_function, @@ -5800,12 +5819,6 @@ fn $0fun_name() -> ControlFlow<()> { extract_function, r#" //- minicore: option, try -impl<T> core::ops::Try for Option<T> { - type Output = T; - type Residual = Option<!>; -} -impl<T> core::ops::FromResidual for Option<T> {} - fn f() -> Option<()> { if true { let a = $0if true { @@ -5820,12 +5833,6 @@ fn f() -> Option<()> { } "#, r#" -impl<T> core::ops::Try for Option<T> { - type Output = T; - type Residual = Option<!>; -} -impl<T> core::ops::FromResidual for Option<T> {} - fn f() -> Option<()> { if true { let a = fun_name()?;; @@ -5852,12 +5859,6 @@ fn $0fun_name() -> Option<()> { extract_function, r#" //- minicore: option, try -impl<T> core::ops::Try for Option<T> { - type Output = T; - type Residual = Option<!>; -} -impl<T> core::ops::FromResidual for Option<T> {} - fn f() -> Option<()> { if true { $0{ @@ -5874,15 +5875,9 @@ fn f() -> Option<()> { } "#, r#" -impl<T> core::ops::Try for Option<T> { - type Output = T; - type Residual = Option<!>; -} -impl<T> core::ops::FromResidual for Option<T> {} - fn f() -> Option<()> { if true { - fun_name()? + fun_name() } else { None } diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_delegate_methods.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_delegate_methods.rs index 31fc69562c9..bbac0a26ea4 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_delegate_methods.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_delegate_methods.rs @@ -95,6 +95,9 @@ pub(crate) fn generate_delegate_methods(acc: &mut Assists, ctx: &AssistContext<' let Some(impl_def) = find_struct_impl(ctx, &adt, std::slice::from_ref(&name)) else { continue; }; + + let field = make::ext::field_from_idents(["self", &field_name])?; + acc.add_group( &GroupLabel("Generate delegate methods…".to_owned()), AssistId("generate_delegate_methods", AssistKind::Generate), @@ -115,11 +118,7 @@ pub(crate) fn generate_delegate_methods(acc: &mut Assists, ctx: &AssistContext<' Some(list) => convert_param_list_to_arg_list(list), None => make::arg_list([]), }; - let tail_expr = make::expr_method_call( - make::ext::field_from_idents(["self", &field_name]).unwrap(), // This unwrap is ok because we have at least 1 arg in the list - make::name_ref(&name), - arg_list, - ); + let tail_expr = make::expr_method_call(field, make::name_ref(&name), arg_list); let ret_type = method_source.ret_type(); let is_async = method_source.async_token().is_some(); let is_const = method_source.const_token().is_some(); diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_derive.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_derive.rs index 747f70f9f6f..53ba144ba9e 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_derive.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_derive.rs @@ -27,13 +27,19 @@ pub(crate) fn generate_derive(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opt let cap = ctx.config.snippet_cap?; let nominal = ctx.find_node_at_offset::<ast::Adt>()?; let target = nominal.syntax().text_range(); + let derive_attr = nominal + .attrs() + .filter_map(|x| x.as_simple_call()) + .filter(|(name, _arg)| name == "derive") + .map(|(_name, arg)| arg) + .next(); + + let delimiter = match &derive_attr { + None => None, + Some(tt) => Some(tt.right_delimiter_token()?), + }; + acc.add(AssistId("generate_derive", AssistKind::Generate), "Add `#[derive]`", target, |edit| { - let derive_attr = nominal - .attrs() - .filter_map(|x| x.as_simple_call()) - .filter(|(name, _arg)| name == "derive") - .map(|(_name, arg)| arg) - .next(); match derive_attr { None => { let derive = make::attr_outer(make::meta_token_tree( @@ -45,16 +51,23 @@ pub(crate) fn generate_derive(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opt let nominal = edit.make_mut(nominal); nominal.add_attr(derive.clone()); + let delimiter = derive + .meta() + .expect("make::attr_outer was expected to have Meta") + .token_tree() + .expect("failed to get token tree out of Meta") + .r_paren_token() + .expect("make::attr_outer was expected to have a R_PAREN"); + + edit.add_tabstop_before_token(cap, delimiter); + } + Some(_) => { + // Just move the cursor. edit.add_tabstop_before_token( cap, - derive.meta().unwrap().token_tree().unwrap().r_paren_token().unwrap(), + delimiter.expect("Right delim token could not be found."), ); } - Some(tt) => { - // Just move the cursor. - let tt = edit.make_mut(tt); - edit.add_tabstop_before_token(cap, tt.right_delimiter_token().unwrap()); - } }; }) } diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_dbg.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_dbg.rs index a403d5bc672..e2b82223289 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_dbg.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_dbg.rs @@ -39,14 +39,11 @@ pub(crate) fn remove_dbg(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<( let replacements = macro_calls.into_iter().filter_map(compute_dbg_replacement).collect::<Vec<_>>(); - if replacements.is_empty() { - return None; - } acc.add( AssistId("remove_dbg", AssistKind::Refactor), "Remove dbg!()", - replacements.iter().map(|&(range, _)| range).reduce(|acc, range| acc.cover(range)).unwrap(), + replacements.iter().map(|&(range, _)| range).reduce(|acc, range| acc.cover(range))?, |builder| { for (range, expr) in replacements { if let Some(expr) = expr { diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_unused_imports.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_unused_imports.rs index dd4839351fb..5fcab8c02b0 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_unused_imports.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_unused_imports.rs @@ -67,7 +67,7 @@ pub(crate) fn remove_unused_imports(acc: &mut Assists, ctx: &AssistContext<'_>) // This case maps to the situation where the * token is braced. // In this case, the parent use tree's path is the one we should use to resolve the glob. match u.syntax().ancestors().skip(1).find_map(ast::UseTree::cast) { - Some(parent_u) if parent_u.path().is_some() => parent_u.path().unwrap(), + Some(parent_u) if parent_u.path().is_some() => parent_u.path()?, _ => return None, } } else { diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions.rs index 480cb77b4fd..f60ac150164 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions.rs @@ -20,6 +20,7 @@ pub(crate) mod r#type; pub(crate) mod use_; pub(crate) mod vis; pub(crate) mod env_vars; +pub(crate) mod extern_crate; use std::iter; @@ -703,7 +704,9 @@ pub(super) fn complete_name_ref( TypeLocation::TypeAscription(ascription) => { r#type::complete_ascribed_type(acc, ctx, path_ctx, ascription); } - TypeLocation::GenericArgList(_) + TypeLocation::GenericArg { .. } + | TypeLocation::AssocConstEq + | TypeLocation::AssocTypeEq | TypeLocation::TypeBound | TypeLocation::ImplTarget | TypeLocation::ImplTrait @@ -737,6 +740,7 @@ pub(super) fn complete_name_ref( } } } + NameRefKind::ExternCrate => extern_crate::complete_extern_crate(acc, ctx), NameRefKind::DotAccess(dot_access) => { flyimport::import_on_the_fly_dot(acc, ctx, dot_access); dot::complete_dot(acc, ctx, dot_access); diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/extern_crate.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/extern_crate.rs new file mode 100644 index 00000000000..0d0e143f5f6 --- /dev/null +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/extern_crate.rs @@ -0,0 +1,71 @@ +//! Completion for extern crates + +use hir::{HasAttrs, Name}; +use ide_db::SymbolKind; + +use crate::{context::CompletionContext, CompletionItem, CompletionItemKind}; + +use super::Completions; + +pub(crate) fn complete_extern_crate(acc: &mut Completions, ctx: &CompletionContext<'_>) { + let imported_extern_crates: Vec<Name> = ctx.scope.extern_crate_decls().collect(); + + for (name, module) in ctx.scope.extern_crates() { + if imported_extern_crates.contains(&name) { + continue; + } + + let mut item = CompletionItem::new( + CompletionItemKind::SymbolKind(SymbolKind::Module), + ctx.source_range(), + name.to_smol_str(), + ); + item.set_documentation(module.docs(ctx.db)); + + item.add_to(acc, ctx.db); + } +} + +#[cfg(test)] +mod test { + use crate::tests::completion_list_no_kw; + + #[test] + fn can_complete_extern_crate() { + let case = r#" +//- /lib.rs crate:other_crate_a +// nothing here +//- /other_crate_b.rs crate:other_crate_b +pub mod good_mod{} +//- /lib.rs crate:crate_c +// nothing here +//- /lib.rs crate:lib deps:other_crate_a,other_crate_b,crate_c extern-prelude:other_crate_a +extern crate oth$0 +mod other_mod {} +"#; + + let completion_list = completion_list_no_kw(case); + + assert_eq!("md other_crate_a\n".to_string(), completion_list); + } + + #[test] + fn will_not_complete_existing_import() { + let case = r#" +//- /lib.rs crate:other_crate_a +// nothing here +//- /lib.rs crate:crate_c +// nothing here +//- /lib.rs crate:other_crate_b +// +//- /lib.rs crate:lib deps:other_crate_a,other_crate_b,crate_c extern-prelude:other_crate_a,other_crate_b +extern crate other_crate_b; +extern crate oth$0 +mod other_mod {} +"#; + + let completion_list = completion_list_no_kw(case); + + assert_eq!("md other_crate_a\n".to_string(), completion_list); + } +} diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/type.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/type.rs index e4705475638..a30fd13b1d5 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/type.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/type.rs @@ -1,7 +1,7 @@ //! Completion of names from the current scope in type position. use hir::{HirDisplay, ScopeDef}; -use syntax::{ast, AstNode, SyntaxKind}; +use syntax::{ast, AstNode}; use crate::{ context::{PathCompletionCtx, Qualified, TypeAscriptionTarget, TypeLocation}, @@ -20,16 +20,15 @@ pub(crate) fn complete_type_path( let scope_def_applicable = |def| { use hir::{GenericParam::*, ModuleDef::*}; match def { - ScopeDef::GenericParam(LifetimeParam(_)) | ScopeDef::Label(_) => false, + ScopeDef::GenericParam(LifetimeParam(_)) => location.complete_lifetimes(), + ScopeDef::Label(_) => false, // no values in type places ScopeDef::ModuleDef(Function(_) | Variant(_) | Static(_)) | ScopeDef::Local(_) => false, // unless its a constant in a generic arg list position ScopeDef::ModuleDef(Const(_)) | ScopeDef::GenericParam(ConstParam(_)) => { - matches!(location, TypeLocation::GenericArgList(_)) - } - ScopeDef::ImplSelfType(_) => { - !matches!(location, TypeLocation::ImplTarget | TypeLocation::ImplTrait) + location.complete_consts() } + ScopeDef::ImplSelfType(_) => location.complete_self_type(), // Don't suggest attribute macros and derives. ScopeDef::ModuleDef(Macro(mac)) => mac.is_fn_like(ctx.db), // Type things are fine @@ -38,12 +37,12 @@ pub(crate) fn complete_type_path( ) | ScopeDef::AdtSelfType(_) | ScopeDef::Unknown - | ScopeDef::GenericParam(TypeParam(_)) => true, + | ScopeDef::GenericParam(TypeParam(_)) => location.complete_types(), } }; let add_assoc_item = |acc: &mut Completions, item| match item { - hir::AssocItem::Const(ct) if matches!(location, TypeLocation::GenericArgList(_)) => { + hir::AssocItem::Const(ct) if matches!(location, TypeLocation::GenericArg { .. }) => { acc.add_const(ctx, ct) } hir::AssocItem::Function(_) | hir::AssocItem::Const(_) => (), @@ -157,56 +156,30 @@ pub(crate) fn complete_type_path( }); return; } - TypeLocation::GenericArgList(Some(arg_list)) => { - let in_assoc_type_arg = ctx - .original_token - .parent_ancestors() - .any(|node| node.kind() == SyntaxKind::ASSOC_TYPE_ARG); - - if !in_assoc_type_arg { - if let Some(path_seg) = - arg_list.syntax().parent().and_then(ast::PathSegment::cast) - { - if path_seg - .syntax() - .ancestors() - .find_map(ast::TypeBound::cast) - .is_some() - { - if let Some(hir::PathResolution::Def(hir::ModuleDef::Trait( - trait_, - ))) = ctx.sema.resolve_path(&path_seg.parent_path()) - { - let arg_idx = arg_list - .generic_args() - .filter(|arg| { - arg.syntax().text_range().end() - < ctx.original_token.text_range().start() - }) - .count(); - - let n_required_params = - trait_.type_or_const_param_count(ctx.sema.db, true); - if arg_idx >= n_required_params { - trait_ - .items_with_supertraits(ctx.sema.db) - .into_iter() - .for_each(|it| { - if let hir::AssocItem::TypeAlias(alias) = it { - cov_mark::hit!( - complete_assoc_type_in_generics_list - ); - acc.add_type_alias_with_eq(ctx, alias); - } - }); - - let n_params = - trait_.type_or_const_param_count(ctx.sema.db, false); - if arg_idx >= n_params { - return; // only show assoc types - } - } + TypeLocation::GenericArg { + args: Some(arg_list), of_trait: Some(trait_), .. + } => { + if arg_list.syntax().ancestors().find_map(ast::TypeBound::cast).is_some() { + let arg_idx = arg_list + .generic_args() + .filter(|arg| { + arg.syntax().text_range().end() + < ctx.original_token.text_range().start() + }) + .count(); + + let n_required_params = trait_.type_or_const_param_count(ctx.sema.db, true); + if arg_idx >= n_required_params { + trait_.items_with_supertraits(ctx.sema.db).into_iter().for_each(|it| { + if let hir::AssocItem::TypeAlias(alias) = it { + cov_mark::hit!(complete_assoc_type_in_generics_list); + acc.add_type_alias_with_eq(ctx, alias); } + }); + + let n_params = trait_.type_or_const_param_count(ctx.sema.db, false); + if arg_idx >= n_params { + return; // only show assoc types } } } diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/context.rs b/src/tools/rust-analyzer/crates/ide-completion/src/context.rs index 3cb65b2729a..0da7ba6d000 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/context.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/context.rs @@ -155,13 +155,63 @@ pub(crate) struct ExprCtx { pub(crate) enum TypeLocation { TupleField, TypeAscription(TypeAscriptionTarget), - GenericArgList(Option<ast::GenericArgList>), + /// Generic argument position e.g. `Foo<$0>` + GenericArg { + /// The generic argument list containing the generic arg + args: Option<ast::GenericArgList>, + /// `Some(trait_)` if `trait_` is being instantiated with `args` + of_trait: Option<hir::Trait>, + /// The generic parameter being filled in by the generic arg + corresponding_param: Option<ast::GenericParam>, + }, + /// Associated type equality constraint e.g. `Foo<Bar = $0>` + AssocTypeEq, + /// Associated constant equality constraint e.g. `Foo<X = $0>` + AssocConstEq, TypeBound, ImplTarget, ImplTrait, Other, } +impl TypeLocation { + pub(crate) fn complete_lifetimes(&self) -> bool { + matches!( + self, + TypeLocation::GenericArg { + corresponding_param: Some(ast::GenericParam::LifetimeParam(_)), + .. + } + ) + } + + pub(crate) fn complete_consts(&self) -> bool { + match self { + TypeLocation::GenericArg { + corresponding_param: Some(ast::GenericParam::ConstParam(_)), + .. + } => true, + TypeLocation::AssocConstEq => true, + _ => false, + } + } + + pub(crate) fn complete_types(&self) -> bool { + match self { + TypeLocation::GenericArg { corresponding_param: Some(param), .. } => { + matches!(param, ast::GenericParam::TypeParam(_)) + } + TypeLocation::AssocConstEq => false, + TypeLocation::AssocTypeEq => true, + _ => true, + } + } + + pub(crate) fn complete_self_type(&self) -> bool { + self.complete_types() && !matches!(self, TypeLocation::ImplTarget | TypeLocation::ImplTrait) + } +} + #[derive(Clone, Debug, PartialEq, Eq)] pub(crate) enum TypeAscriptionTarget { Let(Option<ast::Pat>), @@ -301,6 +351,7 @@ pub(super) enum NameRefKind { expr: ast::RecordExpr, }, Pattern(PatternContext), + ExternCrate, } /// The identifier we are currently completing. diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs b/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs index 3ea50659030..1e6b2f319aa 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs @@ -1,11 +1,11 @@ //! Module responsible for analyzing the code surrounding the cursor for completion. use std::iter; -use hir::{Semantics, Type, TypeInfo, Variant}; +use hir::{HasSource, Semantics, Type, TypeInfo, Variant}; use ide_db::{active_parameter::ActiveParameter, RootDatabase}; use syntax::{ algo::{find_node_at_offset, non_trivia_sibling}, - ast::{self, AttrKind, HasArgList, HasLoopBody, HasName, NameOrNameRef}, + ast::{self, AttrKind, HasArgList, HasGenericParams, HasLoopBody, HasName, NameOrNameRef}, match_ast, AstNode, AstToken, Direction, NodeOrToken, SyntaxElement, SyntaxKind, SyntaxNode, SyntaxToken, TextRange, TextSize, T, }; @@ -624,6 +624,10 @@ fn classify_name_ref( }); return Some(make_res(kind)); }, + ast::ExternCrate(_) => { + let kind = NameRefKind::ExternCrate; + return Some(make_res(kind)); + }, ast::MethodCallExpr(method) => { let receiver = find_opt_node_in_file(original_file, method.receiver()); let kind = NameRefKind::DotAccess(DotAccess { @@ -719,6 +723,136 @@ fn classify_name_ref( None }; + let generic_arg_location = |arg: ast::GenericArg| { + let mut override_location = None; + let location = find_opt_node_in_file_compensated( + sema, + original_file, + arg.syntax().parent().and_then(ast::GenericArgList::cast), + ) + .map(|args| { + let mut in_trait = None; + let param = (|| { + let parent = args.syntax().parent()?; + let params = match_ast! { + match parent { + ast::PathSegment(segment) => { + match sema.resolve_path(&segment.parent_path().top_path())? { + hir::PathResolution::Def(def) => match def { + hir::ModuleDef::Function(func) => { + func.source(sema.db)?.value.generic_param_list() + } + hir::ModuleDef::Adt(adt) => { + adt.source(sema.db)?.value.generic_param_list() + } + hir::ModuleDef::Variant(variant) => { + variant.parent_enum(sema.db).source(sema.db)?.value.generic_param_list() + } + hir::ModuleDef::Trait(trait_) => { + if let ast::GenericArg::AssocTypeArg(arg) = &arg { + let arg_name = arg.name_ref()?; + let arg_name = arg_name.text(); + for item in trait_.items_with_supertraits(sema.db) { + match item { + hir::AssocItem::TypeAlias(assoc_ty) => { + if assoc_ty.name(sema.db).as_str()? == arg_name { + override_location = Some(TypeLocation::AssocTypeEq); + return None; + } + }, + hir::AssocItem::Const(const_) => { + if const_.name(sema.db)?.as_str()? == arg_name { + override_location = Some(TypeLocation::AssocConstEq); + return None; + } + }, + _ => (), + } + } + return None; + } else { + in_trait = Some(trait_); + trait_.source(sema.db)?.value.generic_param_list() + } + } + hir::ModuleDef::TraitAlias(trait_) => { + trait_.source(sema.db)?.value.generic_param_list() + } + hir::ModuleDef::TypeAlias(ty_) => { + ty_.source(sema.db)?.value.generic_param_list() + } + _ => None, + }, + _ => None, + } + }, + ast::MethodCallExpr(call) => { + let func = sema.resolve_method_call(&call)?; + func.source(sema.db)?.value.generic_param_list() + }, + ast::AssocTypeArg(arg) => { + let trait_ = ast::PathSegment::cast(arg.syntax().parent()?.parent()?)?; + match sema.resolve_path(&trait_.parent_path().top_path())? { + hir::PathResolution::Def(def) => match def { + hir::ModuleDef::Trait(trait_) => { + let arg_name = arg.name_ref()?; + let arg_name = arg_name.text(); + let trait_items = trait_.items_with_supertraits(sema.db); + let assoc_ty = trait_items.iter().find_map(|item| match item { + hir::AssocItem::TypeAlias(assoc_ty) => { + (assoc_ty.name(sema.db).as_str()? == arg_name) + .then_some(assoc_ty) + }, + _ => None, + })?; + assoc_ty.source(sema.db)?.value.generic_param_list() + } + _ => None, + }, + _ => None, + } + }, + _ => None, + } + }?; + // Determine the index of the argument in the `GenericArgList` and match it with + // the corresponding parameter in the `GenericParamList`. Since lifetime parameters + // are often omitted, ignore them for the purposes of matching the argument with + // its parameter unless a lifetime argument is provided explicitly. That is, for + // `struct S<'a, 'b, T>`, match `S::<$0>` to `T` and `S::<'a, $0, _>` to `'b`. + // FIXME: This operates on the syntax tree and will produce incorrect results when + // generic parameters are disabled by `#[cfg]` directives. It should operate on the + // HIR, but the functionality necessary to do so is not exposed at the moment. + let mut explicit_lifetime_arg = false; + let arg_idx = arg + .syntax() + .siblings(Direction::Prev) + // Skip the node itself + .skip(1) + .map(|arg| if ast::LifetimeArg::can_cast(arg.kind()) { explicit_lifetime_arg = true }) + .count(); + let param_idx = if explicit_lifetime_arg { + arg_idx + } else { + // Lifetimes parameters always precede type and generic parameters, + // so offset the argument index by the total number of lifetime params + arg_idx + params.lifetime_params().count() + }; + params.generic_params().nth(param_idx) + })(); + (args, in_trait, param) + }); + let (arg_list, of_trait, corresponding_param) = match location { + Some((arg_list, of_trait, param)) => (Some(arg_list), of_trait, param), + _ => (None, None, None), + }; + override_location.unwrap_or(TypeLocation::GenericArg { + args: arg_list, + of_trait, + corresponding_param, + }) + }; + let type_location = |node: &SyntaxNode| { let parent = node.parent()?; let res = match_ast! { @@ -774,9 +908,12 @@ fn classify_name_ref( ast::TypeBound(_) => TypeLocation::TypeBound, // is this case needed? ast::TypeBoundList(_) => TypeLocation::TypeBound, - ast::GenericArg(it) => TypeLocation::GenericArgList(find_opt_node_in_file_compensated(sema, original_file, it.syntax().parent().and_then(ast::GenericArgList::cast))), + ast::GenericArg(it) => generic_arg_location(it), // is this case needed? - ast::GenericArgList(it) => TypeLocation::GenericArgList(find_opt_node_in_file_compensated(sema, original_file, Some(it))), + ast::GenericArgList(it) => { + let args = find_opt_node_in_file_compensated(sema, original_file, Some(it)); + TypeLocation::GenericArg { args, of_trait: None, corresponding_param: None } + }, ast::TupleField(_) => TypeLocation::TupleField, _ => return None, } diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/flyimport.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/flyimport.rs index 8c038c0fbaa..4cdfd546f6a 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/flyimport.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests/flyimport.rs @@ -1286,3 +1286,57 @@ macro_rules! println { expect![""], ) } + +#[test] +fn no_completions_for_external_doc_hidden_in_path() { + check( + r#" +//- /main.rs crate:main deps:dep +fn main() { + Span$0 +} +//- /lib.rs crate:dep +#[doc(hidden)] +pub mod bridge { + pub mod server { + pub trait Span + } +} +pub mod bridge2 { + #[doc(hidden)] + pub mod server2 { + pub trait Span + } +} +"#, + expect![""], + ); + // unless re-exported + check( + r#" +//- /main.rs crate:main deps:dep +fn main() { + Span$0 +} +//- /lib.rs crate:dep +#[doc(hidden)] +pub mod bridge { + pub mod server { + pub trait Span + } +} +pub use bridge::server::Span; +pub mod bridge2 { + #[doc(hidden)] + pub mod server2 { + pub trait Span2 + } +} +pub use bridge2::server2::Span2; +"#, + expect![[r#" + tt Span (use dep::Span) + tt Span2 (use dep::Span2) + "#]], + ); +} diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/type_pos.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/type_pos.rs index 8cb1ff4a125..d518dd76410 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/type_pos.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests/type_pos.rs @@ -384,10 +384,8 @@ trait Trait2<T>: Trait1 { fn foo<'lt, T: Trait2<$0>, const CONST_PARAM: usize>(_: T) {} "#, expect![[r#" - ct CONST - cp CONST_PARAM en Enum - ma makro!(…) macro_rules! makro + ma makro!(…) macro_rules! makro md module st Record st Tuple @@ -404,14 +402,13 @@ fn foo<'lt, T: Trait2<$0>, const CONST_PARAM: usize>(_: T) {} ); check( r#" -trait Trait2 { +trait Trait2<T> { type Foo; } fn foo<'lt, T: Trait2<self::$0>, const CONST_PARAM: usize>(_: T) {} "#, expect![[r#" - ct CONST en Enum ma makro!(…) macro_rules! makro md module @@ -437,7 +434,6 @@ trait Tr<T> { impl Tr<$0 "#, expect![[r#" - ct CONST en Enum ma makro!(…) macro_rules! makro md module @@ -485,7 +481,6 @@ trait MyTrait<T, U> { fn f(t: impl MyTrait<u$0 "#, expect![[r#" - ct CONST en Enum ma makro!(…) macro_rules! makro md module @@ -511,7 +506,6 @@ trait MyTrait<T, U> { fn f(t: impl MyTrait<u8, u$0 "#, expect![[r#" - ct CONST en Enum ma makro!(…) macro_rules! makro md module @@ -555,7 +549,6 @@ trait MyTrait<T, U = u8> { fn f(t: impl MyTrait<u$0 "#, expect![[r#" - ct CONST en Enum ma makro!(…) macro_rules! makro md module @@ -581,7 +574,6 @@ trait MyTrait<T, U = u8> { fn f(t: impl MyTrait<u8, u$0 "#, expect![[r#" - ct CONST en Enum ma makro!(…) macro_rules! makro md module @@ -627,7 +619,6 @@ trait MyTrait { fn f(t: impl MyTrait<Item1 = $0 "#, expect![[r#" - ct CONST en Enum ma makro!(…) macro_rules! makro md module @@ -653,7 +644,6 @@ trait MyTrait { fn f(t: impl MyTrait<Item1 = u8, Item2 = $0 "#, expect![[r#" - ct CONST en Enum ma makro!(…) macro_rules! makro md module @@ -668,6 +658,22 @@ fn f(t: impl MyTrait<Item1 = u8, Item2 = $0 kw self:: "#]], ); + + check( + r#" +trait MyTrait { + const C: usize; +}; + +fn f(t: impl MyTrait<C = $0 +"#, + expect![[r#" + ct CONST + ma makro!(…) macro_rules! makro + kw crate:: + kw self:: + "#]], + ); } #[test] @@ -719,3 +725,267 @@ pub struct S; "#]], ) } + +#[test] +fn completes_const_and_type_generics_separately() { + // Function generic params + check( + r#" + struct Foo; + const X: usize = 0; + fn foo<T, const N: usize>() {} + fn main() { + foo::<F$0, _>(); + } + "#, + expect![[r#" + en Enum + ma makro!(…) macro_rules! makro + md module + st Foo + st Record + st Tuple + st Unit + tt Trait + un Union + bt u32 + kw crate:: + kw self:: + "#]], + ); + // FIXME: This should probably also suggest completions for types, at least those that have + // associated constants usable in this position. For example, a user could be typing + // `foo::<_, { usize::MAX }>()`, but we currently don't suggest `usize` in constant position. + check( + r#" + struct Foo; + const X: usize = 0; + fn foo<T, const N: usize>() {} + fn main() { + foo::<_, $0>(); + } + "#, + expect![[r#" + ct CONST + ct X + ma makro!(…) macro_rules! makro + kw crate:: + kw self:: + "#]], + ); + + // Method generic params + check( + r#" + const X: usize = 0; + struct Foo; + impl Foo { fn bar<const N: usize, T>(self) {} } + fn main() { + Foo.bar::<_, $0>(); + } + "#, + expect![[r#" + en Enum + ma makro!(…) macro_rules! makro + md module + st Foo + st Record + st Tuple + st Unit + tt Trait + un Union + bt u32 + kw crate:: + kw self:: + "#]], + ); + check( + r#" + const X: usize = 0; + struct Foo; + impl Foo { fn bar<const N: usize, T>(self) {} } + fn main() { + Foo.bar::<X$0, _>(); + } + "#, + expect![[r#" + ct CONST + ct X + ma makro!(…) macro_rules! makro + kw crate:: + kw self:: + "#]], + ); + + // Associated type generic params + check( + r#" + const X: usize = 0; + struct Foo; + trait Bar { + type Baz<T, const X: usize>; + } + fn foo(_: impl Bar<Baz<F$0, 0> = ()>) {} + "#, + expect![[r#" + en Enum + ma makro!(…) macro_rules! makro + md module + st Foo + st Record + st Tuple + st Unit + tt Bar + tt Trait + un Union + bt u32 + kw crate:: + kw self:: + "#]], + ); + check( + r#" + const X: usize = 0; + struct Foo; + trait Bar { + type Baz<T, const X: usize>; + } + fn foo<T: Bar<Baz<(), $0> = ()>>() {} + "#, + expect![[r#" + ct CONST + ct X + ma makro!(…) macro_rules! makro + kw crate:: + kw self:: + "#]], + ); + + // Type generic params + check( + r#" + const X: usize = 0; + struct Foo<T, const N: usize>(T); + fn main() { + let _: Foo::<_, $0> = Foo(()); + } + "#, + expect![[r#" + ct CONST + ct X + ma makro!(…) macro_rules! makro + kw crate:: + kw self:: + "#]], + ); + + // Type alias generic params + check( + r#" + const X: usize = 0; + struct Foo<T, const N: usize>(T); + type Bar<const X: usize, U> = Foo<U, X>; + fn main() { + let _: Bar::<X$0, _> = Bar(()); + } + "#, + expect![[r#" + ct CONST + ct X + ma makro!(…) macro_rules! makro + kw crate:: + kw self:: + "#]], + ); + + // Enum variant params + check( + r#" + const X: usize = 0; + enum Foo<T, const N: usize> { A(T), B } + fn main() { + Foo::B::<(), $0>; + } + "#, + expect![[r#" + ct CONST + ct X + ma makro!(…) macro_rules! makro + kw crate:: + kw self:: + "#]], + ); + + // Trait params + check( + r#" + const X: usize = 0; + trait Foo<T, const N: usize> {} + impl Foo<(), $0> for () {} + "#, + expect![[r#" + ct CONST + ct X + ma makro!(…) macro_rules! makro + kw crate:: + kw self:: + "#]], + ); + + // Trait alias params + check( + r#" + #![feature(trait_alias)] + const X: usize = 0; + trait Foo<T, const N: usize> {} + trait Bar<const M: usize, U> = Foo<U, M>; + fn foo<T: Bar<X$0, ()>>() {} + "#, + expect![[r#" + ct CONST + ct X + ma makro!(…) macro_rules! makro + kw crate:: + kw self:: + "#]], + ); + + // Omitted lifetime params + check( + r#" +struct S<'a, 'b, const C: usize, T>(core::marker::PhantomData<&'a &'b T>); +fn foo<'a>() { S::<F$0, _>; } + "#, + expect![[r#" + ct CONST + ma makro!(…) macro_rules! makro + kw crate:: + kw self:: + "#]], + ); + // Explicit lifetime params + check( + r#" +struct S<'a, 'b, const C: usize, T>(core::marker::PhantomData<&'a &'b T>); +fn foo<'a>() { S::<'static, 'static, F$0, _>; } + "#, + expect![[r#" + ct CONST + ma makro!(…) macro_rules! makro + kw crate:: + kw self:: + "#]], + ); + check( + r#" +struct S<'a, 'b, const C: usize, T>(core::marker::PhantomData<&'a &'b T>); +fn foo<'a>() { S::<'static, F$0, _, _>; } + "#, + expect![[r#" + lt 'a + ma makro!(…) macro_rules! makro + kw crate:: + kw self:: + "#]], + ); +} diff --git a/src/tools/rust-analyzer/crates/ide-db/src/defs.rs b/src/tools/rust-analyzer/crates/ide-db/src/defs.rs index 5e4562d9c58..4ce80532e8e 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/defs.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/defs.rs @@ -7,7 +7,7 @@ use arrayvec::ArrayVec; use hir::{ - Adt, AsAssocItem, AssocItem, BuiltinAttr, BuiltinType, Const, Crate, DeriveHelper, + Adt, AsAssocItem, AssocItem, BuiltinAttr, BuiltinType, Const, Crate, DeriveHelper, DocLinkDef, ExternCrateDecl, Field, Function, GenericParam, HasVisibility, Impl, Label, Local, Macro, Module, ModuleDef, Name, PathResolution, Semantics, Static, ToolModule, Trait, TraitAlias, TypeAlias, Variant, Visibility, @@ -649,3 +649,13 @@ impl From<ModuleDef> for Definition { } } } + +impl From<DocLinkDef> for Definition { + fn from(def: DocLinkDef) -> Self { + match def { + DocLinkDef::ModuleDef(it) => it.into(), + DocLinkDef::Field(it) => it.into(), + DocLinkDef::SelfType(it) => it.into(), + } + } +} diff --git a/src/tools/rust-analyzer/crates/ide-db/src/helpers.rs b/src/tools/rust-analyzer/crates/ide-db/src/helpers.rs index 1eb8f00020b..330af442f75 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/helpers.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/helpers.rs @@ -117,7 +117,7 @@ pub fn get_definition( sema: &Semantics<'_, RootDatabase>, token: SyntaxToken, ) -> Option<Definition> { - for token in sema.descend_into_macros(token) { + for token in sema.descend_into_macros(token, 0.into()) { let def = IdentClass::classify_token(sema, &token).map(IdentClass::definitions_no_ops); if let Some(&[x]) = def.as_deref() { return Some(x); diff --git a/src/tools/rust-analyzer/crates/ide-db/src/imports/import_assets.rs b/src/tools/rust-analyzer/crates/ide-db/src/imports/import_assets.rs index e52dc356775..e475c5cd66b 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/imports/import_assets.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/imports/import_assets.rs @@ -6,7 +6,7 @@ use hir::{ use itertools::Itertools; use rustc_hash::FxHashSet; use syntax::{ - ast::{self, HasName}, + ast::{self, make, HasName}, utils::path_to_string_stripping_turbo_fish, AstNode, SyntaxNode, }; @@ -607,7 +607,7 @@ impl ImportCandidate { fn for_name(sema: &Semantics<'_, RootDatabase>, name: &ast::Name) -> Option<Self> { if sema .scope(name.syntax())? - .speculative_resolve(&ast::make::ext::ident_path(&name.text())) + .speculative_resolve(&make::ext::ident_path(&name.text())) .is_some() { return None; diff --git a/src/tools/rust-analyzer/crates/ide-db/src/lib.rs b/src/tools/rust-analyzer/crates/ide-db/src/lib.rs index f27ed485d81..ac3511ba47b 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/lib.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/lib.rs @@ -94,18 +94,21 @@ impl fmt::Debug for RootDatabase { } impl Upcast<dyn ExpandDatabase> for RootDatabase { + #[inline] fn upcast(&self) -> &(dyn ExpandDatabase + 'static) { &*self } } impl Upcast<dyn DefDatabase> for RootDatabase { + #[inline] fn upcast(&self) -> &(dyn DefDatabase + 'static) { &*self } } impl Upcast<dyn HirDatabase> for RootDatabase { + #[inline] fn upcast(&self) -> &(dyn HirDatabase + 'static) { &*self } diff --git a/src/tools/rust-analyzer/crates/ide-db/src/path_transform.rs b/src/tools/rust-analyzer/crates/ide-db/src/path_transform.rs index 1d0cb426a57..fb75b5b4584 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/path_transform.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/path_transform.rs @@ -5,7 +5,7 @@ use either::Either; use hir::{AsAssocItem, HirDisplay, SemanticsScope}; use rustc_hash::FxHashMap; use syntax::{ - ast::{self, AstNode}, + ast::{self, make, AstNode}, ted, SyntaxNode, }; @@ -21,6 +21,7 @@ enum TypeOrConst { } type LifetimeName = String; +type DefaultedParam = Either<hir::TypeParam, hir::ConstParam>; /// `PathTransform` substitutes path in SyntaxNodes in bulk. /// @@ -115,7 +116,7 @@ impl<'a> PathTransform<'a> { }; let mut type_substs: FxHashMap<hir::TypeParam, ast::Type> = Default::default(); let mut const_substs: FxHashMap<hir::ConstParam, SyntaxNode> = Default::default(); - let mut default_types: Vec<hir::TypeParam> = Default::default(); + let mut defaulted_params: Vec<DefaultedParam> = Default::default(); self.generic_def .into_iter() .flat_map(|it| it.type_params(db)) @@ -138,8 +139,8 @@ impl<'a> PathTransform<'a> { if let Some(default) = &default.display_source_code(db, source_module.into(), false).ok() { - type_substs.insert(k, ast::make::ty(default).clone_for_update()); - default_types.push(k); + type_substs.insert(k, make::ty(default).clone_for_update()); + defaulted_params.push(Either::Left(k)); } } } @@ -155,11 +156,19 @@ impl<'a> PathTransform<'a> { // is a standalone statement or a part of another expresson) // and sometimes require slight modifications; see // https://doc.rust-lang.org/reference/statements.html#expression-statements + // (default values in curly brackets can cause the same problem) const_substs.insert(k, expr.syntax().clone()); } } - (Either::Left(_), None) => (), // FIXME: get default const value - _ => (), // ignore mismatching params + (Either::Left(k), None) => { + if let Some(default) = k.default(db) { + if let Some(default) = default.expr() { + const_substs.insert(k, default.syntax().clone_for_update()); + defaulted_params.push(Either::Right(k)); + } + } + } + _ => (), // ignore mismatching params }); let lifetime_substs: FxHashMap<_, _> = self .generic_def @@ -175,7 +184,7 @@ impl<'a> PathTransform<'a> { target_module, source_scope: self.source_scope, }; - ctx.transform_default_type_substs(default_types); + ctx.transform_default_values(defaulted_params); ctx } } @@ -212,13 +221,19 @@ impl Ctx<'_> { }); } - fn transform_default_type_substs(&self, default_types: Vec<hir::TypeParam>) { - for k in default_types { - let v = self.type_substs.get(&k).unwrap(); + fn transform_default_values(&self, defaulted_params: Vec<DefaultedParam>) { + // By now the default values are simply copied from where they are declared + // and should be transformed. As any value is allowed to refer to previous + // generic (both type and const) parameters, they should be all iterated left-to-right. + for param in defaulted_params { + let value = match param { + Either::Left(k) => self.type_substs.get(&k).unwrap().syntax(), + Either::Right(k) => self.const_substs.get(&k).unwrap(), + }; // `transform_path` may update a node's parent and that would break the // tree traversal. Thus all paths in the tree are collected into a vec // so that such operation is safe. - let paths = postorder(&v.syntax()).filter_map(ast::Path::cast).collect::<Vec<_>>(); + let paths = postorder(value).filter_map(ast::Path::cast).collect::<Vec<_>>(); for path in paths { self.transform_path(path); } @@ -263,15 +278,14 @@ impl Ctx<'_> { hir::ModuleDef::Trait(trait_ref), false, )?; - match ast::make::ty_path(mod_path_to_ast(&found_path)) { + match make::ty_path(mod_path_to_ast(&found_path)) { ast::Type::PathType(path_ty) => Some(path_ty), _ => None, } }); - let segment = ast::make::path_segment_ty(subst.clone(), trait_ref); - let qualified = - ast::make::path_from_segments(std::iter::once(segment), false); + let segment = make::path_segment_ty(subst.clone(), trait_ref); + let qualified = make::path_from_segments(std::iter::once(segment), false); ted::replace(path.syntax(), qualified.clone_for_update().syntax()); } else if let Some(path_ty) = ast::PathType::cast(parent) { ted::replace( diff --git a/src/tools/rust-analyzer/crates/ide-db/src/search.rs b/src/tools/rust-analyzer/crates/ide-db/src/search.rs index d5abd099126..7e00d368652 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/search.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/search.rs @@ -456,14 +456,14 @@ impl<'a> FindUsages<'a> { it.text().trim_start_matches("r#") == name }) .into_iter() - .flat_map(|token| { + .flat_map(move |token| { // FIXME: There should be optimization potential here // Currently we try to descend everything we find which // means we call `Semantics::descend_into_macros` on // every textual hit. That function is notoriously // expensive even for things that do not get down mapped // into macros. - sema.descend_into_macros(token).into_iter().filter_map(|it| it.parent()) + sema.descend_into_macros(token, offset).into_iter().filter_map(|it| it.parent()) }) }; diff --git a/src/tools/rust-analyzer/crates/ide-db/src/symbol_index.rs b/src/tools/rust-analyzer/crates/ide-db/src/symbol_index.rs index b54c43b296b..f699f999baf 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/symbol_index.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/symbol_index.rs @@ -323,6 +323,8 @@ impl Query { hir::ModuleDef::Adt(..) | hir::ModuleDef::TypeAlias(..) | hir::ModuleDef::BuiltinType(..) + | hir::ModuleDef::TraitAlias(..) + | hir::ModuleDef::Trait(..) ) { continue; @@ -417,9 +419,16 @@ const CONST_WITH_INNER: () = { mod b_mod; + +use define_struct as really_define_struct; +use Macro as ItemLikeMacro; +use Macro as Trait; // overlay namespaces //- /b_mod.rs struct StructInModB; - "#, +use super::Macro as SuperItemLikeMacro; +use crate::b_mod::StructInModB as ThisStruct; +use crate::Trait as IsThisJustATrait; +"#, ); let symbols: Vec<_> = Crate::from(db.test_crate()) diff --git a/src/tools/rust-analyzer/crates/ide-db/src/test_data/test_symbol_index_collection.txt b/src/tools/rust-analyzer/crates/ide-db/src/test_data/test_symbol_index_collection.txt index 1a00e29384e..87ad5844c64 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/test_data/test_symbol_index_collection.txt +++ b/src/tools/rust-analyzer/crates/ide-db/src/test_data/test_symbol_index_collection.txt @@ -119,6 +119,35 @@ is_alias: false, }, FileSymbol { + name: "ItemLikeMacro", + def: Macro( + Macro { + id: Macro2Id( + Macro2Id( + 0, + ), + ), + }, + ), + loc: DeclarationLocation { + hir_file_id: FileId( + FileId( + 0, + ), + ), + ptr: SyntaxNodePtr { + kind: USE_TREE, + range: 654..676, + }, + name_ptr: SyntaxNodePtr { + kind: NAME, + range: 663..676, + }, + }, + container_name: None, + is_alias: false, + }, + FileSymbol { name: "Macro", def: Macro( Macro { @@ -353,6 +382,35 @@ is_alias: false, }, FileSymbol { + name: "Trait", + def: Macro( + Macro { + id: Macro2Id( + Macro2Id( + 0, + ), + ), + }, + ), + loc: DeclarationLocation { + hir_file_id: FileId( + FileId( + 0, + ), + ), + ptr: SyntaxNodePtr { + kind: USE_TREE, + range: 682..696, + }, + name_ptr: SyntaxNodePtr { + kind: NAME, + range: 691..696, + }, + }, + container_name: None, + is_alias: false, + }, + FileSymbol { name: "Union", def: Adt( Union( @@ -552,6 +610,35 @@ is_alias: false, }, FileSymbol { + name: "really_define_struct", + def: Macro( + Macro { + id: MacroRulesId( + MacroRulesId( + 1, + ), + ), + }, + ), + loc: DeclarationLocation { + hir_file_id: FileId( + FileId( + 0, + ), + ), + ptr: SyntaxNodePtr { + kind: USE_TREE, + range: 611..648, + }, + name_ptr: SyntaxNodePtr { + kind: NAME, + range: 628..648, + }, + }, + container_name: None, + is_alias: false, + }, + FileSymbol { name: "trait_fn", def: Function( Function { @@ -632,6 +719,35 @@ }, [ FileSymbol { + name: "IsThisJustATrait", + def: Macro( + Macro { + id: Macro2Id( + Macro2Id( + 0, + ), + ), + }, + ), + loc: DeclarationLocation { + hir_file_id: FileId( + FileId( + 1, + ), + ), + ptr: SyntaxNodePtr { + kind: USE_TREE, + range: 111..143, + }, + name_ptr: SyntaxNodePtr { + kind: NAME, + range: 127..143, + }, + }, + container_name: None, + is_alias: false, + }, + FileSymbol { name: "StructInModB", def: Adt( Struct( @@ -660,6 +776,93 @@ container_name: None, is_alias: false, }, + FileSymbol { + name: "SuperItemLikeMacro", + def: Macro( + Macro { + id: Macro2Id( + Macro2Id( + 0, + ), + ), + }, + ), + loc: DeclarationLocation { + hir_file_id: FileId( + FileId( + 1, + ), + ), + ptr: SyntaxNodePtr { + kind: USE_TREE, + range: 25..59, + }, + name_ptr: SyntaxNodePtr { + kind: NAME, + range: 41..59, + }, + }, + container_name: None, + is_alias: false, + }, + FileSymbol { + name: "ThisStruct", + def: Adt( + Struct( + Struct { + id: StructId( + 3, + ), + }, + ), + ), + loc: DeclarationLocation { + hir_file_id: FileId( + FileId( + 1, + ), + ), + ptr: SyntaxNodePtr { + kind: USE_TREE, + range: 65..105, + }, + name_ptr: SyntaxNodePtr { + kind: NAME, + range: 95..105, + }, + }, + container_name: None, + is_alias: false, + }, + FileSymbol { + name: "ThisStruct", + def: Adt( + Struct( + Struct { + id: StructId( + 3, + ), + }, + ), + ), + loc: DeclarationLocation { + hir_file_id: FileId( + FileId( + 1, + ), + ), + ptr: SyntaxNodePtr { + kind: USE_TREE, + range: 65..105, + }, + name_ptr: SyntaxNodePtr { + kind: NAME, + range: 95..105, + }, + }, + container_name: None, + is_alias: false, + }, ], ), ] diff --git a/src/tools/rust-analyzer/crates/ide-db/src/use_trivial_constructor.rs b/src/tools/rust-analyzer/crates/ide-db/src/use_trivial_constructor.rs index f96ea29ae2f..a915391ad90 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/use_trivial_constructor.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/use_trivial_constructor.rs @@ -1,31 +1,29 @@ //! Functionality for generating trivial constructors use hir::StructKind; -use syntax::ast; +use syntax::ast::{make, Expr, Path}; /// given a type return the trivial constructor (if one exists) pub fn use_trivial_constructor( db: &crate::RootDatabase, - path: ast::Path, + path: Path, ty: &hir::Type, -) -> Option<ast::Expr> { +) -> Option<Expr> { match ty.as_adt() { Some(hir::Adt::Enum(x)) => { if let &[variant] = &*x.variants(db) { if variant.kind(db) == hir::StructKind::Unit { - let path = ast::make::path_qualified( + let path = make::path_qualified( path, - syntax::ast::make::path_segment(ast::make::name_ref( - &variant.name(db).to_smol_str(), - )), + make::path_segment(make::name_ref(&variant.name(db).to_smol_str())), ); - return Some(syntax::ast::make::expr_path(path)); + return Some(make::expr_path(path)); } } } Some(hir::Adt::Struct(x)) if x.kind(db) == StructKind::Unit => { - return Some(syntax::ast::make::expr_path(path)); + return Some(make::expr_path(path)); } _ => {} } diff --git a/src/tools/rust-analyzer/crates/ide-ssr/src/matching.rs b/src/tools/rust-analyzer/crates/ide-ssr/src/matching.rs index a8e88369088..60fcbbbd397 100644 --- a/src/tools/rust-analyzer/crates/ide-ssr/src/matching.rs +++ b/src/tools/rust-analyzer/crates/ide-ssr/src/matching.rs @@ -560,8 +560,10 @@ impl<'db, 'sema> Matcher<'db, 'sema> { placeholder_value.autoref_kind = self .sema .resolve_method_call_as_callable(code) - .and_then(|callable| callable.receiver_param(self.sema.db)) - .map(|(self_param, _)| self_param.kind()) + .and_then(|callable| { + let (self_param, _) = callable.receiver_param(self.sema.db)?; + Some(self_param.source(self.sema.db)?.value.kind()) + }) .unwrap_or(ast::SelfParamKind::Owned); } } diff --git a/src/tools/rust-analyzer/crates/ide/src/call_hierarchy.rs b/src/tools/rust-analyzer/crates/ide/src/call_hierarchy.rs index dd1d0d75c63..f834f2ce592 100644 --- a/src/tools/rust-analyzer/crates/ide/src/call_hierarchy.rs +++ b/src/tools/rust-analyzer/crates/ide/src/call_hierarchy.rs @@ -74,18 +74,20 @@ pub(crate) fn incoming_calls( Some(calls.into_items()) } -pub(crate) fn outgoing_calls(db: &RootDatabase, position: FilePosition) -> Option<Vec<CallItem>> { +pub(crate) fn outgoing_calls( + db: &RootDatabase, + FilePosition { file_id, offset }: FilePosition, +) -> Option<Vec<CallItem>> { let sema = Semantics::new(db); - let file_id = position.file_id; let file = sema.parse(file_id); let file = file.syntax(); - let token = pick_best_token(file.token_at_offset(position.offset), |kind| match kind { + let token = pick_best_token(file.token_at_offset(offset), |kind| match kind { IDENT => 1, _ => 0, })?; let mut calls = CallLocations::default(); - sema.descend_into_macros(token) + sema.descend_into_macros(token, offset) .into_iter() .filter_map(|it| it.parent_ancestors().nth(1).and_then(ast::Item::cast)) .filter_map(|item| match item { diff --git a/src/tools/rust-analyzer/crates/ide/src/doc_links.rs b/src/tools/rust-analyzer/crates/ide/src/doc_links.rs index d240127f376..901f7a2a79a 100644 --- a/src/tools/rust-analyzer/crates/ide/src/doc_links.rs +++ b/src/tools/rust-analyzer/crates/ide/src/doc_links.rs @@ -131,19 +131,19 @@ pub(crate) fn remove_links(markdown: &str) -> String { // |=== pub(crate) fn external_docs( db: &RootDatabase, - position: &FilePosition, + FilePosition { file_id, offset }: FilePosition, target_dir: Option<&OsStr>, sysroot: Option<&OsStr>, ) -> Option<DocumentationLinks> { let sema = &Semantics::new(db); - let file = sema.parse(position.file_id).syntax().clone(); - let token = pick_best_token(file.token_at_offset(position.offset), |kind| match kind { + let file = sema.parse(file_id).syntax().clone(); + let token = pick_best_token(file.token_at_offset(offset), |kind| match kind { IDENT | INT_NUMBER | T![self] => 3, T!['('] | T![')'] => 2, kind if kind.is_trivia() => 0, _ => 1, })?; - let token = sema.descend_into_macros_single(token); + let token = sema.descend_into_macros_single(token, offset); let node = token.parent()?; let definition = match_ast! { @@ -285,7 +285,7 @@ impl DocCommentToken { let original_start = doc_token.text_range().start(); let relative_comment_offset = offset - original_start - prefix_len; - sema.descend_into_macros(doc_token).into_iter().find_map(|t| { + sema.descend_into_macros(doc_token, offset).into_iter().find_map(|t| { let (node, descended_prefix_len) = match_ast! { match t { ast::Comment(comment) => (t.parent()?, TextSize::try_from(comment.prefix().len()).ok()?), diff --git a/src/tools/rust-analyzer/crates/ide/src/doc_links/tests.rs b/src/tools/rust-analyzer/crates/ide/src/doc_links/tests.rs index 05a64b33bfd..8036c77072b 100644 --- a/src/tools/rust-analyzer/crates/ide/src/doc_links/tests.rs +++ b/src/tools/rust-analyzer/crates/ide/src/doc_links/tests.rs @@ -518,6 +518,62 @@ fn function(); } #[test] +fn doc_links_field() { + check_doc_links( + r#" +/// [`S::f`] +/// [`S2::f`] +/// [`T::0`] +/// [`U::a`] +/// [`E::A::f`] +/// [`E::B::0`] +struct S$0 { + f: i32, + //^ S::f + //^ S2::f +} +type S2 = S; +struct T(i32); + //^^^ T::0 +union U { + a: i32, + //^ U::a +} +enum E { + A { f: i32 }, + //^ E::A::f + B(i32), + //^^^ E::B::0 +} +"#, + ); +} + +#[test] +fn doc_links_field_via_self() { + check_doc_links( + r#" +/// [`Self::f`] +struct S$0 { + f: i32, + //^ Self::f +} +"#, + ); +} + +#[test] +fn doc_links_tuple_field_via_self() { + check_doc_links( + r#" +/// [`Self::0`] +struct S$0(i32); + //^^^ Self::0 +"#, + ); +} + +#[test] fn rewrite_html_root_url() { check_rewrite( r#" diff --git a/src/tools/rust-analyzer/crates/ide/src/expand_macro.rs b/src/tools/rust-analyzer/crates/ide/src/expand_macro.rs index d6bbf2bf794..119a9c7c3f4 100644 --- a/src/tools/rust-analyzer/crates/ide/src/expand_macro.rs +++ b/src/tools/rust-analyzer/crates/ide/src/expand_macro.rs @@ -40,28 +40,33 @@ pub(crate) fn expand_macro(db: &RootDatabase, position: FilePosition) -> Option< // struct Bar; // ``` - let derive = sema.descend_into_macros(tok.clone()).into_iter().find_map(|descended| { - let hir_file = sema.hir_file_for(&descended.parent()?); - if !hir_file.is_derive_attr_pseudo_expansion(db) { - return None; - } + let derive = + sema.descend_into_macros(tok.clone(), 0.into()).into_iter().find_map(|descended| { + let hir_file = sema.hir_file_for(&descended.parent()?); + if !hir_file.is_derive_attr_pseudo_expansion(db) { + return None; + } - let name = descended.parent_ancestors().filter_map(ast::Path::cast).last()?.to_string(); - // up map out of the #[derive] expansion - let token = hir::InFile::new(hir_file, descended).upmap(db)?.value; - let attr = token.parent_ancestors().find_map(ast::Attr::cast)?; - let expansions = sema.expand_derive_macro(&attr)?; - let idx = attr - .token_tree()? - .token_trees_and_tokens() - .filter_map(NodeOrToken::into_token) - .take_while(|it| it != &token) - .filter(|it| it.kind() == T![,]) - .count(); - let expansion = - format(db, SyntaxKind::MACRO_ITEMS, position.file_id, expansions.get(idx).cloned()?); - Some(ExpandedMacro { name, expansion }) - }); + let name = descended.parent_ancestors().filter_map(ast::Path::cast).last()?.to_string(); + // up map out of the #[derive] expansion + let token = hir::InFile::new(hir_file, descended).upmap(db)?.value; + let attr = token.parent_ancestors().find_map(ast::Attr::cast)?; + let expansions = sema.expand_derive_macro(&attr)?; + let idx = attr + .token_tree()? + .token_trees_and_tokens() + .filter_map(NodeOrToken::into_token) + .take_while(|it| it != &token) + .filter(|it| it.kind() == T![,]) + .count(); + let expansion = format( + db, + SyntaxKind::MACRO_ITEMS, + position.file_id, + expansions.get(idx).cloned()?, + ); + Some(ExpandedMacro { name, expansion }) + }); if derive.is_some() { return derive; diff --git a/src/tools/rust-analyzer/crates/ide/src/extend_selection.rs b/src/tools/rust-analyzer/crates/ide/src/extend_selection.rs index f9061822244..3d89599c583 100644 --- a/src/tools/rust-analyzer/crates/ide/src/extend_selection.rs +++ b/src/tools/rust-analyzer/crates/ide/src/extend_selection.rs @@ -17,8 +17,6 @@ use crate::FileRange; // Extends or shrinks the current selection to the encompassing syntactic construct // (expression, statement, item, module, etc). It works with multiple cursors. // -// This is a standard LSP feature and not a protocol extension. -// // |=== // | Editor | Shortcut // @@ -142,8 +140,10 @@ fn extend_tokens_from_range( // compute original mapped token range let extended = { - let fst_expanded = sema.descend_into_macros_single(first_token.clone()); - let lst_expanded = sema.descend_into_macros_single(last_token.clone()); + let fst_expanded = + sema.descend_into_macros_single(first_token.clone(), original_range.start()); + let lst_expanded = + sema.descend_into_macros_single(last_token.clone(), original_range.end()); let mut lca = algo::least_common_ancestor(&fst_expanded.parent()?, &lst_expanded.parent()?)?; lca = shallowest_node(&lca); @@ -154,13 +154,16 @@ fn extend_tokens_from_range( }; // Compute parent node range - let validate = |token: &SyntaxToken| -> bool { - let expanded = sema.descend_into_macros_single(token.clone()); - let parent = match expanded.parent() { - Some(it) => it, - None => return false, - }; - algo::least_common_ancestor(&extended, &parent).as_ref() == Some(&extended) + let validate = |offset: TextSize| { + let extended = &extended; + move |token: &SyntaxToken| -> bool { + let expanded = sema.descend_into_macros_single(token.clone(), offset); + let parent = match expanded.parent() { + Some(it) => it, + None => return false, + }; + algo::least_common_ancestor(extended, &parent).as_ref() == Some(extended) + } }; // Find the first and last text range under expanded parent @@ -168,14 +171,14 @@ fn extend_tokens_from_range( let token = token.prev_token()?; skip_trivia_token(token, Direction::Prev) }) - .take_while(validate) + .take_while(validate(original_range.start())) .last()?; let last = successors(Some(last_token), |token| { let token = token.next_token()?; skip_trivia_token(token, Direction::Next) }) - .take_while(validate) + .take_while(validate(original_range.end())) .last()?; let range = first.text_range().cover(last.text_range()); diff --git a/src/tools/rust-analyzer/crates/ide/src/goto_declaration.rs b/src/tools/rust-analyzer/crates/ide/src/goto_declaration.rs index c39c696cfd9..7e0fab42608 100644 --- a/src/tools/rust-analyzer/crates/ide/src/goto_declaration.rs +++ b/src/tools/rust-analyzer/crates/ide/src/goto_declaration.rs @@ -20,16 +20,16 @@ use crate::{ // - fields in patterns will navigate to the field declaration of the struct, union or variant pub(crate) fn goto_declaration( db: &RootDatabase, - position: FilePosition, + position @ FilePosition { file_id, offset }: FilePosition, ) -> Option<RangeInfo<Vec<NavigationTarget>>> { let sema = Semantics::new(db); - let file = sema.parse(position.file_id).syntax().clone(); + let file = sema.parse(file_id).syntax().clone(); let original_token = file - .token_at_offset(position.offset) + .token_at_offset(offset) .find(|it| matches!(it.kind(), IDENT | T![self] | T![super] | T![crate] | T![Self]))?; let range = original_token.text_range(); let info: Vec<NavigationTarget> = sema - .descend_into_macros(original_token) + .descend_into_macros(original_token, offset) .iter() .filter_map(|token| { let parent = token.parent()?; diff --git a/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs b/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs index 21471ab2a03..e09b9f39148 100644 --- a/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs +++ b/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs @@ -29,45 +29,39 @@ use syntax::{ast, AstNode, AstToken, SyntaxKind::*, SyntaxToken, TextRange, T}; // image::https://user-images.githubusercontent.com/48062697/113065563-025fbe00-91b1-11eb-83e4-a5a703610b23.gif[] pub(crate) fn goto_definition( db: &RootDatabase, - position: FilePosition, + FilePosition { file_id, offset }: FilePosition, ) -> Option<RangeInfo<Vec<NavigationTarget>>> { let sema = &Semantics::new(db); - let file = sema.parse(position.file_id).syntax().clone(); - let original_token = - pick_best_token(file.token_at_offset(position.offset), |kind| match kind { - IDENT - | INT_NUMBER - | LIFETIME_IDENT - | T![self] - | T![super] - | T![crate] - | T![Self] - | COMMENT => 4, - // index and prefix ops - T!['['] | T![']'] | T![?] | T![*] | T![-] | T![!] => 3, - kind if kind.is_keyword() => 2, - T!['('] | T![')'] => 2, - kind if kind.is_trivia() => 0, - _ => 1, - })?; + let file = sema.parse(file_id).syntax().clone(); + let original_token = pick_best_token(file.token_at_offset(offset), |kind| match kind { + IDENT + | INT_NUMBER + | LIFETIME_IDENT + | T![self] + | T![super] + | T![crate] + | T![Self] + | COMMENT => 4, + // index and prefix ops + T!['['] | T![']'] | T![?] | T![*] | T![-] | T![!] => 3, + kind if kind.is_keyword() => 2, + T!['('] | T![')'] => 2, + kind if kind.is_trivia() => 0, + _ => 1, + })?; if let Some(doc_comment) = token_as_doc_comment(&original_token) { - return doc_comment.get_definition_with_descend_at( - sema, - position.offset, - |def, _, link_range| { - let nav = def.try_to_nav(db)?; - Some(RangeInfo::new(link_range, vec![nav])) - }, - ); + return doc_comment.get_definition_with_descend_at(sema, offset, |def, _, link_range| { + let nav = def.try_to_nav(db)?; + Some(RangeInfo::new(link_range, vec![nav])) + }); } let navs = sema - .descend_into_macros(original_token.clone()) + .descend_into_macros(original_token.clone(), offset) .into_iter() .filter_map(|token| { let parent = token.parent()?; if let Some(tt) = ast::TokenTree::cast(parent) { - if let Some(x) = try_lookup_include_path(sema, tt, token.clone(), position.file_id) - { + if let Some(x) = try_lookup_include_path(sema, tt, token.clone(), file_id) { return Some(vec![x]); } } diff --git a/src/tools/rust-analyzer/crates/ide/src/goto_implementation.rs b/src/tools/rust-analyzer/crates/ide/src/goto_implementation.rs index 37166bdbd0c..544c6b42317 100644 --- a/src/tools/rust-analyzer/crates/ide/src/goto_implementation.rs +++ b/src/tools/rust-analyzer/crates/ide/src/goto_implementation.rs @@ -22,20 +22,19 @@ use crate::{FilePosition, NavigationTarget, RangeInfo, TryToNav}; // image::https://user-images.githubusercontent.com/48062697/113065566-02f85480-91b1-11eb-9288-aaad8abd8841.gif[] pub(crate) fn goto_implementation( db: &RootDatabase, - position: FilePosition, + FilePosition { file_id, offset }: FilePosition, ) -> Option<RangeInfo<Vec<NavigationTarget>>> { let sema = Semantics::new(db); - let source_file = sema.parse(position.file_id); + let source_file = sema.parse(file_id); let syntax = source_file.syntax().clone(); - let original_token = - pick_best_token(syntax.token_at_offset(position.offset), |kind| match kind { - IDENT | T![self] | INT_NUMBER => 1, - _ => 0, - })?; + let original_token = pick_best_token(syntax.token_at_offset(offset), |kind| match kind { + IDENT | T![self] | INT_NUMBER => 1, + _ => 0, + })?; let range = original_token.text_range(); let navs = - sema.descend_into_macros(original_token) + sema.descend_into_macros(original_token, offset) .into_iter() .filter_map(|token| token.parent().and_then(ast::NameLike::cast)) .filter_map(|node| match &node { diff --git a/src/tools/rust-analyzer/crates/ide/src/goto_type_definition.rs b/src/tools/rust-analyzer/crates/ide/src/goto_type_definition.rs index 6048990f749..955923d7691 100644 --- a/src/tools/rust-analyzer/crates/ide/src/goto_type_definition.rs +++ b/src/tools/rust-analyzer/crates/ide/src/goto_type_definition.rs @@ -16,13 +16,13 @@ use crate::{FilePosition, NavigationTarget, RangeInfo, TryToNav}; // image::https://user-images.githubusercontent.com/48062697/113020657-b560f500-917a-11eb-9007-0f809733a338.gif[] pub(crate) fn goto_type_definition( db: &RootDatabase, - position: FilePosition, + FilePosition { file_id, offset }: FilePosition, ) -> Option<RangeInfo<Vec<NavigationTarget>>> { let sema = hir::Semantics::new(db); - let file: ast::SourceFile = sema.parse(position.file_id); + let file: ast::SourceFile = sema.parse(file_id); let token: SyntaxToken = - pick_best_token(file.syntax().token_at_offset(position.offset), |kind| match kind { + pick_best_token(file.syntax().token_at_offset(offset), |kind| match kind { IDENT | INT_NUMBER | T![self] => 2, kind if kind.is_trivia() => 0, _ => 1, @@ -37,7 +37,7 @@ pub(crate) fn goto_type_definition( } }; let range = token.text_range(); - sema.descend_into_macros(token) + sema.descend_into_macros(token, offset) .into_iter() .filter_map(|token| { let ty = sema diff --git a/src/tools/rust-analyzer/crates/ide/src/highlight_related.rs b/src/tools/rust-analyzer/crates/ide/src/highlight_related.rs index 43e89a334bf..46a0464e9e6 100644 --- a/src/tools/rust-analyzer/crates/ide/src/highlight_related.rs +++ b/src/tools/rust-analyzer/crates/ide/src/highlight_related.rs @@ -15,6 +15,7 @@ use syntax::{ SyntaxKind::{self, IDENT, INT_NUMBER}, SyntaxNode, SyntaxToken, TextRange, T, }; +use text_edit::TextSize; use crate::{navigation_target::ToNav, references, NavigationTarget, TryToNav}; @@ -51,7 +52,7 @@ pub struct HighlightRelatedConfig { pub(crate) fn highlight_related( sema: &Semantics<'_, RootDatabase>, config: HighlightRelatedConfig, - FilePosition { offset, file_id }: FilePosition, + pos @ FilePosition { offset, file_id }: FilePosition, ) -> Option<Vec<HighlightedRange>> { let _p = profile::span("highlight_related"); let syntax = sema.parse(file_id).syntax().clone(); @@ -79,7 +80,7 @@ pub(crate) fn highlight_related( } T![|] if config.closure_captures => highlight_closure_captures(sema, token, file_id), T![move] if config.closure_captures => highlight_closure_captures(sema, token, file_id), - _ if config.references => highlight_references(sema, &syntax, token, file_id), + _ if config.references => highlight_references(sema, &syntax, token, pos), _ => None, } } @@ -129,9 +130,9 @@ fn highlight_references( sema: &Semantics<'_, RootDatabase>, node: &SyntaxNode, token: SyntaxToken, - file_id: FileId, + FilePosition { file_id, offset }: FilePosition, ) -> Option<Vec<HighlightedRange>> { - let defs = find_defs(sema, token.clone()); + let defs = find_defs(sema, token.clone(), offset); let usages = defs .iter() .filter_map(|&d| { @@ -455,8 +456,12 @@ fn cover_range(r0: Option<TextRange>, r1: Option<TextRange>) -> Option<TextRange } } -fn find_defs(sema: &Semantics<'_, RootDatabase>, token: SyntaxToken) -> FxHashSet<Definition> { - sema.descend_into_macros(token) +fn find_defs( + sema: &Semantics<'_, RootDatabase>, + token: SyntaxToken, + offset: TextSize, +) -> FxHashSet<Definition> { + sema.descend_into_macros(token, offset) .into_iter() .filter_map(|token| IdentClass::classify_token(sema, &token)) .map(IdentClass::definitions_no_ops) diff --git a/src/tools/rust-analyzer/crates/ide/src/hover.rs b/src/tools/rust-analyzer/crates/ide/src/hover.rs index 40659e6c263..21934b9480e 100644 --- a/src/tools/rust-analyzer/crates/ide/src/hover.rs +++ b/src/tools/rust-analyzer/crates/ide/src/hover.rs @@ -162,9 +162,9 @@ fn hover_simple( // prefer descending the same token kind in attribute expansions, in normal macros text // equivalency is more important let descended = if in_attr { - [sema.descend_into_macros_with_kind_preference(original_token.clone())].into() + [sema.descend_into_macros_with_kind_preference(original_token.clone(), offset)].into() } else { - sema.descend_into_macros_with_same_text(original_token.clone()) + sema.descend_into_macros_with_same_text(original_token.clone(), offset) }; let descended = || descended.iter(); diff --git a/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs b/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs index ddc71dffa8a..d0f9f7b0e16 100644 --- a/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs +++ b/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs @@ -1557,6 +1557,49 @@ fn test_hover_function_show_types() { } #[test] +fn test_hover_function_associated_type_params() { + check( + r#" +trait Foo { type Bar; } +impl Foo for i32 { type Bar = i64; } +fn foo(arg: <i32 as Foo>::Bar) {} +fn main() { foo$0; } +"#, + expect![[r#" + *foo* + + ```rust + test + ``` + + ```rust + fn foo(arg: <i32 as Foo>::Bar) + ``` + "#]], + ); + + check( + r#" +trait Foo<T> { type Bar<U>; } +impl Foo<i64> for i32 { type Bar<U> = i32; } +fn foo(arg: <<i32 as Foo<i64>>::Bar<i8> as Foo<i64>>::Bar<i8>) {} +fn main() { foo$0; } +"#, + expect![[r#" + *foo* + + ```rust + test + ``` + + ```rust + fn foo(arg: <<i32 as Foo<i64>>::Bar<i8> as Foo<i64>>::Bar<i8>) + ``` + "#]], + ); +} + +#[test] fn test_hover_function_pointer_show_identifiers() { check( r#"type foo$0 = fn(a: i32, b: i32) -> i32;"#, @@ -3292,7 +3335,50 @@ struct S$0T<const C: usize = 1, T = Foo>(T); ``` ```rust - struct ST<const C: usize, T = Foo> + struct ST<const C: usize = 1, T = Foo> + ``` + "#]], + ); +} + +#[test] +fn const_generic_default_value() { + check( + r#" +struct Foo; +struct S$0T<const C: usize = {40 + 2}, T = Foo>(T); +"#, + expect![[r#" + *ST* + + ```rust + test + ``` + + ```rust + struct ST<const C: usize = {const}, T = Foo> + ``` + "#]], + ); +} + +#[test] +fn const_generic_default_value_2() { + check( + r#" +struct Foo; +const VAL = 1; +struct S$0T<const C: usize = VAL, T = Foo>(T); +"#, + expect![[r#" + *ST* + + ```rust + test + ``` + + ```rust + struct ST<const C: usize = VAL, T = Foo> ``` "#]], ); @@ -6469,3 +6555,22 @@ fn test() { "#]], ); } + +#[test] +fn generic_params_disabled_by_cfg() { + check( + r#" +struct S<#[cfg(never)] T>; +fn test() { + let s$0: S = S; +} +"#, + expect![[r#" + *s* + + ```rust + let s: S // size = 0, align = 1 + ``` + "#]], + ); +} diff --git a/src/tools/rust-analyzer/crates/ide/src/lib.rs b/src/tools/rust-analyzer/crates/ide/src/lib.rs index bf77d55d58e..c9cdbff7d7d 100644 --- a/src/tools/rust-analyzer/crates/ide/src/lib.rs +++ b/src/tools/rust-analyzer/crates/ide/src/lib.rs @@ -484,7 +484,7 @@ impl Analysis { sysroot: Option<&OsStr>, ) -> Cancellable<doc_links::DocumentationLinks> { self.with_db(|db| { - doc_links::external_docs(db, &position, target_dir, sysroot).unwrap_or_default() + doc_links::external_docs(db, position, target_dir, sysroot).unwrap_or_default() }) } diff --git a/src/tools/rust-analyzer/crates/ide/src/moniker.rs b/src/tools/rust-analyzer/crates/ide/src/moniker.rs index 17f3771b1a5..2ca2b5b1d5f 100644 --- a/src/tools/rust-analyzer/crates/ide/src/moniker.rs +++ b/src/tools/rust-analyzer/crates/ide/src/moniker.rs @@ -99,7 +99,7 @@ pub(crate) fn moniker( }); } let navs = sema - .descend_into_macros(original_token.clone()) + .descend_into_macros(original_token.clone(), offset) .into_iter() .filter_map(|token| { IdentClass::classify_token(sema, &token).map(IdentClass::definitions_no_ops).map(|it| { diff --git a/src/tools/rust-analyzer/crates/ide/src/navigation_target.rs b/src/tools/rust-analyzer/crates/ide/src/navigation_target.rs index d1479dd1e58..0740bfbc7b1 100644 --- a/src/tools/rust-analyzer/crates/ide/src/navigation_target.rs +++ b/src/tools/rust-analyzer/crates/ide/src/navigation_target.rs @@ -175,8 +175,12 @@ impl TryToNav for FileSymbol { Some(NavigationTarget { file_id: full_range.file_id, - name: if self.is_alias { self.def.name(db)?.to_smol_str() } else { self.name.clone() }, - alias: if self.is_alias { Some(self.name.clone()) } else { None }, + name: self + .is_alias + .then(|| self.def.name(db)) + .flatten() + .map_or_else(|| self.name.clone(), |it| it.to_smol_str()), + alias: self.is_alias.then(|| self.name.clone()), kind: Some(hir::ModuleDefId::from(self.def).into()), full_range: full_range.range, focus_range, diff --git a/src/tools/rust-analyzer/crates/ide/src/references.rs b/src/tools/rust-analyzer/crates/ide/src/references.rs index 813f9ed943f..2d0295692ac 100644 --- a/src/tools/rust-analyzer/crates/ide/src/references.rs +++ b/src/tools/rust-analyzer/crates/ide/src/references.rs @@ -126,7 +126,7 @@ pub(crate) fn find_defs<'a>( ) }); token.map(|token| { - sema.descend_into_macros_with_same_text(token) + sema.descend_into_macros_with_same_text(token, offset) .into_iter() .filter_map(|it| ast::NameLike::cast(it.parent()?)) .filter_map(move |name_like| { diff --git a/src/tools/rust-analyzer/crates/ide/src/signature_help.rs b/src/tools/rust-analyzer/crates/ide/src/signature_help.rs index 7795be54e26..847ab3d21ad 100644 --- a/src/tools/rust-analyzer/crates/ide/src/signature_help.rs +++ b/src/tools/rust-analyzer/crates/ide/src/signature_help.rs @@ -67,17 +67,20 @@ impl SignatureHelp { } /// Computes parameter information for the given position. -pub(crate) fn signature_help(db: &RootDatabase, position: FilePosition) -> Option<SignatureHelp> { +pub(crate) fn signature_help( + db: &RootDatabase, + FilePosition { file_id, offset }: FilePosition, +) -> Option<SignatureHelp> { let sema = Semantics::new(db); - let file = sema.parse(position.file_id); + let file = sema.parse(file_id); let file = file.syntax(); let token = file - .token_at_offset(position.offset) + .token_at_offset(offset) .left_biased() // if the cursor is sandwiched between two space tokens and the call is unclosed // this prevents us from leaving the CallExpression .and_then(|tok| algo::skip_trivia_token(tok, Direction::Prev))?; - let token = sema.descend_into_macros_single(token); + let token = sema.descend_into_macros_single(token, offset); for node in token.parent_ancestors() { match_ast! { @@ -202,7 +205,7 @@ fn signature_help_for_call( res.signature.push('('); { if let Some((self_param, _)) = callable.receiver_param(db) { - format_to!(res.signature, "{}", self_param) + format_to!(res.signature, "{}", self_param.display(db)) } let mut buf = String::new(); for (idx, (pat, ty)) in callable.params(db).into_iter().enumerate() { @@ -1315,6 +1318,25 @@ id! { } #[test] + fn fn_signature_for_method_call_defined_in_macro() { + check( + r#" +macro_rules! id { ($($tt:tt)*) => { $($tt)* } } +struct S; +id! { + impl S { + fn foo<'a>(&'a mut self) {} + } +} +fn test() { S.foo($0); } +"#, + expect![[r#" + fn foo(&'a mut self) + "#]], + ); + } + + #[test] fn call_info_for_lambdas() { check( r#" diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting.rs b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting.rs index ae97236409e..bb01c81d66f 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting.rs +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting.rs @@ -395,10 +395,10 @@ fn traverse( NodeOrToken::Token(token) if token.kind() != COMMENT => { let token = match attr_or_derive_item { Some(AttrOrDerive::Attr(_)) => { - sema.descend_into_macros_with_kind_preference(token) + sema.descend_into_macros_with_kind_preference(token, 0.into()) } Some(AttrOrDerive::Derive(_)) | None => { - sema.descend_into_macros_single(token) + sema.descend_into_macros_single(token, 0.into()) } }; match token.parent().and_then(ast::NameLike::cast) { diff --git a/src/tools/rust-analyzer/crates/mbe/src/syntax_bridge.rs b/src/tools/rust-analyzer/crates/mbe/src/syntax_bridge.rs index 62b2accf5cd..7b9bb61e696 100644 --- a/src/tools/rust-analyzer/crates/mbe/src/syntax_bridge.rs +++ b/src/tools/rust-analyzer/crates/mbe/src/syntax_bridge.rs @@ -961,6 +961,7 @@ impl TtTreeSink<'_> { if has_pseudo_dot { assert!(right.is_empty(), "{left}.{right}"); } else { + assert!(!right.is_empty(), "{left}.{right}"); self.inner.start_node(SyntaxKind::NAME_REF); self.inner.token(SyntaxKind::INT_NUMBER, right); self.inner.finish_node(); diff --git a/src/tools/rust-analyzer/crates/parser/src/grammar.rs b/src/tools/rust-analyzer/crates/parser/src/grammar.rs index 333318f53b7..6a2a9adce15 100644 --- a/src/tools/rust-analyzer/crates/parser/src/grammar.rs +++ b/src/tools/rust-analyzer/crates/parser/src/grammar.rs @@ -184,7 +184,9 @@ pub(crate) mod entry { }; p.bump_any(); while !p.at(EOF) && !p.at(closing_paren_kind) { - expressions::expr(p); + if expressions::expr(p).is_none() { + break; + } if !p.at(EOF) && !p.at(closing_paren_kind) { p.expect(T![,]); } diff --git a/src/tools/rust-analyzer/crates/parser/src/grammar/generic_params.rs b/src/tools/rust-analyzer/crates/parser/src/grammar/generic_params.rs index 8ed1c84c4c6..29d9b05d3f3 100644 --- a/src/tools/rust-analyzer/crates/parser/src/grammar/generic_params.rs +++ b/src/tools/rust-analyzer/crates/parser/src/grammar/generic_params.rs @@ -88,7 +88,7 @@ fn const_param(p: &mut Parser<'_>, m: Marker) { // test const_param_default_path // struct A<const N: i32 = i32::MAX>; - generic_args::const_arg_expr(p); + generic_args::const_arg(p); } m.complete(p, CONST_PARAM); diff --git a/src/tools/rust-analyzer/crates/parser/src/shortcuts.rs b/src/tools/rust-analyzer/crates/parser/src/shortcuts.rs index 53cdad64992..2c47e3d086d 100644 --- a/src/tools/rust-analyzer/crates/parser/src/shortcuts.rs +++ b/src/tools/rust-analyzer/crates/parser/src/shortcuts.rs @@ -46,12 +46,16 @@ impl LexedStr<'_> { // Tag the token as joint if it is float with a fractional part // we use this jointness to inform the parser about what token split // event to emit when we encounter a float literal in a field access - if kind == SyntaxKind::FLOAT_NUMBER && !self.text(i).ends_with('.') { - res.was_joint(); + if kind == SyntaxKind::FLOAT_NUMBER { + if !self.text(i).ends_with('.') { + res.was_joint(); + } else { + was_joint = false; + } + } else { + was_joint = true; } } - - was_joint = true; } } res @@ -204,6 +208,7 @@ impl Builder<'_, '_> { assert!(right.is_empty(), "{left}.{right}"); self.state = State::Normal; } else { + assert!(!right.is_empty(), "{left}.{right}"); (self.sink)(StrStep::Enter { kind: SyntaxKind::NAME_REF }); (self.sink)(StrStep::Token { kind: SyntaxKind::INT_NUMBER, text: right }); (self.sink)(StrStep::Exit); diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0022_recover_from_missing_const_default.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0022_recover_from_missing_const_default.rast index 809ad1b8d5b..49f163b164a 100644 --- a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0022_recover_from_missing_const_default.rast +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0022_recover_from_missing_const_default.rast @@ -20,7 +20,8 @@ SOURCE_FILE IDENT "i32" WHITESPACE " " EQ "=" - WHITESPACE " " + WHITESPACE " " + CONST_ARG COMMA "," WHITESPACE " " CONST_PARAM @@ -37,8 +38,9 @@ SOURCE_FILE IDENT "i32" WHITESPACE " " EQ "=" + CONST_ARG R_ANGLE ">" SEMICOLON ";" WHITESPACE "\n" -error 23: expected a generic const argument +error 24: expected a generic const argument error 40: expected a generic const argument diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0188_const_param_default_path.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0188_const_param_default_path.rast index 11002bf98d0..3f5fb47d287 100644 --- a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0188_const_param_default_path.rast +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0188_const_param_default_path.rast @@ -21,16 +21,17 @@ SOURCE_FILE WHITESPACE " " EQ "=" WHITESPACE " " - PATH_EXPR - PATH + CONST_ARG + PATH_EXPR PATH + PATH + PATH_SEGMENT + NAME_REF + IDENT "i32" + COLON2 "::" PATH_SEGMENT NAME_REF - IDENT "i32" - COLON2 "::" - PATH_SEGMENT - NAME_REF - IDENT "MAX" + IDENT "MAX" R_ANGLE ">" SEMICOLON ";" WHITESPACE "\n" diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0199_const_param_default_expression.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0199_const_param_default_expression.rast index 0607ff54fbb..d6501137498 100644 --- a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0199_const_param_default_expression.rast +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0199_const_param_default_expression.rast @@ -21,14 +21,15 @@ SOURCE_FILE WHITESPACE " " EQ "=" WHITESPACE " " - BLOCK_EXPR - STMT_LIST - L_CURLY "{" - WHITESPACE " " - LITERAL - INT_NUMBER "1" - WHITESPACE " " - R_CURLY "}" + CONST_ARG + BLOCK_EXPR + STMT_LIST + L_CURLY "{" + WHITESPACE " " + LITERAL + INT_NUMBER "1" + WHITESPACE " " + R_CURLY "}" R_ANGLE ">" SEMICOLON ";" WHITESPACE "\n" diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0200_const_param_default_literal.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0200_const_param_default_literal.rast index 8e52313651c..6de10353bf0 100644 --- a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0200_const_param_default_literal.rast +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0200_const_param_default_literal.rast @@ -21,10 +21,11 @@ SOURCE_FILE WHITESPACE " " EQ "=" WHITESPACE " " - PREFIX_EXPR - MINUS "-" - LITERAL - INT_NUMBER "1" + CONST_ARG + PREFIX_EXPR + MINUS "-" + LITERAL + INT_NUMBER "1" R_ANGLE ">" SEMICOLON ";" WHITESPACE "\n" diff --git a/src/tools/rust-analyzer/crates/profile/src/stop_watch.rs b/src/tools/rust-analyzer/crates/profile/src/stop_watch.rs index 71303d5a631..814a0257402 100644 --- a/src/tools/rust-analyzer/crates/profile/src/stop_watch.rs +++ b/src/tools/rust-analyzer/crates/profile/src/stop_watch.rs @@ -10,13 +10,13 @@ pub struct StopWatch { time: Instant, #[cfg(target_os = "linux")] counter: Option<perf_event::Counter>, - memory: Option<MemoryUsage>, + memory: MemoryUsage, } pub struct StopWatchSpan { pub time: Duration, pub instructions: Option<u64>, - pub memory: Option<MemoryUsage>, + pub memory: MemoryUsage, } impl StopWatch { @@ -45,20 +45,16 @@ impl StopWatch { None } }; + let memory = MemoryUsage::now(); let time = Instant::now(); StopWatch { time, #[cfg(target_os = "linux")] counter, - memory: None, + memory, } } - pub fn memory(mut self, yes: bool) -> StopWatch { - if yes { - self.memory = Some(MemoryUsage::now()); - } - self - } + pub fn elapsed(&mut self) -> StopWatchSpan { let time = self.time.elapsed(); @@ -69,7 +65,7 @@ impl StopWatch { #[cfg(not(target_os = "linux"))] let instructions = None; - let memory = self.memory.map(|it| MemoryUsage::now() - it); + let memory = MemoryUsage::now() - self.memory; StopWatchSpan { time, instructions, memory } } } @@ -93,9 +89,7 @@ impl fmt::Display for StopWatchSpan { } write!(f, ", {instructions}{prefix}instr")?; } - if let Some(memory) = self.memory { - write!(f, ", {memory}")?; - } + write!(f, ", {}", self.memory)?; Ok(()) } } diff --git a/src/tools/rust-analyzer/crates/project-model/src/workspace.rs b/src/tools/rust-analyzer/crates/project-model/src/workspace.rs index f51ea7eeb22..13463e9f72e 100644 --- a/src/tools/rust-analyzer/crates/project-model/src/workspace.rs +++ b/src/tools/rust-analyzer/crates/project-model/src/workspace.rs @@ -167,7 +167,8 @@ impl ProjectWorkspace { cmd.envs(&config.extra_env); cmd.arg("--version").current_dir(current_dir); cmd - })?; + }) + .with_context(|| format!("Failed to query rust toolchain version at {current_dir}, is your toolchain setup correctly?"))?; anyhow::Ok( cargo_version .get(prefix.len()..) diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/Cargo.toml b/src/tools/rust-analyzer/crates/rust-analyzer/Cargo.toml index 5bfac7ee45c..1f9d6db9314 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/Cargo.toml +++ b/src/tools/rust-analyzer/crates/rust-analyzer/Cargo.toml @@ -24,7 +24,7 @@ crossbeam-channel = "0.5.5" dissimilar = "1.0.4" itertools = "0.10.5" scip = "0.1.1" -lsp-types = { version = "=0.94", features = ["proposed"] } +lsp-types = { version = "=0.94.0", features = ["proposed"] } parking_lot = "0.12.1" xflags = "0.3.0" oorandom = "11.1.3" diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs index f446a7c0596..4a03be1893c 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs @@ -235,9 +235,7 @@ impl flags::AnalysisStats { if let Some(instructions) = total_span.instructions { report_metric("total instructions", instructions, "#instr"); } - if let Some(memory) = total_span.memory { - report_metric("total memory", memory.allocated.megabytes() as u64, "MB"); - } + report_metric("total memory", total_span.memory.allocated.megabytes() as u64, "MB"); if env::var("RA_COUNT").is_ok() { eprintln!("{}", profile::countme::get_all()); @@ -257,7 +255,7 @@ impl flags::AnalysisStats { eprintln!("source files: {total_file_size}, macro files: {total_macro_file_size}"); } - if self.memory_usage && verbosity.is_verbose() { + if verbosity.is_verbose() { print_memory_usage(host, vfs); } @@ -814,7 +812,7 @@ impl flags::AnalysisStats { } fn stop_watch(&self) -> StopWatch { - StopWatch::start().memory(self.memory_usage) + StopWatch::start() } } diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/flags.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/flags.rs index 13b7f039bb0..419440b6df7 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/flags.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/flags.rs @@ -62,8 +62,6 @@ xflags::xflags! { optional --randomize /// Run type inference in parallel. optional --parallel - /// Collect memory usage statistics. - optional --memory-usage /// Print the total length of all source and macro files (whitespace is not counted). optional --source-stats @@ -191,7 +189,6 @@ pub struct AnalysisStats { pub output: Option<OutputFormat>, pub randomize: bool, pub parallel: bool, - pub memory_usage: bool, pub source_stats: bool, pub only: Option<String>, pub with_deps: bool, diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs index fa20c796ec2..40c50f6d176 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs @@ -150,18 +150,22 @@ config_data! { /// /// Set to `"all"` to pass `--all-features` to Cargo. check_features | checkOnSave_features: Option<CargoFeaturesDef> = "null", + /// List of `cargo check` (or other command specified in `check.command`) diagnostics to ignore. + /// + /// For example for `cargo check`: `dead_code`, `unused_imports`, `unused_variables`,... + check_ignore: FxHashSet<String> = "[]", /// Specifies the working directory for running checks. /// - "workspace": run checks for workspaces in the corresponding workspaces' root directories. // FIXME: Ideally we would support this in some way - /// This falls back to "root" if `#rust-analyzer.cargo.checkOnSave.invocationStrategy#` is set to `once`. + /// This falls back to "root" if `#rust-analyzer.cargo.check.invocationStrategy#` is set to `once`. /// - "root": run checks in the project's root directory. - /// This config only has an effect when `#rust-analyzer.cargo.buildScripts.overrideCommand#` + /// This config only has an effect when `#rust-analyzer.cargo.check.overrideCommand#` /// is set. check_invocationLocation | checkOnSave_invocationLocation: InvocationLocation = "\"workspace\"", - /// Specifies the invocation strategy to use when running the checkOnSave command. + /// Specifies the invocation strategy to use when running the check command. /// If `per_workspace` is set, the command will be executed for each workspace. /// If `once` is set, the command will be executed once. - /// This config only has an effect when `#rust-analyzer.cargo.buildScripts.overrideCommand#` + /// This config only has an effect when `#rust-analyzer.cargo.check.overrideCommand#` /// is set. check_invocationStrategy | checkOnSave_invocationStrategy: InvocationStrategy = "\"per_workspace\"", /// Whether to pass `--no-default-features` to Cargo. Defaults to @@ -1098,6 +1102,7 @@ impl Config { remap_prefix: self.data.diagnostics_remapPrefix.clone(), warnings_as_info: self.data.diagnostics_warningsAsInfo.clone(), warnings_as_hint: self.data.diagnostics_warningsAsHint.clone(), + check_ignore: self.data.check_ignore.clone(), } } diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics.rs index 33422fd058e..b65f38a0c71 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics.rs @@ -6,6 +6,7 @@ use std::mem; use ide::FileId; use ide_db::FxHashMap; use nohash_hasher::{IntMap, IntSet}; +use rustc_hash::FxHashSet; use triomphe::Arc; use crate::lsp_ext; @@ -17,6 +18,7 @@ pub struct DiagnosticsMapConfig { pub remap_prefix: FxHashMap<String, String>, pub warnings_as_info: Vec<String>, pub warnings_as_hint: Vec<String>, + pub check_ignore: FxHashSet<String>, } #[derive(Debug, Default, Clone)] diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics/to_proto.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics/to_proto.rs index e1d1130ff1b..06564578d80 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics/to_proto.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics/to_proto.rs @@ -292,6 +292,13 @@ pub(crate) fn map_rust_diagnostic_to_lsp( let mut source = String::from("rustc"); let mut code = rd.code.as_ref().map(|c| c.code.clone()); + + if let Some(code_val) = &code { + if config.check_ignore.contains(code_val) { + return Vec::new(); + } + } + if let Some(code_val) = &code { // See if this is an RFC #2103 scoped lint (e.g. from Clippy) let scoped_code: Vec<&str> = code_val.split("::").collect(); diff --git a/src/tools/rust-analyzer/crates/syntax/rust.ungram b/src/tools/rust-analyzer/crates/syntax/rust.ungram index 138ddd20897..ea7ebd85b33 100644 --- a/src/tools/rust-analyzer/crates/syntax/rust.ungram +++ b/src/tools/rust-analyzer/crates/syntax/rust.ungram @@ -296,7 +296,7 @@ TypeParam = ConstParam = Attr* 'const' Name ':' Type - ('=' default_val:Expr)? + ('=' default_val:ConstArg)? LifetimeParam = Attr* Lifetime (':' TypeBoundList?)? diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/generated/nodes.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/generated/nodes.rs index 0b27faa535d..16448db04f8 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/ast/generated/nodes.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/ast/generated/nodes.rs @@ -709,7 +709,7 @@ impl ConstParam { pub fn colon_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![:]) } pub fn ty(&self) -> Option<Type> { support::child(&self.syntax) } pub fn eq_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![=]) } - pub fn default_val(&self) -> Option<Expr> { support::child(&self.syntax) } + pub fn default_val(&self) -> Option<ConstArg> { support::child(&self.syntax) } } #[derive(Debug, Clone, PartialEq, Eq, Hash)] diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/make.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/make.rs index 4c6db0ef06c..17e311c0c50 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/ast/make.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/ast/make.rs @@ -503,11 +503,16 @@ pub fn hacky_block_expr( pub fn expr_unit() -> ast::Expr { expr_from_text("()") } + pub fn expr_literal(text: &str) -> ast::Literal { assert_eq!(text.trim(), text); ast_from_text(&format!("fn f() {{ let _ = {text}; }}")) } +pub fn expr_const_value(text: &str) -> ast::ConstArg { + ast_from_text(&format!("trait Foo<const N: usize = {text}> {{}}")) +} + pub fn expr_empty_block() -> ast::Expr { expr_from_text("{}") } @@ -1100,7 +1105,7 @@ pub mod tokens { pub(super) static SOURCE_FILE: Lazy<Parse<SourceFile>> = Lazy::new(|| { SourceFile::parse( - "const C: <()>::Item = (1 != 1, 2 == 2, 3 < 3, 4 <= 4, 5 > 5, 6 >= 6, !true, *p, &p , &mut p)\n;\n\n", + "const C: <()>::Item = ( true && true , true || true , 1 != 1, 2 == 2, 3 < 3, 4 <= 4, 5 > 5, 6 >= 6, !true, *p, &p , &mut p)\n;\n\n", ) }); diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/node_ext.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/node_ext.rs index 3308077da5b..691d0c618f3 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/ast/node_ext.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/ast/node_ext.rs @@ -61,6 +61,14 @@ impl ast::BlockExpr { pub fn tail_expr(&self) -> Option<ast::Expr> { self.stmt_list()?.tail_expr() } + /// Block expressions accept outer and inner attributes, but only when they are the outer + /// expression of an expression statement or the final expression of another block expression. + pub fn may_carry_attributes(&self) -> bool { + matches!( + self.syntax().parent().map(|it| it.kind()), + Some(SyntaxKind::BLOCK_EXPR | SyntaxKind::EXPR_STMT) + ) + } } #[derive(Debug, PartialEq, Eq, Clone)] diff --git a/src/tools/rust-analyzer/crates/syntax/src/lib.rs b/src/tools/rust-analyzer/crates/syntax/src/lib.rs index 4cd668a0cd5..27c8a13e58d 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/lib.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/lib.rs @@ -195,11 +195,16 @@ impl ast::TokenTree { // Tag the token as joint if it is float with a fractional part // we use this jointness to inform the parser about what token split // event to emit when we encounter a float literal in a field access - if kind == SyntaxKind::FLOAT_NUMBER && !t.text().ends_with('.') { - parser_input.was_joint(); + if kind == SyntaxKind::FLOAT_NUMBER { + if !t.text().ends_with('.') { + parser_input.was_joint(); + } else { + was_joint = false; + } + } else { + was_joint = true; } } - was_joint = true; } } @@ -250,6 +255,7 @@ impl ast::TokenTree { if has_pseudo_dot { assert!(right.is_empty(), "{left}.{right}"); } else { + assert!(!right.is_empty(), "{left}.{right}"); builder.start_node(SyntaxKind::NAME_REF); builder.token(SyntaxKind::INT_NUMBER, right); builder.finish_node(); diff --git a/src/tools/rust-analyzer/crates/test-utils/src/fixture.rs b/src/tools/rust-analyzer/crates/test-utils/src/fixture.rs index 75e7a3fec00..3f8b5a08969 100644 --- a/src/tools/rust-analyzer/crates/test-utils/src/fixture.rs +++ b/src/tools/rust-analyzer/crates/test-utils/src/fixture.rs @@ -313,7 +313,7 @@ impl FixtureWithProjectMeta { } impl MiniCore { - const RAW_SOURCE: &str = include_str!("./minicore.rs"); + const RAW_SOURCE: &'static str = include_str!("./minicore.rs"); fn has_flag(&self, flag: &str) -> bool { self.activated_flags.iter().any(|it| it == flag) diff --git a/src/tools/rust-analyzer/docs/user/generated_config.adoc b/src/tools/rust-analyzer/docs/user/generated_config.adoc index ea00c9540ff..71feed0f72c 100644 --- a/src/tools/rust-analyzer/docs/user/generated_config.adoc +++ b/src/tools/rust-analyzer/docs/user/generated_config.adoc @@ -161,23 +161,30 @@ List of features to activate. Defaults to Set to `"all"` to pass `--all-features` to Cargo. -- +[[rust-analyzer.check.ignore]]rust-analyzer.check.ignore (default: `[]`):: ++ +-- +List of `cargo check` (or other command specified in `check.command`) diagnostics to ignore. + +For example for `cargo check`: `dead_code`, `unused_imports`, `unused_variables`,... +-- [[rust-analyzer.check.invocationLocation]]rust-analyzer.check.invocationLocation (default: `"workspace"`):: + -- Specifies the working directory for running checks. - "workspace": run checks for workspaces in the corresponding workspaces' root directories. - This falls back to "root" if `#rust-analyzer.cargo.checkOnSave.invocationStrategy#` is set to `once`. + This falls back to "root" if `#rust-analyzer.cargo.check.invocationStrategy#` is set to `once`. - "root": run checks in the project's root directory. -This config only has an effect when `#rust-analyzer.cargo.buildScripts.overrideCommand#` +This config only has an effect when `#rust-analyzer.cargo.check.overrideCommand#` is set. -- [[rust-analyzer.check.invocationStrategy]]rust-analyzer.check.invocationStrategy (default: `"per_workspace"`):: + -- -Specifies the invocation strategy to use when running the checkOnSave command. +Specifies the invocation strategy to use when running the check command. If `per_workspace` is set, the command will be executed for each workspace. If `once` is set, the command will be executed once. -This config only has an effect when `#rust-analyzer.cargo.buildScripts.overrideCommand#` +This config only has an effect when `#rust-analyzer.cargo.check.overrideCommand#` is set. -- [[rust-analyzer.check.noDefaultFeatures]]rust-analyzer.check.noDefaultFeatures (default: `null`):: diff --git a/src/tools/rust-analyzer/editors/code/package.json b/src/tools/rust-analyzer/editors/code/package.json index 76d7e91f381..44f1b81675a 100644 --- a/src/tools/rust-analyzer/editors/code/package.json +++ b/src/tools/rust-analyzer/editors/code/package.json @@ -292,6 +292,11 @@ "command": "rust-analyzer.viewMemoryLayout", "title": "View Memory Layout", "category": "rust-analyzer" + }, + { + "command": "rust-analyzer.toggleCheckOnSave", + "title": "Toggle Check on Save", + "category": "rust-analyzer" } ], "keybindings": [ @@ -700,8 +705,17 @@ } ] }, + "rust-analyzer.check.ignore": { + "markdownDescription": "List of `cargo check` (or other command specified in `check.command`) diagnostics to ignore.\n\nFor example for `cargo check`: `dead_code`, `unused_imports`, `unused_variables`,...", + "default": [], + "type": "array", + "items": { + "type": "string" + }, + "uniqueItems": true + }, "rust-analyzer.check.invocationLocation": { - "markdownDescription": "Specifies the working directory for running checks.\n- \"workspace\": run checks for workspaces in the corresponding workspaces' root directories.\n This falls back to \"root\" if `#rust-analyzer.cargo.checkOnSave.invocationStrategy#` is set to `once`.\n- \"root\": run checks in the project's root directory.\nThis config only has an effect when `#rust-analyzer.cargo.buildScripts.overrideCommand#`\nis set.", + "markdownDescription": "Specifies the working directory for running checks.\n- \"workspace\": run checks for workspaces in the corresponding workspaces' root directories.\n This falls back to \"root\" if `#rust-analyzer.cargo.check.invocationStrategy#` is set to `once`.\n- \"root\": run checks in the project's root directory.\nThis config only has an effect when `#rust-analyzer.cargo.check.overrideCommand#`\nis set.", "default": "workspace", "type": "string", "enum": [ @@ -714,7 +728,7 @@ ] }, "rust-analyzer.check.invocationStrategy": { - "markdownDescription": "Specifies the invocation strategy to use when running the checkOnSave command.\nIf `per_workspace` is set, the command will be executed for each workspace.\nIf `once` is set, the command will be executed once.\nThis config only has an effect when `#rust-analyzer.cargo.buildScripts.overrideCommand#`\nis set.", + "markdownDescription": "Specifies the invocation strategy to use when running the check command.\nIf `per_workspace` is set, the command will be executed for each workspace.\nIf `once` is set, the command will be executed once.\nThis config only has an effect when `#rust-analyzer.cargo.check.overrideCommand#`\nis set.", "default": "per_workspace", "type": "string", "enum": [ diff --git a/src/tools/rust-analyzer/editors/code/src/bootstrap.ts b/src/tools/rust-analyzer/editors/code/src/bootstrap.ts index ef4dff095cf..6cf399599d9 100644 --- a/src/tools/rust-analyzer/editors/code/src/bootstrap.ts +++ b/src/tools/rust-analyzer/editors/code/src/bootstrap.ts @@ -20,7 +20,7 @@ export async function bootstrap( log.info("Using server binary at", path); - if (!isValidExecutable(path)) { + if (!isValidExecutable(path, config.serverExtraEnv)) { if (config.serverPath) { throw new Error(`Failed to execute ${path} --version. \`config.server.path\` or \`config.serverPath\` has been set explicitly.\ Consider removing this config or making a valid server binary available at that path.`); diff --git a/src/tools/rust-analyzer/editors/code/src/commands.ts b/src/tools/rust-analyzer/editors/code/src/commands.ts index e21f536f26a..aba37bac27d 100644 --- a/src/tools/rust-analyzer/editors/code/src/commands.ts +++ b/src/tools/rust-analyzer/editors/code/src/commands.ts @@ -1407,3 +1407,10 @@ locate() ctx.pushExtCleanup(document); }; } + +export function toggleCheckOnSave(ctx: Ctx): Cmd { + return async () => { + await ctx.config.toggleCheckOnSave(); + ctx.refreshServerStatus(); + }; +} diff --git a/src/tools/rust-analyzer/editors/code/src/config.ts b/src/tools/rust-analyzer/editors/code/src/config.ts index 0e64054c11d..39e2f767c76 100644 --- a/src/tools/rust-analyzer/editors/code/src/config.ts +++ b/src/tools/rust-analyzer/editors/code/src/config.ts @@ -216,6 +216,39 @@ export class Config { ), ); } + get checkOnSave() { + return this.get<boolean>("checkOnSave") ?? false; + } + async toggleCheckOnSave() { + const config = this.cfg.inspect<boolean>("checkOnSave") ?? { key: "checkOnSave" }; + let overrideInLanguage; + let target; + let value; + if ( + config.workspaceFolderValue !== undefined || + config.workspaceFolderLanguageValue !== undefined + ) { + target = vscode.ConfigurationTarget.WorkspaceFolder; + overrideInLanguage = config.workspaceFolderLanguageValue; + value = config.workspaceFolderValue || config.workspaceFolderLanguageValue; + } else if ( + config.workspaceValue !== undefined || + config.workspaceLanguageValue !== undefined + ) { + target = vscode.ConfigurationTarget.Workspace; + overrideInLanguage = config.workspaceLanguageValue; + value = config.workspaceValue || config.workspaceLanguageValue; + } else if (config.globalValue !== undefined || config.globalLanguageValue !== undefined) { + target = vscode.ConfigurationTarget.Global; + overrideInLanguage = config.globalLanguageValue; + value = config.globalValue || config.globalLanguageValue; + } else if (config.defaultValue !== undefined || config.defaultLanguageValue !== undefined) { + overrideInLanguage = config.defaultLanguageValue; + value = config.defaultValue || config.defaultLanguageValue; + } + await this.cfg.update("checkOnSave", !(value || false), target || null, overrideInLanguage); + } + get traceExtension() { return this.get<boolean>("trace.extension"); } diff --git a/src/tools/rust-analyzer/editors/code/src/ctx.ts b/src/tools/rust-analyzer/editors/code/src/ctx.ts index 16c14ca54f2..363a7a82e68 100644 --- a/src/tools/rust-analyzer/editors/code/src/ctx.ts +++ b/src/tools/rust-analyzer/editors/code/src/ctx.ts @@ -94,6 +94,7 @@ export class Ctx { private unlinkedFiles: vscode.Uri[]; private _dependencies: RustDependenciesProvider | undefined; private _treeView: vscode.TreeView<Dependency | DependencyFile | DependencyId> | undefined; + private lastStatus: ServerStatusParams | { health: "stopped" } = { health: "stopped" }; get client() { return this._client; @@ -404,7 +405,15 @@ export class Ctx { } setServerStatus(status: ServerStatusParams | { health: "stopped" }) { + this.lastStatus = status; + this.updateStatusBarItem(); + } + refreshServerStatus() { + this.updateStatusBarItem(); + } + private updateStatusBarItem() { let icon = ""; + const status = this.lastStatus; const statusBar = this.statusBar; statusBar.show(); statusBar.tooltip = new vscode.MarkdownString("", true); @@ -447,13 +456,18 @@ export class Ctx { "statusBarItem.warningBackground", ); statusBar.command = "rust-analyzer.startServer"; - statusBar.text = `$(stop-circle) rust-analyzer`; + statusBar.text = "$(stop-circle) rust-analyzer"; return; } if (statusBar.tooltip.value) { statusBar.tooltip.appendMarkdown("\n\n---\n\n"); } - statusBar.tooltip.appendMarkdown("\n\n[Open logs](command:rust-analyzer.openLogs)"); + statusBar.tooltip.appendMarkdown("\n\n[Open Logs](command:rust-analyzer.openLogs)"); + statusBar.tooltip.appendMarkdown( + `\n\n[${ + this.config.checkOnSave ? "Disable" : "Enable" + } Check on Save](command:rust-analyzer.toggleCheckOnSave)`, + ); statusBar.tooltip.appendMarkdown( "\n\n[Reload Workspace](command:rust-analyzer.reloadWorkspace)", ); diff --git a/src/tools/rust-analyzer/editors/code/src/main.ts b/src/tools/rust-analyzer/editors/code/src/main.ts index 448150bac06..ee5e5b1b80c 100644 --- a/src/tools/rust-analyzer/editors/code/src/main.ts +++ b/src/tools/rust-analyzer/editors/code/src/main.ts @@ -180,6 +180,7 @@ function createCommands(): Record<string, CommandFactory> { ssr: { enabled: commands.ssr }, serverVersion: { enabled: commands.serverVersion }, viewMemoryLayout: { enabled: commands.viewMemoryLayout }, + toggleCheckOnSave: { enabled: commands.toggleCheckOnSave }, // Internal commands which are invoked by the server. applyActionGroup: { enabled: commands.applyActionGroup }, applySnippetWorkspaceEdit: { enabled: commands.applySnippetWorkspaceEditCommand }, diff --git a/src/tools/rust-analyzer/editors/code/src/util.ts b/src/tools/rust-analyzer/editors/code/src/util.ts index 38ce6761578..0414ea0f806 100644 --- a/src/tools/rust-analyzer/editors/code/src/util.ts +++ b/src/tools/rust-analyzer/editors/code/src/util.ts @@ -2,6 +2,7 @@ import * as vscode from "vscode"; import { strict as nativeAssert } from "assert"; import { exec, type ExecOptions, spawnSync } from "child_process"; import { inspect } from "util"; +import type { Env } from "./client"; export function assert(condition: boolean, explanation: string): asserts condition { try { @@ -93,10 +94,13 @@ export function isDocumentInWorkspace(document: RustDocument): boolean { return false; } -export function isValidExecutable(path: string): boolean { +export function isValidExecutable(path: string, extraEnv: Env): boolean { log.debug("Checking availability of a binary at", path); - const res = spawnSync(path, ["--version"], { encoding: "utf8" }); + const res = spawnSync(path, ["--version"], { + encoding: "utf8", + env: { ...process.env, ...extraEnv }, + }); const printOutput = res.error ? log.warn : log.info; printOutput(path, "--version:", res); @@ -151,6 +155,7 @@ export function execute(command: string, options: ExecOptions): Promise<string> } export function executeDiscoverProject(command: string, options: ExecOptions): Promise<string> { + options = Object.assign({ maxBuffer: 10 * 1024 * 1024 }, options); log.info(`running command: ${command}`); return new Promise((resolve, reject) => { exec(command, options, (err, stdout, _) => { diff --git a/src/tools/rust-analyzer/lib/lsp-server/Cargo.toml b/src/tools/rust-analyzer/lib/lsp-server/Cargo.toml index 01707d30191..e1c49db39d5 100644 --- a/src/tools/rust-analyzer/lib/lsp-server/Cargo.toml +++ b/src/tools/rust-analyzer/lib/lsp-server/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "lsp-server" -version = "0.7.2" +version = "0.7.3" description = "Generic LSP server scaffold." license = "MIT OR Apache-2.0" repository = "https://github.com/rust-lang/rust-analyzer/tree/master/lib/lsp-server" @@ -9,7 +9,8 @@ edition = "2021" [dependencies] log = "0.4.17" serde_json = "1.0.96" -serde = { version = "1.0.156", features = ["derive"] } +# See https://github.com/serde-rs/serde/issues/2538#issuecomment-1684517372 for why we pin serde +serde = { version = "1.0.156, < 1.0.172", features = ["derive"] } crossbeam-channel = "0.5.6" [dev-dependencies] diff --git a/src/tools/rust-analyzer/lib/lsp-server/src/stdio.rs b/src/tools/rust-analyzer/lib/lsp-server/src/stdio.rs index 49a825e579b..e487b9b4622 100644 --- a/src/tools/rust-analyzer/lib/lsp-server/src/stdio.rs +++ b/src/tools/rust-analyzer/lib/lsp-server/src/stdio.rs @@ -3,6 +3,8 @@ use std::{ thread, }; +use log::debug; + use crossbeam_channel::{bounded, Receiver, Sender}; use crate::Message; @@ -23,7 +25,8 @@ pub(crate) fn stdio_transport() -> (Sender<Message>, Receiver<Message>, IoThread while let Some(msg) = Message::read(&mut stdin)? { let is_exit = matches!(&msg, Message::Notification(n) if n.is_exit()); - reader_sender.send(msg).unwrap(); + debug!("sending message {:#?}", msg); + reader_sender.send(msg).expect("receiver was dropped, failed to send a message"); if is_exit { break; diff --git a/src/tools/rust-analyzer/xtask/src/metrics.rs b/src/tools/rust-analyzer/xtask/src/metrics.rs index 68537423195..e4710260409 100644 --- a/src/tools/rust-analyzer/xtask/src/metrics.rs +++ b/src/tools/rust-analyzer/xtask/src/metrics.rs @@ -103,9 +103,7 @@ impl Metrics { path: &str, ) -> anyhow::Result<()> { eprintln!("\nMeasuring analysis-stats/{name}"); - let output = - cmd!(sh, "./target/release/rust-analyzer -q analysis-stats --memory-usage {path}") - .read()?; + let output = cmd!(sh, "./target/release/rust-analyzer -q analysis-stats {path}").read()?; for (metric, value, unit) in parse_metrics(&output) { self.report(&format!("analysis-stats/{name}/{metric}"), value, unit.into()); } diff --git a/src/version b/src/version index 5e3a4256626..dc87e8af82f 100644 --- a/src/version +++ b/src/version @@ -1 +1 @@ -1.73.0 +1.74.0 diff --git a/tests/codegen/debuginfo-inline-callsite-location.rs b/tests/codegen/debuginfo-inline-callsite-location.rs new file mode 100644 index 00000000000..e2ea9dda4a0 --- /dev/null +++ b/tests/codegen/debuginfo-inline-callsite-location.rs @@ -0,0 +1,26 @@ +// compile-flags: -g -O + +// Check that each inline call site for the same function uses the same "sub-program" so that LLVM +// can correctly merge the debug info if it merges the inlined code (e.g., for merging of tail +// calls to panic. + +// CHECK: tail call void @_ZN4core9panicking5panic17h{{([0-9a-z]{16})}}E +// CHECK-SAME: !dbg ![[#first_dbg:]] +// CHECK: tail call void @_ZN4core9panicking5panic17h{{([0-9a-z]{16})}}E +// CHECK-SAME: !dbg ![[#second_dbg:]] + +// CHECK: ![[#func_dbg:]] = distinct !DISubprogram(name: "unwrap<i32>" +// CHECK: ![[#first_dbg]] = !DILocation(line: [[#]] +// CHECK-SAME: scope: ![[#func_dbg]], inlinedAt: ![[#]]) +// CHECK: ![[#second_dbg]] = !DILocation(line: [[#]] +// CHECK-SAME: scope: ![[#func_dbg]], inlinedAt: ![[#]]) + +#![crate_type = "lib"] + +#[no_mangle] +extern "C" fn add_numbers(x: &Option<i32>, y: &Option<i32>) -> i32 { + let x1 = x.unwrap(); + let y1 = y.unwrap(); + + x1 + y1 +} diff --git a/tests/codegen/slice-ref-equality.rs b/tests/codegen/slice-ref-equality.rs index 4d0dce7b074..afbdf66ce0a 100644 --- a/tests/codegen/slice-ref-equality.rs +++ b/tests/codegen/slice-ref-equality.rs @@ -44,48 +44,48 @@ pub fn is_zero_array(data: &[u8; 4]) -> bool { // equality for non-byte types also just emit a `bcmp`, not a loop. // CHECK-LABEL: @eq_slice_of_nested_u8( -// CHECK-SAME: [[USIZE:i16|i32|i64]] noundef %x.1 -// CHECK-SAME: [[USIZE]] noundef %y.1 +// CHECK-SAME: [[USIZE:i16|i32|i64]] noundef %1 +// CHECK-SAME: [[USIZE]] noundef %3 #[no_mangle] fn eq_slice_of_nested_u8(x: &[[u8; 3]], y: &[[u8; 3]]) -> bool { - // CHECK: icmp eq [[USIZE]] %x.1, %y.1 - // CHECK: %[[BYTES:.+]] = mul nsw [[USIZE]] %x.1, 3 + // CHECK: icmp eq [[USIZE]] %1, %3 + // CHECK: %[[BYTES:.+]] = mul nsw [[USIZE]] %1, 3 // CHECK: tail call{{( noundef)?}} i32 @{{bcmp|memcmp}}(ptr // CHECK-SAME: , [[USIZE]]{{( noundef)?}} %[[BYTES]]) x == y } // CHECK-LABEL: @eq_slice_of_i32( -// CHECK-SAME: [[USIZE:i16|i32|i64]] noundef %x.1 -// CHECK-SAME: [[USIZE]] noundef %y.1 +// CHECK-SAME: [[USIZE:i16|i32|i64]] noundef %1 +// CHECK-SAME: [[USIZE]] noundef %3 #[no_mangle] fn eq_slice_of_i32(x: &[i32], y: &[i32]) -> bool { - // CHECK: icmp eq [[USIZE]] %x.1, %y.1 - // CHECK: %[[BYTES:.+]] = shl nsw [[USIZE]] %x.1, 2 + // CHECK: icmp eq [[USIZE]] %1, %3 + // CHECK: %[[BYTES:.+]] = shl nsw [[USIZE]] %1, 2 // CHECK: tail call{{( noundef)?}} i32 @{{bcmp|memcmp}}(ptr // CHECK-SAME: , [[USIZE]]{{( noundef)?}} %[[BYTES]]) x == y } // CHECK-LABEL: @eq_slice_of_nonzero( -// CHECK-SAME: [[USIZE:i16|i32|i64]] noundef %x.1 -// CHECK-SAME: [[USIZE]] noundef %y.1 +// CHECK-SAME: [[USIZE:i16|i32|i64]] noundef %1 +// CHECK-SAME: [[USIZE]] noundef %3 #[no_mangle] fn eq_slice_of_nonzero(x: &[NonZeroU32], y: &[NonZeroU32]) -> bool { - // CHECK: icmp eq [[USIZE]] %x.1, %y.1 - // CHECK: %[[BYTES:.+]] = shl nsw [[USIZE]] %x.1, 2 + // CHECK: icmp eq [[USIZE]] %1, %3 + // CHECK: %[[BYTES:.+]] = shl nsw [[USIZE]] %1, 2 // CHECK: tail call{{( noundef)?}} i32 @{{bcmp|memcmp}}(ptr // CHECK-SAME: , [[USIZE]]{{( noundef)?}} %[[BYTES]]) x == y } // CHECK-LABEL: @eq_slice_of_option_of_nonzero( -// CHECK-SAME: [[USIZE:i16|i32|i64]] noundef %x.1 -// CHECK-SAME: [[USIZE]] noundef %y.1 +// CHECK-SAME: [[USIZE:i16|i32|i64]] noundef %1 +// CHECK-SAME: [[USIZE]] noundef %3 #[no_mangle] fn eq_slice_of_option_of_nonzero(x: &[Option<NonZeroI16>], y: &[Option<NonZeroI16>]) -> bool { - // CHECK: icmp eq [[USIZE]] %x.1, %y.1 - // CHECK: %[[BYTES:.+]] = shl nsw [[USIZE]] %x.1, 1 + // CHECK: icmp eq [[USIZE]] %1, %3 + // CHECK: %[[BYTES:.+]] = shl nsw [[USIZE]] %1, 1 // CHECK: tail call{{( noundef)?}} i32 @{{bcmp|memcmp}}(ptr // CHECK-SAME: , [[USIZE]]{{( noundef)?}} %[[BYTES]]) x == y diff --git a/tests/mir-opt/building/async_await.a-{closure#0}.generator_resume.0.mir b/tests/mir-opt/building/async_await.a-{closure#0}.generator_resume.0.mir index 074ebddf78b..9be5b8509c7 100644 --- a/tests/mir-opt/building/async_await.a-{closure#0}.generator_resume.0.mir +++ b/tests/mir-opt/building/async_await.a-{closure#0}.generator_resume.0.mir @@ -30,7 +30,7 @@ fn a::{closure#0}(_1: Pin<&mut [async fn body@$DIR/async_await.rs:11:14: 11:16]> } bb2: { - assert(const false, "`async fn` resumed after completion") -> [success: bb2, unwind continue]; + assert(const false, "`async fn` resumed after completion") -> [success: bb2, unwind unreachable]; } bb3: { diff --git a/tests/mir-opt/building/async_await.b-{closure#0}.generator_resume.0.mir b/tests/mir-opt/building/async_await.b-{closure#0}.generator_resume.0.mir index f774f32eb23..80ac92d59f3 100644 --- a/tests/mir-opt/building/async_await.b-{closure#0}.generator_resume.0.mir +++ b/tests/mir-opt/building/async_await.b-{closure#0}.generator_resume.0.mir @@ -310,7 +310,7 @@ fn b::{closure#0}(_1: Pin<&mut [async fn body@$DIR/async_await.rs:14:18: 17:2]>, } bb28: { - assert(const false, "`async fn` resumed after completion") -> [success: bb28, unwind continue]; + assert(const false, "`async fn` resumed after completion") -> [success: bb28, unwind unreachable]; } bb29: { diff --git a/tests/mir-opt/building/custom/terminators.rs b/tests/mir-opt/building/custom/terminators.rs index f1240566168..123118f654e 100644 --- a/tests/mir-opt/building/custom/terminators.rs +++ b/tests/mir-opt/building/custom/terminators.rs @@ -12,7 +12,7 @@ fn ident<T>(t: T) -> T { fn direct_call(x: i32) -> i32 { mir!( { - Call(RET, retblock, ident(x)) + Call(RET = ident(x), retblock) } retblock = { @@ -26,7 +26,7 @@ fn direct_call(x: i32) -> i32 { fn indirect_call(x: i32, f: fn(i32) -> i32) -> i32 { mir!( { - Call(RET, retblock, f(x)) + Call(RET = f(x), retblock) } retblock = { diff --git a/tests/mir-opt/copy-prop/borrowed_local.rs b/tests/mir-opt/copy-prop/borrowed_local.rs index a89b64441d0..bf94dc57d33 100644 --- a/tests/mir-opt/copy-prop/borrowed_local.rs +++ b/tests/mir-opt/copy-prop/borrowed_local.rs @@ -21,11 +21,11 @@ fn f() -> bool { let b = a; // We cannot propagate the place `a`. let r2 = &b; - Call(RET, next, cmp_ref(r1, r2)) + Call(RET = cmp_ref(r1, r2), next) } next = { // But we can propagate the value `a`. - Call(RET, ret, opaque(b)) + Call(RET = opaque(b), ret) } ret = { Return() diff --git a/tests/mir-opt/copy-prop/custom_move_arg.rs b/tests/mir-opt/copy-prop/custom_move_arg.rs index a90db08fa51..d1c5ffdff0d 100644 --- a/tests/mir-opt/copy-prop/custom_move_arg.rs +++ b/tests/mir-opt/copy-prop/custom_move_arg.rs @@ -13,11 +13,11 @@ struct NotCopy(bool); fn f(_1: NotCopy) { mir!({ let _2 = _1; - Call(RET, bb1, opaque(Move(_1))) + Call(RET = opaque(Move(_1)), bb1) } bb1 = { let _3 = Move(_2); - Call(RET, bb2, opaque(_3)) + Call(RET = opaque(_3), bb2) } bb2 = { Return() diff --git a/tests/mir-opt/copy-prop/move_projection.rs b/tests/mir-opt/copy-prop/move_projection.rs index 40f51ce8406..f94addb5629 100644 --- a/tests/mir-opt/copy-prop/move_projection.rs +++ b/tests/mir-opt/copy-prop/move_projection.rs @@ -17,10 +17,10 @@ fn f(a: Foo) -> bool { let b = a; // This is a move out of a copy, so must become a copy of `a.0`. let c = Move(b.0); - Call(RET, bb1, opaque(Move(a))) + Call(RET = opaque(Move(a)), bb1) } bb1 = { - Call(RET, ret, opaque(Move(c))) + Call(RET = opaque(Move(c)), ret) } ret = { Return() diff --git a/tests/mir-opt/inline/unwrap_unchecked.unwrap_unchecked.Inline.panic-abort.diff b/tests/mir-opt/inline/unwrap_unchecked.unwrap_unchecked.Inline.panic-abort.diff index 486f276b21c..e3c57347392 100644 --- a/tests/mir-opt/inline/unwrap_unchecked.unwrap_unchecked.Inline.panic-abort.diff +++ b/tests/mir-opt/inline/unwrap_unchecked.unwrap_unchecked.Inline.panic-abort.diff @@ -7,7 +7,8 @@ let mut _2: std::option::Option<T>; + scope 1 (inlined #[track_caller] Option::<T>::unwrap_unchecked) { + debug self => _2; -+ let mut _3: isize; ++ let mut _3: &std::option::Option<T>; ++ let mut _4: isize; + scope 2 { + debug val => _0; + } @@ -20,7 +21,7 @@ + } + } + scope 4 (inlined Option::<T>::is_some) { -+ debug self => &_2; ++ debug self => _3; + } + } @@ -28,8 +29,9 @@ StorageLive(_2); _2 = move _1; - _0 = Option::<T>::unwrap_unchecked(move _2) -> [return: bb1, unwind unreachable]; -+ _3 = discriminant(_2); -+ switchInt(move _3) -> [1: bb2, otherwise: bb1]; ++ StorageLive(_3); ++ _4 = discriminant(_2); ++ switchInt(move _4) -> [1: bb2, otherwise: bb1]; } bb1: { @@ -38,6 +40,7 @@ + + bb2: { + _0 = move ((_2 as Some).0: T); ++ StorageDead(_3); StorageDead(_2); return; } diff --git a/tests/mir-opt/inline/unwrap_unchecked.unwrap_unchecked.Inline.panic-unwind.diff b/tests/mir-opt/inline/unwrap_unchecked.unwrap_unchecked.Inline.panic-unwind.diff index 1c3aa537946..fc638cb3ace 100644 --- a/tests/mir-opt/inline/unwrap_unchecked.unwrap_unchecked.Inline.panic-unwind.diff +++ b/tests/mir-opt/inline/unwrap_unchecked.unwrap_unchecked.Inline.panic-unwind.diff @@ -7,7 +7,8 @@ let mut _2: std::option::Option<T>; + scope 1 (inlined #[track_caller] Option::<T>::unwrap_unchecked) { + debug self => _2; -+ let mut _3: isize; ++ let mut _3: &std::option::Option<T>; ++ let mut _4: isize; + scope 2 { + debug val => _0; + } @@ -20,7 +21,7 @@ + } + } + scope 4 (inlined Option::<T>::is_some) { -+ debug self => &_2; ++ debug self => _3; + } + } @@ -28,8 +29,9 @@ StorageLive(_2); _2 = move _1; - _0 = Option::<T>::unwrap_unchecked(move _2) -> [return: bb1, unwind: bb2]; -+ _3 = discriminant(_2); -+ switchInt(move _3) -> [1: bb2, otherwise: bb1]; ++ StorageLive(_3); ++ _4 = discriminant(_2); ++ switchInt(move _4) -> [1: bb2, otherwise: bb1]; } bb1: { @@ -42,6 +44,7 @@ - resume; + bb2: { + _0 = move ((_2 as Some).0: T); ++ StorageDead(_3); + StorageDead(_2); + return; } diff --git a/tests/mir-opt/inline/unwrap_unchecked.unwrap_unchecked.PreCodegen.after.panic-abort.mir b/tests/mir-opt/inline/unwrap_unchecked.unwrap_unchecked.PreCodegen.after.panic-abort.mir index 82238626798..fcc4d43ced6 100644 --- a/tests/mir-opt/inline/unwrap_unchecked.unwrap_unchecked.PreCodegen.after.panic-abort.mir +++ b/tests/mir-opt/inline/unwrap_unchecked.unwrap_unchecked.PreCodegen.after.panic-abort.mir @@ -6,6 +6,7 @@ fn unwrap_unchecked(_1: Option<T>) -> T { scope 1 (inlined #[track_caller] Option::<T>::unwrap_unchecked) { debug self => _1; let mut _2: isize; + let mut _3: &std::option::Option<T>; scope 2 { debug val => _0; } @@ -18,17 +19,19 @@ fn unwrap_unchecked(_1: Option<T>) -> T { } } scope 4 (inlined Option::<T>::is_some) { - debug self => &_1; + debug self => _3; } } bb0: { + StorageLive(_3); _2 = discriminant(_1); switchInt(move _2) -> [1: bb1, otherwise: bb2]; } bb1: { _0 = move ((_1 as Some).0: T); + StorageDead(_3); return; } diff --git a/tests/mir-opt/inline/unwrap_unchecked.unwrap_unchecked.PreCodegen.after.panic-unwind.mir b/tests/mir-opt/inline/unwrap_unchecked.unwrap_unchecked.PreCodegen.after.panic-unwind.mir index 82238626798..fcc4d43ced6 100644 --- a/tests/mir-opt/inline/unwrap_unchecked.unwrap_unchecked.PreCodegen.after.panic-unwind.mir +++ b/tests/mir-opt/inline/unwrap_unchecked.unwrap_unchecked.PreCodegen.after.panic-unwind.mir @@ -6,6 +6,7 @@ fn unwrap_unchecked(_1: Option<T>) -> T { scope 1 (inlined #[track_caller] Option::<T>::unwrap_unchecked) { debug self => _1; let mut _2: isize; + let mut _3: &std::option::Option<T>; scope 2 { debug val => _0; } @@ -18,17 +19,19 @@ fn unwrap_unchecked(_1: Option<T>) -> T { } } scope 4 (inlined Option::<T>::is_some) { - debug self => &_1; + debug self => _3; } } bb0: { + StorageLive(_3); _2 = discriminant(_1); switchInt(move _2) -> [1: bb1, otherwise: bb2]; } bb1: { _0 = move ((_1 as Some).0: T); + StorageDead(_3); return; } diff --git a/tests/mir-opt/issue_76432.test.SimplifyComparisonIntegral.panic-abort.diff b/tests/mir-opt/issue_76432.test.SimplifyComparisonIntegral.panic-abort.diff index b647455aeec..f61632728ba 100644 --- a/tests/mir-opt/issue_76432.test.SimplifyComparisonIntegral.panic-abort.diff +++ b/tests/mir-opt/issue_76432.test.SimplifyComparisonIntegral.panic-abort.diff @@ -13,13 +13,16 @@ let mut _8: usize; let mut _9: usize; let mut _10: bool; - let mut _11: !; + let mut _14: !; scope 1 { debug v => _2; + let _11: &T; + let _12: &T; + let _13: &T; scope 2 { - debug v1 => &(*_2)[0 of 3]; - debug v2 => &(*_2)[1 of 3]; - debug v3 => &(*_2)[2 of 3]; + debug v1 => _11; + debug v2 => _12; + debug v3 => _13; } } @@ -39,10 +42,19 @@ } bb1: { - _11 = core::panicking::panic(const "internal error: entered unreachable code") -> unwind unreachable; + _14 = core::panicking::panic(const "internal error: entered unreachable code") -> unwind unreachable; } bb2: { + StorageLive(_11); + _11 = &(*_2)[0 of 3]; + StorageLive(_12); + _12 = &(*_2)[1 of 3]; + StorageLive(_13); + _13 = &(*_2)[2 of 3]; + StorageDead(_13); + StorageDead(_12); + StorageDead(_11); StorageDead(_4); return; } diff --git a/tests/mir-opt/issue_76432.test.SimplifyComparisonIntegral.panic-unwind.diff b/tests/mir-opt/issue_76432.test.SimplifyComparisonIntegral.panic-unwind.diff index b02be61d031..f6c337be10f 100644 --- a/tests/mir-opt/issue_76432.test.SimplifyComparisonIntegral.panic-unwind.diff +++ b/tests/mir-opt/issue_76432.test.SimplifyComparisonIntegral.panic-unwind.diff @@ -13,13 +13,16 @@ let mut _8: usize; let mut _9: usize; let mut _10: bool; - let mut _11: !; + let mut _14: !; scope 1 { debug v => _2; + let _11: &T; + let _12: &T; + let _13: &T; scope 2 { - debug v1 => &(*_2)[0 of 3]; - debug v2 => &(*_2)[1 of 3]; - debug v3 => &(*_2)[2 of 3]; + debug v1 => _11; + debug v2 => _12; + debug v3 => _13; } } @@ -39,10 +42,19 @@ } bb1: { - _11 = core::panicking::panic(const "internal error: entered unreachable code") -> unwind continue; + _14 = core::panicking::panic(const "internal error: entered unreachable code") -> unwind continue; } bb2: { + StorageLive(_11); + _11 = &(*_2)[0 of 3]; + StorageLive(_12); + _12 = &(*_2)[1 of 3]; + StorageLive(_13); + _13 = &(*_2)[2 of 3]; + StorageDead(_13); + StorageDead(_12); + StorageDead(_11); StorageDead(_4); return; } diff --git a/tests/mir-opt/issues/issue_59352.num_to_digit.PreCodegen.after.panic-abort.mir b/tests/mir-opt/issues/issue_59352.num_to_digit.PreCodegen.after.panic-abort.mir index a7a14ea645b..f8c85941813 100644 --- a/tests/mir-opt/issues/issue_59352.num_to_digit.PreCodegen.after.panic-abort.mir +++ b/tests/mir-opt/issues/issue_59352.num_to_digit.PreCodegen.after.panic-abort.mir @@ -8,8 +8,9 @@ fn num_to_digit(_1: char) -> u32 { debug self => _1; debug radix => const 8_u32; let _2: std::option::Option<u32>; + let mut _7: &std::option::Option<u32>; scope 2 (inlined Option::<u32>::is_some) { - debug self => &_2; + debug self => _7; let mut _3: isize; } } @@ -23,12 +24,14 @@ fn num_to_digit(_1: char) -> u32 { } bb0: { + StorageLive(_7); StorageLive(_2); _2 = char::methods::<impl char>::to_digit(_1, const 8_u32) -> [return: bb1, unwind unreachable]; } bb1: { _3 = discriminant(_2); + StorageDead(_7); StorageDead(_2); switchInt(move _3) -> [1: bb2, otherwise: bb7]; } diff --git a/tests/mir-opt/issues/issue_59352.num_to_digit.PreCodegen.after.panic-unwind.mir b/tests/mir-opt/issues/issue_59352.num_to_digit.PreCodegen.after.panic-unwind.mir index 5f8c6f7283c..df7392edc50 100644 --- a/tests/mir-opt/issues/issue_59352.num_to_digit.PreCodegen.after.panic-unwind.mir +++ b/tests/mir-opt/issues/issue_59352.num_to_digit.PreCodegen.after.panic-unwind.mir @@ -8,8 +8,9 @@ fn num_to_digit(_1: char) -> u32 { debug self => _1; debug radix => const 8_u32; let _2: std::option::Option<u32>; + let mut _7: &std::option::Option<u32>; scope 2 (inlined Option::<u32>::is_some) { - debug self => &_2; + debug self => _7; let mut _3: isize; } } @@ -23,12 +24,14 @@ fn num_to_digit(_1: char) -> u32 { } bb0: { + StorageLive(_7); StorageLive(_2); _2 = char::methods::<impl char>::to_digit(_1, const 8_u32) -> [return: bb1, unwind continue]; } bb1: { _3 = discriminant(_2); + StorageDead(_7); StorageDead(_2); switchInt(move _3) -> [1: bb2, otherwise: bb7]; } diff --git a/tests/mir-opt/pre-codegen/checked_ops.step_forward.PreCodegen.after.mir b/tests/mir-opt/pre-codegen/checked_ops.step_forward.PreCodegen.after.mir index 9be41bff3ca..b2ea96f033e 100644 --- a/tests/mir-opt/pre-codegen/checked_ops.step_forward.PreCodegen.after.mir +++ b/tests/mir-opt/pre-codegen/checked_ops.step_forward.PreCodegen.after.mir @@ -10,13 +10,14 @@ fn step_forward(_1: u32, _2: usize) -> u32 { let _3: std::option::Option<u32>; let mut _6: bool; let mut _7: u32; + let mut _8: &std::option::Option<u32>; scope 2 { } scope 3 (inlined Option::<u32>::is_none) { - debug self => &_3; + debug self => _8; let mut _5: bool; scope 4 (inlined Option::<u32>::is_some) { - debug self => &_3; + debug self => _8; let mut _4: isize; } } @@ -28,6 +29,7 @@ fn step_forward(_1: u32, _2: usize) -> u32 { bb0: { StorageLive(_6); + StorageLive(_8); StorageLive(_3); _3 = <u32 as Step>::forward_checked(_1, _2) -> [return: bb1, unwind continue]; } @@ -39,6 +41,7 @@ fn step_forward(_1: u32, _2: usize) -> u32 { _6 = Not(move _5); StorageDead(_5); StorageDead(_3); + StorageDead(_8); switchInt(move _6) -> [0: bb3, otherwise: bb2]; } diff --git a/tests/mir-opt/pre-codegen/loops.filter_mapped.PreCodegen.after.mir b/tests/mir-opt/pre-codegen/loops.filter_mapped.PreCodegen.after.mir index 07a57a7b578..940b9ae1156 100644 --- a/tests/mir-opt/pre-codegen/loops.filter_mapped.PreCodegen.after.mir +++ b/tests/mir-opt/pre-codegen/loops.filter_mapped.PreCodegen.after.mir @@ -10,6 +10,7 @@ fn filter_mapped(_1: impl Iterator<Item = T>, _2: impl Fn(T) -> Option<U>) -> () let mut _8: std::option::Option<U>; let mut _9: isize; let _11: (); + let mut _12: &mut std::iter::FilterMap<impl Iterator<Item = T>, impl Fn(T) -> Option<U>>; scope 1 { debug iter => _5; let _10: U; @@ -17,7 +18,7 @@ fn filter_mapped(_1: impl Iterator<Item = T>, _2: impl Fn(T) -> Option<U>) -> () debug x => _10; } scope 4 (inlined <FilterMap<impl Iterator<Item = T>, impl Fn(T) -> Option<U>> as Iterator>::next) { - debug self => &_5; + debug self => _12; let mut _6: &mut impl Iterator<Item = T>; let mut _7: &mut impl Fn(T) -> Option<U>; } diff --git a/tests/mir-opt/pre-codegen/loops.int_range.PreCodegen.after.mir b/tests/mir-opt/pre-codegen/loops.int_range.PreCodegen.after.mir index 4c6bcd1bdbd..2e51faeba5a 100644 --- a/tests/mir-opt/pre-codegen/loops.int_range.PreCodegen.after.mir +++ b/tests/mir-opt/pre-codegen/loops.int_range.PreCodegen.after.mir @@ -4,95 +4,108 @@ fn int_range(_1: usize, _2: usize) -> () { debug start => _1; debug end => _2; let mut _0: (); - let mut _3: usize; - let mut _6: std::option::Option<usize>; - let mut _9: isize; - let _11: (); + let mut _3: std::ops::Range<usize>; + let mut _4: std::ops::Range<usize>; + let mut _8: std::option::Option<usize>; + let mut _11: isize; + let _13: (); + let mut _14: &mut std::ops::Range<usize>; scope 1 { - debug iter => std::ops::Range<usize>{ .0 => _3, .1 => _2, }; - let _10: usize; + debug iter => _4; + let _12: usize; scope 2 { - debug i => _10; + debug i => _12; } scope 4 (inlined iter::range::<impl Iterator for std::ops::Range<usize>>::next) { - debug self => &std::ops::Range<usize>{ .0 => _3, .1 => _2, }; + debug self => _14; scope 5 (inlined <std::ops::Range<usize> as iter::range::RangeIteratorImpl>::spec_next) { - debug self => &std::ops::Range<usize>{ .0 => _3, .1 => _2, }; - let mut _5: bool; - let _7: usize; - let mut _8: usize; + debug self => _14; + let mut _7: bool; + let _9: usize; + let mut _10: usize; + let mut _15: &usize; + let mut _16: &usize; scope 6 { - debug old => _7; + debug old => _9; scope 7 { } } scope 8 (inlined cmp::impls::<impl PartialOrd for usize>::lt) { - debug self => &_3; - debug other => &_2; - let mut _4: usize; + debug self => _15; + debug other => _16; + let mut _5: usize; + let mut _6: usize; } } } } scope 3 (inlined <std::ops::Range<usize> as IntoIterator>::into_iter) { - debug self => std::ops::Range<usize>{ .0 => _1, .1 => _2, }; + debug self => _3; } bb0: { - StorageLive(_3); - _3 = _1; + _3 = std::ops::Range::<usize> { start: _1, end: _2 }; + StorageLive(_4); + _4 = move _3; goto -> bb1; } bb1: { - StorageLive(_6); + StorageLive(_8); + StorageLive(_9); StorageLive(_7); + StorageLive(_15); + StorageLive(_16); StorageLive(_5); - StorageLive(_4); - _4 = _3; - _5 = Lt(move _4, _2); - StorageDead(_4); - switchInt(move _5) -> [0: bb2, otherwise: bb3]; + _5 = (_4.0: usize); + StorageLive(_6); + _6 = (_4.1: usize); + _7 = Lt(move _5, move _6); + StorageDead(_6); + StorageDead(_5); + StorageDead(_16); + StorageDead(_15); + switchInt(move _7) -> [0: bb2, otherwise: bb3]; } bb2: { - _6 = Option::<usize>::None; + _8 = Option::<usize>::None; goto -> bb5; } bb3: { - _7 = _3; - StorageLive(_8); - _8 = <usize as Step>::forward_unchecked(_7, const 1_usize) -> [return: bb4, unwind continue]; + _9 = (_4.0: usize); + StorageLive(_10); + _10 = <usize as Step>::forward_unchecked(_9, const 1_usize) -> [return: bb4, unwind continue]; } bb4: { - _3 = move _8; - StorageDead(_8); - _6 = Option::<usize>::Some(_7); + (_4.0: usize) = move _10; + StorageDead(_10); + _8 = Option::<usize>::Some(_9); goto -> bb5; } bb5: { - StorageDead(_5); StorageDead(_7); - _9 = discriminant(_6); - switchInt(move _9) -> [0: bb6, 1: bb7, otherwise: bb9]; + StorageDead(_9); + _11 = discriminant(_8); + switchInt(move _11) -> [0: bb6, 1: bb7, otherwise: bb9]; } bb6: { - StorageDead(_6); - StorageDead(_3); + StorageDead(_8); + StorageDead(_4); return; } bb7: { - _10 = ((_6 as Some).0: usize); - _11 = opaque::<usize>(move _10) -> [return: bb8, unwind continue]; + _12 = ((_8 as Some).0: usize); + _13 = opaque::<usize>(move _12) -> [return: bb8, unwind continue]; } bb8: { - StorageDead(_6); + StorageDead(_8); goto -> bb1; } diff --git a/tests/mir-opt/pre-codegen/range_iter.forward_loop.PreCodegen.after.panic-abort.mir b/tests/mir-opt/pre-codegen/range_iter.forward_loop.PreCodegen.after.panic-abort.mir index cdaa3cfc995..d76b46bdd94 100644 --- a/tests/mir-opt/pre-codegen/range_iter.forward_loop.PreCodegen.after.panic-abort.mir +++ b/tests/mir-opt/pre-codegen/range_iter.forward_loop.PreCodegen.after.panic-abort.mir @@ -5,87 +5,100 @@ fn forward_loop(_1: u32, _2: u32, _3: impl Fn(u32)) -> () { debug end => _2; debug f => _3; let mut _0: (); - let mut _4: u32; - let mut _7: std::option::Option<u32>; - let mut _10: isize; - let mut _12: &impl Fn(u32); - let mut _13: (u32,); - let _14: (); + let mut _4: std::ops::Range<u32>; + let mut _5: std::ops::Range<u32>; + let mut _9: std::option::Option<u32>; + let mut _12: isize; + let mut _14: &impl Fn(u32); + let mut _15: (u32,); + let _16: (); + let mut _17: &mut std::ops::Range<u32>; scope 1 { - debug iter => std::ops::Range<u32>{ .0 => _4, .1 => _2, }; - let _11: u32; + debug iter => _5; + let _13: u32; scope 2 { - debug x => _11; + debug x => _13; } scope 4 (inlined iter::range::<impl Iterator for std::ops::Range<u32>>::next) { - debug self => &std::ops::Range<u32>{ .0 => _4, .1 => _2, }; + debug self => _17; scope 5 (inlined <std::ops::Range<u32> as iter::range::RangeIteratorImpl>::spec_next) { - debug self => &std::ops::Range<u32>{ .0 => _4, .1 => _2, }; - let mut _6: bool; - let _8: u32; - let mut _9: u32; + debug self => _17; + let mut _8: bool; + let _10: u32; + let mut _11: u32; + let mut _18: &u32; + let mut _19: &u32; scope 6 { - debug old => _8; + debug old => _10; scope 7 { } } scope 8 (inlined cmp::impls::<impl PartialOrd for u32>::lt) { - debug self => &_4; - debug other => &_2; - let mut _5: u32; + debug self => _18; + debug other => _19; + let mut _6: u32; + let mut _7: u32; } } } } scope 3 (inlined <std::ops::Range<u32> as IntoIterator>::into_iter) { - debug self => std::ops::Range<u32>{ .0 => _1, .1 => _2, }; + debug self => _4; } bb0: { - StorageLive(_4); - _4 = _1; + _4 = std::ops::Range::<u32> { start: _1, end: _2 }; + StorageLive(_5); + _5 = move _4; goto -> bb1; } bb1: { - StorageLive(_7); + StorageLive(_9); + StorageLive(_10); StorageLive(_8); + StorageLive(_18); + StorageLive(_19); StorageLive(_6); - StorageLive(_5); - _5 = _4; - _6 = Lt(move _5, _2); - StorageDead(_5); - switchInt(move _6) -> [0: bb2, otherwise: bb3]; + _6 = (_5.0: u32); + StorageLive(_7); + _7 = (_5.1: u32); + _8 = Lt(move _6, move _7); + StorageDead(_7); + StorageDead(_6); + StorageDead(_19); + StorageDead(_18); + switchInt(move _8) -> [0: bb2, otherwise: bb3]; } bb2: { - _7 = Option::<u32>::None; + _9 = Option::<u32>::None; goto -> bb5; } bb3: { - _8 = _4; - StorageLive(_9); - _9 = <u32 as Step>::forward_unchecked(_8, const 1_usize) -> [return: bb4, unwind unreachable]; + _10 = (_5.0: u32); + StorageLive(_11); + _11 = <u32 as Step>::forward_unchecked(_10, const 1_usize) -> [return: bb4, unwind unreachable]; } bb4: { - _4 = move _9; - StorageDead(_9); - _7 = Option::<u32>::Some(_8); + (_5.0: u32) = move _11; + StorageDead(_11); + _9 = Option::<u32>::Some(_10); goto -> bb5; } bb5: { - StorageDead(_6); StorageDead(_8); - _10 = discriminant(_7); - switchInt(move _10) -> [0: bb6, 1: bb8, otherwise: bb10]; + StorageDead(_10); + _12 = discriminant(_9); + switchInt(move _12) -> [0: bb6, 1: bb8, otherwise: bb10]; } bb6: { - StorageDead(_7); - StorageDead(_4); + StorageDead(_9); + StorageDead(_5); drop(_3) -> [return: bb7, unwind unreachable]; } @@ -94,18 +107,18 @@ fn forward_loop(_1: u32, _2: u32, _3: impl Fn(u32)) -> () { } bb8: { - _11 = ((_7 as Some).0: u32); - StorageLive(_12); - _12 = &_3; - StorageLive(_13); - _13 = (_11,); - _14 = <impl Fn(u32) as Fn<(u32,)>>::call(move _12, move _13) -> [return: bb9, unwind unreachable]; + _13 = ((_9 as Some).0: u32); + StorageLive(_14); + _14 = &_3; + StorageLive(_15); + _15 = (_13,); + _16 = <impl Fn(u32) as Fn<(u32,)>>::call(move _14, move _15) -> [return: bb9, unwind unreachable]; } bb9: { - StorageDead(_13); - StorageDead(_12); - StorageDead(_7); + StorageDead(_15); + StorageDead(_14); + StorageDead(_9); goto -> bb1; } diff --git a/tests/mir-opt/pre-codegen/range_iter.forward_loop.PreCodegen.after.panic-unwind.mir b/tests/mir-opt/pre-codegen/range_iter.forward_loop.PreCodegen.after.panic-unwind.mir index c4e56ea3b23..4d7c017dad4 100644 --- a/tests/mir-opt/pre-codegen/range_iter.forward_loop.PreCodegen.after.panic-unwind.mir +++ b/tests/mir-opt/pre-codegen/range_iter.forward_loop.PreCodegen.after.panic-unwind.mir @@ -5,87 +5,100 @@ fn forward_loop(_1: u32, _2: u32, _3: impl Fn(u32)) -> () { debug end => _2; debug f => _3; let mut _0: (); - let mut _4: u32; - let mut _7: std::option::Option<u32>; - let mut _10: isize; - let mut _12: &impl Fn(u32); - let mut _13: (u32,); - let _14: (); + let mut _4: std::ops::Range<u32>; + let mut _5: std::ops::Range<u32>; + let mut _9: std::option::Option<u32>; + let mut _12: isize; + let mut _14: &impl Fn(u32); + let mut _15: (u32,); + let _16: (); + let mut _17: &mut std::ops::Range<u32>; scope 1 { - debug iter => std::ops::Range<u32>{ .0 => _4, .1 => _2, }; - let _11: u32; + debug iter => _5; + let _13: u32; scope 2 { - debug x => _11; + debug x => _13; } scope 4 (inlined iter::range::<impl Iterator for std::ops::Range<u32>>::next) { - debug self => &std::ops::Range<u32>{ .0 => _4, .1 => _2, }; + debug self => _17; scope 5 (inlined <std::ops::Range<u32> as iter::range::RangeIteratorImpl>::spec_next) { - debug self => &std::ops::Range<u32>{ .0 => _4, .1 => _2, }; - let mut _6: bool; - let _8: u32; - let mut _9: u32; + debug self => _17; + let mut _8: bool; + let _10: u32; + let mut _11: u32; + let mut _18: &u32; + let mut _19: &u32; scope 6 { - debug old => _8; + debug old => _10; scope 7 { } } scope 8 (inlined cmp::impls::<impl PartialOrd for u32>::lt) { - debug self => &_4; - debug other => &_2; - let mut _5: u32; + debug self => _18; + debug other => _19; + let mut _6: u32; + let mut _7: u32; } } } } scope 3 (inlined <std::ops::Range<u32> as IntoIterator>::into_iter) { - debug self => std::ops::Range<u32>{ .0 => _1, .1 => _2, }; + debug self => _4; } bb0: { - StorageLive(_4); - _4 = _1; + _4 = std::ops::Range::<u32> { start: _1, end: _2 }; + StorageLive(_5); + _5 = move _4; goto -> bb1; } bb1: { - StorageLive(_7); + StorageLive(_9); + StorageLive(_10); StorageLive(_8); + StorageLive(_18); + StorageLive(_19); StorageLive(_6); - StorageLive(_5); - _5 = _4; - _6 = Lt(move _5, _2); - StorageDead(_5); - switchInt(move _6) -> [0: bb2, otherwise: bb3]; + _6 = (_5.0: u32); + StorageLive(_7); + _7 = (_5.1: u32); + _8 = Lt(move _6, move _7); + StorageDead(_7); + StorageDead(_6); + StorageDead(_19); + StorageDead(_18); + switchInt(move _8) -> [0: bb2, otherwise: bb3]; } bb2: { - _7 = Option::<u32>::None; + _9 = Option::<u32>::None; goto -> bb5; } bb3: { - _8 = _4; - StorageLive(_9); - _9 = <u32 as Step>::forward_unchecked(_8, const 1_usize) -> [return: bb4, unwind: bb11]; + _10 = (_5.0: u32); + StorageLive(_11); + _11 = <u32 as Step>::forward_unchecked(_10, const 1_usize) -> [return: bb4, unwind: bb11]; } bb4: { - _4 = move _9; - StorageDead(_9); - _7 = Option::<u32>::Some(_8); + (_5.0: u32) = move _11; + StorageDead(_11); + _9 = Option::<u32>::Some(_10); goto -> bb5; } bb5: { - StorageDead(_6); StorageDead(_8); - _10 = discriminant(_7); - switchInt(move _10) -> [0: bb6, 1: bb8, otherwise: bb10]; + StorageDead(_10); + _12 = discriminant(_9); + switchInt(move _12) -> [0: bb6, 1: bb8, otherwise: bb10]; } bb6: { - StorageDead(_7); - StorageDead(_4); + StorageDead(_9); + StorageDead(_5); drop(_3) -> [return: bb7, unwind continue]; } @@ -94,18 +107,18 @@ fn forward_loop(_1: u32, _2: u32, _3: impl Fn(u32)) -> () { } bb8: { - _11 = ((_7 as Some).0: u32); - StorageLive(_12); - _12 = &_3; - StorageLive(_13); - _13 = (_11,); - _14 = <impl Fn(u32) as Fn<(u32,)>>::call(move _12, move _13) -> [return: bb9, unwind: bb11]; + _13 = ((_9 as Some).0: u32); + StorageLive(_14); + _14 = &_3; + StorageLive(_15); + _15 = (_13,); + _16 = <impl Fn(u32) as Fn<(u32,)>>::call(move _14, move _15) -> [return: bb9, unwind: bb11]; } bb9: { - StorageDead(_13); - StorageDead(_12); - StorageDead(_7); + StorageDead(_15); + StorageDead(_14); + StorageDead(_9); goto -> bb1; } diff --git a/tests/mir-opt/pre-codegen/range_iter.range_iter_next.PreCodegen.after.panic-abort.mir b/tests/mir-opt/pre-codegen/range_iter.range_iter_next.PreCodegen.after.panic-abort.mir index 14fd049ede8..7360aa3e698 100644 --- a/tests/mir-opt/pre-codegen/range_iter.range_iter_next.PreCodegen.after.panic-abort.mir +++ b/tests/mir-opt/pre-codegen/range_iter.range_iter_next.PreCodegen.after.panic-abort.mir @@ -10,14 +10,16 @@ fn range_iter_next(_1: &mut std::ops::Range<u32>) -> Option<u32> { let mut _4: bool; let _5: u32; let mut _6: u32; + let mut _7: &u32; + let mut _8: &u32; scope 3 { debug old => _5; scope 4 { } } scope 5 (inlined cmp::impls::<impl PartialOrd for u32>::lt) { - debug self => &((*_1).0: u32); - debug other => &((*_1).1: u32); + debug self => _7; + debug other => _8; let mut _2: u32; let mut _3: u32; } @@ -27,6 +29,8 @@ fn range_iter_next(_1: &mut std::ops::Range<u32>) -> Option<u32> { bb0: { StorageLive(_5); StorageLive(_4); + StorageLive(_7); + StorageLive(_8); StorageLive(_2); _2 = ((*_1).0: u32); StorageLive(_3); @@ -34,6 +38,8 @@ fn range_iter_next(_1: &mut std::ops::Range<u32>) -> Option<u32> { _4 = Lt(move _2, move _3); StorageDead(_3); StorageDead(_2); + StorageDead(_8); + StorageDead(_7); switchInt(move _4) -> [0: bb1, otherwise: bb2]; } diff --git a/tests/mir-opt/pre-codegen/range_iter.range_iter_next.PreCodegen.after.panic-unwind.mir b/tests/mir-opt/pre-codegen/range_iter.range_iter_next.PreCodegen.after.panic-unwind.mir index 668a2ac1e20..61957082d8b 100644 --- a/tests/mir-opt/pre-codegen/range_iter.range_iter_next.PreCodegen.after.panic-unwind.mir +++ b/tests/mir-opt/pre-codegen/range_iter.range_iter_next.PreCodegen.after.panic-unwind.mir @@ -10,14 +10,16 @@ fn range_iter_next(_1: &mut std::ops::Range<u32>) -> Option<u32> { let mut _4: bool; let _5: u32; let mut _6: u32; + let mut _7: &u32; + let mut _8: &u32; scope 3 { debug old => _5; scope 4 { } } scope 5 (inlined cmp::impls::<impl PartialOrd for u32>::lt) { - debug self => &((*_1).0: u32); - debug other => &((*_1).1: u32); + debug self => _7; + debug other => _8; let mut _2: u32; let mut _3: u32; } @@ -27,6 +29,8 @@ fn range_iter_next(_1: &mut std::ops::Range<u32>) -> Option<u32> { bb0: { StorageLive(_5); StorageLive(_4); + StorageLive(_7); + StorageLive(_8); StorageLive(_2); _2 = ((*_1).0: u32); StorageLive(_3); @@ -34,6 +38,8 @@ fn range_iter_next(_1: &mut std::ops::Range<u32>) -> Option<u32> { _4 = Lt(move _2, move _3); StorageDead(_3); StorageDead(_2); + StorageDead(_8); + StorageDead(_7); switchInt(move _4) -> [0: bb1, otherwise: bb2]; } diff --git a/tests/mir-opt/pre-codegen/slice_filter.variant_a-{closure#0}.PreCodegen.after.mir b/tests/mir-opt/pre-codegen/slice_filter.variant_a-{closure#0}.PreCodegen.after.mir index f9b0c85c852..1488779f93b 100644 --- a/tests/mir-opt/pre-codegen/slice_filter.variant_a-{closure#0}.PreCodegen.after.mir +++ b/tests/mir-opt/pre-codegen/slice_filter.variant_a-{closure#0}.PreCodegen.after.mir @@ -3,138 +3,206 @@ fn variant_a::{closure#0}(_1: &mut [closure@$DIR/slice_filter.rs:7:25: 7:39], _2: &&(usize, usize, usize, usize)) -> bool { let mut _0: bool; let mut _3: &(usize, usize, usize, usize); - let mut _4: &(usize, usize, usize, usize); + let _4: &usize; let mut _5: &(usize, usize, usize, usize); - let mut _6: &(usize, usize, usize, usize); - let mut _9: bool; - let mut _10: bool; - let mut _13: bool; + let _6: &usize; + let mut _7: &(usize, usize, usize, usize); + let _8: &usize; + let mut _9: &(usize, usize, usize, usize); + let _10: &usize; + let _11: &usize; let mut _16: bool; let mut _17: bool; - let mut _20: bool; + let _18: &usize; + let mut _23: bool; + let _24: &usize; + let mut _29: bool; + let mut _30: bool; + let _31: &usize; + let mut _36: bool; + let mut _37: &&usize; + let mut _38: &&usize; + let mut _39: &&usize; + let mut _40: &&usize; + let mut _41: &&usize; + let mut _42: &&usize; + let mut _43: &&usize; + let mut _44: &&usize; scope 1 { - debug a => &((*_3).0: usize); - debug b => &((*_4).1: usize); - debug c => &((*_5).2: usize); - debug d => &((*_6).3: usize); + debug a => _4; + debug b => _6; + debug c => _8; + debug d => _10; scope 2 (inlined cmp::impls::<impl PartialOrd for &usize>::le) { - debug self => &&((*_3).0: usize); - debug other => &&((*_5).2: usize); + debug self => _37; + debug other => _38; + let mut _12: &usize; + let mut _13: &usize; scope 3 (inlined cmp::impls::<impl PartialOrd for usize>::le) { - debug self => &((*_3).0: usize); - debug other => &((*_5).2: usize); - let mut _7: usize; - let mut _8: usize; + debug self => _12; + debug other => _13; + let mut _14: usize; + let mut _15: usize; } } scope 4 (inlined cmp::impls::<impl PartialOrd for &usize>::le) { - debug self => &&((*_5).2: usize); - debug other => &&((*_3).0: usize); + debug self => _41; + debug other => _42; + let mut _25: &usize; + let mut _26: &usize; scope 5 (inlined cmp::impls::<impl PartialOrd for usize>::le) { - debug self => &((*_5).2: usize); - debug other => &((*_3).0: usize); - let mut _14: usize; - let mut _15: usize; + debug self => _25; + debug other => _26; + let mut _27: usize; + let mut _28: usize; } } scope 6 (inlined cmp::impls::<impl PartialOrd for &usize>::le) { - debug self => &&((*_6).3: usize); - debug other => &&((*_4).1: usize); + debug self => _39; + debug other => _40; + let mut _19: &usize; + let mut _20: &usize; scope 7 (inlined cmp::impls::<impl PartialOrd for usize>::le) { - debug self => &((*_6).3: usize); - debug other => &((*_4).1: usize); - let mut _11: usize; - let mut _12: usize; + debug self => _19; + debug other => _20; + let mut _21: usize; + let mut _22: usize; } } scope 8 (inlined cmp::impls::<impl PartialOrd for &usize>::le) { - debug self => &&((*_4).1: usize); - debug other => &&((*_6).3: usize); + debug self => _43; + debug other => _44; + let mut _32: &usize; + let mut _33: &usize; scope 9 (inlined cmp::impls::<impl PartialOrd for usize>::le) { - debug self => &((*_4).1: usize); - debug other => &((*_6).3: usize); - let mut _18: usize; - let mut _19: usize; + debug self => _32; + debug other => _33; + let mut _34: usize; + let mut _35: usize; } } } bb0: { + StorageLive(_4); _3 = deref_copy (*_2); - _4 = deref_copy (*_2); + _4 = &((*_3).0: usize); + StorageLive(_6); _5 = deref_copy (*_2); - _6 = deref_copy (*_2); - StorageLive(_10); - StorageLive(_9); - StorageLive(_7); - _7 = ((*_3).0: usize); + _6 = &((*_5).1: usize); StorageLive(_8); - _8 = ((*_5).2: usize); - _9 = Le(move _7, move _8); - StorageDead(_8); - StorageDead(_7); - switchInt(move _9) -> [0: bb1, otherwise: bb2]; + _7 = deref_copy (*_2); + _8 = &((*_7).2: usize); + StorageLive(_10); + _9 = deref_copy (*_2); + _10 = &((*_9).3: usize); + StorageLive(_17); + StorageLive(_16); + StorageLive(_37); + StorageLive(_38); + StorageLive(_11); + _11 = _8; + _12 = deref_copy _4; + _13 = deref_copy _11; + StorageLive(_14); + _14 = (*_12); + StorageLive(_15); + _15 = (*_13); + _16 = Le(move _14, move _15); + StorageDead(_15); + StorageDead(_14); + StorageDead(_11); + StorageDead(_38); + StorageDead(_37); + switchInt(move _16) -> [0: bb1, otherwise: bb2]; } bb1: { - _10 = const false; + _17 = const false; goto -> bb3; } bb2: { - StorageLive(_13); - StorageLive(_11); - _11 = ((*_6).3: usize); - StorageLive(_12); - _12 = ((*_4).1: usize); - _13 = Le(move _11, move _12); - StorageDead(_12); - StorageDead(_11); - _10 = move _13; + StorageLive(_23); + StorageLive(_39); + StorageLive(_40); + StorageLive(_18); + _18 = _6; + _19 = deref_copy _10; + _20 = deref_copy _18; + StorageLive(_21); + _21 = (*_19); + StorageLive(_22); + _22 = (*_20); + _23 = Le(move _21, move _22); + StorageDead(_22); + StorageDead(_21); + StorageDead(_18); + StorageDead(_40); + StorageDead(_39); + _17 = move _23; goto -> bb3; } bb3: { - StorageDead(_13); - StorageDead(_9); - switchInt(move _10) -> [0: bb4, otherwise: bb8]; + StorageDead(_23); + StorageDead(_16); + switchInt(move _17) -> [0: bb4, otherwise: bb8]; } bb4: { - StorageLive(_17); - StorageLive(_16); - StorageLive(_14); - _14 = ((*_5).2: usize); - StorageLive(_15); - _15 = ((*_3).0: usize); - _16 = Le(move _14, move _15); - StorageDead(_15); - StorageDead(_14); - switchInt(move _16) -> [0: bb5, otherwise: bb6]; + StorageLive(_30); + StorageLive(_29); + StorageLive(_41); + StorageLive(_42); + StorageLive(_24); + _24 = _4; + _25 = deref_copy _8; + _26 = deref_copy _24; + StorageLive(_27); + _27 = (*_25); + StorageLive(_28); + _28 = (*_26); + _29 = Le(move _27, move _28); + StorageDead(_28); + StorageDead(_27); + StorageDead(_24); + StorageDead(_42); + StorageDead(_41); + switchInt(move _29) -> [0: bb5, otherwise: bb6]; } bb5: { - _17 = const false; + _30 = const false; goto -> bb7; } bb6: { - StorageLive(_20); - StorageLive(_18); - _18 = ((*_4).1: usize); - StorageLive(_19); - _19 = ((*_6).3: usize); - _20 = Le(move _18, move _19); - StorageDead(_19); - StorageDead(_18); - _17 = move _20; + StorageLive(_36); + StorageLive(_43); + StorageLive(_44); + StorageLive(_31); + _31 = _10; + _32 = deref_copy _6; + _33 = deref_copy _31; + StorageLive(_34); + _34 = (*_32); + StorageLive(_35); + _35 = (*_33); + _36 = Le(move _34, move _35); + StorageDead(_35); + StorageDead(_34); + StorageDead(_31); + StorageDead(_44); + StorageDead(_43); + _30 = move _36; goto -> bb7; } bb7: { - StorageDead(_20); - StorageDead(_16); - _0 = move _17; + StorageDead(_36); + StorageDead(_29); + _0 = move _30; goto -> bb9; } @@ -144,8 +212,12 @@ fn variant_a::{closure#0}(_1: &mut [closure@$DIR/slice_filter.rs:7:25: 7:39], _2 } bb9: { + StorageDead(_30); StorageDead(_17); StorageDead(_10); + StorageDead(_8); + StorageDead(_6); + StorageDead(_4); return; } } diff --git a/tests/mir-opt/pre-codegen/slice_iter.range_loop.PreCodegen.after.panic-abort.mir b/tests/mir-opt/pre-codegen/slice_iter.range_loop.PreCodegen.after.panic-abort.mir index 901381f070b..4edf4b4fb44 100644 --- a/tests/mir-opt/pre-codegen/slice_iter.range_loop.PreCodegen.after.panic-abort.mir +++ b/tests/mir-opt/pre-codegen/slice_iter.range_loop.PreCodegen.after.panic-abort.mir @@ -5,95 +5,109 @@ fn range_loop(_1: &[T], _2: impl Fn(usize, &T)) -> () { debug f => _2; let mut _0: (); let mut _3: usize; - let mut _4: usize; - let mut _7: std::option::Option<usize>; - let mut _10: isize; - let mut _12: usize; - let mut _13: bool; - let mut _15: &impl Fn(usize, &T); - let mut _16: (usize, &T); - let _17: (); - let mut _18: usize; + let mut _4: std::ops::Range<usize>; + let mut _5: std::ops::Range<usize>; + let mut _9: std::option::Option<usize>; + let mut _12: isize; + let mut _14: usize; + let mut _15: bool; + let mut _17: &impl Fn(usize, &T); + let mut _18: (usize, &T); + let _19: (); + let mut _20: &mut std::ops::Range<usize>; scope 1 { - debug iter => std::ops::Range<usize>{ .0 => _4, .1 => _3, }; - let _11: usize; + debug iter => _5; + let _13: usize; scope 2 { - debug i => _11; - let _14: &T; + debug i => _13; + let _16: &T; scope 3 { - debug x => _14; + debug x => _16; } } scope 5 (inlined iter::range::<impl Iterator for std::ops::Range<usize>>::next) { - debug self => &std::ops::Range<usize>{ .0 => _4, .1 => _3, }; + debug self => _20; scope 6 (inlined <std::ops::Range<usize> as iter::range::RangeIteratorImpl>::spec_next) { - debug self => &std::ops::Range<usize>{ .0 => _4, .1 => _3, }; - let mut _6: bool; - let _8: usize; - let mut _9: usize; + debug self => _20; + let mut _8: bool; + let _10: usize; + let mut _11: usize; + let mut _21: &usize; + let mut _22: &usize; scope 7 { - debug old => _8; + debug old => _10; scope 8 { } } scope 9 (inlined cmp::impls::<impl PartialOrd for usize>::lt) { - debug self => &_4; - debug other => &_3; - let mut _5: usize; + debug self => _21; + debug other => _22; + let mut _6: usize; + let mut _7: usize; } } } } scope 4 (inlined <std::ops::Range<usize> as IntoIterator>::into_iter) { - debug self => std::ops::Range<usize>{ .0 => _18, .1 => _3, }; + debug self => _4; } bb0: { + StorageLive(_3); _3 = Len((*_1)); - StorageLive(_4); - _4 = const 0_usize; + _4 = std::ops::Range::<usize> { start: const 0_usize, end: move _3 }; + StorageDead(_3); + StorageLive(_5); + _5 = move _4; goto -> bb1; } bb1: { - StorageLive(_7); + StorageLive(_9); + StorageLive(_10); StorageLive(_8); + StorageLive(_21); + StorageLive(_22); StorageLive(_6); - StorageLive(_5); - _5 = _4; - _6 = Lt(move _5, _3); - StorageDead(_5); - switchInt(move _6) -> [0: bb2, otherwise: bb3]; + _6 = (_5.0: usize); + StorageLive(_7); + _7 = (_5.1: usize); + _8 = Lt(move _6, move _7); + StorageDead(_7); + StorageDead(_6); + StorageDead(_22); + StorageDead(_21); + switchInt(move _8) -> [0: bb2, otherwise: bb3]; } bb2: { - _7 = Option::<usize>::None; + _9 = Option::<usize>::None; goto -> bb5; } bb3: { - _8 = _4; - StorageLive(_9); - _9 = <usize as Step>::forward_unchecked(_8, const 1_usize) -> [return: bb4, unwind unreachable]; + _10 = (_5.0: usize); + StorageLive(_11); + _11 = <usize as Step>::forward_unchecked(_10, const 1_usize) -> [return: bb4, unwind unreachable]; } bb4: { - _4 = move _9; - StorageDead(_9); - _7 = Option::<usize>::Some(_8); + (_5.0: usize) = move _11; + StorageDead(_11); + _9 = Option::<usize>::Some(_10); goto -> bb5; } bb5: { - StorageDead(_6); StorageDead(_8); - _10 = discriminant(_7); - switchInt(move _10) -> [0: bb6, 1: bb8, otherwise: bb11]; + StorageDead(_10); + _12 = discriminant(_9); + switchInt(move _12) -> [0: bb6, 1: bb8, otherwise: bb11]; } bb6: { - StorageDead(_7); - StorageDead(_4); + StorageDead(_9); + StorageDead(_5); drop(_2) -> [return: bb7, unwind unreachable]; } @@ -102,25 +116,25 @@ fn range_loop(_1: &[T], _2: impl Fn(usize, &T)) -> () { } bb8: { - _11 = ((_7 as Some).0: usize); - _12 = Len((*_1)); - _13 = Lt(_11, _12); - assert(move _13, "index out of bounds: the length is {} but the index is {}", move _12, _11) -> [success: bb9, unwind unreachable]; + _13 = ((_9 as Some).0: usize); + _14 = Len((*_1)); + _15 = Lt(_13, _14); + assert(move _15, "index out of bounds: the length is {} but the index is {}", move _14, _13) -> [success: bb9, unwind unreachable]; } bb9: { - _14 = &(*_1)[_11]; - StorageLive(_15); - _15 = &_2; - StorageLive(_16); - _16 = (_11, _14); - _17 = <impl Fn(usize, &T) as Fn<(usize, &T)>>::call(move _15, move _16) -> [return: bb10, unwind unreachable]; + _16 = &(*_1)[_13]; + StorageLive(_17); + _17 = &_2; + StorageLive(_18); + _18 = (_13, _16); + _19 = <impl Fn(usize, &T) as Fn<(usize, &T)>>::call(move _17, move _18) -> [return: bb10, unwind unreachable]; } bb10: { - StorageDead(_16); - StorageDead(_15); - StorageDead(_7); + StorageDead(_18); + StorageDead(_17); + StorageDead(_9); goto -> bb1; } diff --git a/tests/mir-opt/pre-codegen/slice_iter.range_loop.PreCodegen.after.panic-unwind.mir b/tests/mir-opt/pre-codegen/slice_iter.range_loop.PreCodegen.after.panic-unwind.mir index a47a73395cf..f7b19e80e44 100644 --- a/tests/mir-opt/pre-codegen/slice_iter.range_loop.PreCodegen.after.panic-unwind.mir +++ b/tests/mir-opt/pre-codegen/slice_iter.range_loop.PreCodegen.after.panic-unwind.mir @@ -5,95 +5,109 @@ fn range_loop(_1: &[T], _2: impl Fn(usize, &T)) -> () { debug f => _2; let mut _0: (); let mut _3: usize; - let mut _4: usize; - let mut _7: std::option::Option<usize>; - let mut _10: isize; - let mut _12: usize; - let mut _13: bool; - let mut _15: &impl Fn(usize, &T); - let mut _16: (usize, &T); - let _17: (); - let mut _18: usize; + let mut _4: std::ops::Range<usize>; + let mut _5: std::ops::Range<usize>; + let mut _9: std::option::Option<usize>; + let mut _12: isize; + let mut _14: usize; + let mut _15: bool; + let mut _17: &impl Fn(usize, &T); + let mut _18: (usize, &T); + let _19: (); + let mut _20: &mut std::ops::Range<usize>; scope 1 { - debug iter => std::ops::Range<usize>{ .0 => _4, .1 => _3, }; - let _11: usize; + debug iter => _5; + let _13: usize; scope 2 { - debug i => _11; - let _14: &T; + debug i => _13; + let _16: &T; scope 3 { - debug x => _14; + debug x => _16; } } scope 5 (inlined iter::range::<impl Iterator for std::ops::Range<usize>>::next) { - debug self => &std::ops::Range<usize>{ .0 => _4, .1 => _3, }; + debug self => _20; scope 6 (inlined <std::ops::Range<usize> as iter::range::RangeIteratorImpl>::spec_next) { - debug self => &std::ops::Range<usize>{ .0 => _4, .1 => _3, }; - let mut _6: bool; - let _8: usize; - let mut _9: usize; + debug self => _20; + let mut _8: bool; + let _10: usize; + let mut _11: usize; + let mut _21: &usize; + let mut _22: &usize; scope 7 { - debug old => _8; + debug old => _10; scope 8 { } } scope 9 (inlined cmp::impls::<impl PartialOrd for usize>::lt) { - debug self => &_4; - debug other => &_3; - let mut _5: usize; + debug self => _21; + debug other => _22; + let mut _6: usize; + let mut _7: usize; } } } } scope 4 (inlined <std::ops::Range<usize> as IntoIterator>::into_iter) { - debug self => std::ops::Range<usize>{ .0 => _18, .1 => _3, }; + debug self => _4; } bb0: { + StorageLive(_3); _3 = Len((*_1)); - StorageLive(_4); - _4 = const 0_usize; + _4 = std::ops::Range::<usize> { start: const 0_usize, end: move _3 }; + StorageDead(_3); + StorageLive(_5); + _5 = move _4; goto -> bb1; } bb1: { - StorageLive(_7); + StorageLive(_9); + StorageLive(_10); StorageLive(_8); + StorageLive(_21); + StorageLive(_22); StorageLive(_6); - StorageLive(_5); - _5 = _4; - _6 = Lt(move _5, _3); - StorageDead(_5); - switchInt(move _6) -> [0: bb2, otherwise: bb3]; + _6 = (_5.0: usize); + StorageLive(_7); + _7 = (_5.1: usize); + _8 = Lt(move _6, move _7); + StorageDead(_7); + StorageDead(_6); + StorageDead(_22); + StorageDead(_21); + switchInt(move _8) -> [0: bb2, otherwise: bb3]; } bb2: { - _7 = Option::<usize>::None; + _9 = Option::<usize>::None; goto -> bb5; } bb3: { - _8 = _4; - StorageLive(_9); - _9 = <usize as Step>::forward_unchecked(_8, const 1_usize) -> [return: bb4, unwind: bb12]; + _10 = (_5.0: usize); + StorageLive(_11); + _11 = <usize as Step>::forward_unchecked(_10, const 1_usize) -> [return: bb4, unwind: bb12]; } bb4: { - _4 = move _9; - StorageDead(_9); - _7 = Option::<usize>::Some(_8); + (_5.0: usize) = move _11; + StorageDead(_11); + _9 = Option::<usize>::Some(_10); goto -> bb5; } bb5: { - StorageDead(_6); StorageDead(_8); - _10 = discriminant(_7); - switchInt(move _10) -> [0: bb6, 1: bb8, otherwise: bb11]; + StorageDead(_10); + _12 = discriminant(_9); + switchInt(move _12) -> [0: bb6, 1: bb8, otherwise: bb11]; } bb6: { - StorageDead(_7); - StorageDead(_4); + StorageDead(_9); + StorageDead(_5); drop(_2) -> [return: bb7, unwind continue]; } @@ -102,25 +116,25 @@ fn range_loop(_1: &[T], _2: impl Fn(usize, &T)) -> () { } bb8: { - _11 = ((_7 as Some).0: usize); - _12 = Len((*_1)); - _13 = Lt(_11, _12); - assert(move _13, "index out of bounds: the length is {} but the index is {}", move _12, _11) -> [success: bb9, unwind: bb12]; + _13 = ((_9 as Some).0: usize); + _14 = Len((*_1)); + _15 = Lt(_13, _14); + assert(move _15, "index out of bounds: the length is {} but the index is {}", move _14, _13) -> [success: bb9, unwind: bb12]; } bb9: { - _14 = &(*_1)[_11]; - StorageLive(_15); - _15 = &_2; - StorageLive(_16); - _16 = (_11, _14); - _17 = <impl Fn(usize, &T) as Fn<(usize, &T)>>::call(move _15, move _16) -> [return: bb10, unwind: bb12]; + _16 = &(*_1)[_13]; + StorageLive(_17); + _17 = &_2; + StorageLive(_18); + _18 = (_13, _16); + _19 = <impl Fn(usize, &T) as Fn<(usize, &T)>>::call(move _17, move _18) -> [return: bb10, unwind: bb12]; } bb10: { - StorageDead(_16); - StorageDead(_15); - StorageDead(_7); + StorageDead(_18); + StorageDead(_17); + StorageDead(_9); goto -> bb1; } diff --git a/tests/mir-opt/pre-codegen/slice_iter.reverse_loop.PreCodegen.after.panic-abort.mir b/tests/mir-opt/pre-codegen/slice_iter.reverse_loop.PreCodegen.after.panic-abort.mir index a5df36ca388..549cb4f46a0 100644 --- a/tests/mir-opt/pre-codegen/slice_iter.reverse_loop.PreCodegen.after.panic-abort.mir +++ b/tests/mir-opt/pre-codegen/slice_iter.reverse_loop.PreCodegen.after.panic-abort.mir @@ -12,6 +12,7 @@ fn reverse_loop(_1: &[T], _2: impl Fn(&T)) -> () { let mut _20: &impl Fn(&T); let mut _21: (&T,); let _22: (); + let mut _23: &mut std::iter::Rev<std::slice::Iter<'_, T>>; scope 1 { debug iter => _15; let _19: &T; @@ -19,7 +20,7 @@ fn reverse_loop(_1: &[T], _2: impl Fn(&T)) -> () { debug x => _19; } scope 25 (inlined <Rev<std::slice::Iter<'_, T>> as Iterator>::next) { - debug self => &_15; + debug self => _23; let mut _16: &mut std::slice::Iter<'_, T>; } } @@ -48,15 +49,15 @@ fn reverse_loop(_1: &[T], _2: impl Fn(&T)) -> () { debug ptr => _9; scope 16 (inlined ptr::mut_ptr::<impl *mut T>::is_null) { debug self => _9; - let mut _23: *mut u8; + let mut _24: *mut u8; scope 17 { scope 18 (inlined ptr::mut_ptr::<impl *mut T>::is_null::runtime_impl) { - debug ptr => _23; + debug ptr => _24; scope 19 (inlined ptr::mut_ptr::<impl *mut u8>::addr) { - debug self => _23; + debug self => _24; scope 20 { scope 21 (inlined ptr::mut_ptr::<impl *mut u8>::cast::<()>) { - debug self => _23; + debug self => _24; } } } @@ -131,10 +132,10 @@ fn reverse_loop(_1: &[T], _2: impl Fn(&T)) -> () { StorageLive(_9); _9 = _4 as *mut T (PtrToPtr); StorageLive(_10); - StorageLive(_23); + StorageLive(_24); _10 = _9 as *const T (PointerCoercion(MutToConstPointer)); _11 = NonNull::<T> { pointer: _10 }; - StorageDead(_23); + StorageDead(_24); StorageDead(_10); StorageDead(_9); StorageLive(_12); diff --git a/tests/mir-opt/pre-codegen/slice_iter.reverse_loop.PreCodegen.after.panic-unwind.mir b/tests/mir-opt/pre-codegen/slice_iter.reverse_loop.PreCodegen.after.panic-unwind.mir index f681da4d275..43f8806e165 100644 --- a/tests/mir-opt/pre-codegen/slice_iter.reverse_loop.PreCodegen.after.panic-unwind.mir +++ b/tests/mir-opt/pre-codegen/slice_iter.reverse_loop.PreCodegen.after.panic-unwind.mir @@ -12,6 +12,7 @@ fn reverse_loop(_1: &[T], _2: impl Fn(&T)) -> () { let mut _20: &impl Fn(&T); let mut _21: (&T,); let _22: (); + let mut _23: &mut std::iter::Rev<std::slice::Iter<'_, T>>; scope 1 { debug iter => _15; let _19: &T; @@ -19,7 +20,7 @@ fn reverse_loop(_1: &[T], _2: impl Fn(&T)) -> () { debug x => _19; } scope 25 (inlined <Rev<std::slice::Iter<'_, T>> as Iterator>::next) { - debug self => &_15; + debug self => _23; let mut _16: &mut std::slice::Iter<'_, T>; } } @@ -48,15 +49,15 @@ fn reverse_loop(_1: &[T], _2: impl Fn(&T)) -> () { debug ptr => _9; scope 16 (inlined ptr::mut_ptr::<impl *mut T>::is_null) { debug self => _9; - let mut _23: *mut u8; + let mut _24: *mut u8; scope 17 { scope 18 (inlined ptr::mut_ptr::<impl *mut T>::is_null::runtime_impl) { - debug ptr => _23; + debug ptr => _24; scope 19 (inlined ptr::mut_ptr::<impl *mut u8>::addr) { - debug self => _23; + debug self => _24; scope 20 { scope 21 (inlined ptr::mut_ptr::<impl *mut u8>::cast::<()>) { - debug self => _23; + debug self => _24; } } } @@ -131,10 +132,10 @@ fn reverse_loop(_1: &[T], _2: impl Fn(&T)) -> () { StorageLive(_9); _9 = _4 as *mut T (PtrToPtr); StorageLive(_10); - StorageLive(_23); + StorageLive(_24); _10 = _9 as *const T (PointerCoercion(MutToConstPointer)); _11 = NonNull::<T> { pointer: _10 }; - StorageDead(_23); + StorageDead(_24); StorageDead(_10); StorageDead(_9); StorageLive(_12); diff --git a/tests/mir-opt/reference_prop.debuginfo.ReferencePropagation.diff b/tests/mir-opt/reference_prop.debuginfo.ReferencePropagation.diff index 132f66a1ad3..8fe361f2be4 100644 --- a/tests/mir-opt/reference_prop.debuginfo.ReferencePropagation.diff +++ b/tests/mir-opt/reference_prop.debuginfo.ReferencePropagation.diff @@ -22,27 +22,23 @@ let _24: &mut u8; let mut _25: debuginfo::T; scope 1 { -- debug ref_mut_u8 => _1; -+ debug ref_mut_u8 => &_2; + debug ref_mut_u8 => _1; let _3: &u8; let mut _28: &debuginfo::T; scope 2 { -- debug field => _3; -+ debug field => &((*_28).0: u8); + debug field => _3; let _5: &u8; scope 3 { - debug reborrow => _5; -+ debug reborrow => &_2; ++ debug reborrow => _1; let _9: &i32; let _22: &&&mut u8; let mut _27: &std::option::Option<i32>; scope 4 { -- debug variant_field => _9; -+ debug variant_field => &(((*_27) as Some).0: i32); + debug variant_field => _9; } scope 5 { -- debug constant_index => _19; -+ debug constant_index => &(*_11)[1 of 3]; + debug constant_index => _19; debug subslice => _20; debug constant_index_from_end => _21; let _19: &i32; @@ -51,21 +47,20 @@ let mut _26: &[i32; 10]; } scope 6 { -- debug multiple_borrow => _22; -+ debug multiple_borrow => &&&(_25.0: u8); + debug multiple_borrow => _22; } } } } bb0: { -- StorageLive(_1); + StorageLive(_1); StorageLive(_2); _2 = const 5_u8; -- _1 = &mut _2; -- StorageLive(_3); + _1 = &mut _2; + StorageLive(_3); _28 = const _; -- _3 = &((*_28).0: u8); + _3 = &((*_28).0: u8); - StorageLive(_5); - _5 = &(*_1); - StorageLive(_6); @@ -76,11 +71,11 @@ } bb1: { -- StorageLive(_9); + StorageLive(_9); _27 = const _; -- _9 = &(((*_27) as Some).0: i32); + _9 = &(((*_27) as Some).0: i32); - _6 = const (); -- StorageDead(_9); + StorageDead(_9); goto -> bb4; } @@ -118,8 +113,8 @@ } bb6: { -- StorageLive(_19); -- _19 = &(*_11)[1 of 3]; + StorageLive(_19); + _19 = &(*_11)[1 of 3]; StorageLive(_20); _20 = &(*_11)[2:-1]; StorageLive(_21); @@ -127,7 +122,7 @@ - _10 = const (); StorageDead(_21); StorageDead(_20); -- StorageDead(_19); + StorageDead(_19); goto -> bb8; } @@ -140,23 +135,23 @@ StorageDead(_12); StorageDead(_11); - StorageDead(_10); -- StorageLive(_22); -- StorageLive(_23); -- StorageLive(_24); + StorageLive(_22); + StorageLive(_23); + StorageLive(_24); StorageLive(_25); _25 = T(const 6_u8); -- _24 = &mut (_25.0: u8); -- _23 = &_24; -- _22 = &_23; + _24 = &mut (_25.0: u8); + _23 = &_24; + _22 = &_23; _0 = const (); StorageDead(_25); -- StorageDead(_24); -- StorageDead(_23); -- StorageDead(_22); + StorageDead(_24); + StorageDead(_23); + StorageDead(_22); - StorageDead(_5); -- StorageDead(_3); + StorageDead(_3); StorageDead(_2); -- StorageDead(_1); + StorageDead(_1); return; } } diff --git a/tests/mir-opt/reference_prop.mut_raw_then_mut_shr.ReferencePropagation.diff b/tests/mir-opt/reference_prop.mut_raw_then_mut_shr.ReferencePropagation.diff index 9ec8f9d78bb..747028e128f 100644 --- a/tests/mir-opt/reference_prop.mut_raw_then_mut_shr.ReferencePropagation.diff +++ b/tests/mir-opt/reference_prop.mut_raw_then_mut_shr.ReferencePropagation.diff @@ -13,16 +13,15 @@ debug x => _1; let _2: &mut i32; scope 2 { -- debug xref => _2; -+ debug xref => &_1; + debug xref => _2; let _3: *mut i32; scope 3 { - debug xraw => _3; -+ debug xraw => &_1; ++ debug xraw => _2; let _6: &i32; scope 4 { - debug xshr => _6; -+ debug xshr => &_1; ++ debug xshr => _2; let _7: i32; scope 5 { debug a => _7; @@ -38,7 +37,7 @@ StorageLive(_1); _1 = const 2_i32; - StorageLive(_2); -- _2 = &mut _1; + _2 = &mut _1; - StorageLive(_3); - StorageLive(_4); - StorageLive(_5); diff --git a/tests/mir-opt/reference_prop.reference_propagation.ReferencePropagation.diff b/tests/mir-opt/reference_prop.reference_propagation.ReferencePropagation.diff index f1f77cffd20..1be2ce8d0bb 100644 --- a/tests/mir-opt/reference_prop.reference_propagation.ReferencePropagation.diff +++ b/tests/mir-opt/reference_prop.reference_propagation.ReferencePropagation.diff @@ -52,8 +52,7 @@ debug a => _4; let _5: &usize; scope 2 { -- debug b => _5; -+ debug b => &_4; + debug b => _5; let _6: usize; scope 3 { debug c => _6; @@ -158,12 +157,10 @@ debug a => _60; let _61: &usize; scope 30 { -- debug b => _61; -+ debug b => &_60; + debug b => _61; let _62: &&usize; scope 31 { -- debug d => _62; -+ debug d => &&_60; + debug d => _62; let _63: usize; scope 32 { debug c => _63; @@ -175,12 +172,10 @@ debug a => _66; let mut _67: &usize; scope 34 { -- debug b => _67; -+ debug b => &_66; + debug b => _67; let _68: &mut &usize; scope 35 { -- debug d => _68; -+ debug d => &&_66; + debug d => _68; let _69: usize; scope 36 { debug c => _69; @@ -193,8 +188,8 @@ - StorageLive(_3); StorageLive(_4); _4 = const 5_usize; -- StorageLive(_5); -- _5 = &_4; + StorageLive(_5); + _5 = &_4; StorageLive(_6); - _6 = (*_5); + _6 = _4; @@ -209,7 +204,7 @@ StorageDead(_7); - _3 = const (); StorageDead(_6); -- StorageDead(_5); + StorageDead(_5); StorageDead(_4); - StorageDead(_3); - StorageLive(_9); @@ -394,13 +389,12 @@ - StorageLive(_59); StorageLive(_60); _60 = const 5_usize; -- StorageLive(_61); -- _61 = &_60; -- StorageLive(_62); -- _62 = &_61; + StorageLive(_61); + _61 = &_60; + StorageLive(_62); + _62 = &_61; StorageLive(_63); -- _63 = (*_61); -+ _63 = _60; + _63 = (*_61); StorageLive(_64); StorageLive(_65); _65 = (); @@ -412,19 +406,18 @@ StorageDead(_64); - _59 = const (); StorageDead(_63); -- StorageDead(_62); -- StorageDead(_61); + StorageDead(_62); + StorageDead(_61); StorageDead(_60); - StorageDead(_59); StorageLive(_66); _66 = const 5_usize; -- StorageLive(_67); -- _67 = &_66; -- StorageLive(_68); -- _68 = &mut _67; + StorageLive(_67); + _67 = &_66; + StorageLive(_68); + _68 = &mut _67; StorageLive(_69); -- _69 = (*_67); -+ _69 = _66; + _69 = (*_67); StorageLive(_70); StorageLive(_71); _71 = (); @@ -436,8 +429,8 @@ StorageDead(_70); _0 = const (); StorageDead(_69); -- StorageDead(_68); -- StorageDead(_67); + StorageDead(_68); + StorageDead(_67); StorageDead(_66); return; } diff --git a/tests/mir-opt/reference_prop.reference_propagation_const_ptr.ReferencePropagation.diff b/tests/mir-opt/reference_prop.reference_propagation_const_ptr.ReferencePropagation.diff index 05eab7989df..ce5ddbfdd12 100644 --- a/tests/mir-opt/reference_prop.reference_propagation_const_ptr.ReferencePropagation.diff +++ b/tests/mir-opt/reference_prop.reference_propagation_const_ptr.ReferencePropagation.diff @@ -45,8 +45,7 @@ debug a => _4; let _5: *const usize; scope 3 { -- debug b => _5; -+ debug b => &_4; + debug b => _5; let _6: usize; scope 4 { debug c => _6; @@ -175,12 +174,10 @@ debug a => _58; let _59: *const usize; scope 39 { -- debug b => _59; -+ debug b => &_58; + debug b => _59; let _60: *const usize; scope 40 { -- debug c => _60; -+ debug c => &_58; + debug c => _60; let _61: usize; scope 41 { debug e => _61; @@ -195,12 +192,10 @@ debug a => _65; let _66: *const usize; scope 44 { -- debug b => _66; -+ debug b => &_65; + debug b => _66; let _67: &*const usize; scope 45 { -- debug d => _67; -+ debug d => &&_65; + debug d => _67; let _68: usize; scope 46 { debug c => _68; @@ -215,12 +210,10 @@ debug a => _71; let mut _72: *const usize; scope 49 { -- debug b => _72; -+ debug b => &_71; + debug b => _72; let _73: &mut *const usize; scope 50 { -- debug d => _73; -+ debug d => &&_71; + debug d => _73; let _74: usize; scope 51 { debug c => _74; @@ -234,8 +227,8 @@ - StorageLive(_3); StorageLive(_4); _4 = const 5_usize; -- StorageLive(_5); -- _5 = &raw const _4; + StorageLive(_5); + _5 = &raw const _4; StorageLive(_6); - _6 = (*_5); + _6 = _4; @@ -250,7 +243,7 @@ StorageDead(_7); - _3 = const (); StorageDead(_6); -- StorageDead(_5); + StorageDead(_5); StorageDead(_4); - StorageDead(_3); - StorageLive(_9); @@ -427,10 +420,11 @@ - StorageLive(_57); StorageLive(_58); _58 = const 13_usize; -- StorageLive(_59); -- _59 = &raw const _58; -- StorageLive(_60); + StorageLive(_59); + _59 = &raw const _58; + StorageLive(_60); - _60 = &raw const (*_59); ++ _60 = &raw const _58; StorageLive(_61); - _61 = (*_60); + _61 = _58; @@ -445,20 +439,19 @@ StorageDead(_62); - _57 = const (); StorageDead(_61); -- StorageDead(_60); -- StorageDead(_59); + StorageDead(_60); + StorageDead(_59); StorageDead(_58); - StorageDead(_57); - StorageLive(_64); StorageLive(_65); _65 = const 5_usize; -- StorageLive(_66); -- _66 = &raw const _65; -- StorageLive(_67); -- _67 = &_66; + StorageLive(_66); + _66 = &raw const _65; + StorageLive(_67); + _67 = &_66; StorageLive(_68); -- _68 = (*_66); -+ _68 = _65; + _68 = (*_66); StorageLive(_69); StorageLive(_70); _70 = (); @@ -470,19 +463,18 @@ StorageDead(_69); - _64 = const (); StorageDead(_68); -- StorageDead(_67); -- StorageDead(_66); + StorageDead(_67); + StorageDead(_66); StorageDead(_65); - StorageDead(_64); StorageLive(_71); _71 = const 5_usize; -- StorageLive(_72); -- _72 = &raw const _71; -- StorageLive(_73); -- _73 = &mut _72; + StorageLive(_72); + _72 = &raw const _71; + StorageLive(_73); + _73 = &mut _72; StorageLive(_74); -- _74 = (*_72); -+ _74 = _71; + _74 = (*_72); StorageLive(_75); StorageLive(_76); _76 = (); @@ -494,8 +486,8 @@ StorageDead(_75); _0 = const (); StorageDead(_74); -- StorageDead(_73); -- StorageDead(_72); + StorageDead(_73); + StorageDead(_72); StorageDead(_71); return; } diff --git a/tests/mir-opt/reference_prop.reference_propagation_mut.ReferencePropagation.diff b/tests/mir-opt/reference_prop.reference_propagation_mut.ReferencePropagation.diff index ee680fdb3f2..7c7f424bba2 100644 --- a/tests/mir-opt/reference_prop.reference_propagation_mut.ReferencePropagation.diff +++ b/tests/mir-opt/reference_prop.reference_propagation_mut.ReferencePropagation.diff @@ -52,8 +52,7 @@ debug a => _4; let _5: &mut usize; scope 2 { -- debug b => _5; -+ debug b => &_4; + debug b => _5; let _6: usize; scope 3 { debug c => _6; @@ -158,12 +157,10 @@ debug a => _60; let _61: &mut usize; scope 30 { -- debug b => _61; -+ debug b => &_60; + debug b => _61; let _62: &&mut usize; scope 31 { -- debug d => _62; -+ debug d => &&_60; + debug d => _62; let _63: usize; scope 32 { debug c => _63; @@ -175,12 +172,10 @@ debug a => _66; let mut _67: &mut usize; scope 34 { -- debug b => _67; -+ debug b => &_66; + debug b => _67; let _68: &mut &mut usize; scope 35 { -- debug d => _68; -+ debug d => &&_66; + debug d => _68; let _69: usize; scope 36 { debug c => _69; @@ -193,8 +188,8 @@ - StorageLive(_3); StorageLive(_4); _4 = const 5_usize; -- StorageLive(_5); -- _5 = &mut _4; + StorageLive(_5); + _5 = &mut _4; StorageLive(_6); - _6 = (*_5); + _6 = _4; @@ -209,7 +204,7 @@ StorageDead(_7); - _3 = const (); StorageDead(_6); -- StorageDead(_5); + StorageDead(_5); StorageDead(_4); - StorageDead(_3); - StorageLive(_9); @@ -391,13 +386,12 @@ - StorageLive(_59); StorageLive(_60); _60 = const 5_usize; -- StorageLive(_61); -- _61 = &mut _60; -- StorageLive(_62); -- _62 = &_61; + StorageLive(_61); + _61 = &mut _60; + StorageLive(_62); + _62 = &_61; StorageLive(_63); -- _63 = (*_61); -+ _63 = _60; + _63 = (*_61); StorageLive(_64); StorageLive(_65); _65 = (); @@ -409,19 +403,18 @@ StorageDead(_64); - _59 = const (); StorageDead(_63); -- StorageDead(_62); -- StorageDead(_61); + StorageDead(_62); + StorageDead(_61); StorageDead(_60); - StorageDead(_59); StorageLive(_66); _66 = const 5_usize; -- StorageLive(_67); -- _67 = &mut _66; -- StorageLive(_68); -- _68 = &mut _67; + StorageLive(_67); + _67 = &mut _66; + StorageLive(_68); + _68 = &mut _67; StorageLive(_69); -- _69 = (*_67); -+ _69 = _66; + _69 = (*_67); StorageLive(_70); StorageLive(_71); _71 = (); @@ -433,8 +426,8 @@ StorageDead(_70); _0 = const (); StorageDead(_69); -- StorageDead(_68); -- StorageDead(_67); + StorageDead(_68); + StorageDead(_67); StorageDead(_66); return; } diff --git a/tests/mir-opt/reference_prop.reference_propagation_mut_ptr.ReferencePropagation.diff b/tests/mir-opt/reference_prop.reference_propagation_mut_ptr.ReferencePropagation.diff index fb0ef3184f0..b6b2acc0b43 100644 --- a/tests/mir-opt/reference_prop.reference_propagation_mut_ptr.ReferencePropagation.diff +++ b/tests/mir-opt/reference_prop.reference_propagation_mut_ptr.ReferencePropagation.diff @@ -42,8 +42,7 @@ debug a => _4; let _5: *mut usize; scope 3 { -- debug b => _5; -+ debug b => &_4; + debug b => _5; let _6: usize; scope 4 { debug c => _6; @@ -172,12 +171,10 @@ debug a => _58; let _59: *mut usize; scope 39 { -- debug b => _59; -+ debug b => &_58; + debug b => _59; let _60: &*mut usize; scope 40 { -- debug d => _60; -+ debug d => &&_58; + debug d => _60; let _61: usize; scope 41 { debug c => _61; @@ -192,12 +189,10 @@ debug a => _64; let mut _65: *mut usize; scope 44 { -- debug b => _65; -+ debug b => &_64; + debug b => _65; let _66: &mut *mut usize; scope 45 { -- debug d => _66; -+ debug d => &&_64; + debug d => _66; let _67: usize; scope 46 { debug c => _67; @@ -211,8 +206,8 @@ - StorageLive(_3); StorageLive(_4); _4 = const 5_usize; -- StorageLive(_5); -- _5 = &raw mut _4; + StorageLive(_5); + _5 = &raw mut _4; StorageLive(_6); - _6 = (*_5); + _6 = _4; @@ -227,7 +222,7 @@ StorageDead(_7); - _3 = const (); StorageDead(_6); -- StorageDead(_5); + StorageDead(_5); StorageDead(_4); - StorageDead(_3); - StorageLive(_9); @@ -401,13 +396,12 @@ - StorageLive(_57); StorageLive(_58); _58 = const 5_usize; -- StorageLive(_59); -- _59 = &raw mut _58; -- StorageLive(_60); -- _60 = &_59; + StorageLive(_59); + _59 = &raw mut _58; + StorageLive(_60); + _60 = &_59; StorageLive(_61); -- _61 = (*_59); -+ _61 = _58; + _61 = (*_59); StorageLive(_62); StorageLive(_63); _63 = (); @@ -419,19 +413,18 @@ StorageDead(_62); - _57 = const (); StorageDead(_61); -- StorageDead(_60); -- StorageDead(_59); + StorageDead(_60); + StorageDead(_59); StorageDead(_58); - StorageDead(_57); StorageLive(_64); _64 = const 5_usize; -- StorageLive(_65); -- _65 = &raw mut _64; -- StorageLive(_66); -- _66 = &mut _65; + StorageLive(_65); + _65 = &raw mut _64; + StorageLive(_66); + _66 = &mut _65; StorageLive(_67); -- _67 = (*_65); -+ _67 = _64; + _67 = (*_65); StorageLive(_68); StorageLive(_69); _69 = (); @@ -443,8 +436,8 @@ StorageDead(_68); _0 = const (); StorageDead(_67); -- StorageDead(_66); -- StorageDead(_65); + StorageDead(_66); + StorageDead(_65); StorageDead(_64); return; } diff --git a/tests/mir-opt/reference_prop.rs b/tests/mir-opt/reference_prop.rs index 4083b45470b..610660131b1 100644 --- a/tests/mir-opt/reference_prop.rs +++ b/tests/mir-opt/reference_prop.rs @@ -426,7 +426,7 @@ fn multiple_storage() { // As there are multiple `StorageLive` statements for `x`, we cannot know if this `z`'s // pointer address is the address of `x`, so do nothing. let y = *z; - Call(RET, retblock, opaque(y)) + Call(RET = opaque(y), retblock) } retblock = { @@ -452,7 +452,7 @@ fn dominate_storage() { } bb1 = { let c = *r; - Call(RET, bb2, opaque(c)) + Call(RET = opaque(c), bb2) } bb2 = { StorageDead(x); @@ -486,18 +486,18 @@ fn maybe_dead(m: bool) { bb1 = { StorageDead(x); StorageDead(y); - Call(RET, bb2, opaque(u)) + Call(RET = opaque(u), bb2) } bb2 = { // As `x` may be `StorageDead`, `a` may be dangling, so we do nothing. let z = *a; - Call(RET, bb3, opaque(z)) + Call(RET = opaque(z), bb3) } bb3 = { // As `y` may be `StorageDead`, `b` may be dangling, so we do nothing. // This implies that we also do not substitute `b` in `bb0`. let t = *b; - Call(RET, retblock, opaque(t)) + Call(RET = opaque(t), retblock) } retblock = { Return() diff --git a/tests/run-make/optimization-remarks-dir-pgo/Makefile b/tests/run-make/optimization-remarks-dir-pgo/Makefile index c88ec1e6cb3..3bc3d7d1428 100644 --- a/tests/run-make/optimization-remarks-dir-pgo/Makefile +++ b/tests/run-make/optimization-remarks-dir-pgo/Makefile @@ -1,5 +1,6 @@ # needs-profiler-support # ignore-windows-gnu +# ignore-cross-compile # FIXME(mati865): MinGW GCC miscompiles compiler-rt profiling library but with Clang it works # properly. Since we only have GCC on the CI ignore the test for now. diff --git a/tests/run-make/panic-abort-eh_frame/Makefile b/tests/run-make/panic-abort-eh_frame/Makefile index 1cb7bf575cb..7020455b742 100644 --- a/tests/run-make/panic-abort-eh_frame/Makefile +++ b/tests/run-make/panic-abort-eh_frame/Makefile @@ -6,5 +6,5 @@ include ../tools.mk all: - $(RUSTC) foo.rs --crate-type=lib --emit=obj=$(TMPDIR)/foo.o -Cpanic=abort + $(RUSTC) foo.rs --crate-type=lib --emit=obj=$(TMPDIR)/foo.o -Cpanic=abort --edition 2021 -Z validate-mir objdump --dwarf=frames $(TMPDIR)/foo.o | $(CGREP) -v 'DW_CFA' diff --git a/tests/run-make/panic-abort-eh_frame/foo.rs b/tests/run-make/panic-abort-eh_frame/foo.rs index e1853529455..e2274d469e7 100644 --- a/tests/run-make/panic-abort-eh_frame/foo.rs +++ b/tests/run-make/panic-abort-eh_frame/foo.rs @@ -1,5 +1,13 @@ #![no_std] +use core::future::Future; + +pub struct NeedsDrop; + +impl Drop for NeedsDrop { + fn drop(&mut self) {} +} + #[panic_handler] fn handler(_: &core::panic::PanicInfo<'_>) -> ! { loop {} @@ -8,3 +16,19 @@ fn handler(_: &core::panic::PanicInfo<'_>) -> ! { pub unsafe fn oops(x: *const u32) -> u32 { *x } + +pub async fn foo(_: NeedsDrop) { + async fn bar() {} + bar().await; +} + +pub fn poll_foo(x: &mut core::task::Context<'_>) { + let _g = NeedsDrop; + let mut p = core::pin::pin!(foo(NeedsDrop)); + let _ = p.as_mut().poll(x); + let _ = p.as_mut().poll(x); +} + +pub fn drop_foo() { + drop(foo(NeedsDrop)); +} diff --git a/tests/rustdoc-gui/docblock-table.goml b/tests/rustdoc-gui/docblock-table.goml index 011451ef4f3..678b302f22e 100644 --- a/tests/rustdoc-gui/docblock-table.goml +++ b/tests/rustdoc-gui/docblock-table.goml @@ -36,17 +36,17 @@ define-function: ( ) call-function: ("check-colors", { - "theme": "dark", - "border_color": "rgb(224, 224, 224)", - "zebra_stripe_color": "rgb(42, 42, 42)", + "theme": "ayu", + "border_color": "#5c6773", + "zebra_stripe_color": "#191f26", }) call-function: ("check-colors", { - "theme": "ayu", - "border_color": "rgb(92, 103, 115)", - "zebra_stripe_color": "rgb(25, 31, 38)", + "theme": "dark", + "border_color": "#e0e0e0", + "zebra_stripe_color": "#2a2a2a", }) call-function: ("check-colors", { "theme": "light", - "border_color": "rgb(224, 224, 224)", - "zebra_stripe_color": "rgb(245, 245, 245)", + "border_color": "#e0e0e0", + "zebra_stripe_color": "#f5f5f5", }) diff --git a/tests/rustdoc-gui/search-form-elements.goml b/tests/rustdoc-gui/search-form-elements.goml index 83c6980909c..a4e22364859 100644 --- a/tests/rustdoc-gui/search-form-elements.goml +++ b/tests/rustdoc-gui/search-form-elements.goml @@ -2,262 +2,138 @@ go-to: "file://" + |DOC_PATH| + "/test_docs/index.html" show-text: true -// Ayu theme -set-local-storage: { - "rustdoc-theme": "ayu", - "rustdoc-use-system-theme": "false", -} -reload: - -assert-css: ( - ".search-input", - { - "border-color": "rgb(92, 103, 115)", - "background-color": "rgb(20, 25, 32)", - "color": "rgb(255, 255, 255)", - }, -) -focus: ".search-input" -// Nothing should change. -assert-css: ( - ".search-input", - { - "border-color": "rgb(92, 103, 115)", - "background-color": "rgb(20, 25, 32)", - "color": "rgb(255, 255, 255)", - }, -) - -assert-css: ( - "#help-button", - {"border-color": "rgb(197, 197, 197)"}, -) -assert-css: ( - "#help-button > a", - { - "color": "rgb(255, 255, 255)", - "border-color": "rgb(92, 103, 115)", - "background-color": "rgb(20, 25, 32)", - }, -) -move-cursor-to: "#help-button" -assert-css: ( - "#help-button:hover", - {"border-color": "rgb(197, 197, 197)"}, -) -// Only "border-color" should change. -assert-css: ( - "#help-button:hover > a", - { - "color": "rgb(255, 255, 255)", - "border-color": "rgb(224, 224, 224)", - "background-color": "rgb(20, 25, 32)", - }, -) -// Link color inside -click: "#help-button" -assert-css: ( - "#help a", - { - "color": "rgb(57, 175, 215)", - }, -) - -assert-css: ( - "#settings-menu", - {"border-color": "rgb(197, 197, 197)"}, -) -assert-css: ( - "#settings-menu > a", - { - "border-color": "rgb(92, 103, 115)", - "background-color": "rgb(20, 25, 32)", - }, -) -move-cursor-to: "#settings-menu" -assert-css: ( - "#settings-menu:hover", - {"border-color": "rgb(197, 197, 197)"}, -) -// Only "border-color" should change. -assert-css: ( - "#settings-menu:hover > a", - { - "border-color": "rgb(224, 224, 224)", - "background-color": "rgb(20, 25, 32)", - }, -) - -// Dark theme -set-local-storage: { - "rustdoc-theme": "dark", - "rustdoc-use-system-theme": "false", -} -reload: - -assert-css: ( - ".search-input", - { - "border-color": "rgb(224, 224, 224)", - "background-color": "rgb(240, 240, 240)", - "color": "rgb(17, 17, 17)", - }, -) -focus: ".search-input" -// Only "border-color" should change. -assert-css: ( - ".search-input", - { - "border-color": "rgb(0, 141, 253)", - "background-color": "rgb(240, 240, 240)", - "color": "rgb(17, 17, 17)", - }, -) - -assert-css: ( - "#help-button", - {"border-color": "rgb(221, 221, 221)"}, -) -assert-css: ( - "#help-button > a", - { - "color": "rgb(0, 0, 0)", - "border-color": "rgb(224, 224, 224)", - "background-color": "rgb(240, 240, 240)", - }, -) -move-cursor-to: "#help-button" -assert-css: ( - "#help-button:hover", - {"border-color": "rgb(221, 221, 221)"}, -) -// Only "border-color" should change. -assert-css: ( - "#help-button:hover > a", - { - "color": "rgb(0, 0, 0)", - "border-color": "rgb(255, 185, 0)", - "background-color": "rgb(240, 240, 240)", - }, -) -// Link color inside -click: "#help-button" -assert-css: ( - "#help a", - { - "color": "rgb(210, 153, 29)", +define-function: ( + "check-search-colors", + ( + theme, border, background, search_input_color, search_input_border_focus, + menu_button_border, menu_button_a_color, menu_button_a_border_hover, menu_a_color, + ), + block { + set-local-storage: { + "rustdoc-theme": |theme|, + "rustdoc-use-system-theme": "false", + } + reload: + assert-css: ( + ".search-input", + { + "border-color": |border|, + "background-color": |background|, + "color": |search_input_color|, + }, + ) + // Focus on search input. + focus: ".search-input" + assert-css: ( + ".search-input", + { + "border-color": |search_input_border_focus|, + "background-color": |background|, + "color": |search_input_color|, + }, + ) + assert-css: ( + "#help-button", + {"border-color": |menu_button_border|}, + ) + assert-css: ( + "#help-button > a", + { + "color": |menu_button_a_color|, + "border-color": |border|, + "background-color": |background|, + }, + ) + // Hover help button. + move-cursor-to: "#help-button" + assert-css: ( + "#help-button:hover", + {"border-color": |menu_button_border|}, + ) + assert-css: ( + "#help-button > a", + { + "color": |menu_button_a_color|, + "border-color": |menu_button_a_border_hover|, + "background-color": |background|, + }, + ) + // Link color inside + click: "#help-button" + assert-css: ( + "#help a", + { + "color": |menu_a_color|, + }, + ) + assert-css: ( + "#settings-menu", + {"border-color": |menu_button_border|}, + ) + assert-css: ( + "#settings-menu > a", + { + "color": |menu_button_a_color|, + "border-color": |border|, + "background-color": |background|, + }, + ) + // Hover settings menu. + move-cursor-to: "#settings-menu" + assert-css: ( + "#settings-menu:hover", + {"border-color": |menu_button_border|}, + ) + assert-css: ( + "#settings-menu:hover > a", + { + "color": |menu_button_a_color|, + "border-color": |menu_button_a_border_hover|, + "background-color": |background|, + }, + ) }, ) -assert-css: ( - "#settings-menu", - {"border-color": "rgb(221, 221, 221)"}, -) -assert-css: ( - "#settings-menu > a", - { - "border-color": "rgb(224, 224, 224)", - "background-color": "rgb(240, 240, 240)", - }, -) -move-cursor-to: "#settings-menu" -assert-css: ( - "#settings-menu:hover", - {"border-color": "rgb(221, 221, 221)"}, -) -// Only "border-color" should change. -assert-css: ( - "#settings-menu:hover > a", - { - "color": "rgb(0, 0, 0)", - "border-color": "rgb(255, 185, 0)", - "background-color": "rgb(240, 240, 240)", - }, -) - -// Light theme -set-local-storage: { - "rustdoc-theme": "light", - "rustdoc-use-system-theme": "false", -} -reload: - -assert-css: ( - ".search-input", - { - "border-color": "rgb(224, 224, 224)", - "background-color": "rgb(255, 255, 255)", - "color": "rgb(0, 0, 0)", - }, -) -focus: ".search-input" -// Nothing should change. -assert-css: ( - ".search-input", - { - "border-color": "rgb(102, 175, 233)", - "background-color": "rgb(255, 255, 255)", - "color": "rgb(0, 0, 0)", - }, -) - -assert-css: ( - "#help-button", - {"border-color": "rgb(0, 0, 0)"}, -) -assert-css: ( - "#help-button > a", - { - "color": "rgb(0, 0, 0)", - "border-color": "rgb(224, 224, 224)", - "background-color": "rgb(255, 255, 255)", - }, -) -move-cursor-to: "#help-button" -assert-css: ( - "#help-button:hover", - {"border-color": "rgb(0, 0, 0)"}, -) -// Only "border-color" should change. -assert-css: ( - "#help-button:hover > a", - { - "color": "rgb(0, 0, 0)", - "border-color": "rgb(113, 113, 113)", - "background-color": "rgb(255, 255, 255)", - }, -) -// Link color inside -click: "#help-button" -assert-css: ( - "#help a", - { - "color": "rgb(56, 115, 173)", - }, -) - -assert-css: ( - "#settings-menu", - {"border-color": "rgb(0, 0, 0)"}, -) -assert-css: ( - "#settings-menu > a", - { - "border-color": "rgb(224, 224, 224)", - "background-color": "rgb(255, 255, 255)", - }, -) -move-cursor-to: "#settings-menu" -assert-css: ( - "#settings-menu:hover", - {"border-color": "rgb(0, 0, 0)"}, -) -// Only "border-color" should change. -assert-css: ( - "#settings-menu:hover > a", - { - "color": "rgb(0, 0, 0)", - "border-color": "rgb(113, 113, 113)", - "background-color": "rgb(255, 255, 255)", - }, +call-function: ( + "check-search-colors", + { + "theme": "ayu", + "border": "#5c6773", + "background": "#141920", + "search_input_color": "#fff", + "search_input_border_focus": "#5c6773", + "menu_button_border": "#c5c5c5", + "menu_button_a_color": "#fff", + "menu_button_a_border_hover": "#e0e0e0", + "menu_a_color": "#39afd7", + } +) +call-function: ( + "check-search-colors", + { + "theme": "dark", + "border": "#e0e0e0", + "background": "#f0f0f0", + "search_input_color": "#111", + "search_input_border_focus": "#008dfd", + "menu_button_border": "#ddd", + "menu_button_a_color": "#000", + "menu_button_a_border_hover": "#ffb900", + "menu_a_color": "#d2991d", + } +) +call-function: ( + "check-search-colors", + { + "theme": "light", + "border": "#e0e0e0", + "background": "#fff", + "search_input_color": "#000", + "search_input_border_focus": "#66afe9", + "menu_button_border": "#000", + "menu_button_a_color": "#000", + "menu_button_a_border_hover": "#717171", + "menu_a_color": "#3873ad", + } ) diff --git a/tests/rustdoc-gui/src/test_docs/lib.rs b/tests/rustdoc-gui/src/test_docs/lib.rs index 49484ee0869..38180aef762 100644 --- a/tests/rustdoc-gui/src/test_docs/lib.rs +++ b/tests/rustdoc-gui/src/test_docs/lib.rs @@ -65,6 +65,18 @@ impl Foo { pub fn must_use(&self) -> bool { true } + + /// hello + /// + /// <div id="doc-warning-1" class="warning">this is a warning</div> + /// + /// done + pub fn warning1() {} + + /// Checking there is no bottom margin if "warning" is the last element. + /// + /// <div id="doc-warning-2" class="warning">this is a warning</div> + pub fn warning2() {} } impl AsRef<str> for Foo { diff --git a/tests/rustdoc-gui/warning-block.goml b/tests/rustdoc-gui/warning-block.goml new file mode 100644 index 00000000000..2a935bd1a9b --- /dev/null +++ b/tests/rustdoc-gui/warning-block.goml @@ -0,0 +1,45 @@ +// Test to check that the "warning blocks" are displayed as expected. +go-to: "file://" + |DOC_PATH| + "/test_docs/struct.Foo.html" +show-text: true + +define-function: ( + "check-warning", + (theme, color, border_color, background_color), + block { + set-local-storage: {"rustdoc-theme": |theme|, "rustdoc-use-system-theme": "false"} + reload: + + // The IDs are added directly into the DOM to make writing this test easier. + assert-css: ("#doc-warning-1", { + "margin-bottom": "12px", + "color": |color|, + "border-left": "2px solid " + |border_color|, + "background-color": |background_color|, + }) + assert-css: ("#doc-warning-2", { + "margin-bottom": "0px", + "color": |color|, + "border-left": "2px solid " + |border_color|, + "background-color": |background_color|, + }) + }, +) + +call-function: ("check-warning", { + "theme": "ayu", + "color": "rgb(197, 197, 197)", + "border_color": "rgb(255, 142, 0)", + "background_color": "rgba(0, 0, 0, 0)", +}) +call-function: ("check-warning", { + "theme": "dark", + "color": "rgb(221, 221, 221)", + "border_color": "rgb(255, 142, 0)", + "background_color": "rgba(0, 0, 0, 0)", +}) +call-function: ("check-warning", { + "theme": "light", + "color": "rgb(0, 0, 0)", + "border_color": "rgb(255, 142, 0)", + "background_color": "rgba(0, 0, 0, 0)", +}) diff --git a/tests/rustdoc-ui/lints/inline-doc-link.rs b/tests/rustdoc-ui/lints/inline-doc-link.rs new file mode 100644 index 00000000000..596f89be3d6 --- /dev/null +++ b/tests/rustdoc-ui/lints/inline-doc-link.rs @@ -0,0 +1,13 @@ +// Regression test for <https://github.com/rust-lang/rust/pull/113167> + +// check-pass +#![deny(rustdoc::redundant_explicit_links)] + +mod m { + pub enum ValueEnum {} +} +mod m2 { + /// [`ValueEnum`] + pub use crate::m::ValueEnum; +} +pub use m2::ValueEnum; diff --git a/tests/rustdoc-ui/lints/no-redundancy.rs b/tests/rustdoc-ui/lints/no-redundancy.rs new file mode 100644 index 00000000000..e3358728b1b --- /dev/null +++ b/tests/rustdoc-ui/lints/no-redundancy.rs @@ -0,0 +1,7 @@ +// check-pass + +#![deny(rustdoc::redundant_explicit_links)] + +/// [Vec][std::vec::Vec#examples] should not warn, because it's not actually redundant! +/// [This is just an `Option`][std::option::Option] has different display content to actual link! +pub fn func() {} diff --git a/tests/rustdoc-ui/lints/redundant_explicit_links-utf8.rs b/tests/rustdoc-ui/lints/redundant_explicit_links-utf8.rs new file mode 100644 index 00000000000..fecefb7b25f --- /dev/null +++ b/tests/rustdoc-ui/lints/redundant_explicit_links-utf8.rs @@ -0,0 +1,18 @@ +// check-pass + +/// [`…foo`] [`…bar`] [`Err`] +pub struct Broken {} + +/// [`…`] [`…`] [`Err`] +pub struct Broken2 {} + +/// [`…`][…] [`…`][…] [`Err`] +pub struct Broken3 {} + +/// […………………………][Broken3] +pub struct Broken4 {} + +/// [Broken3][…………………………] +pub struct Broken5 {} + +pub struct Err; diff --git a/tests/rustdoc-ui/lints/redundant_explicit_links.fixed b/tests/rustdoc-ui/lints/redundant_explicit_links.fixed new file mode 100644 index 00000000000..900234e31e9 --- /dev/null +++ b/tests/rustdoc-ui/lints/redundant_explicit_links.fixed @@ -0,0 +1,158 @@ +// run-rustfix + +#![deny(rustdoc::redundant_explicit_links)] + +pub fn dummy_target() {} + +/// [dummy_target] +//~^ ERROR redundant explicit link target +/// [`dummy_target`] +//~^ ERROR redundant explicit link target +/// +/// [Vec] +//~^ ERROR redundant explicit link target +/// [`Vec`] +//~^ ERROR redundant explicit link target +/// [Vec] +//~^ ERROR redundant explicit link target +/// [`Vec`] +//~^ ERROR redundant explicit link target +/// [std::vec::Vec] +//~^ ERROR redundant explicit link target +/// [`std::vec::Vec`] +//~^ ERROR redundant explicit link target +/// [std::vec::Vec] +//~^ ERROR redundant explicit link target +/// [`std::vec::Vec`] +//~^ ERROR redundant explicit link target +/// +/// [usize] +//~^ ERROR redundant explicit link target +/// [`usize`] +//~^ ERROR redundant explicit link target +/// [usize] +//~^ ERROR redundant explicit link target +/// [`usize`] +//~^ ERROR redundant explicit link target +/// [std::primitive::usize] +//~^ ERROR redundant explicit link target +/// [`std::primitive::usize`] +//~^ ERROR redundant explicit link target +/// [std::primitive::usize] +//~^ ERROR redundant explicit link target +/// [`std::primitive::usize`] +//~^ ERROR redundant explicit link target +/// +/// [dummy_target] TEXT +//~^ ERROR redundant explicit link target +/// [`dummy_target`] TEXT +//~^ ERROR redundant explicit link target +pub fn should_warn_inline() {} + +/// [`Vec<T>`](Vec) +/// [`Vec<T>`](std::vec::Vec) +pub fn should_not_warn_inline() {} + +/// [dummy_target] +//~^ ERROR redundant explicit link target +/// [`dummy_target`] +//~^ ERROR redundant explicit link target +/// +/// [Vec] +//~^ ERROR redundant explicit link target +/// [`Vec`] +//~^ ERROR redundant explicit link target +/// [Vec] +//~^ ERROR redundant explicit link target +/// [`Vec`] +//~^ ERROR redundant explicit link target +/// [std::vec::Vec] +//~^ ERROR redundant explicit link target +/// [`std::vec::Vec`] +//~^ ERROR redundant explicit link target +/// [std::vec::Vec] +//~^ ERROR redundant explicit link target +/// [`std::vec::Vec`] +//~^ ERROR redundant explicit link target +/// +/// [usize] +//~^ ERROR redundant explicit link target +/// [`usize`] +//~^ ERROR redundant explicit link target +/// [usize] +//~^ ERROR redundant explicit link target +/// [`usize`] +//~^ ERROR redundant explicit link target +/// [std::primitive::usize] +//~^ ERROR redundant explicit link target +/// [`std::primitive::usize`] +//~^ ERROR redundant explicit link target +/// [std::primitive::usize] +//~^ ERROR redundant explicit link target +/// [`std::primitive::usize`] +//~^ ERROR redundant explicit link target +/// +/// [dummy_target] TEXT +//~^ ERROR redundant explicit link target +/// [`dummy_target`] TEXT +//~^ ERROR redundant explicit link target +pub fn should_warn_reference_unknown() {} + +/// [`Vec<T>`][Vec] +/// [`Vec<T>`][std::vec::Vec] +pub fn should_not_warn_reference_unknown() {} + +/// [dummy_target] +//~^ ERROR redundant explicit link target +/// [`dummy_target`] +//~^ ERROR redundant explicit link target +/// +/// [Vec] +//~^ ERROR redundant explicit link target +/// [`Vec`] +//~^ ERROR redundant explicit link target +/// [Vec] +//~^ ERROR redundant explicit link target +/// [`Vec`] +//~^ ERROR redundant explicit link target +/// [std::vec::Vec] +//~^ ERROR redundant explicit link target +/// [`std::vec::Vec`] +//~^ ERROR redundant explicit link target +/// [std::vec::Vec] +//~^ ERROR redundant explicit link target +/// [`std::vec::Vec`] +//~^ ERROR redundant explicit link target +/// +/// [usize] +//~^ ERROR redundant explicit link target +/// [`usize`] +//~^ ERROR redundant explicit link target +/// [usize] +//~^ ERROR redundant explicit link target +/// [`usize`] +//~^ ERROR redundant explicit link target +/// [std::primitive::usize] +//~^ ERROR redundant explicit link target +/// [`std::primitive::usize`] +//~^ ERROR redundant explicit link target +/// [std::primitive::usize] +//~^ ERROR redundant explicit link target +/// [`std::primitive::usize`] +//~^ ERROR redundant explicit link target +/// +/// [dummy_target] TEXT +//~^ ERROR redundant explicit link target +/// [`dummy_target`] TEXT +//~^ ERROR redundant explicit link target +/// +/// [dummy_target]: dummy_target +/// [Vec]: Vec +/// [std::vec::Vec]: Vec +/// [usize]: usize +/// [std::primitive::usize]: usize +pub fn should_warn_reference() {} + +/// [`Vec<T>`]: Vec +/// [`Vec<T>`]: std::vec::Vec +pub fn should_not_warn_reference() {} diff --git a/tests/rustdoc-ui/lints/redundant_explicit_links.rs b/tests/rustdoc-ui/lints/redundant_explicit_links.rs new file mode 100644 index 00000000000..13feb85e051 --- /dev/null +++ b/tests/rustdoc-ui/lints/redundant_explicit_links.rs @@ -0,0 +1,158 @@ +// run-rustfix + +#![deny(rustdoc::redundant_explicit_links)] + +pub fn dummy_target() {} + +/// [dummy_target](dummy_target) +//~^ ERROR redundant explicit link target +/// [`dummy_target`](dummy_target) +//~^ ERROR redundant explicit link target +/// +/// [Vec](Vec) +//~^ ERROR redundant explicit link target +/// [`Vec`](Vec) +//~^ ERROR redundant explicit link target +/// [Vec](std::vec::Vec) +//~^ ERROR redundant explicit link target +/// [`Vec`](std::vec::Vec) +//~^ ERROR redundant explicit link target +/// [std::vec::Vec](Vec) +//~^ ERROR redundant explicit link target +/// [`std::vec::Vec`](Vec) +//~^ ERROR redundant explicit link target +/// [std::vec::Vec](std::vec::Vec) +//~^ ERROR redundant explicit link target +/// [`std::vec::Vec`](std::vec::Vec) +//~^ ERROR redundant explicit link target +/// +/// [usize](usize) +//~^ ERROR redundant explicit link target +/// [`usize`](usize) +//~^ ERROR redundant explicit link target +/// [usize](std::primitive::usize) +//~^ ERROR redundant explicit link target +/// [`usize`](std::primitive::usize) +//~^ ERROR redundant explicit link target +/// [std::primitive::usize](usize) +//~^ ERROR redundant explicit link target +/// [`std::primitive::usize`](usize) +//~^ ERROR redundant explicit link target +/// [std::primitive::usize](std::primitive::usize) +//~^ ERROR redundant explicit link target +/// [`std::primitive::usize`](std::primitive::usize) +//~^ ERROR redundant explicit link target +/// +/// [dummy_target](dummy_target) TEXT +//~^ ERROR redundant explicit link target +/// [`dummy_target`](dummy_target) TEXT +//~^ ERROR redundant explicit link target +pub fn should_warn_inline() {} + +/// [`Vec<T>`](Vec) +/// [`Vec<T>`](std::vec::Vec) +pub fn should_not_warn_inline() {} + +/// [dummy_target][dummy_target] +//~^ ERROR redundant explicit link target +/// [`dummy_target`][dummy_target] +//~^ ERROR redundant explicit link target +/// +/// [Vec][Vec] +//~^ ERROR redundant explicit link target +/// [`Vec`][Vec] +//~^ ERROR redundant explicit link target +/// [Vec][std::vec::Vec] +//~^ ERROR redundant explicit link target +/// [`Vec`][std::vec::Vec] +//~^ ERROR redundant explicit link target +/// [std::vec::Vec][Vec] +//~^ ERROR redundant explicit link target +/// [`std::vec::Vec`][Vec] +//~^ ERROR redundant explicit link target +/// [std::vec::Vec][std::vec::Vec] +//~^ ERROR redundant explicit link target +/// [`std::vec::Vec`][std::vec::Vec] +//~^ ERROR redundant explicit link target +/// +/// [usize][usize] +//~^ ERROR redundant explicit link target +/// [`usize`][usize] +//~^ ERROR redundant explicit link target +/// [usize][std::primitive::usize] +//~^ ERROR redundant explicit link target +/// [`usize`][std::primitive::usize] +//~^ ERROR redundant explicit link target +/// [std::primitive::usize][usize] +//~^ ERROR redundant explicit link target +/// [`std::primitive::usize`][usize] +//~^ ERROR redundant explicit link target +/// [std::primitive::usize][std::primitive::usize] +//~^ ERROR redundant explicit link target +/// [`std::primitive::usize`][std::primitive::usize] +//~^ ERROR redundant explicit link target +/// +/// [dummy_target][dummy_target] TEXT +//~^ ERROR redundant explicit link target +/// [`dummy_target`][dummy_target] TEXT +//~^ ERROR redundant explicit link target +pub fn should_warn_reference_unknown() {} + +/// [`Vec<T>`][Vec] +/// [`Vec<T>`][std::vec::Vec] +pub fn should_not_warn_reference_unknown() {} + +/// [dummy_target][dummy_target] +//~^ ERROR redundant explicit link target +/// [`dummy_target`][dummy_target] +//~^ ERROR redundant explicit link target +/// +/// [Vec][Vec] +//~^ ERROR redundant explicit link target +/// [`Vec`][Vec] +//~^ ERROR redundant explicit link target +/// [Vec][std::vec::Vec] +//~^ ERROR redundant explicit link target +/// [`Vec`][std::vec::Vec] +//~^ ERROR redundant explicit link target +/// [std::vec::Vec][Vec] +//~^ ERROR redundant explicit link target +/// [`std::vec::Vec`][Vec] +//~^ ERROR redundant explicit link target +/// [std::vec::Vec][std::vec::Vec] +//~^ ERROR redundant explicit link target +/// [`std::vec::Vec`][std::vec::Vec] +//~^ ERROR redundant explicit link target +/// +/// [usize][usize] +//~^ ERROR redundant explicit link target +/// [`usize`][usize] +//~^ ERROR redundant explicit link target +/// [usize][std::primitive::usize] +//~^ ERROR redundant explicit link target +/// [`usize`][std::primitive::usize] +//~^ ERROR redundant explicit link target +/// [std::primitive::usize][usize] +//~^ ERROR redundant explicit link target +/// [`std::primitive::usize`][usize] +//~^ ERROR redundant explicit link target +/// [std::primitive::usize][std::primitive::usize] +//~^ ERROR redundant explicit link target +/// [`std::primitive::usize`][std::primitive::usize] +//~^ ERROR redundant explicit link target +/// +/// [dummy_target][dummy_target] TEXT +//~^ ERROR redundant explicit link target +/// [`dummy_target`][dummy_target] TEXT +//~^ ERROR redundant explicit link target +/// +/// [dummy_target]: dummy_target +/// [Vec]: Vec +/// [std::vec::Vec]: Vec +/// [usize]: usize +/// [std::primitive::usize]: usize +pub fn should_warn_reference() {} + +/// [`Vec<T>`]: Vec +/// [`Vec<T>`]: std::vec::Vec +pub fn should_not_warn_reference() {} diff --git a/tests/rustdoc-ui/lints/redundant_explicit_links.stderr b/tests/rustdoc-ui/lints/redundant_explicit_links.stderr new file mode 100644 index 00000000000..34ec9be6646 --- /dev/null +++ b/tests/rustdoc-ui/lints/redundant_explicit_links.stderr @@ -0,0 +1,1007 @@ +error: redundant explicit link target + --> $DIR/redundant_explicit_links.rs:7:20 + | +LL | /// [dummy_target](dummy_target) + | ------------ ^^^^^^^^^^^^ explicit target is redundant + | | + | because label contains path that resolves to same destination + | + = note: when a link's destination is not specified, + the label is used to resolve intra-doc links +note: the lint level is defined here + --> $DIR/redundant_explicit_links.rs:3:9 + | +LL | #![deny(rustdoc::redundant_explicit_links)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +help: remove explicit link target + | +LL | /// [dummy_target] + | ~~~~~~~~~~~~~~ + +error: redundant explicit link target + --> $DIR/redundant_explicit_links.rs:9:22 + | +LL | /// [`dummy_target`](dummy_target) + | -------------- ^^^^^^^^^^^^ explicit target is redundant + | | + | because label contains path that resolves to same destination + | + = note: when a link's destination is not specified, + the label is used to resolve intra-doc links +help: remove explicit link target + | +LL | /// [`dummy_target`] + | ~~~~~~~~~~~~~~~~ + +error: redundant explicit link target + --> $DIR/redundant_explicit_links.rs:12:11 + | +LL | /// [Vec](Vec) + | --- ^^^ explicit target is redundant + | | + | because label contains path that resolves to same destination + | + = note: when a link's destination is not specified, + the label is used to resolve intra-doc links +help: remove explicit link target + | +LL | /// [Vec] + | ~~~~~ + +error: redundant explicit link target + --> $DIR/redundant_explicit_links.rs:14:13 + | +LL | /// [`Vec`](Vec) + | ----- ^^^ explicit target is redundant + | | + | because label contains path that resolves to same destination + | + = note: when a link's destination is not specified, + the label is used to resolve intra-doc links +help: remove explicit link target + | +LL | /// [`Vec`] + | ~~~~~~~ + +error: redundant explicit link target + --> $DIR/redundant_explicit_links.rs:16:11 + | +LL | /// [Vec](std::vec::Vec) + | --- ^^^^^^^^^^^^^ explicit target is redundant + | | + | because label contains path that resolves to same destination + | + = note: when a link's destination is not specified, + the label is used to resolve intra-doc links +help: remove explicit link target + | +LL | /// [Vec] + | ~~~~~ + +error: redundant explicit link target + --> $DIR/redundant_explicit_links.rs:18:13 + | +LL | /// [`Vec`](std::vec::Vec) + | ----- ^^^^^^^^^^^^^ explicit target is redundant + | | + | because label contains path that resolves to same destination + | + = note: when a link's destination is not specified, + the label is used to resolve intra-doc links +help: remove explicit link target + | +LL | /// [`Vec`] + | ~~~~~~~ + +error: redundant explicit link target + --> $DIR/redundant_explicit_links.rs:20:21 + | +LL | /// [std::vec::Vec](Vec) + | ------------- ^^^ explicit target is redundant + | | + | because label contains path that resolves to same destination + | + = note: when a link's destination is not specified, + the label is used to resolve intra-doc links +help: remove explicit link target + | +LL | /// [std::vec::Vec] + | ~~~~~~~~~~~~~~~ + +error: redundant explicit link target + --> $DIR/redundant_explicit_links.rs:22:23 + | +LL | /// [`std::vec::Vec`](Vec) + | --------------- ^^^ explicit target is redundant + | | + | because label contains path that resolves to same destination + | + = note: when a link's destination is not specified, + the label is used to resolve intra-doc links +help: remove explicit link target + | +LL | /// [`std::vec::Vec`] + | ~~~~~~~~~~~~~~~~~ + +error: redundant explicit link target + --> $DIR/redundant_explicit_links.rs:24:21 + | +LL | /// [std::vec::Vec](std::vec::Vec) + | ------------- ^^^^^^^^^^^^^ explicit target is redundant + | | + | because label contains path that resolves to same destination + | + = note: when a link's destination is not specified, + the label is used to resolve intra-doc links +help: remove explicit link target + | +LL | /// [std::vec::Vec] + | ~~~~~~~~~~~~~~~ + +error: redundant explicit link target + --> $DIR/redundant_explicit_links.rs:26:23 + | +LL | /// [`std::vec::Vec`](std::vec::Vec) + | --------------- ^^^^^^^^^^^^^ explicit target is redundant + | | + | because label contains path that resolves to same destination + | + = note: when a link's destination is not specified, + the label is used to resolve intra-doc links +help: remove explicit link target + | +LL | /// [`std::vec::Vec`] + | ~~~~~~~~~~~~~~~~~ + +error: redundant explicit link target + --> $DIR/redundant_explicit_links.rs:29:13 + | +LL | /// [usize](usize) + | ----- ^^^^^ explicit target is redundant + | | + | because label contains path that resolves to same destination + | + = note: when a link's destination is not specified, + the label is used to resolve intra-doc links +help: remove explicit link target + | +LL | /// [usize] + | ~~~~~~~ + +error: redundant explicit link target + --> $DIR/redundant_explicit_links.rs:31:15 + | +LL | /// [`usize`](usize) + | ------- ^^^^^ explicit target is redundant + | | + | because label contains path that resolves to same destination + | + = note: when a link's destination is not specified, + the label is used to resolve intra-doc links +help: remove explicit link target + | +LL | /// [`usize`] + | ~~~~~~~~~ + +error: redundant explicit link target + --> $DIR/redundant_explicit_links.rs:33:13 + | +LL | /// [usize](std::primitive::usize) + | ----- ^^^^^^^^^^^^^^^^^^^^^ explicit target is redundant + | | + | because label contains path that resolves to same destination + | + = note: when a link's destination is not specified, + the label is used to resolve intra-doc links +help: remove explicit link target + | +LL | /// [usize] + | ~~~~~~~ + +error: redundant explicit link target + --> $DIR/redundant_explicit_links.rs:35:15 + | +LL | /// [`usize`](std::primitive::usize) + | ------- ^^^^^^^^^^^^^^^^^^^^^ explicit target is redundant + | | + | because label contains path that resolves to same destination + | + = note: when a link's destination is not specified, + the label is used to resolve intra-doc links +help: remove explicit link target + | +LL | /// [`usize`] + | ~~~~~~~~~ + +error: redundant explicit link target + --> $DIR/redundant_explicit_links.rs:37:29 + | +LL | /// [std::primitive::usize](usize) + | --------------------- ^^^^^ explicit target is redundant + | | + | because label contains path that resolves to same destination + | + = note: when a link's destination is not specified, + the label is used to resolve intra-doc links +help: remove explicit link target + | +LL | /// [std::primitive::usize] + | ~~~~~~~~~~~~~~~~~~~~~~~ + +error: redundant explicit link target + --> $DIR/redundant_explicit_links.rs:39:31 + | +LL | /// [`std::primitive::usize`](usize) + | ----------------------- ^^^^^ explicit target is redundant + | | + | because label contains path that resolves to same destination + | + = note: when a link's destination is not specified, + the label is used to resolve intra-doc links +help: remove explicit link target + | +LL | /// [`std::primitive::usize`] + | ~~~~~~~~~~~~~~~~~~~~~~~~~ + +error: redundant explicit link target + --> $DIR/redundant_explicit_links.rs:41:29 + | +LL | /// [std::primitive::usize](std::primitive::usize) + | --------------------- ^^^^^^^^^^^^^^^^^^^^^ explicit target is redundant + | | + | because label contains path that resolves to same destination + | + = note: when a link's destination is not specified, + the label is used to resolve intra-doc links +help: remove explicit link target + | +LL | /// [std::primitive::usize] + | ~~~~~~~~~~~~~~~~~~~~~~~ + +error: redundant explicit link target + --> $DIR/redundant_explicit_links.rs:43:31 + | +LL | /// [`std::primitive::usize`](std::primitive::usize) + | ----------------------- ^^^^^^^^^^^^^^^^^^^^^ explicit target is redundant + | | + | because label contains path that resolves to same destination + | + = note: when a link's destination is not specified, + the label is used to resolve intra-doc links +help: remove explicit link target + | +LL | /// [`std::primitive::usize`] + | ~~~~~~~~~~~~~~~~~~~~~~~~~ + +error: redundant explicit link target + --> $DIR/redundant_explicit_links.rs:46:20 + | +LL | /// [dummy_target](dummy_target) TEXT + | ------------ ^^^^^^^^^^^^ explicit target is redundant + | | + | because label contains path that resolves to same destination + | + = note: when a link's destination is not specified, + the label is used to resolve intra-doc links +help: remove explicit link target + | +LL | /// [dummy_target] TEXT + | ~~~~~~~~~~~~~~ + +error: redundant explicit link target + --> $DIR/redundant_explicit_links.rs:48:22 + | +LL | /// [`dummy_target`](dummy_target) TEXT + | -------------- ^^^^^^^^^^^^ explicit target is redundant + | | + | because label contains path that resolves to same destination + | + = note: when a link's destination is not specified, + the label is used to resolve intra-doc links +help: remove explicit link target + | +LL | /// [`dummy_target`] TEXT + | ~~~~~~~~~~~~~~~~ + +error: redundant explicit link target + --> $DIR/redundant_explicit_links.rs:56:20 + | +LL | /// [dummy_target][dummy_target] + | ------------ ^^^^^^^^^^^^ explicit target is redundant + | | + | because label contains path that resolves to same destination + | + = note: when a link's destination is not specified, + the label is used to resolve intra-doc links +help: remove explicit link target + | +LL | /// [dummy_target] + | ~~~~~~~~~~~~~~ + +error: redundant explicit link target + --> $DIR/redundant_explicit_links.rs:58:22 + | +LL | /// [`dummy_target`][dummy_target] + | -------------- ^^^^^^^^^^^^ explicit target is redundant + | | + | because label contains path that resolves to same destination + | + = note: when a link's destination is not specified, + the label is used to resolve intra-doc links +help: remove explicit link target + | +LL | /// [`dummy_target`] + | ~~~~~~~~~~~~~~~~ + +error: redundant explicit link target + --> $DIR/redundant_explicit_links.rs:61:11 + | +LL | /// [Vec][Vec] + | --- ^^^ explicit target is redundant + | | + | because label contains path that resolves to same destination + | + = note: when a link's destination is not specified, + the label is used to resolve intra-doc links +help: remove explicit link target + | +LL | /// [Vec] + | ~~~~~ + +error: redundant explicit link target + --> $DIR/redundant_explicit_links.rs:63:13 + | +LL | /// [`Vec`][Vec] + | ----- ^^^ explicit target is redundant + | | + | because label contains path that resolves to same destination + | + = note: when a link's destination is not specified, + the label is used to resolve intra-doc links +help: remove explicit link target + | +LL | /// [`Vec`] + | ~~~~~~~ + +error: redundant explicit link target + --> $DIR/redundant_explicit_links.rs:65:11 + | +LL | /// [Vec][std::vec::Vec] + | --- ^^^^^^^^^^^^^ explicit target is redundant + | | + | because label contains path that resolves to same destination + | + = note: when a link's destination is not specified, + the label is used to resolve intra-doc links +help: remove explicit link target + | +LL | /// [Vec] + | ~~~~~ + +error: redundant explicit link target + --> $DIR/redundant_explicit_links.rs:67:13 + | +LL | /// [`Vec`][std::vec::Vec] + | ----- ^^^^^^^^^^^^^ explicit target is redundant + | | + | because label contains path that resolves to same destination + | + = note: when a link's destination is not specified, + the label is used to resolve intra-doc links +help: remove explicit link target + | +LL | /// [`Vec`] + | ~~~~~~~ + +error: redundant explicit link target + --> $DIR/redundant_explicit_links.rs:69:21 + | +LL | /// [std::vec::Vec][Vec] + | ------------- ^^^ explicit target is redundant + | | + | because label contains path that resolves to same destination + | + = note: when a link's destination is not specified, + the label is used to resolve intra-doc links +help: remove explicit link target + | +LL | /// [std::vec::Vec] + | ~~~~~~~~~~~~~~~ + +error: redundant explicit link target + --> $DIR/redundant_explicit_links.rs:71:23 + | +LL | /// [`std::vec::Vec`][Vec] + | --------------- ^^^ explicit target is redundant + | | + | because label contains path that resolves to same destination + | + = note: when a link's destination is not specified, + the label is used to resolve intra-doc links +help: remove explicit link target + | +LL | /// [`std::vec::Vec`] + | ~~~~~~~~~~~~~~~~~ + +error: redundant explicit link target + --> $DIR/redundant_explicit_links.rs:73:21 + | +LL | /// [std::vec::Vec][std::vec::Vec] + | ------------- ^^^^^^^^^^^^^ explicit target is redundant + | | + | because label contains path that resolves to same destination + | + = note: when a link's destination is not specified, + the label is used to resolve intra-doc links +help: remove explicit link target + | +LL | /// [std::vec::Vec] + | ~~~~~~~~~~~~~~~ + +error: redundant explicit link target + --> $DIR/redundant_explicit_links.rs:75:23 + | +LL | /// [`std::vec::Vec`][std::vec::Vec] + | --------------- ^^^^^^^^^^^^^ explicit target is redundant + | | + | because label contains path that resolves to same destination + | + = note: when a link's destination is not specified, + the label is used to resolve intra-doc links +help: remove explicit link target + | +LL | /// [`std::vec::Vec`] + | ~~~~~~~~~~~~~~~~~ + +error: redundant explicit link target + --> $DIR/redundant_explicit_links.rs:78:13 + | +LL | /// [usize][usize] + | ----- ^^^^^ explicit target is redundant + | | + | because label contains path that resolves to same destination + | + = note: when a link's destination is not specified, + the label is used to resolve intra-doc links +help: remove explicit link target + | +LL | /// [usize] + | ~~~~~~~ + +error: redundant explicit link target + --> $DIR/redundant_explicit_links.rs:80:15 + | +LL | /// [`usize`][usize] + | ------- ^^^^^ explicit target is redundant + | | + | because label contains path that resolves to same destination + | + = note: when a link's destination is not specified, + the label is used to resolve intra-doc links +help: remove explicit link target + | +LL | /// [`usize`] + | ~~~~~~~~~ + +error: redundant explicit link target + --> $DIR/redundant_explicit_links.rs:82:13 + | +LL | /// [usize][std::primitive::usize] + | ----- ^^^^^^^^^^^^^^^^^^^^^ explicit target is redundant + | | + | because label contains path that resolves to same destination + | + = note: when a link's destination is not specified, + the label is used to resolve intra-doc links +help: remove explicit link target + | +LL | /// [usize] + | ~~~~~~~ + +error: redundant explicit link target + --> $DIR/redundant_explicit_links.rs:84:15 + | +LL | /// [`usize`][std::primitive::usize] + | ------- ^^^^^^^^^^^^^^^^^^^^^ explicit target is redundant + | | + | because label contains path that resolves to same destination + | + = note: when a link's destination is not specified, + the label is used to resolve intra-doc links +help: remove explicit link target + | +LL | /// [`usize`] + | ~~~~~~~~~ + +error: redundant explicit link target + --> $DIR/redundant_explicit_links.rs:86:29 + | +LL | /// [std::primitive::usize][usize] + | --------------------- ^^^^^ explicit target is redundant + | | + | because label contains path that resolves to same destination + | + = note: when a link's destination is not specified, + the label is used to resolve intra-doc links +help: remove explicit link target + | +LL | /// [std::primitive::usize] + | ~~~~~~~~~~~~~~~~~~~~~~~ + +error: redundant explicit link target + --> $DIR/redundant_explicit_links.rs:88:31 + | +LL | /// [`std::primitive::usize`][usize] + | ----------------------- ^^^^^ explicit target is redundant + | | + | because label contains path that resolves to same destination + | + = note: when a link's destination is not specified, + the label is used to resolve intra-doc links +help: remove explicit link target + | +LL | /// [`std::primitive::usize`] + | ~~~~~~~~~~~~~~~~~~~~~~~~~ + +error: redundant explicit link target + --> $DIR/redundant_explicit_links.rs:90:29 + | +LL | /// [std::primitive::usize][std::primitive::usize] + | --------------------- ^^^^^^^^^^^^^^^^^^^^^ explicit target is redundant + | | + | because label contains path that resolves to same destination + | + = note: when a link's destination is not specified, + the label is used to resolve intra-doc links +help: remove explicit link target + | +LL | /// [std::primitive::usize] + | ~~~~~~~~~~~~~~~~~~~~~~~ + +error: redundant explicit link target + --> $DIR/redundant_explicit_links.rs:92:31 + | +LL | /// [`std::primitive::usize`][std::primitive::usize] + | ----------------------- ^^^^^^^^^^^^^^^^^^^^^ explicit target is redundant + | | + | because label contains path that resolves to same destination + | + = note: when a link's destination is not specified, + the label is used to resolve intra-doc links +help: remove explicit link target + | +LL | /// [`std::primitive::usize`] + | ~~~~~~~~~~~~~~~~~~~~~~~~~ + +error: redundant explicit link target + --> $DIR/redundant_explicit_links.rs:95:20 + | +LL | /// [dummy_target][dummy_target] TEXT + | ------------ ^^^^^^^^^^^^ explicit target is redundant + | | + | because label contains path that resolves to same destination + | + = note: when a link's destination is not specified, + the label is used to resolve intra-doc links +help: remove explicit link target + | +LL | /// [dummy_target] TEXT + | ~~~~~~~~~~~~~~ + +error: redundant explicit link target + --> $DIR/redundant_explicit_links.rs:97:22 + | +LL | /// [`dummy_target`][dummy_target] TEXT + | -------------- ^^^^^^^^^^^^ explicit target is redundant + | | + | because label contains path that resolves to same destination + | + = note: when a link's destination is not specified, + the label is used to resolve intra-doc links +help: remove explicit link target + | +LL | /// [`dummy_target`] TEXT + | ~~~~~~~~~~~~~~~~ + +error: redundant explicit link target + --> $DIR/redundant_explicit_links.rs:105:20 + | +LL | /// [dummy_target][dummy_target] + | ------------ ^^^^^^^^^^^^ explicit target is redundant + | | + | because label contains path that resolves to same destination + | +note: referenced explicit link target defined here + --> $DIR/redundant_explicit_links.rs:149:21 + | +LL | /// [dummy_target]: dummy_target + | ^^^^^^^^^^^^ + = note: when a link's destination is not specified, + the label is used to resolve intra-doc links +help: remove explicit link target + | +LL | /// [dummy_target] + | ~~~~~~~~~~~~~~ + +error: redundant explicit link target + --> $DIR/redundant_explicit_links.rs:107:22 + | +LL | /// [`dummy_target`][dummy_target] + | -------------- ^^^^^^^^^^^^ explicit target is redundant + | | + | because label contains path that resolves to same destination + | +note: referenced explicit link target defined here + --> $DIR/redundant_explicit_links.rs:149:21 + | +LL | /// [dummy_target]: dummy_target + | ^^^^^^^^^^^^ + = note: when a link's destination is not specified, + the label is used to resolve intra-doc links +help: remove explicit link target + | +LL | /// [`dummy_target`] + | ~~~~~~~~~~~~~~~~ + +error: redundant explicit link target + --> $DIR/redundant_explicit_links.rs:110:11 + | +LL | /// [Vec][Vec] + | --- ^^^ explicit target is redundant + | | + | because label contains path that resolves to same destination + | +note: referenced explicit link target defined here + --> $DIR/redundant_explicit_links.rs:150:12 + | +LL | /// [Vec]: Vec + | ^^^ + = note: when a link's destination is not specified, + the label is used to resolve intra-doc links +help: remove explicit link target + | +LL | /// [Vec] + | ~~~~~ + +error: redundant explicit link target + --> $DIR/redundant_explicit_links.rs:112:13 + | +LL | /// [`Vec`][Vec] + | ----- ^^^ explicit target is redundant + | | + | because label contains path that resolves to same destination + | +note: referenced explicit link target defined here + --> $DIR/redundant_explicit_links.rs:150:12 + | +LL | /// [Vec]: Vec + | ^^^ + = note: when a link's destination is not specified, + the label is used to resolve intra-doc links +help: remove explicit link target + | +LL | /// [`Vec`] + | ~~~~~~~ + +error: redundant explicit link target + --> $DIR/redundant_explicit_links.rs:114:11 + | +LL | /// [Vec][std::vec::Vec] + | --- ^^^^^^^^^^^^^ explicit target is redundant + | | + | because label contains path that resolves to same destination + | +note: referenced explicit link target defined here + --> $DIR/redundant_explicit_links.rs:151:22 + | +LL | /// [std::vec::Vec]: Vec + | ^^^ + = note: when a link's destination is not specified, + the label is used to resolve intra-doc links +help: remove explicit link target + | +LL | /// [Vec] + | ~~~~~ + +error: redundant explicit link target + --> $DIR/redundant_explicit_links.rs:116:13 + | +LL | /// [`Vec`][std::vec::Vec] + | ----- ^^^^^^^^^^^^^ explicit target is redundant + | | + | because label contains path that resolves to same destination + | +note: referenced explicit link target defined here + --> $DIR/redundant_explicit_links.rs:151:22 + | +LL | /// [std::vec::Vec]: Vec + | ^^^ + = note: when a link's destination is not specified, + the label is used to resolve intra-doc links +help: remove explicit link target + | +LL | /// [`Vec`] + | ~~~~~~~ + +error: redundant explicit link target + --> $DIR/redundant_explicit_links.rs:118:21 + | +LL | /// [std::vec::Vec][Vec] + | ------------- ^^^ explicit target is redundant + | | + | because label contains path that resolves to same destination + | +note: referenced explicit link target defined here + --> $DIR/redundant_explicit_links.rs:150:12 + | +LL | /// [Vec]: Vec + | ^^^ + = note: when a link's destination is not specified, + the label is used to resolve intra-doc links +help: remove explicit link target + | +LL | /// [std::vec::Vec] + | ~~~~~~~~~~~~~~~ + +error: redundant explicit link target + --> $DIR/redundant_explicit_links.rs:120:23 + | +LL | /// [`std::vec::Vec`][Vec] + | --------------- ^^^ explicit target is redundant + | | + | because label contains path that resolves to same destination + | +note: referenced explicit link target defined here + --> $DIR/redundant_explicit_links.rs:150:12 + | +LL | /// [Vec]: Vec + | ^^^ + = note: when a link's destination is not specified, + the label is used to resolve intra-doc links +help: remove explicit link target + | +LL | /// [`std::vec::Vec`] + | ~~~~~~~~~~~~~~~~~ + +error: redundant explicit link target + --> $DIR/redundant_explicit_links.rs:122:21 + | +LL | /// [std::vec::Vec][std::vec::Vec] + | ------------- ^^^^^^^^^^^^^ explicit target is redundant + | | + | because label contains path that resolves to same destination + | +note: referenced explicit link target defined here + --> $DIR/redundant_explicit_links.rs:151:22 + | +LL | /// [std::vec::Vec]: Vec + | ^^^ + = note: when a link's destination is not specified, + the label is used to resolve intra-doc links +help: remove explicit link target + | +LL | /// [std::vec::Vec] + | ~~~~~~~~~~~~~~~ + +error: redundant explicit link target + --> $DIR/redundant_explicit_links.rs:124:23 + | +LL | /// [`std::vec::Vec`][std::vec::Vec] + | --------------- ^^^^^^^^^^^^^ explicit target is redundant + | | + | because label contains path that resolves to same destination + | +note: referenced explicit link target defined here + --> $DIR/redundant_explicit_links.rs:151:22 + | +LL | /// [std::vec::Vec]: Vec + | ^^^ + = note: when a link's destination is not specified, + the label is used to resolve intra-doc links +help: remove explicit link target + | +LL | /// [`std::vec::Vec`] + | ~~~~~~~~~~~~~~~~~ + +error: redundant explicit link target + --> $DIR/redundant_explicit_links.rs:127:13 + | +LL | /// [usize][usize] + | ----- ^^^^^ explicit target is redundant + | | + | because label contains path that resolves to same destination + | +note: referenced explicit link target defined here + --> $DIR/redundant_explicit_links.rs:152:14 + | +LL | /// [usize]: usize + | ^^^^^ + = note: when a link's destination is not specified, + the label is used to resolve intra-doc links +help: remove explicit link target + | +LL | /// [usize] + | ~~~~~~~ + +error: redundant explicit link target + --> $DIR/redundant_explicit_links.rs:129:15 + | +LL | /// [`usize`][usize] + | ------- ^^^^^ explicit target is redundant + | | + | because label contains path that resolves to same destination + | +note: referenced explicit link target defined here + --> $DIR/redundant_explicit_links.rs:152:14 + | +LL | /// [usize]: usize + | ^^^^^ + = note: when a link's destination is not specified, + the label is used to resolve intra-doc links +help: remove explicit link target + | +LL | /// [`usize`] + | ~~~~~~~~~ + +error: redundant explicit link target + --> $DIR/redundant_explicit_links.rs:131:13 + | +LL | /// [usize][std::primitive::usize] + | ----- ^^^^^^^^^^^^^^^^^^^^^ explicit target is redundant + | | + | because label contains path that resolves to same destination + | +note: referenced explicit link target defined here + --> $DIR/redundant_explicit_links.rs:153:30 + | +LL | /// [std::primitive::usize]: usize + | ^^^^^ + = note: when a link's destination is not specified, + the label is used to resolve intra-doc links +help: remove explicit link target + | +LL | /// [usize] + | ~~~~~~~ + +error: redundant explicit link target + --> $DIR/redundant_explicit_links.rs:133:15 + | +LL | /// [`usize`][std::primitive::usize] + | ------- ^^^^^^^^^^^^^^^^^^^^^ explicit target is redundant + | | + | because label contains path that resolves to same destination + | +note: referenced explicit link target defined here + --> $DIR/redundant_explicit_links.rs:153:30 + | +LL | /// [std::primitive::usize]: usize + | ^^^^^ + = note: when a link's destination is not specified, + the label is used to resolve intra-doc links +help: remove explicit link target + | +LL | /// [`usize`] + | ~~~~~~~~~ + +error: redundant explicit link target + --> $DIR/redundant_explicit_links.rs:135:29 + | +LL | /// [std::primitive::usize][usize] + | --------------------- ^^^^^ explicit target is redundant + | | + | because label contains path that resolves to same destination + | +note: referenced explicit link target defined here + --> $DIR/redundant_explicit_links.rs:152:14 + | +LL | /// [usize]: usize + | ^^^^^ + = note: when a link's destination is not specified, + the label is used to resolve intra-doc links +help: remove explicit link target + | +LL | /// [std::primitive::usize] + | ~~~~~~~~~~~~~~~~~~~~~~~ + +error: redundant explicit link target + --> $DIR/redundant_explicit_links.rs:137:31 + | +LL | /// [`std::primitive::usize`][usize] + | ----------------------- ^^^^^ explicit target is redundant + | | + | because label contains path that resolves to same destination + | +note: referenced explicit link target defined here + --> $DIR/redundant_explicit_links.rs:152:14 + | +LL | /// [usize]: usize + | ^^^^^ + = note: when a link's destination is not specified, + the label is used to resolve intra-doc links +help: remove explicit link target + | +LL | /// [`std::primitive::usize`] + | ~~~~~~~~~~~~~~~~~~~~~~~~~ + +error: redundant explicit link target + --> $DIR/redundant_explicit_links.rs:139:29 + | +LL | /// [std::primitive::usize][std::primitive::usize] + | --------------------- ^^^^^^^^^^^^^^^^^^^^^ explicit target is redundant + | | + | because label contains path that resolves to same destination + | +note: referenced explicit link target defined here + --> $DIR/redundant_explicit_links.rs:153:30 + | +LL | /// [std::primitive::usize]: usize + | ^^^^^ + = note: when a link's destination is not specified, + the label is used to resolve intra-doc links +help: remove explicit link target + | +LL | /// [std::primitive::usize] + | ~~~~~~~~~~~~~~~~~~~~~~~ + +error: redundant explicit link target + --> $DIR/redundant_explicit_links.rs:141:31 + | +LL | /// [`std::primitive::usize`][std::primitive::usize] + | ----------------------- ^^^^^^^^^^^^^^^^^^^^^ explicit target is redundant + | | + | because label contains path that resolves to same destination + | +note: referenced explicit link target defined here + --> $DIR/redundant_explicit_links.rs:153:30 + | +LL | /// [std::primitive::usize]: usize + | ^^^^^ + = note: when a link's destination is not specified, + the label is used to resolve intra-doc links +help: remove explicit link target + | +LL | /// [`std::primitive::usize`] + | ~~~~~~~~~~~~~~~~~~~~~~~~~ + +error: redundant explicit link target + --> $DIR/redundant_explicit_links.rs:144:20 + | +LL | /// [dummy_target][dummy_target] TEXT + | ------------ ^^^^^^^^^^^^ explicit target is redundant + | | + | because label contains path that resolves to same destination + | +note: referenced explicit link target defined here + --> $DIR/redundant_explicit_links.rs:149:21 + | +LL | /// [dummy_target]: dummy_target + | ^^^^^^^^^^^^ + = note: when a link's destination is not specified, + the label is used to resolve intra-doc links +help: remove explicit link target + | +LL | /// [dummy_target] TEXT + | ~~~~~~~~~~~~~~ + +error: redundant explicit link target + --> $DIR/redundant_explicit_links.rs:146:22 + | +LL | /// [`dummy_target`][dummy_target] TEXT + | -------------- ^^^^^^^^^^^^ explicit target is redundant + | | + | because label contains path that resolves to same destination + | +note: referenced explicit link target defined here + --> $DIR/redundant_explicit_links.rs:149:21 + | +LL | /// [dummy_target]: dummy_target + | ^^^^^^^^^^^^ + = note: when a link's destination is not specified, + the label is used to resolve intra-doc links +help: remove explicit link target + | +LL | /// [`dummy_target`] TEXT + | ~~~~~~~~~~~~~~~~ + +error: aborting due to 60 previous errors + diff --git a/tests/rustdoc-ui/unescaped_backticks.rs b/tests/rustdoc-ui/unescaped_backticks.rs index e99cd1f3d58..e960e9f59e9 100644 --- a/tests/rustdoc-ui/unescaped_backticks.rs +++ b/tests/rustdoc-ui/unescaped_backticks.rs @@ -1,6 +1,7 @@ #![deny(rustdoc::unescaped_backticks)] #![allow(rustdoc::broken_intra_doc_links)] #![allow(rustdoc::invalid_html_tags)] +#![allow(rustdoc::redundant_explicit_links)] /// pub fn empty() {} diff --git a/tests/rustdoc-ui/unescaped_backticks.stderr b/tests/rustdoc-ui/unescaped_backticks.stderr index bf1f18889c4..83822f778d0 100644 --- a/tests/rustdoc-ui/unescaped_backticks.stderr +++ b/tests/rustdoc-ui/unescaped_backticks.stderr @@ -1,5 +1,5 @@ error: unescaped backtick - --> $DIR/unescaped_backticks.rs:186:70 + --> $DIR/unescaped_backticks.rs:187:70 | LL | /// if you want your MIR to be modified by the full MIR pipeline, or `#![custom_mir(dialect = | ^ @@ -19,7 +19,7 @@ LL | /// if you want your MIR to be modified by the full MIR pipeline, or \`#![c | + error: unescaped backtick - --> $DIR/unescaped_backticks.rs:231:13 + --> $DIR/unescaped_backticks.rs:232:13 | LL | //! `#![rustc_expected_cgu_reuse(module="spike", cfg="rpass2", kind="post-lto")] | ^ @@ -34,7 +34,7 @@ LL | //! \`#![rustc_expected_cgu_reuse(module="spike", cfg="rpass2", kin | + error: unescaped backtick - --> $DIR/unescaped_backticks.rs:236:13 + --> $DIR/unescaped_backticks.rs:237:13 | LL | /// `cfg=... | ^ @@ -49,7 +49,7 @@ LL | /// \`cfg=... | + error: unescaped backtick - --> $DIR/unescaped_backticks.rs:240:42 + --> $DIR/unescaped_backticks.rs:241:42 | LL | /// `cfg=... and not `#[cfg_attr]` | ^ @@ -64,7 +64,7 @@ LL | /// `cfg=... and not `#[cfg_attr]\` | + error: unescaped backtick - --> $DIR/unescaped_backticks.rs:192:91 + --> $DIR/unescaped_backticks.rs:193:91 | LL | /// Constructs a `TyKind::Error` type and registers a `delay_span_bug` with the given `msg to | ^ @@ -79,7 +79,7 @@ LL | /// Constructs a `TyKind::Error` type and registers a `delay_span_bug` | + error: unescaped backtick - --> $DIR/unescaped_backticks.rs:201:34 + --> $DIR/unescaped_backticks.rs:202:34 | LL | /// in `nt_to_tokenstream` | ^ @@ -94,7 +94,7 @@ LL | /// in `nt_to_tokenstream\` | + error: unescaped backtick - --> $DIR/unescaped_backticks.rs:207:62 + --> $DIR/unescaped_backticks.rs:208:62 | LL | /// that `Option<Symbol>` only takes up 4 bytes, because `newtype_index! reserves | ^ @@ -109,7 +109,7 @@ LL | /// that `Option<Symbol>` only takes up 4 bytes, because \`newtype_inde | + error: unescaped backtick - --> $DIR/unescaped_backticks.rs:215:52 + --> $DIR/unescaped_backticks.rs:216:52 | LL | /// also avoids the need to import `OpenOptions`. | ^ @@ -124,7 +124,7 @@ LL | /// also avoids the need to import `OpenOptions\`. | + error: unescaped backtick - --> $DIR/unescaped_backticks.rs:220:46 + --> $DIR/unescaped_backticks.rs:221:46 | LL | /// `HybridBitSet`. Has no effect if `row` does not exist. | ^ @@ -139,7 +139,7 @@ LL | /// `HybridBitSet`. Has no effect if `row\` does not exist. | + error: unescaped backtick - --> $DIR/unescaped_backticks.rs:246:12 + --> $DIR/unescaped_backticks.rs:247:12 | LL | /// RWU`s can get very large, so it uses a more compact representation. | ^ @@ -154,7 +154,7 @@ LL | /// RWU\`s can get very large, so it uses a more compact representation | + error: unescaped backtick - --> $DIR/unescaped_backticks.rs:253:15 + --> $DIR/unescaped_backticks.rs:254:15 | LL | /// in `U2`. | ^ @@ -169,7 +169,7 @@ LL | /// in `U2\`. | + error: unescaped backtick - --> $DIR/unescaped_backticks.rs:270:42 + --> $DIR/unescaped_backticks.rs:271:42 | LL | /// because it contains `[type error]`. Yuck! (See issue #29857 for | ^ @@ -184,7 +184,7 @@ LL | /// because it contains `[type error]\`. Yuck! (See issue #29857 for | + error: unescaped backtick - --> $DIR/unescaped_backticks.rs:280:53 + --> $DIR/unescaped_backticks.rs:281:53 | LL | /// well as the second instance of `A: AutoTrait`) to suppress | ^ @@ -199,7 +199,7 @@ LL | /// well as the second instance of `A: AutoTrait\`) to suppress | + error: unescaped backtick - --> $DIR/unescaped_backticks.rs:290:40 + --> $DIR/unescaped_backticks.rs:291:40 | LL | /// `'a` with `'b` and not `'static`. But it will have to do for | ^ @@ -211,7 +211,7 @@ LL | /// `'a` with `'b` and not `'static\`. But it will have to do for | + error: unescaped backtick - --> $DIR/unescaped_backticks.rs:299:54 + --> $DIR/unescaped_backticks.rs:300:54 | LL | /// `None`. Otherwise, it will return `Some(Dispatch)`. | ^ @@ -226,7 +226,7 @@ LL | /// `None`. Otherwise, it will return `Some(Dispatch)\`. | + error: unescaped backtick - --> $DIR/unescaped_backticks.rs:303:13 + --> $DIR/unescaped_backticks.rs:304:13 | LL | /// or `None` if it isn't. | ^ @@ -238,7 +238,7 @@ LL | /// or `None\` if it isn't. | + error: unescaped backtick - --> $DIR/unescaped_backticks.rs:307:14 + --> $DIR/unescaped_backticks.rs:308:14 | LL | /// `on_event` should be called. | ^ @@ -253,7 +253,7 @@ LL | /// `on_event\` should be called. | + error: unescaped backtick - --> $DIR/unescaped_backticks.rs:312:29 + --> $DIR/unescaped_backticks.rs:313:29 | LL | /// [`rebuild_interest_cache`][rebuild] is called after the value of the max | ^ @@ -268,7 +268,7 @@ LL | /// [`rebuild_interest_cache\`][rebuild] is called after the value of the m | + error: unescaped backtick - --> $DIR/unescaped_backticks.rs:322:5 + --> $DIR/unescaped_backticks.rs:323:5 | LL | / /// The Subscriber` may be accessed by calling [`WeakDispatch::upgrade`], LL | | @@ -287,7 +287,7 @@ LL | | /// level changes. to this: `None`. Otherwise, it will return `Some(Dispatch)\`. error: unescaped backtick - --> $DIR/unescaped_backticks.rs:322:5 + --> $DIR/unescaped_backticks.rs:323:5 | LL | / /// The Subscriber` may be accessed by calling [`WeakDispatch::upgrade`], LL | | @@ -304,7 +304,7 @@ LL | | /// level changes. to this: or `None\` if it isn't. error: unescaped backtick - --> $DIR/unescaped_backticks.rs:322:5 + --> $DIR/unescaped_backticks.rs:323:5 | LL | / /// The Subscriber` may be accessed by calling [`WeakDispatch::upgrade`], LL | | @@ -323,7 +323,7 @@ LL | | /// level changes. to this: `on_event\` should be called. error: unescaped backtick - --> $DIR/unescaped_backticks.rs:322:5 + --> $DIR/unescaped_backticks.rs:323:5 | LL | / /// The Subscriber` may be accessed by calling [`WeakDispatch::upgrade`], LL | | @@ -342,7 +342,7 @@ LL | | /// level changes. to this: [`rebuild_interest_cache\`][rebuild] is called after the value of the max error: unescaped backtick - --> $DIR/unescaped_backticks.rs:348:56 + --> $DIR/unescaped_backticks.rs:349:56 | LL | /// instead and use [`CloneCounterObserver::counter`] to increment. | ^ @@ -354,7 +354,7 @@ LL | /// instead and use [`CloneCounterObserver::counter\`] to increment. | + error: unescaped backtick - --> $DIR/unescaped_backticks.rs:11:5 + --> $DIR/unescaped_backticks.rs:12:5 | LL | /// ` | ^ @@ -366,7 +366,7 @@ LL | /// \` | + error: unescaped backtick - --> $DIR/unescaped_backticks.rs:18:7 + --> $DIR/unescaped_backticks.rs:19:7 | LL | /// \` | ^ @@ -381,7 +381,7 @@ LL | /// \\` | + error: unescaped backtick - --> $DIR/unescaped_backticks.rs:25:6 + --> $DIR/unescaped_backticks.rs:26:6 | LL | /// [`link1] | ^ @@ -396,7 +396,7 @@ LL | /// [\`link1] | + error: unescaped backtick - --> $DIR/unescaped_backticks.rs:29:11 + --> $DIR/unescaped_backticks.rs:30:11 | LL | /// [link2`] | ^ @@ -411,7 +411,7 @@ LL | /// [link2\`] | + error: unescaped backtick - --> $DIR/unescaped_backticks.rs:33:6 + --> $DIR/unescaped_backticks.rs:34:6 | LL | /// [`link_long](link_long) | ^ @@ -426,7 +426,7 @@ LL | /// [\`link_long](link_long) | + error: unescaped backtick - --> $DIR/unescaped_backticks.rs:37:6 + --> $DIR/unescaped_backticks.rs:38:6 | LL | /// [`broken-link] | ^ @@ -441,7 +441,7 @@ LL | /// [\`broken-link] | + error: unescaped backtick - --> $DIR/unescaped_backticks.rs:44:8 + --> $DIR/unescaped_backticks.rs:45:8 | LL | /// <x:`> | ^ @@ -456,7 +456,7 @@ LL | /// <x:\`> | + error: unescaped backtick - --> $DIR/unescaped_backticks.rs:54:6 + --> $DIR/unescaped_backticks.rs:55:6 | LL | /// 🦀`🦀 | ^ @@ -475,7 +475,7 @@ LL | /// 🦀\`🦀 | + error: unescaped backtick - --> $DIR/unescaped_backticks.rs:58:5 + --> $DIR/unescaped_backticks.rs:59:5 | LL | /// `foo( | ^ @@ -490,7 +490,7 @@ LL | /// \`foo( | + error: unescaped backtick - --> $DIR/unescaped_backticks.rs:64:14 + --> $DIR/unescaped_backticks.rs:65:14 | LL | /// `foo `bar` | ^ @@ -505,7 +505,7 @@ LL | /// `foo `bar\` | + error: unescaped backtick - --> $DIR/unescaped_backticks.rs:70:5 + --> $DIR/unescaped_backticks.rs:71:5 | LL | /// `foo( | ^ @@ -520,7 +520,7 @@ LL | /// \`foo( | + error: unescaped backtick - --> $DIR/unescaped_backticks.rs:75:83 + --> $DIR/unescaped_backticks.rs:76:83 | LL | /// Addition is commutative, which means that add(a, b)` is the same as `add(b, a)`. | ^ @@ -535,7 +535,7 @@ LL | /// Addition is commutative, which means that add(a, b)` is the same as `ad | + error: unescaped backtick - --> $DIR/unescaped_backticks.rs:79:51 + --> $DIR/unescaped_backticks.rs:80:51 | LL | /// or even to add a number `n` to 42 (`add(42, b)`)! | ^ @@ -550,7 +550,7 @@ LL | /// or even to add a number `n` to 42 (`add(42, b)\`)! | + error: unescaped backtick - --> $DIR/unescaped_backticks.rs:83:83 + --> $DIR/unescaped_backticks.rs:84:83 | LL | /// Addition is commutative, which means that `add(a, b) is the same as `add(b, a)`. | ^ @@ -565,7 +565,7 @@ LL | /// Addition is commutative, which means that `add(a, b) is the same as `ad | + error: unescaped backtick - --> $DIR/unescaped_backticks.rs:87:51 + --> $DIR/unescaped_backticks.rs:88:51 | LL | /// or even to add a number `n` to 42 (`add(42, n)`)! | ^ @@ -580,7 +580,7 @@ LL | /// or even to add a number `n` to 42 (`add(42, n)\`)! | + error: unescaped backtick - --> $DIR/unescaped_backticks.rs:91:83 + --> $DIR/unescaped_backticks.rs:92:83 | LL | /// Addition is commutative, which means that `add(a, b)` is the same as add(b, a)`. | ^ @@ -595,7 +595,7 @@ LL | /// Addition is commutative, which means that `add(a, b)` is the same as ad | + error: unescaped backtick - --> $DIR/unescaped_backticks.rs:95:50 + --> $DIR/unescaped_backticks.rs:96:50 | LL | /// or even to add a number `n` to 42 (add(42, n)`)! | ^ @@ -610,7 +610,7 @@ LL | /// or even to add a number `n` to 42 (add(42, n)\`)! | + error: unescaped backtick - --> $DIR/unescaped_backticks.rs:99:74 + --> $DIR/unescaped_backticks.rs:100:74 | LL | /// Addition is commutative, which means that `add(a, b)` is the same as `add(b, a). | ^ @@ -625,7 +625,7 @@ LL | /// Addition is commutative, which means that `add(a, b)` is the same as \` | + error: unescaped backtick - --> $DIR/unescaped_backticks.rs:103:51 + --> $DIR/unescaped_backticks.rs:104:51 | LL | /// or even to add a number `n` to 42 (`add(42, n)`)! | ^ @@ -640,7 +640,7 @@ LL | /// or even to add a number `n` to 42 (`add(42, n)\`)! | + error: unescaped backtick - --> $DIR/unescaped_backticks.rs:107:1 + --> $DIR/unescaped_backticks.rs:108:1 | LL | #[doc = "`"] | ^^^^^^^^^^^^ @@ -651,7 +651,7 @@ LL | #[doc = "`"] to this: \` error: unescaped backtick - --> $DIR/unescaped_backticks.rs:114:1 + --> $DIR/unescaped_backticks.rs:115:1 | LL | #[doc = concat!("\\", "`")] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -664,7 +664,7 @@ LL | #[doc = concat!("\\", "`")] to this: \\` error: unescaped backtick - --> $DIR/unescaped_backticks.rs:118:1 + --> $DIR/unescaped_backticks.rs:119:1 | LL | #[doc = "Addition is commutative, which means that add(a, b)` is the same as `add(b, a)`."] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -677,7 +677,7 @@ LL | #[doc = "Addition is commutative, which means that add(a, b)` is the same a to this: Addition is commutative, which means that add(a, b)` is the same as `add(b, a)\`. error: unescaped backtick - --> $DIR/unescaped_backticks.rs:122:1 + --> $DIR/unescaped_backticks.rs:123:1 | LL | #[doc = "Addition is commutative, which means that `add(a, b) is the same as `add(b, a)`."] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -690,7 +690,7 @@ LL | #[doc = "Addition is commutative, which means that `add(a, b) is the same a to this: Addition is commutative, which means that `add(a, b) is the same as `add(b, a)\`. error: unescaped backtick - --> $DIR/unescaped_backticks.rs:126:1 + --> $DIR/unescaped_backticks.rs:127:1 | LL | #[doc = "Addition is commutative, which means that `add(a, b)` is the same as add(b, a)`."] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -703,7 +703,7 @@ LL | #[doc = "Addition is commutative, which means that `add(a, b)` is the same to this: Addition is commutative, which means that `add(a, b)` is the same as add(b, a)\`. error: unescaped backtick - --> $DIR/unescaped_backticks.rs:130:1 + --> $DIR/unescaped_backticks.rs:131:1 | LL | #[doc = "Addition is commutative, which means that `add(a, b)` is the same as `add(b, a)."] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -716,7 +716,7 @@ LL | #[doc = "Addition is commutative, which means that `add(a, b)` is the same to this: Addition is commutative, which means that `add(a, b)` is the same as \`add(b, a). error: unescaped backtick - --> $DIR/unescaped_backticks.rs:135:5 + --> $DIR/unescaped_backticks.rs:136:5 | LL | /// `foo | ^ @@ -731,7 +731,7 @@ LL | /// \`foo | + error: unescaped backtick - --> $DIR/unescaped_backticks.rs:139:7 + --> $DIR/unescaped_backticks.rs:140:7 | LL | /// # `(heading | ^ @@ -746,7 +746,7 @@ LL | /// # \`(heading | + error: unescaped backtick - --> $DIR/unescaped_backticks.rs:141:17 + --> $DIR/unescaped_backticks.rs:142:17 | LL | /// ## heading2)` | ^ @@ -761,7 +761,7 @@ LL | /// ## heading2)\` | + error: unescaped backtick - --> $DIR/unescaped_backticks.rs:144:11 + --> $DIR/unescaped_backticks.rs:145:11 | LL | /// multi `( | ^ @@ -776,7 +776,7 @@ LL | /// multi \`( | + error: unescaped backtick - --> $DIR/unescaped_backticks.rs:150:10 + --> $DIR/unescaped_backticks.rs:151:10 | LL | /// para)`(graph | ^ @@ -795,7 +795,7 @@ LL | /// para)\`(graph | + error: unescaped backtick - --> $DIR/unescaped_backticks.rs:153:10 + --> $DIR/unescaped_backticks.rs:154:10 | LL | /// para)`(graph2 | ^ @@ -814,7 +814,7 @@ LL | /// para)\`(graph2 | + error: unescaped backtick - --> $DIR/unescaped_backticks.rs:156:12 + --> $DIR/unescaped_backticks.rs:157:12 | LL | /// 1. foo)` | ^ @@ -829,7 +829,7 @@ LL | /// 1. foo)\` | + error: unescaped backtick - --> $DIR/unescaped_backticks.rs:158:8 + --> $DIR/unescaped_backticks.rs:159:8 | LL | /// 2. `(bar | ^ @@ -844,7 +844,7 @@ LL | /// 2. \`(bar | + error: unescaped backtick - --> $DIR/unescaped_backticks.rs:160:11 + --> $DIR/unescaped_backticks.rs:161:11 | LL | /// * baz)` | ^ @@ -859,7 +859,7 @@ LL | /// * baz)\` | + error: unescaped backtick - --> $DIR/unescaped_backticks.rs:162:7 + --> $DIR/unescaped_backticks.rs:163:7 | LL | /// * `(quux | ^ @@ -874,7 +874,7 @@ LL | /// * \`(quux | + error: unescaped backtick - --> $DIR/unescaped_backticks.rs:165:5 + --> $DIR/unescaped_backticks.rs:166:5 | LL | /// `#![this_is_actually_an_image(and(not), an = "attribute")] | ^ @@ -889,7 +889,7 @@ LL | /// \`#![this_is_actually_an_image(and(not), an = "attribute")] | + error: unescaped backtick - --> $DIR/unescaped_backticks.rs:168:62 + --> $DIR/unescaped_backticks.rs:169:62 | LL | /// #![this_is_actually_an_image(and(not), an = "attribute")]` | ^ @@ -904,7 +904,7 @@ LL | /// #![this_is_actually_an_image(and(not), an = "attribute")]\` | + error: unescaped backtick - --> $DIR/unescaped_backticks.rs:173:7 + --> $DIR/unescaped_backticks.rs:174:7 | LL | /// | `table( | )head` | | ^ @@ -919,7 +919,7 @@ LL | /// | \`table( | )head` | | + error: unescaped backtick - --> $DIR/unescaped_backticks.rs:173:22 + --> $DIR/unescaped_backticks.rs:174:22 | LL | /// | `table( | )head` | | ^ @@ -934,7 +934,7 @@ LL | /// | `table( | )head\` | | + error: unescaped backtick - --> $DIR/unescaped_backticks.rs:177:12 + --> $DIR/unescaped_backticks.rs:178:12 | LL | /// | table`( | )`body | | ^ @@ -949,7 +949,7 @@ LL | /// | table\`( | )`body | | + error: unescaped backtick - --> $DIR/unescaped_backticks.rs:177:18 + --> $DIR/unescaped_backticks.rs:178:18 | LL | /// | table`( | )`body | | ^ diff --git a/tests/rustdoc/description.rs b/tests/rustdoc/description.rs index 43cd59ebd09..aabbb4c4c8f 100644 --- a/tests/rustdoc/description.rs +++ b/tests/rustdoc/description.rs @@ -1,4 +1,5 @@ #![crate_name = "foo"] +#![allow(rustdoc::redundant_explicit_links)] //! # Description test crate //! //! This is the contents of the test crate docstring. diff --git a/tests/rustdoc/intra-doc/basic.rs b/tests/rustdoc/intra-doc/basic.rs index 96e21137b2d..c88a7887f11 100644 --- a/tests/rustdoc/intra-doc/basic.rs +++ b/tests/rustdoc/intra-doc/basic.rs @@ -1,3 +1,5 @@ +#![allow(rustdoc::redundant_explicit_links)] + // @has basic/index.html // @has - '//a/@href' 'struct.ThisType.html' // @has - '//a/@title' 'struct basic::ThisType' diff --git a/tests/rustdoc/intra-doc/generic-params.rs b/tests/rustdoc/intra-doc/generic-params.rs index fbc9fc6a9bc..359f775f97f 100644 --- a/tests/rustdoc/intra-doc/generic-params.rs +++ b/tests/rustdoc/intra-doc/generic-params.rs @@ -1,6 +1,7 @@ // ignore-tidy-linelength #![crate_name = "foo"] +#![allow(rustdoc::redundant_explicit_links)] //! Here's a link to [`Vec<T>`] and one to [`Box<Vec<Option<T>>>`]. //! Here's a link to [`Iterator<Box<T>>::Item`]. diff --git a/tests/rustdoc/intra-doc/issue-108459.rs b/tests/rustdoc/intra-doc/issue-108459.rs index eb1c7a05e54..b8cd478b4df 100644 --- a/tests/rustdoc/intra-doc/issue-108459.rs +++ b/tests/rustdoc/intra-doc/issue-108459.rs @@ -1,4 +1,5 @@ #![deny(rustdoc::broken_intra_doc_links)] +#![allow(rustdoc::redundant_explicit_links)] pub struct S; pub mod char {} diff --git a/tests/ui/abi/relocation_model_pic.rs b/tests/ui/abi/relocation_model_pic.rs new file mode 100644 index 00000000000..0cfc44cd09d --- /dev/null +++ b/tests/ui/abi/relocation_model_pic.rs @@ -0,0 +1,9 @@ +// run-pass +// compile-flags: -C relocation-model=pic +// ignore-emscripten no pic +// ignore-wasm + +#![feature(cfg_relocation_model)] + +#[cfg(relocation_model = "pic")] +fn main() {} diff --git a/tests/ui/associated-consts/infer-placeholder-in-non-suggestable-pos.rs b/tests/ui/associated-consts/infer-placeholder-in-non-suggestable-pos.rs index 40896c32e11..1e0b77b0d3b 100644 --- a/tests/ui/associated-consts/infer-placeholder-in-non-suggestable-pos.rs +++ b/tests/ui/associated-consts/infer-placeholder-in-non-suggestable-pos.rs @@ -5,6 +5,8 @@ trait Trait { impl Trait for () { const ASSOC: &dyn Fn(_) = 1i32; //~^ ERROR the placeholder `_` is not allowed within types on item signatures for associated constants + //~| WARN `&` without an explicit lifetime name cannot be used here + //~| WARN this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! } fn main() {} diff --git a/tests/ui/associated-consts/infer-placeholder-in-non-suggestable-pos.stderr b/tests/ui/associated-consts/infer-placeholder-in-non-suggestable-pos.stderr index 993a08faba9..f8c02420f96 100644 --- a/tests/ui/associated-consts/infer-placeholder-in-non-suggestable-pos.stderr +++ b/tests/ui/associated-consts/infer-placeholder-in-non-suggestable-pos.stderr @@ -1,9 +1,23 @@ +warning: `&` without an explicit lifetime name cannot be used here + --> $DIR/infer-placeholder-in-non-suggestable-pos.rs:6:18 + | +LL | const ASSOC: &dyn Fn(_) = 1i32; + | ^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #115010 <https://github.com/rust-lang/rust/issues/115010> + = note: `#[warn(elided_lifetimes_in_associated_constant)]` on by default +help: use the `'static` lifetime + | +LL | const ASSOC: &'static dyn Fn(_) = 1i32; + | +++++++ + error[E0121]: the placeholder `_` is not allowed within types on item signatures for associated constants --> $DIR/infer-placeholder-in-non-suggestable-pos.rs:6:26 | LL | const ASSOC: &dyn Fn(_) = 1i32; | ^ not allowed in type signatures -error: aborting due to previous error +error: aborting due to previous error; 1 warning emitted For more information about this error, try `rustc --explain E0121`. diff --git a/tests/ui/associated-type-bounds/overlaping-bound-suggestion.rs b/tests/ui/associated-type-bounds/overlaping-bound-suggestion.rs new file mode 100644 index 00000000000..3853bc8594f --- /dev/null +++ b/tests/ui/associated-type-bounds/overlaping-bound-suggestion.rs @@ -0,0 +1,12 @@ +#![allow(bare_trait_objects)] +#![feature(associated_type_bounds)] +trait Item { + type Core; +} +pub struct Flatten<I> { + inner: <IntoIterator<Item: IntoIterator<Item: >>::IntoIterator as Item>::Core, + //~^ ERROR E0191 + //~| ERROR E0223 +} + +fn main() {} diff --git a/tests/ui/associated-type-bounds/overlaping-bound-suggestion.stderr b/tests/ui/associated-type-bounds/overlaping-bound-suggestion.stderr new file mode 100644 index 00000000000..61299550e98 --- /dev/null +++ b/tests/ui/associated-type-bounds/overlaping-bound-suggestion.stderr @@ -0,0 +1,24 @@ +error[E0191]: the value of the associated types `IntoIter` (from trait `IntoIterator`), `IntoIter` (from trait `IntoIterator`), `Item` (from trait `IntoIterator`), `Item` (from trait `IntoIterator`) must be specified + --> $DIR/overlaping-bound-suggestion.rs:7:13 + | +LL | inner: <IntoIterator<Item: IntoIterator<Item: >>::IntoIterator as Item>::Core, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | | + | | associated types `Item`, `IntoIter` must be specified + | associated types `Item`, `IntoIter` must be specified + +error[E0223]: ambiguous associated type + --> $DIR/overlaping-bound-suggestion.rs:7:13 + | +LL | inner: <IntoIterator<Item: IntoIterator<Item: >>::IntoIterator as Item>::Core, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: if there were a trait named `Example` with associated type `IntoIterator` implemented for `(dyn IntoIterator + 'static)`, you could use the fully-qualified path + | +LL | inner: <<(dyn IntoIterator + 'static) as Example>::IntoIterator as Item>::Core, + | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +error: aborting due to 2 previous errors + +Some errors have detailed explanations: E0191, E0223. +For more information about an error, try `rustc --explain E0191`. diff --git a/tests/ui/associated-types/dont-suggest-cyclic-constraint.fixed b/tests/ui/associated-types/dont-suggest-cyclic-constraint.fixed deleted file mode 100644 index ec4165cc71e..00000000000 --- a/tests/ui/associated-types/dont-suggest-cyclic-constraint.fixed +++ /dev/null @@ -1,13 +0,0 @@ -// run-rustfix - -use std::fmt::Debug; - -pub fn foo<I: Iterator>(mut iter: I, value: &I::Item) -where - I::Item: Eq + Debug, -{ - debug_assert_eq!(iter.next().as_ref(), Some(value)); - //~^ ERROR mismatched types -} - -fn main() {} diff --git a/tests/ui/associated-types/dont-suggest-cyclic-constraint.rs b/tests/ui/associated-types/dont-suggest-cyclic-constraint.rs index 0b4df08783d..0848b4c559b 100644 --- a/tests/ui/associated-types/dont-suggest-cyclic-constraint.rs +++ b/tests/ui/associated-types/dont-suggest-cyclic-constraint.rs @@ -1,5 +1,3 @@ -// run-rustfix - use std::fmt::Debug; pub fn foo<I: Iterator>(mut iter: I, value: &I::Item) diff --git a/tests/ui/associated-types/dont-suggest-cyclic-constraint.stderr b/tests/ui/associated-types/dont-suggest-cyclic-constraint.stderr index 65d18761b18..3ecac9c83e5 100644 --- a/tests/ui/associated-types/dont-suggest-cyclic-constraint.stderr +++ b/tests/ui/associated-types/dont-suggest-cyclic-constraint.stderr @@ -1,15 +1,11 @@ error[E0308]: mismatched types - --> $DIR/dont-suggest-cyclic-constraint.rs:9:35 + --> $DIR/dont-suggest-cyclic-constraint.rs:7:35 | LL | debug_assert_eq!(iter.next(), Some(value)); | ^^^^^^^^^^^ expected `Option<<I as Iterator>::Item>`, found `Option<&<I as Iterator>::Item>` | = note: expected enum `Option<<I as Iterator>::Item>` found enum `Option<&<I as Iterator>::Item>` -help: use `Option::as_ref` to convert `Option<<I as Iterator>::Item>` to `Option<&<I as Iterator>::Item>` - | -LL | debug_assert_eq!(iter.next().as_ref(), Some(value)); - | +++++++++ error: aborting due to previous error diff --git a/tests/ui/async-await/deep-futures-are-freeze.rs b/tests/ui/async-await/deep-futures-are-freeze.rs new file mode 100644 index 00000000000..dd676d5e18c --- /dev/null +++ b/tests/ui/async-await/deep-futures-are-freeze.rs @@ -0,0 +1,179 @@ +// build-pass +// compile-flags: -Copt-level=s -Clto=fat +// no-prefer-dynamic +// edition: 2021 + +#![recursion_limit = "256"] + +fn main() { + spawn(move || main0()) +} + +fn spawn<F>(future: impl FnOnce() -> F) { + future(); +} + +async fn main0() { + main1().await; + main2().await; +} +async fn main1() { + main2().await; + main3().await; +} +async fn main2() { + main3().await; + main4().await; +} +async fn main3() { + main4().await; + main5().await; +} +async fn main4() { + main5().await; + main6().await; +} +async fn main5() { + main6().await; + main7().await; +} +async fn main6() { + main7().await; + main8().await; +} +async fn main7() { + main8().await; + main9().await; +} +async fn main8() { + main9().await; + main10().await; +} +async fn main9() { + main10().await; + main11().await; +} +async fn main10() { + main11().await; + main12().await; +} +async fn main11() { + main12().await; + main13().await; +} +async fn main12() { + main13().await; + main14().await; +} +async fn main13() { + main14().await; + main15().await; +} +async fn main14() { + main15().await; + main16().await; +} +async fn main15() { + main16().await; + main17().await; +} +async fn main16() { + main17().await; + main18().await; +} +async fn main17() { + main18().await; + main19().await; +} +async fn main18() { + main19().await; + main20().await; +} +async fn main19() { + main20().await; + main21().await; +} +async fn main20() { + main21().await; + main22().await; +} +async fn main21() { + main22().await; + main23().await; +} +async fn main22() { + main23().await; + main24().await; +} +async fn main23() { + main24().await; + main25().await; +} +async fn main24() { + main25().await; + main26().await; +} +async fn main25() { + main26().await; + main27().await; +} +async fn main26() { + main27().await; + main28().await; +} +async fn main27() { + main28().await; + main29().await; +} +async fn main28() { + main29().await; + main30().await; +} +async fn main29() { + main30().await; + main31().await; +} +async fn main30() { + main31().await; + main32().await; +} +async fn main31() { + main32().await; + main33().await; +} +async fn main32() { + main33().await; + main34().await; +} +async fn main33() { + main34().await; + main35().await; +} +async fn main34() { + main35().await; + main36().await; +} +async fn main35() { + main36().await; + main37().await; +} +async fn main36() { + main37().await; + main38().await; +} +async fn main37() { + main38().await; + main39().await; +} +async fn main38() { + main39().await; + main40().await; +} +async fn main39() { + main40().await; +} +async fn main40() { + boom(&mut ()).await; +} + +async fn boom(f: &mut ()) {} diff --git a/tests/ui/async-await/normalize-output-in-signature-deduction.rs b/tests/ui/async-await/normalize-output-in-signature-deduction.rs new file mode 100644 index 00000000000..960065a83a4 --- /dev/null +++ b/tests/ui/async-await/normalize-output-in-signature-deduction.rs @@ -0,0 +1,19 @@ +// edition:2021 +// revisions: current next +//[next] compile-flags: -Ztrait-solver=next +// check-pass + +#![feature(type_alias_impl_trait)] + +struct Foo; + +impl Trait for Foo {} +pub trait Trait {} + +pub type TAIT<T> = impl Trait; + +async fn foo<T>() -> TAIT<T> { + Foo +} + +fn main() {} diff --git a/tests/ui/consts/assert-type-intrinsics.stderr b/tests/ui/consts/assert-type-intrinsics.stderr index 70aec91e226..3c03b03deee 100644 --- a/tests/ui/consts/assert-type-intrinsics.stderr +++ b/tests/ui/consts/assert-type-intrinsics.stderr @@ -2,19 +2,19 @@ error[E0080]: evaluation of constant value failed --> $DIR/assert-type-intrinsics.rs:12:9 | LL | MaybeUninit::<!>::uninit().assume_init(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ aborted execution: attempted to instantiate uninhabited type `!` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the evaluated program panicked at 'aborted execution: attempted to instantiate uninhabited type `!`', $DIR/assert-type-intrinsics.rs:12:36 error[E0080]: evaluation of constant value failed --> $DIR/assert-type-intrinsics.rs:16:9 | LL | intrinsics::assert_mem_uninitialized_valid::<&'static i32>(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ aborted execution: attempted to leave type `&i32` uninitialized, which is invalid + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the evaluated program panicked at 'aborted execution: attempted to leave type `&i32` uninitialized, which is invalid', $DIR/assert-type-intrinsics.rs:16:9 error[E0080]: evaluation of constant value failed --> $DIR/assert-type-intrinsics.rs:20:9 | LL | intrinsics::assert_zero_valid::<&'static i32>(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ aborted execution: attempted to zero-initialize type `&i32`, which is invalid + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the evaluated program panicked at 'aborted execution: attempted to zero-initialize type `&i32`, which is invalid', $DIR/assert-type-intrinsics.rs:20:9 error: aborting due to 3 previous errors diff --git a/tests/ui/consts/assoc-const-elided-lifetime.rs b/tests/ui/consts/assoc-const-elided-lifetime.rs new file mode 100644 index 00000000000..10cd33a8fed --- /dev/null +++ b/tests/ui/consts/assoc-const-elided-lifetime.rs @@ -0,0 +1,19 @@ +#![deny(elided_lifetimes_in_associated_constant)] + +use std::marker::PhantomData; + +struct Foo<'a> { + x: PhantomData<&'a ()>, +} + +impl<'a> Foo<'a> { + const FOO: Foo<'_> = Foo { x: PhantomData::<&()> }; + //~^ ERROR `'_` cannot be used here + //~| WARN this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + + const BAR: &() = &(); + //~^ ERROR `&` without an explicit lifetime name cannot be used here + //~| WARN this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! +} + +fn main() {} diff --git a/tests/ui/consts/assoc-const-elided-lifetime.stderr b/tests/ui/consts/assoc-const-elided-lifetime.stderr new file mode 100644 index 00000000000..a1eeaff4ba8 --- /dev/null +++ b/tests/ui/consts/assoc-const-elided-lifetime.stderr @@ -0,0 +1,33 @@ +error: `'_` cannot be used here + --> $DIR/assoc-const-elided-lifetime.rs:10:20 + | +LL | const FOO: Foo<'_> = Foo { x: PhantomData::<&()> }; + | ^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #115010 <https://github.com/rust-lang/rust/issues/115010> +note: the lint level is defined here + --> $DIR/assoc-const-elided-lifetime.rs:1:9 + | +LL | #![deny(elided_lifetimes_in_associated_constant)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +help: use the `'static` lifetime + | +LL | const FOO: Foo<'static> = Foo { x: PhantomData::<&()> }; + | ~~~~~~~ + +error: `&` without an explicit lifetime name cannot be used here + --> $DIR/assoc-const-elided-lifetime.rs:14:16 + | +LL | const BAR: &() = &(); + | ^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #115010 <https://github.com/rust-lang/rust/issues/115010> +help: use the `'static` lifetime + | +LL | const BAR: &'static () = &(); + | +++++++ + +error: aborting due to 2 previous errors + diff --git a/tests/ui/deprecation/issue-84637-deprecated-associated-function.fixed b/tests/ui/deprecation/issue-84637-deprecated-associated-function.fixed index 85e88287094..659b5465522 100644 --- a/tests/ui/deprecation/issue-84637-deprecated-associated-function.fixed +++ b/tests/ui/deprecation/issue-84637-deprecated-associated-function.fixed @@ -6,4 +6,6 @@ fn main() { let _foo = str::trim_start(" aoeu"); //~ ERROR use of deprecated method `core::str::<impl str>::trim_left`: superseded by `trim_start` [deprecated] let _bar = " aoeu".trim_start(); //~ ERROR use of deprecated method `core::str::<impl str>::trim_left`: superseded by `trim_start` [deprecated] + + let _baz = ["a", "b"].join(" "); //~ ERROR use of deprecated method `std::slice::<impl [T]>::connect`: renamed to join [deprecated] } diff --git a/tests/ui/deprecation/issue-84637-deprecated-associated-function.rs b/tests/ui/deprecation/issue-84637-deprecated-associated-function.rs index 246de2f5e4b..cfc6c4450b4 100644 --- a/tests/ui/deprecation/issue-84637-deprecated-associated-function.rs +++ b/tests/ui/deprecation/issue-84637-deprecated-associated-function.rs @@ -6,4 +6,6 @@ fn main() { let _foo = str::trim_left(" aoeu"); //~ ERROR use of deprecated method `core::str::<impl str>::trim_left`: superseded by `trim_start` [deprecated] let _bar = " aoeu".trim_left(); //~ ERROR use of deprecated method `core::str::<impl str>::trim_left`: superseded by `trim_start` [deprecated] + + let _baz = ["a", "b"].connect(" "); //~ ERROR use of deprecated method `std::slice::<impl [T]>::connect`: renamed to join [deprecated] } diff --git a/tests/ui/deprecation/issue-84637-deprecated-associated-function.stderr b/tests/ui/deprecation/issue-84637-deprecated-associated-function.stderr index 3b518d1802b..d1f5ea3602a 100644 --- a/tests/ui/deprecation/issue-84637-deprecated-associated-function.stderr +++ b/tests/ui/deprecation/issue-84637-deprecated-associated-function.stderr @@ -25,5 +25,16 @@ help: replace the use of the deprecated method LL | let _bar = " aoeu".trim_start(); | ~~~~~~~~~~ -error: aborting due to 2 previous errors +error: use of deprecated method `std::slice::<impl [T]>::connect`: renamed to join + --> $DIR/issue-84637-deprecated-associated-function.rs:10:27 + | +LL | let _baz = ["a", "b"].connect(" "); + | ^^^^^^^ + | +help: replace the use of the deprecated method + | +LL | let _baz = ["a", "b"].join(" "); + | ~~~~ + +error: aborting due to 3 previous errors diff --git a/tests/ui/feature-gates/feature-gate-cfg-relocation-model.rs b/tests/ui/feature-gates/feature-gate-cfg-relocation-model.rs new file mode 100644 index 00000000000..7529014ece2 --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-cfg-relocation-model.rs @@ -0,0 +1,4 @@ +#[cfg(relocation_model = "pic")] //~ ERROR +fn _foo() {} + +fn main() {} diff --git a/tests/ui/feature-gates/feature-gate-cfg-relocation-model.stderr b/tests/ui/feature-gates/feature-gate-cfg-relocation-model.stderr new file mode 100644 index 00000000000..592768a4203 --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-cfg-relocation-model.stderr @@ -0,0 +1,12 @@ +error[E0658]: `cfg(relocation_model)` is experimental and subject to change + --> $DIR/feature-gate-cfg-relocation-model.rs:1:7 + | +LL | #[cfg(relocation_model = "pic")] + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #114929 <https://github.com/rust-lang/rust/issues/114929> for more information + = help: add `#![feature(cfg_relocation_model)]` to the crate attributes to enable + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/implied-bounds/implied_bounds_entailment_alias_var.rs b/tests/ui/implied-bounds/implied_bounds_entailment_alias_var.rs new file mode 100644 index 00000000000..e0df96b0de8 --- /dev/null +++ b/tests/ui/implied-bounds/implied_bounds_entailment_alias_var.rs @@ -0,0 +1,32 @@ +// check-pass + +trait Data { + type Elem; +} + +impl<F, S: Data<Elem = F>> Data for ArrayBase<S> { + type Elem = F; +} + +struct DatasetIter<'a, R: Data> { + data: &'a R::Elem, +} + +pub struct ArrayBase<S> { + data: S, +} + +trait Trait { + type Item; + fn next() -> Option<Self::Item>; +} + +impl<'a, D: Data> Trait for DatasetIter<'a, ArrayBase<D>> { + type Item = (); + + fn next() -> Option<Self::Item> { + None + } +} + +fn main() {} diff --git a/tests/ui/inference/str-as-char.fixed b/tests/ui/inference/str-as-char.fixed index 6aea809cbdb..911b067c4d1 100644 --- a/tests/ui/inference/str-as-char.fixed +++ b/tests/ui/inference/str-as-char.fixed @@ -7,4 +7,5 @@ fn main() { let _: &str = "a"; //~ ERROR mismatched types let _: &str = "\"\"\""; //~ ERROR character literal may only contain one codepoint let _: &str = "\"\"\""; //~ ERROR character literal may only contain one codepoint + let _: &str = "\"\"\\\"\\\"\\\\\""; //~ ERROR character literal may only contain one codepoint } diff --git a/tests/ui/inference/str-as-char.rs b/tests/ui/inference/str-as-char.rs index eaa8d788c34..832bc871a9e 100644 --- a/tests/ui/inference/str-as-char.rs +++ b/tests/ui/inference/str-as-char.rs @@ -7,4 +7,5 @@ fn main() { let _: &str = 'a'; //~ ERROR mismatched types let _: &str = '"""'; //~ ERROR character literal may only contain one codepoint let _: &str = '\"\"\"'; //~ ERROR character literal may only contain one codepoint + let _: &str = '"\"\\"\\\"\\\\"'; //~ ERROR character literal may only contain one codepoint } diff --git a/tests/ui/inference/str-as-char.stderr b/tests/ui/inference/str-as-char.stderr index 2c84dac8e0c..216f4cda698 100644 --- a/tests/ui/inference/str-as-char.stderr +++ b/tests/ui/inference/str-as-char.stderr @@ -20,6 +20,17 @@ help: if you meant to write a `str` literal, use double quotes LL | let _: &str = "\"\"\""; | ~~~~~~~~ +error: character literal may only contain one codepoint + --> $DIR/str-as-char.rs:10:19 + | +LL | let _: &str = '"\"\"\\"\\"'; + | ^^^^^^^^^^^^^^^^^ + | +help: if you meant to write a `str` literal, use double quotes + | +LL | let _: &str = "\"\"\\"\\"\\\""; + | ~~~~~~~~~~~~~~~~~~~~ + error[E0308]: mismatched types --> $DIR/str-as-char.rs:7:19 | @@ -33,6 +44,6 @@ help: if you meant to write a `str` literal, use double quotes LL | let _: &str = "a"; | ~~~ -error: aborting due to 3 previous errors +error: aborting due to 4 previous errors For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/inline-const/required-const.rs b/tests/ui/inline-const/required-const.rs new file mode 100644 index 00000000000..0483410662b --- /dev/null +++ b/tests/ui/inline-const/required-const.rs @@ -0,0 +1,13 @@ +// build-fail +// compile-flags: -Zmir-opt-level=3 +#![feature(inline_const)] + +fn foo<T>() { + if false { + const { panic!() } //~ ERROR E0080 + } +} + +fn main() { + foo::<i32>(); +} diff --git a/tests/ui/inline-const/required-const.stderr b/tests/ui/inline-const/required-const.stderr new file mode 100644 index 00000000000..d6948e7acc0 --- /dev/null +++ b/tests/ui/inline-const/required-const.stderr @@ -0,0 +1,11 @@ +error[E0080]: evaluation of `foo::<i32>::{constant#0}` failed + --> $DIR/required-const.rs:7:17 + | +LL | const { panic!() } + | ^^^^^^^^ the evaluated program panicked at 'explicit panic', $DIR/required-const.rs:7:17 + | + = note: this error originates in the macro `$crate::panic::panic_2015` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0080`. diff --git a/tests/ui/macros/macro-interpolation.rs b/tests/ui/macros/macro-interpolation.rs index 35003a79ad7..48c1f19e777 100644 --- a/tests/ui/macros/macro-interpolation.rs +++ b/tests/ui/macros/macro-interpolation.rs @@ -1,5 +1,3 @@ -// run-pass - macro_rules! overly_complicated { ($fnname:ident, $arg:ident, $ty:ty, $body:block, $val:expr, $pat:pat, $res:path) => ({ @@ -21,12 +19,14 @@ macro_rules! qpath { (ty, <$type:ty as $trait:ty>::$name:ident) => { <$type as $trait>::$name + //~^ ERROR expected identifier, found `!` }; } pub fn main() { let _: qpath!(path, <str as ToOwned>::Owned); let _: qpath!(ty, <str as ToOwned>::Owned); + let _: qpath!(ty, <str as !>::Owned); assert!(overly_complicated!(f, x, Option<usize>, { return Some(x); }, Some(8), Some(y), y) == 8) diff --git a/tests/ui/macros/macro-interpolation.stderr b/tests/ui/macros/macro-interpolation.stderr new file mode 100644 index 00000000000..7ef1fcbbce3 --- /dev/null +++ b/tests/ui/macros/macro-interpolation.stderr @@ -0,0 +1,16 @@ +error: expected identifier, found `!` + --> $DIR/macro-interpolation.rs:21:19 + | +LL | <$type as $trait>::$name + | ^^^^^^ expected identifier +... +LL | let _: qpath!(ty, <str as !>::Owned); + | ----------------------------- + | | + | this macro call doesn't expand to a type + | in this macro invocation + | + = note: this error originates in the macro `qpath` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to previous error + diff --git a/tests/ui/macros/rfc-3086-metavar-expr/issue-111904.rs b/tests/ui/macros/rfc-3086-metavar-expr/issue-111904.rs new file mode 100644 index 00000000000..9cc572c23a1 --- /dev/null +++ b/tests/ui/macros/rfc-3086-metavar-expr/issue-111904.rs @@ -0,0 +1,14 @@ +#![feature(macro_metavar_expr)] + +macro_rules! foo { + ( $( $($t:ident),* );* ) => { ${count(t,)} } + //~^ ERROR `count` followed by a comma must have an associated + //~| ERROR expected expression, found `$` +} + +fn test() { + foo!(a, a; b, b); +} + +fn main() { +} diff --git a/tests/ui/macros/rfc-3086-metavar-expr/issue-111904.stderr b/tests/ui/macros/rfc-3086-metavar-expr/issue-111904.stderr new file mode 100644 index 00000000000..e9317a5c347 --- /dev/null +++ b/tests/ui/macros/rfc-3086-metavar-expr/issue-111904.stderr @@ -0,0 +1,19 @@ +error: `count` followed by a comma must have an associated index indicating its depth + --> $DIR/issue-111904.rs:4:37 + | +LL | ( $( $($t:ident),* );* ) => { ${count(t,)} } + | ^^^^^ + +error: expected expression, found `$` + --> $DIR/issue-111904.rs:4:35 + | +LL | ( $( $($t:ident),* );* ) => { ${count(t,)} } + | ^ expected expression +... +LL | foo!(a, a; b, b); + | ---------------- in this macro invocation + | + = note: this error originates in the macro `foo` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 2 previous errors + diff --git a/tests/ui/mir/debug-ref-undef.rs b/tests/ui/mir/debug-ref-undef.rs new file mode 100644 index 00000000000..37fd22a9dd2 --- /dev/null +++ b/tests/ui/mir/debug-ref-undef.rs @@ -0,0 +1,57 @@ +// run-pass +// compile-flags: -g -O -Zmir-opt-level=0 -Zinline-mir=y -Zmir-enable-passes=+ReferencePropagation + +#![allow(dead_code)] + +use std::marker::PhantomData; + +struct RawTable<T> { + marker: PhantomData<T>, +} + +impl<T> RawTable<T> { + fn iter(&self) -> RawIter<T> { + RawIter { marker: PhantomData } + } +} + +struct RawIter<T> { + marker: PhantomData<T>, +} + +impl<T> Iterator for RawIter<T> { + type Item = (); + fn next(&mut self) -> Option<()> { + None + } +} + +struct HashMap<T> { + table: RawTable<T>, +} + +struct Iter<T> { + inner: RawIter<T>, // Removing this breaks the reproducer +} + +impl<T> IntoIterator for &HashMap<T> { + type Item = T; + type IntoIter = Iter<T>; + fn into_iter(self) -> Iter<T> { + Iter { inner: self.table.iter() } + } +} + +impl<T> Iterator for Iter<T> { + type Item = T; + fn next(&mut self) -> Option<T> { + None + } +} + +pub fn main() { + let maybe_hash_set: Option<HashMap<()>> = None; + for _ in maybe_hash_set.as_ref().unwrap_or(&HashMap { table: RawTable { marker: PhantomData } }) + { + } +} diff --git a/tests/ui/or-patterns/missing-bindings.stderr b/tests/ui/or-patterns/missing-bindings.stderr index 8fafa275b5c..4457b7893d5 100644 --- a/tests/ui/or-patterns/missing-bindings.stderr +++ b/tests/ui/or-patterns/missing-bindings.stderr @@ -79,6 +79,14 @@ LL | let (A(A(..) | B(a), _) | B(A(a, _) | B(a))) = Y; | | | pattern doesn't bind `a` +error[E0408]: variable `c` is not bound in all patterns + --> $DIR/missing-bindings.rs:45:12 + | +LL | let (A(A(a, b) | B(c), d) | B(e)) = Y; + | ^^^^^^^ - variable not in all patterns + | | + | pattern doesn't bind `c` + error[E0408]: variable `a` is not bound in all patterns --> $DIR/missing-bindings.rs:45:22 | @@ -96,12 +104,12 @@ LL | let (A(A(a, b) | B(c), d) | B(e)) = Y; | variable not in all patterns error[E0408]: variable `c` is not bound in all patterns - --> $DIR/missing-bindings.rs:45:12 + --> $DIR/missing-bindings.rs:45:33 | LL | let (A(A(a, b) | B(c), d) | B(e)) = Y; - | ^^^^^^^ - variable not in all patterns - | | - | pattern doesn't bind `c` + | - ^^^^ pattern doesn't bind `c` + | | + | variable not in all patterns error[E0408]: variable `d` is not bound in all patterns --> $DIR/missing-bindings.rs:45:33 @@ -135,14 +143,6 @@ LL | let (A(A(a, b) | B(c), d) | B(e)) = Y; | | | variable not in all patterns -error[E0408]: variable `c` is not bound in all patterns - --> $DIR/missing-bindings.rs:45:33 - | -LL | let (A(A(a, b) | B(c), d) | B(e)) = Y; - | - ^^^^ pattern doesn't bind `c` - | | - | variable not in all patterns - error[E0408]: variable `a` is not bound in all patterns --> $DIR/missing-bindings.rs:61:29 | @@ -185,6 +185,28 @@ LL | B(b), LL | B(_) | ^^^^ pattern doesn't bind `b` +error[E0408]: variable `c` is not bound in all patterns + --> $DIR/missing-bindings.rs:57:13 + | +LL | / V1( +LL | | +LL | | +LL | | A( +... | +LL | | B(Ok(a) | Err(a)) +LL | | ) | + | |_____________^ pattern doesn't bind `c` +LL | / V2( +LL | | A( +LL | | A(_, a) | +LL | | B(b), +... | +LL | | +LL | | ) | + | |_____________^ pattern doesn't bind `c` +LL | V3(c), + | - variable not in all patterns + error[E0408]: variable `a` is not bound in all patterns --> $DIR/missing-bindings.rs:76:13 | @@ -215,28 +237,6 @@ LL | B(b), LL | V3(c), | ^^^^^ pattern doesn't bind `b` -error[E0408]: variable `c` is not bound in all patterns - --> $DIR/missing-bindings.rs:57:13 - | -LL | / V1( -LL | | -LL | | -LL | | A( -... | -LL | | B(Ok(a) | Err(a)) -LL | | ) | - | |_____________^ pattern doesn't bind `c` -LL | / V2( -LL | | A( -LL | | A(_, a) | -LL | | B(b), -... | -LL | | -LL | | ) | - | |_____________^ pattern doesn't bind `c` -LL | V3(c), - | - variable not in all patterns - error: aborting due to 26 previous errors For more information about this error, try `rustc --explain E0408`. diff --git a/tests/ui/parser/trait-object-delimiters.rs b/tests/ui/parser/trait-object-delimiters.rs index c41cda18743..e9b13defe03 100644 --- a/tests/ui/parser/trait-object-delimiters.rs +++ b/tests/ui/parser/trait-object-delimiters.rs @@ -3,9 +3,9 @@ fn foo1(_: &dyn Drop + AsRef<str>) {} //~ ERROR ambiguous `+` in a type //~^ ERROR only auto traits can be used as additional traits in a trait object -fn foo2(_: &dyn (Drop + AsRef<str>)) {} //~ ERROR incorrect braces around trait bounds +fn foo2(_: &dyn (Drop + AsRef<str>)) {} //~ ERROR incorrect parentheses around trait bounds -fn foo2_no_space(_: &dyn(Drop + AsRef<str>)) {} //~ ERROR incorrect braces around trait bounds +fn foo2_no_space(_: &dyn(Drop + AsRef<str>)) {} //~ ERROR incorrect parentheses around trait bounds fn foo3(_: &dyn {Drop + AsRef<str>}) {} //~ ERROR expected parameter name, found `{` //~^ ERROR expected one of `!`, `(`, `)`, `*`, `,`, `?`, `for`, `~`, lifetime, or path, found `{` diff --git a/tests/ui/parser/trait-object-delimiters.stderr b/tests/ui/parser/trait-object-delimiters.stderr index ccce3a8053e..51954675093 100644 --- a/tests/ui/parser/trait-object-delimiters.stderr +++ b/tests/ui/parser/trait-object-delimiters.stderr @@ -4,28 +4,28 @@ error: ambiguous `+` in a type LL | fn foo1(_: &dyn Drop + AsRef<str>) {} | ^^^^^^^^^^^^^^^^^^^^^ help: use parentheses to disambiguate: `(dyn Drop + AsRef<str>)` -error: incorrect braces around trait bounds +error: incorrect parentheses around trait bounds --> $DIR/trait-object-delimiters.rs:6:17 | LL | fn foo2(_: &dyn (Drop + AsRef<str>)) {} | ^ ^ | -help: remove the parentheses +help: fix the parentheses | LL - fn foo2(_: &dyn (Drop + AsRef<str>)) {} -LL + fn foo2(_: &dyn Drop + AsRef<str>) {} +LL + fn foo2(_: &(dyn Drop + AsRef<str>)) {} | -error: incorrect braces around trait bounds +error: incorrect parentheses around trait bounds --> $DIR/trait-object-delimiters.rs:8:25 | LL | fn foo2_no_space(_: &dyn(Drop + AsRef<str>)) {} | ^ ^ | -help: remove the parentheses +help: fix the parentheses | LL - fn foo2_no_space(_: &dyn(Drop + AsRef<str>)) {} -LL + fn foo2_no_space(_: &dyn Drop + AsRef<str>) {} +LL + fn foo2_no_space(_: &(dyn Drop + AsRef<str>)) {} | error: expected parameter name, found `{` diff --git a/tests/ui/resolve/resolve-inconsistent-names.stderr b/tests/ui/resolve/resolve-inconsistent-names.stderr index 023db303dd0..42b7281d7b0 100644 --- a/tests/ui/resolve/resolve-inconsistent-names.stderr +++ b/tests/ui/resolve/resolve-inconsistent-names.stderr @@ -14,6 +14,15 @@ LL | a | b => {} | | | pattern doesn't bind `b` +error[E0408]: variable `c` is not bound in all patterns + --> $DIR/resolve-inconsistent-names.rs:19:9 + | +LL | (A, B) | (ref B, c) | (c, A) => () + | ^^^^^^ - - variable not in all patterns + | | | + | | variable not in all patterns + | pattern doesn't bind `c` + error[E0408]: variable `A` is not bound in all patterns --> $DIR/resolve-inconsistent-names.rs:19:18 | @@ -37,15 +46,6 @@ LL | (A, B) | (ref B, c) | (c, A) => () | | variable not in all patterns | variable not in all patterns -error[E0408]: variable `c` is not bound in all patterns - --> $DIR/resolve-inconsistent-names.rs:19:9 - | -LL | (A, B) | (ref B, c) | (c, A) => () - | ^^^^^^ - - variable not in all patterns - | | | - | | variable not in all patterns - | pattern doesn't bind `c` - error[E0409]: variable `B` is bound inconsistently across alternatives separated by `|` --> $DIR/resolve-inconsistent-names.rs:19:23 | diff --git a/tests/ui/span/issue-39698.stderr b/tests/ui/span/issue-39698.stderr index 25c35fd5479..81211b20a01 100644 --- a/tests/ui/span/issue-39698.stderr +++ b/tests/ui/span/issue-39698.stderr @@ -1,3 +1,13 @@ +error[E0408]: variable `c` is not bound in all patterns + --> $DIR/issue-39698.rs:10:9 + | +LL | T::T1(a, d) | T::T2(d, b) | T::T3(c) | T::T4(a) => { println!("{:?}", a); } + | ^^^^^^^^^^^ ^^^^^^^^^^^ - ^^^^^^^^ pattern doesn't bind `c` + | | | | + | | | variable not in all patterns + | | pattern doesn't bind `c` + | pattern doesn't bind `c` + error[E0408]: variable `d` is not bound in all patterns --> $DIR/issue-39698.rs:10:37 | @@ -28,16 +38,6 @@ LL | T::T1(a, d) | T::T2(d, b) | T::T3(c) | T::T4(a) => { println!("{:?} | | variable not in all patterns | pattern doesn't bind `b` -error[E0408]: variable `c` is not bound in all patterns - --> $DIR/issue-39698.rs:10:9 - | -LL | T::T1(a, d) | T::T2(d, b) | T::T3(c) | T::T4(a) => { println!("{:?}", a); } - | ^^^^^^^^^^^ ^^^^^^^^^^^ - ^^^^^^^^ pattern doesn't bind `c` - | | | | - | | | variable not in all patterns - | | pattern doesn't bind `c` - | pattern doesn't bind `c` - error: aborting due to 4 previous errors For more information about this error, try `rustc --explain E0408`. diff --git a/tests/ui/suggestions/copied-and-cloned.fixed b/tests/ui/suggestions/copied-and-cloned.fixed index 77159d5075a..4cecf9e26f9 100644 --- a/tests/ui/suggestions/copied-and-cloned.fixed +++ b/tests/ui/suggestions/copied-and-cloned.fixed @@ -2,6 +2,16 @@ fn expect<T>(_: T) {} +struct Issue114925 { + x: Option<String>, +} + +fn issue_114925(lol: &mut Issue114925, x: Option<&String>) { + lol.x = x.clone().cloned(); + //~^ ERROR mismatched types + //~| HELP use `Option::cloned` to clone the value inside the `Option` +} + fn main() { let x = Some(&()); expect::<Option<()>>(x.copied()); @@ -24,10 +34,10 @@ fn main() { let s = String::new(); let x = Some(s.clone()); let y = Some(&s); - println!("{}", x.as_ref() == y); + println!("{}", x == y.cloned()); //~^ ERROR mismatched types - //~| HELP use `Option::as_ref` to convert `Option<String>` to `Option<&String>` - + //~| HELP use `Option::cloned` to clone the value inside the `Option` + //FIXME(#114050) ~| HELP use `Option::as_ref` to convert `Option<String>` to `Option<&String>` let mut s = (); let x = Some(s); @@ -42,4 +52,6 @@ fn main() { println!("{}", x == y.cloned()); //~^ ERROR mismatched types //~| HELP use `Option::cloned` to clone the value inside the `Option` + + issue_114925(&mut Issue114925 { x: None }, None); } diff --git a/tests/ui/suggestions/copied-and-cloned.rs b/tests/ui/suggestions/copied-and-cloned.rs index c506494ee14..a79928c50d5 100644 --- a/tests/ui/suggestions/copied-and-cloned.rs +++ b/tests/ui/suggestions/copied-and-cloned.rs @@ -2,6 +2,16 @@ fn expect<T>(_: T) {} +struct Issue114925 { + x: Option<String>, +} + +fn issue_114925(lol: &mut Issue114925, x: Option<&String>) { + lol.x = x.clone(); + //~^ ERROR mismatched types + //~| HELP use `Option::cloned` to clone the value inside the `Option` +} + fn main() { let x = Some(&()); expect::<Option<()>>(x); @@ -26,8 +36,8 @@ fn main() { let y = Some(&s); println!("{}", x == y); //~^ ERROR mismatched types - //~| HELP use `Option::as_ref` to convert `Option<String>` to `Option<&String>` - + //~| HELP use `Option::cloned` to clone the value inside the `Option` + //FIXME(#114050) ~| HELP use `Option::as_ref` to convert `Option<String>` to `Option<&String>` let mut s = (); let x = Some(s); @@ -42,4 +52,6 @@ fn main() { println!("{}", x == y); //~^ ERROR mismatched types //~| HELP use `Option::cloned` to clone the value inside the `Option` + + issue_114925(&mut Issue114925 { x: None }, None); } diff --git a/tests/ui/suggestions/copied-and-cloned.stderr b/tests/ui/suggestions/copied-and-cloned.stderr index f8712d0a39e..87b0624d48b 100644 --- a/tests/ui/suggestions/copied-and-cloned.stderr +++ b/tests/ui/suggestions/copied-and-cloned.stderr @@ -1,5 +1,20 @@ error[E0308]: mismatched types - --> $DIR/copied-and-cloned.rs:7:26 + --> $DIR/copied-and-cloned.rs:10:13 + | +LL | lol.x = x.clone(); + | ----- ^^^^^^^^^ expected `Option<String>`, found `Option<&String>` + | | + | expected due to the type of this binding + | + = note: expected enum `Option<String>` + found enum `Option<&String>` +help: use `Option::cloned` to clone the value inside the `Option` + | +LL | lol.x = x.clone().cloned(); + | +++++++++ + +error[E0308]: mismatched types + --> $DIR/copied-and-cloned.rs:17:26 | LL | expect::<Option<()>>(x); | -------------------- ^ expected `Option<()>`, found `Option<&()>` @@ -19,7 +34,7 @@ LL | expect::<Option<()>>(x.copied()); | +++++++++ error[E0308]: mismatched types - --> $DIR/copied-and-cloned.rs:11:30 + --> $DIR/copied-and-cloned.rs:21:30 | LL | expect::<Result<(), ()>>(x); | ------------------------ ^ expected `Result<(), ()>`, found `Result<&(), _>` @@ -39,7 +54,7 @@ LL | expect::<Result<(), ()>>(x.copied()); | +++++++++ error[E0308]: mismatched types - --> $DIR/copied-and-cloned.rs:16:30 + --> $DIR/copied-and-cloned.rs:26:30 | LL | expect::<Option<String>>(x); | ------------------------ ^ expected `Option<String>`, found `Option<&String>` @@ -59,7 +74,7 @@ LL | expect::<Option<String>>(x.cloned()); | +++++++++ error[E0308]: mismatched types - --> $DIR/copied-and-cloned.rs:20:34 + --> $DIR/copied-and-cloned.rs:30:34 | LL | expect::<Result<String, ()>>(x); | ---------------------------- ^ expected `Result<String, ()>`, found `Result<&String, _>` @@ -79,20 +94,20 @@ LL | expect::<Result<String, ()>>(x.cloned()); | +++++++++ error[E0308]: mismatched types - --> $DIR/copied-and-cloned.rs:27:25 + --> $DIR/copied-and-cloned.rs:37:25 | LL | println!("{}", x == y); | ^ expected `Option<String>`, found `Option<&String>` | = note: expected enum `Option<String>` found enum `Option<&String>` -help: use `Option::as_ref` to convert `Option<String>` to `Option<&String>` +help: use `Option::cloned` to clone the value inside the `Option` | -LL | println!("{}", x.as_ref() == y); - | +++++++++ +LL | println!("{}", x == y.cloned()); + | +++++++++ error[E0308]: mismatched types - --> $DIR/copied-and-cloned.rs:35:25 + --> $DIR/copied-and-cloned.rs:45:25 | LL | println!("{}", x == y); | ^ expected `Option<()>`, found `Option<&mut ()>` @@ -105,7 +120,7 @@ LL | println!("{}", x == y.copied()); | +++++++++ error[E0308]: mismatched types - --> $DIR/copied-and-cloned.rs:42:25 + --> $DIR/copied-and-cloned.rs:52:25 | LL | println!("{}", x == y); | ^ expected `Option<String>`, found `Option<&mut String>` @@ -117,6 +132,6 @@ help: use `Option::cloned` to clone the value inside the `Option` LL | println!("{}", x == y.cloned()); | +++++++++ -error: aborting due to 7 previous errors +error: aborting due to 8 previous errors For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/suggestions/issue-114797-bad-parentheses-dyn-trait.fixed b/tests/ui/suggestions/issue-114797-bad-parentheses-dyn-trait.fixed new file mode 100644 index 00000000000..57387936a4c --- /dev/null +++ b/tests/ui/suggestions/issue-114797-bad-parentheses-dyn-trait.fixed @@ -0,0 +1,17 @@ +//run-rustfix +#![allow(dead_code)] + +trait Trait {} + +fn assert_send(ptr: *mut dyn Trait) -> *mut (dyn Trait + Send) { + //~^ ERROR incorrect parentheses around trait bounds + ptr as _ +} + +fn foo2(_: &(dyn Trait + Send)) {} +//~^ ERROR incorrect parentheses around trait bounds + +fn foo3(_: &(dyn Trait + Send)) {} +//~^ ERROR incorrect parentheses around trait bounds + +fn main() {} diff --git a/tests/ui/suggestions/issue-114797-bad-parentheses-dyn-trait.rs b/tests/ui/suggestions/issue-114797-bad-parentheses-dyn-trait.rs new file mode 100644 index 00000000000..8a1939bcfe9 --- /dev/null +++ b/tests/ui/suggestions/issue-114797-bad-parentheses-dyn-trait.rs @@ -0,0 +1,17 @@ +//run-rustfix +#![allow(dead_code)] + +trait Trait {} + +fn assert_send(ptr: *mut dyn Trait) -> *mut dyn (Trait + Send) { + //~^ ERROR incorrect parentheses around trait bounds + ptr as _ +} + +fn foo2(_: &dyn (Trait + Send)) {} +//~^ ERROR incorrect parentheses around trait bounds + +fn foo3(_: &dyn(Trait + Send)) {} +//~^ ERROR incorrect parentheses around trait bounds + +fn main() {} diff --git a/tests/ui/suggestions/issue-114797-bad-parentheses-dyn-trait.stderr b/tests/ui/suggestions/issue-114797-bad-parentheses-dyn-trait.stderr new file mode 100644 index 00000000000..2d1abe91a1e --- /dev/null +++ b/tests/ui/suggestions/issue-114797-bad-parentheses-dyn-trait.stderr @@ -0,0 +1,38 @@ +error: incorrect parentheses around trait bounds + --> $DIR/issue-114797-bad-parentheses-dyn-trait.rs:6:49 + | +LL | fn assert_send(ptr: *mut dyn Trait) -> *mut dyn (Trait + Send) { + | ^ ^ + | +help: fix the parentheses + | +LL - fn assert_send(ptr: *mut dyn Trait) -> *mut dyn (Trait + Send) { +LL + fn assert_send(ptr: *mut dyn Trait) -> *mut (dyn Trait + Send) { + | + +error: incorrect parentheses around trait bounds + --> $DIR/issue-114797-bad-parentheses-dyn-trait.rs:11:17 + | +LL | fn foo2(_: &dyn (Trait + Send)) {} + | ^ ^ + | +help: fix the parentheses + | +LL - fn foo2(_: &dyn (Trait + Send)) {} +LL + fn foo2(_: &(dyn Trait + Send)) {} + | + +error: incorrect parentheses around trait bounds + --> $DIR/issue-114797-bad-parentheses-dyn-trait.rs:14:16 + | +LL | fn foo3(_: &dyn(Trait + Send)) {} + | ^ ^ + | +help: fix the parentheses + | +LL - fn foo3(_: &dyn(Trait + Send)) {} +LL + fn foo3(_: &(dyn Trait + Send)) {} + | + +error: aborting due to 3 previous errors + diff --git a/tests/ui/traits/new-solver/generalize/generalize-proj-new-universe-index-1.rs b/tests/ui/traits/new-solver/generalize/generalize-proj-new-universe-index-1.rs new file mode 100644 index 00000000000..b0b9b6bbd20 --- /dev/null +++ b/tests/ui/traits/new-solver/generalize/generalize-proj-new-universe-index-1.rs @@ -0,0 +1,73 @@ +// compile-flags: -Ztrait-solver=next +// check-pass + +// A minimization of an ambiguity when using typenum. See +// https://github.com/rust-lang/trait-system-refactor-initiative/issues/55 +// for more details. +trait Id { + type Assoc: ?Sized; +} +impl<T: ?Sized> Id for T { + type Assoc = T; +} + +trait WithAssoc<T: ?Sized> { + type Assoc: ?Sized; +} + + +struct Leaf; +struct Wrapper<U: ?Sized>(U); + +impl<U: ?Sized> WithAssoc<U> for Leaf { + type Assoc = U; +} + +impl<Ul: ?Sized, Ur: ?Sized> WithAssoc<Wrapper<Ur>> for Wrapper<Ul> +where + Ul: WithAssoc<Ur>, +{ + type Assoc = <<Ul as WithAssoc<Ur>>::Assoc as Id>::Assoc; +} + +fn bound<T: ?Sized, U: ?Sized, V: ?Sized>() +where + T: WithAssoc<U, Assoc = V>, +{ +} + +// normalize self type to `Wrapper<Leaf>` +// This succeeds, HOWEVER, instantiating the query response previously +// incremented the universe index counter. +// equate impl headers: +// <Wrapper<Leaf> as WithAssoc<<Wrapper<Leaf> as Id>::Assoc>> +// <Wrapper<?2t> as WithAssoc<Wrapper<?3t>>> +// ~> AliasRelate(<Wrapper<Leaf> as Id>::Assoc, Equate, Wrapper<?3t>) +// add where bounds: +// ~> Leaf: WithAssoc<?3t> +// equate with assoc type: +// ?0t +// <Leaf as WithAssoc<?3t>>::Assoc as Id>::Assoc +// ~> AliasRelate( +// <<Leaf as WithAssoc<?3t>>::Assoc as Id>::Assoc, +// Equate, +// <<Leaf as WithAssoc<?4t>>::Assoc as Id>::Assoc, +// ) +// +// We do not reuse `?3t` during generalization because `?0t` cannot name `?4t` as we created +// it after incrementing the universe index while normalizing the self type. +// +// evaluate_added_goals_and_make_query_response: +// AliasRelate(<Wrapper<Leaf> as Id>::Assoc, Equate, Wrapper<?3t>) +// YES, constrains ?3t to Leaf +// AliasRelate( +// <<Leaf as WithAssoc<Leaf>>::Assoc as Id>::Assoc, +// Equate, +// <<Leaf as WithAssoc<?4t>>::Assoc as Id>::Assoc, +// ) +// +// Normalizing <<Leaf as WithAssoc<?4t>>::Assoc as Id>::Assoc then *correctly* +// results in ambiguity. +fn main() { + bound::<<Wrapper<Leaf> as Id>::Assoc, <Wrapper<Leaf> as Id>::Assoc, _>() +} diff --git a/tests/ui/traits/new-solver/generalize/generalize-proj-new-universe-index-2.rs b/tests/ui/traits/new-solver/generalize/generalize-proj-new-universe-index-2.rs new file mode 100644 index 00000000000..94d645a9859 --- /dev/null +++ b/tests/ui/traits/new-solver/generalize/generalize-proj-new-universe-index-2.rs @@ -0,0 +1,75 @@ +// compile-flags: -Ztrait-solver=next +// known-bug: trait-system-refactor-initiative#60 + +// Generalizing a projection containing an inference variable +// which cannot be named by the `root_vid` can result in ambiguity. +// +// Because we do not decrement the universe index when exiting a forall, +// this can cause unexpected failures. +// +// See generalize-proj-new-universe-index-1.rs for more details. + +// For this reproduction we need: +// - an inference variable with a lower universe +// - enter a binder to increment the current universe +// - create a new inference variable which is constrained by proving a goal +// - equate a projection containing the new variable with the first variable +// - generalization creates yet another inference variable which is then +// part of an alias-relate, resulting this to fail with ambiguity. +// +// Because we need to enter the binder in-between the creation of the first +// and second inference variable, this is easiest via +// `assemble_candidates_after_normalizing_self_ty` because eagerly call +// `try_evaluate_added_goals` there before creating the inference variables +// for the impl parameters. +trait Id { + type Assoc: ?Sized; +} +impl<T: ?Sized> Id for T { + type Assoc = T; +} + +// By adding an higher ranked bound to the impl we currently +// propagate this bound to the caller, forcing us to create a new +// universe. +trait IdHigherRankedBound { + type Assoc: ?Sized; +} + +impl<T: ?Sized> IdHigherRankedBound for T +where + for<'a> T: 'a, +{ + type Assoc = T; +} + +trait WithAssoc<T: ?Sized> { + type Assoc: ?Sized; +} + + +struct Leaf; +struct Wrapper<U: ?Sized>(U); +struct Rigid; + +impl<U: ?Sized> WithAssoc<U> for Leaf { + type Assoc = U; +} + + +impl<Ur: ?Sized> WithAssoc<Wrapper<Ur>> for Rigid +where + Leaf: WithAssoc<Ur>, +{ + type Assoc = <<Leaf as WithAssoc<Ur>>::Assoc as Id>::Assoc; +} + +fn bound<T: ?Sized, U: ?Sized, V: ?Sized>() +where + T: WithAssoc<U, Assoc = V>, +{ +} + +fn main() { + bound::<<Rigid as IdHigherRankedBound>::Assoc, <Wrapper<Leaf> as Id>::Assoc, _>() +} diff --git a/tests/ui/traits/new-solver/generalize/generalize-proj-new-universe-index-2.stderr b/tests/ui/traits/new-solver/generalize/generalize-proj-new-universe-index-2.stderr new file mode 100644 index 00000000000..9a8060133b8 --- /dev/null +++ b/tests/ui/traits/new-solver/generalize/generalize-proj-new-universe-index-2.stderr @@ -0,0 +1,9 @@ +error[E0282]: type annotations needed + --> $DIR/generalize-proj-new-universe-index-2.rs:74:5 + | +LL | bound::<<Rigid as IdHigherRankedBound>::Assoc, <Wrapper<Leaf> as Id>::Assoc, _>() + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot infer type of the type parameter `V` declared on the function `bound` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0282`. diff --git a/tests/ui/traits/trait-upcasting/type-checking-test-1.current.stderr b/tests/ui/traits/trait-upcasting/type-checking-test-1.current.stderr index d48d9b89d1d..b612005fcb0 100644 --- a/tests/ui/traits/trait-upcasting/type-checking-test-1.current.stderr +++ b/tests/ui/traits/trait-upcasting/type-checking-test-1.current.stderr @@ -2,22 +2,8 @@ error[E0605]: non-primitive cast: `&dyn Foo` as `&dyn Bar<_>` --> $DIR/type-checking-test-1.rs:19:13 | LL | let _ = x as &dyn Bar<_>; // Ambiguous - | ^^^^^^^^^^^^^^^^ invalid cast - | -help: consider borrowing the value - | -LL | let _ = &x as &dyn Bar<_>; // Ambiguous - | + - -error[E0277]: the trait bound `&dyn Foo: Bar<_>` is not satisfied - --> $DIR/type-checking-test-1.rs:19:13 - | -LL | let _ = x as &dyn Bar<_>; // Ambiguous - | ^ the trait `Bar<_>` is not implemented for `&dyn Foo` - | - = note: required for the cast from `&&dyn Foo` to `&dyn Bar<_>` + | ^^^^^^^^^^^^^^^^ an `as` expression can only be used to convert between primitive types or to coerce to a specific trait object -error: aborting due to 2 previous errors +error: aborting due to previous error -Some errors have detailed explanations: E0277, E0605. -For more information about an error, try `rustc --explain E0277`. +For more information about this error, try `rustc --explain E0605`. diff --git a/tests/ui/traits/trait-upcasting/type-checking-test-1.rs b/tests/ui/traits/trait-upcasting/type-checking-test-1.rs index 7c7beec0809..afea8521e87 100644 --- a/tests/ui/traits/trait-upcasting/type-checking-test-1.rs +++ b/tests/ui/traits/trait-upcasting/type-checking-test-1.rs @@ -18,7 +18,6 @@ fn test_specific(x: &dyn Foo) { fn test_unknown_version(x: &dyn Foo) { let _ = x as &dyn Bar<_>; // Ambiguous //~^ ERROR non-primitive cast - //[current]~^^ ERROR the trait bound `&dyn Foo: Bar<_>` is not satisfied } fn test_infer_version(x: &dyn Foo) { diff --git a/tests/ui/traits/trait-upcasting/type-checking-test-2.rs b/tests/ui/traits/trait-upcasting/type-checking-test-2.rs index 36b11dffdb1..b024b27750b 100644 --- a/tests/ui/traits/trait-upcasting/type-checking-test-2.rs +++ b/tests/ui/traits/trait-upcasting/type-checking-test-2.rs @@ -18,13 +18,11 @@ fn test_specific2(x: &dyn Foo<u32>) { fn test_specific3(x: &dyn Foo<i32>) { let _ = x as &dyn Bar<u32>; // Error //~^ ERROR non-primitive cast - //~^^ ERROR the trait bound `&dyn Foo<i32>: Bar<u32>` is not satisfied } fn test_infer_arg(x: &dyn Foo<u32>) { let a = x as &dyn Bar<_>; // Ambiguous //~^ ERROR non-primitive cast - //~^^ ERROR the trait bound `&dyn Foo<u32>: Bar<_>` is not satisfied let _ = a.bar(); } diff --git a/tests/ui/traits/trait-upcasting/type-checking-test-2.stderr b/tests/ui/traits/trait-upcasting/type-checking-test-2.stderr index 856303ef4dd..3e59b9d3363 100644 --- a/tests/ui/traits/trait-upcasting/type-checking-test-2.stderr +++ b/tests/ui/traits/trait-upcasting/type-checking-test-2.stderr @@ -2,41 +2,14 @@ error[E0605]: non-primitive cast: `&dyn Foo<i32>` as `&dyn Bar<u32>` --> $DIR/type-checking-test-2.rs:19:13 | LL | let _ = x as &dyn Bar<u32>; // Error - | ^^^^^^^^^^^^^^^^^^ invalid cast - | -help: consider borrowing the value - | -LL | let _ = &x as &dyn Bar<u32>; // Error - | + - -error[E0277]: the trait bound `&dyn Foo<i32>: Bar<u32>` is not satisfied - --> $DIR/type-checking-test-2.rs:19:13 - | -LL | let _ = x as &dyn Bar<u32>; // Error - | ^ the trait `Bar<u32>` is not implemented for `&dyn Foo<i32>` - | - = note: required for the cast from `&&dyn Foo<i32>` to `&dyn Bar<u32>` + | ^^^^^^^^^^^^^^^^^^ an `as` expression can only be used to convert between primitive types or to coerce to a specific trait object error[E0605]: non-primitive cast: `&dyn Foo<u32>` as `&dyn Bar<_>` - --> $DIR/type-checking-test-2.rs:25:13 + --> $DIR/type-checking-test-2.rs:24:13 | LL | let a = x as &dyn Bar<_>; // Ambiguous - | ^^^^^^^^^^^^^^^^ invalid cast - | -help: consider borrowing the value - | -LL | let a = &x as &dyn Bar<_>; // Ambiguous - | + - -error[E0277]: the trait bound `&dyn Foo<u32>: Bar<_>` is not satisfied - --> $DIR/type-checking-test-2.rs:25:13 - | -LL | let a = x as &dyn Bar<_>; // Ambiguous - | ^ the trait `Bar<_>` is not implemented for `&dyn Foo<u32>` - | - = note: required for the cast from `&&dyn Foo<u32>` to `&dyn Bar<_>` + | ^^^^^^^^^^^^^^^^ an `as` expression can only be used to convert between primitive types or to coerce to a specific trait object -error: aborting due to 4 previous errors +error: aborting due to 2 previous errors -Some errors have detailed explanations: E0277, E0605. -For more information about an error, try `rustc --explain E0277`. +For more information about this error, try `rustc --explain E0605`. diff --git a/tests/ui/type-alias-impl-trait/auxiliary/drop-shim-relates-opaque-aux.rs b/tests/ui/type-alias-impl-trait/auxiliary/drop-shim-relates-opaque-aux.rs new file mode 100644 index 00000000000..54a22510066 --- /dev/null +++ b/tests/ui/type-alias-impl-trait/auxiliary/drop-shim-relates-opaque-aux.rs @@ -0,0 +1,21 @@ +// crate foo + +#![feature(type_alias_impl_trait)] + +type Tait = impl Sized; +fn _constrain() -> Tait {} + +struct WrapperWithDrop<T>(T); +impl<T> Drop for WrapperWithDrop<T> { + fn drop(&mut self) {} +} + +pub struct Foo(WrapperWithDrop<Tait>); + +trait Id { + type Id: ?Sized; +} +impl<T: ?Sized> Id for T { + type Id = T; +} +pub struct Bar(WrapperWithDrop<<Tait as Id>::Id>); diff --git a/tests/ui/type-alias-impl-trait/drop-shim-relates-opaque-issue-114375.rs b/tests/ui/type-alias-impl-trait/drop-shim-relates-opaque-issue-114375.rs new file mode 100644 index 00000000000..51d28704972 --- /dev/null +++ b/tests/ui/type-alias-impl-trait/drop-shim-relates-opaque-issue-114375.rs @@ -0,0 +1,10 @@ +// aux-build:drop-shim-relates-opaque-aux.rs +// compile-flags: -Zvalidate-mir --crate-type=lib +// build-pass + +extern crate drop_shim_relates_opaque_aux; + +pub fn drop_foo(_: drop_shim_relates_opaque_aux::Foo) {} +pub fn drop_bar(_: drop_shim_relates_opaque_aux::Bar) {} + +fn main() {} diff --git a/tests/ui/unknown-unstable-lints/deny-unstable-lint-command-line.stderr b/tests/ui/unknown-unstable-lints/deny-unstable-lint-command-line.stderr index aa73b824a57..32f8d2f45dc 100644 --- a/tests/ui/unknown-unstable-lints/deny-unstable-lint-command-line.stderr +++ b/tests/ui/unknown-unstable-lints/deny-unstable-lint-command-line.stderr @@ -1,18 +1,18 @@ error: unknown lint: `test_unstable_lint` | = note: the `test_unstable_lint` lint is unstable - = help: add `#![feature(test_unstable_lint)]` to the crate attributes to enable + = help: add `-Zcrate-attr="feature(test_unstable_lint)"` to the command-line options to enable = note: requested on the command line with `-D unknown-lints` error: unknown lint: `test_unstable_lint` | = note: the `test_unstable_lint` lint is unstable - = help: add `#![feature(test_unstable_lint)]` to the crate attributes to enable + = help: add `-Zcrate-attr="feature(test_unstable_lint)"` to the command-line options to enable error: unknown lint: `test_unstable_lint` | = note: the `test_unstable_lint` lint is unstable - = help: add `#![feature(test_unstable_lint)]` to the crate attributes to enable + = help: add `-Zcrate-attr="feature(test_unstable_lint)"` to the command-line options to enable error: aborting due to 3 previous errors diff --git a/tests/ui/unknown-unstable-lints/warn-unknown-unstable-lint-command-line.stderr b/tests/ui/unknown-unstable-lints/warn-unknown-unstable-lint-command-line.stderr index 82851c80064..dd9ecf02fa6 100644 --- a/tests/ui/unknown-unstable-lints/warn-unknown-unstable-lint-command-line.stderr +++ b/tests/ui/unknown-unstable-lints/warn-unknown-unstable-lint-command-line.stderr @@ -1,18 +1,18 @@ warning: unknown lint: `test_unstable_lint` | = note: the `test_unstable_lint` lint is unstable - = help: add `#![feature(test_unstable_lint)]` to the crate attributes to enable + = help: add `-Zcrate-attr="feature(test_unstable_lint)"` to the command-line options to enable = note: requested on the command line with `-W unknown-lints` warning: unknown lint: `test_unstable_lint` | = note: the `test_unstable_lint` lint is unstable - = help: add `#![feature(test_unstable_lint)]` to the crate attributes to enable + = help: add `-Zcrate-attr="feature(test_unstable_lint)"` to the command-line options to enable warning: unknown lint: `test_unstable_lint` | = note: the `test_unstable_lint` lint is unstable - = help: add `#![feature(test_unstable_lint)]` to the crate attributes to enable + = help: add `-Zcrate-attr="feature(test_unstable_lint)"` to the command-line options to enable warning: 3 warnings emitted diff --git a/triagebot.toml b/triagebot.toml index e4bcb9361af..aef5b5bb8dc 100644 --- a/triagebot.toml +++ b/triagebot.toml @@ -562,6 +562,10 @@ Otherwise, you can ignore this comment. [mentions."src/tools/x"] message = "`src/tools/x` was changed. Bump version of Cargo.toml in `src/tools/x` so tidy will suggest installing the new version." +[mentions."src/tools/tidy/src/deps.rs"] +message = "Third-party dependency whitelist may have been modified! You must ensure that any new dependencies have compatible licenses before merging." +cc = ["@davidtwco", "@wesleywiser"] + [mentions."src/bootstrap/defaults/config.compiler.toml"] message = "This PR changes src/bootstrap/defaults/config.compiler.toml. If appropriate, please also update `config.codegen.toml` so the defaults are in sync." [mentions."src/bootstrap/defaults/config.codegen.toml"] @@ -581,7 +585,7 @@ cc = ["@nnethercote"] [assign] warn_non_default_branch = true contributing_url = "https://rustc-dev-guide.rust-lang.org/getting-started.html" -users_on_vacation = ["jyn514", "WaffleLapkin", "clubby789", "oli-obk"] +users_on_vacation = ["jyn514", "clubby789", "oli-obk"] [assign.adhoc_groups] compiler-team = [ @@ -597,7 +601,6 @@ compiler-team-contributors = [ "@TaKO8Ki", "@WaffleLapkin", "@b-naber", - "@fee1-dead", ] compiler = [ "compiler-team", |
