| author | bors <bors@rust-lang.org> 2025-10-05 04:36:49 UTC |
| committer | bors <bors@rust-lang.org> 2025-10-05 04:36:49 UTC |
| log | e2c96cc06bdbdbc6f59c7551194d6a742260d6ff |
| tree | 5da6ddca7b7ec935878c35826b9b3db41f698f07 |
| parent | 227ac7c3cd486872d5c2352b3df02b571500e53a |
| parent | a0a4905723b3aca4a68fa7527fd66a10290c782c |
Rollup of 7 pull requests
Successful merges:
- rust-lang/rust#147288 (compiletest: Make `DirectiveLine` responsible for name/value splitting)
- rust-lang/rust#147309 (Add documentation about unwinding to wasm targets)
- rust-lang/rust#147310 (Mark `PatternTypo` suggestion as maybe incorrect)
- rust-lang/rust#147320 (Avoid to suggest pattern match on the similarly named in fn signature)
- rust-lang/rust#147328 (Implement non-poisoning `Mutex::with_mut`, `RwLock::with` and `RwLock::with_mut`)
- rust-lang/rust#147337 (Make `fmt::Write` a diagnostic item)
- rust-lang/rust#147349 (Improve the advice given by panic_immediate_abort)
r? `@ghost`
`@rustbot` modify labels: rollup22 files changed, 442 insertions(+), 171 deletions(-)
compiler/rustc_passes/src/errors.rs+1-1| ... | ... | @@ -1365,7 +1365,7 @@ pub(crate) struct UnusedVarAssignedOnly { |
| 1365 | 1365 | #[multipart_suggestion( |
| 1366 | 1366 | passes_unused_var_typo, |
| 1367 | 1367 | style = "verbose", |
| 1368 | applicability = "machine-applicable" | |
| 1368 | applicability = "maybe-incorrect" | |
| 1369 | 1369 | )] |
| 1370 | 1370 | pub(crate) struct PatternTypo { |
| 1371 | 1371 | #[suggestion_part(code = "{code}")] |
compiler/rustc_passes/src/liveness.rs+5-2| ... | ... | @@ -1691,7 +1691,10 @@ impl<'tcx> Liveness<'_, 'tcx> { |
| 1691 | 1691 | if ln == self.exit_ln { false } else { self.assigned_on_exit(ln, var) }; |
| 1692 | 1692 | |
| 1693 | 1693 | let mut typo = None; |
| 1694 | for (hir_id, _, span) in &hir_ids_and_spans { | |
| 1694 | let filtered_hir_ids_and_spans = hir_ids_and_spans.iter().filter(|(hir_id, ..)| { | |
| 1695 | !matches!(self.ir.tcx.parent_hir_node(*hir_id), hir::Node::Param(_)) | |
| 1696 | }); | |
| 1697 | for (hir_id, _, span) in filtered_hir_ids_and_spans.clone() { | |
| 1695 | 1698 | let ty = self.typeck_results.node_type(*hir_id); |
| 1696 | 1699 | if let ty::Adt(adt, _) = ty.peel_refs().kind() { |
| 1697 | 1700 | let name = Symbol::intern(&name); |
| ... | ... | @@ -1717,7 +1720,7 @@ impl<'tcx> Liveness<'_, 'tcx> { |
| 1717 | 1720 | } |
| 1718 | 1721 | } |
| 1719 | 1722 | if typo.is_none() { |
| 1720 | for (hir_id, _, span) in &hir_ids_and_spans { | |
| 1723 | for (hir_id, _, span) in filtered_hir_ids_and_spans { | |
| 1721 | 1724 | let ty = self.typeck_results.node_type(*hir_id); |
| 1722 | 1725 | // Look for consts of the same type with similar names as well, not just unit |
| 1723 | 1726 | // structs and variants. |
compiler/rustc_span/src/symbol.rs+1| ... | ... | @@ -236,6 +236,7 @@ symbols! { |
| 236 | 236 | File, |
| 237 | 237 | FileType, |
| 238 | 238 | FmtArgumentsNew, |
| 239 | FmtWrite, | |
| 239 | 240 | Fn, |
| 240 | 241 | FnMut, |
| 241 | 242 | FnOnce, |
library/core/src/fmt/mod.rs+1| ... | ... | @@ -115,6 +115,7 @@ pub struct Error; |
| 115 | 115 | /// [`std::io::Write`]: ../../std/io/trait.Write.html |
| 116 | 116 | /// [flushable]: ../../std/io/trait.Write.html#tymethod.flush |
| 117 | 117 | #[stable(feature = "rust1", since = "1.0.0")] |
| 118 | #[rustc_diagnostic_item = "FmtWrite"] | |
| 118 | 119 | pub trait Write { |
| 119 | 120 | /// Writes a string slice into this writer, returning whether the write |
| 120 | 121 | /// succeeded. |
library/core/src/panicking.rs+2-1| ... | ... | @@ -35,7 +35,8 @@ use crate::panic::{Location, PanicInfo}; |
| 35 | 35 | #[cfg(feature = "panic_immediate_abort")] |
| 36 | 36 | compile_error!( |
| 37 | 37 | "panic_immediate_abort is now a real panic strategy! \ |
| 38 | Enable it with the compiler flags `-Zunstable-options -Cpanic=immediate-abort`" | |
| 38 | Enable it with `panic = \"immediate-abort\"` in Cargo.toml, \ | |
| 39 | or with the compiler flags `-Zunstable-options -Cpanic=immediate-abort`" | |
| 39 | 40 | ); |
| 40 | 41 | |
| 41 | 42 | // First we define the two main entry points that all panics go through. |
library/std/src/sync/nonpoison/mutex.rs+34| ... | ... | @@ -376,6 +376,40 @@ impl<T: ?Sized> Mutex<T> { |
| 376 | 376 | pub const fn data_ptr(&self) -> *mut T { |
| 377 | 377 | self.data.get() |
| 378 | 378 | } |
| 379 | ||
| 380 | /// Acquires the mutex and provides mutable access to the underlying data by passing | |
| 381 | /// a mutable reference to the given closure. | |
| 382 | /// | |
| 383 | /// This method acquires the lock, calls the provided closure with a mutable reference | |
| 384 | /// to the data, and returns the result of the closure. The lock is released after | |
| 385 | /// the closure completes, even if it panics. | |
| 386 | /// | |
| 387 | /// # Examples | |
| 388 | /// | |
| 389 | /// ``` | |
| 390 | /// #![feature(lock_value_accessors, nonpoison_mutex)] | |
| 391 | /// | |
| 392 | /// use std::sync::nonpoison::Mutex; | |
| 393 | /// | |
| 394 | /// let mutex = Mutex::new(2); | |
| 395 | /// | |
| 396 | /// let result = mutex.with_mut(|data| { | |
| 397 | /// *data += 3; | |
| 398 | /// | |
| 399 | /// *data + 5 | |
| 400 | /// }); | |
| 401 | /// | |
| 402 | /// assert_eq!(*mutex.lock(), 5); | |
| 403 | /// assert_eq!(result, 10); | |
| 404 | /// ``` | |
| 405 | #[unstable(feature = "lock_value_accessors", issue = "133407")] | |
| 406 | // #[unstable(feature = "nonpoison_mutex", issue = "134645")] | |
| 407 | pub fn with_mut<F, R>(&self, f: F) -> R | |
| 408 | where | |
| 409 | F: FnOnce(&mut T) -> R, | |
| 410 | { | |
| 411 | f(&mut self.lock()) | |
| 412 | } | |
| 379 | 413 | } |
| 380 | 414 | |
| 381 | 415 | #[unstable(feature = "nonpoison_mutex", issue = "134645")] |
library/std/src/sync/nonpoison/rwlock.rs+62| ... | ... | @@ -498,6 +498,68 @@ impl<T: ?Sized> RwLock<T> { |
| 498 | 498 | pub const fn data_ptr(&self) -> *mut T { |
| 499 | 499 | self.data.get() |
| 500 | 500 | } |
| 501 | ||
| 502 | /// Locks this `RwLock` with shared read access to the underlying data by passing | |
| 503 | /// a reference to the given closure. | |
| 504 | /// | |
| 505 | /// This method acquires the lock, calls the provided closure with a reference | |
| 506 | /// to the data, and returns the result of the closure. The lock is released after | |
| 507 | /// the closure completes, even if it panics. | |
| 508 | /// | |
| 509 | /// # Examples | |
| 510 | /// | |
| 511 | /// ``` | |
| 512 | /// #![feature(lock_value_accessors, nonpoison_rwlock)] | |
| 513 | /// | |
| 514 | /// use std::sync::nonpoison::RwLock; | |
| 515 | /// | |
| 516 | /// let rwlock = RwLock::new(2); | |
| 517 | /// let result = rwlock.with(|data| *data + 3); | |
| 518 | /// | |
| 519 | /// assert_eq!(result, 5); | |
| 520 | /// ``` | |
| 521 | #[unstable(feature = "lock_value_accessors", issue = "133407")] | |
| 522 | // #[unstable(feature = "nonpoison_rwlock", issue = "134645")] | |
| 523 | pub fn with<F, R>(&self, f: F) -> R | |
| 524 | where | |
| 525 | F: FnOnce(&T) -> R, | |
| 526 | { | |
| 527 | f(&self.read()) | |
| 528 | } | |
| 529 | ||
| 530 | /// Locks this `RwLock` with exclusive write access to the underlying data by passing | |
| 531 | /// a mutable reference to the given closure. | |
| 532 | /// | |
| 533 | /// This method acquires the lock, calls the provided closure with a mutable reference | |
| 534 | /// to the data, and returns the result of the closure. The lock is released after | |
| 535 | /// the closure completes, even if it panics. | |
| 536 | /// | |
| 537 | /// # Examples | |
| 538 | /// | |
| 539 | /// ``` | |
| 540 | /// #![feature(lock_value_accessors, nonpoison_rwlock)] | |
| 541 | /// | |
| 542 | /// use std::sync::nonpoison::RwLock; | |
| 543 | /// | |
| 544 | /// let rwlock = RwLock::new(2); | |
| 545 | /// | |
| 546 | /// let result = rwlock.with_mut(|data| { | |
| 547 | /// *data += 3; | |
| 548 | /// | |
| 549 | /// *data + 5 | |
| 550 | /// }); | |
| 551 | /// | |
| 552 | /// assert_eq!(*rwlock.read(), 5); | |
| 553 | /// assert_eq!(result, 10); | |
| 554 | /// ``` | |
| 555 | #[unstable(feature = "lock_value_accessors", issue = "133407")] | |
| 556 | // #[unstable(feature = "nonpoison_rwlock", issue = "134645")] | |
| 557 | pub fn with_mut<F, R>(&self, f: F) -> R | |
| 558 | where | |
| 559 | F: FnOnce(&mut T) -> R, | |
| 560 | { | |
| 561 | f(&mut self.write()) | |
| 562 | } | |
| 501 | 563 | } |
| 502 | 564 | |
| 503 | 565 | #[unstable(feature = "nonpoison_rwlock", issue = "134645")] |
library/std/tests/sync/mutex.rs+14| ... | ... | @@ -549,3 +549,17 @@ fn panic_while_mapping_unlocked_poison() { |
| 549 | 549 | |
| 550 | 550 | drop(lock); |
| 551 | 551 | } |
| 552 | ||
| 553 | #[test] | |
| 554 | fn test_mutex_with_mut() { | |
| 555 | let mutex = std::sync::nonpoison::Mutex::new(2); | |
| 556 | ||
| 557 | let result = mutex.with_mut(|value| { | |
| 558 | *value += 3; | |
| 559 | ||
| 560 | *value + 5 | |
| 561 | }); | |
| 562 | ||
| 563 | assert_eq!(*mutex.lock(), 5); | |
| 564 | assert_eq!(result, 10); | |
| 565 | } |
library/std/tests/sync/rwlock.rs+22| ... | ... | @@ -861,3 +861,25 @@ fn panic_while_mapping_write_unlocked_poison() { |
| 861 | 861 | |
| 862 | 862 | drop(lock); |
| 863 | 863 | } |
| 864 | ||
| 865 | #[test] | |
| 866 | fn test_rwlock_with() { | |
| 867 | let rwlock = std::sync::nonpoison::RwLock::new(2); | |
| 868 | let result = rwlock.with(|value| *value + 3); | |
| 869 | ||
| 870 | assert_eq!(result, 5); | |
| 871 | } | |
| 872 | ||
| 873 | #[test] | |
| 874 | fn test_rwlock_with_mut() { | |
| 875 | let rwlock = std::sync::nonpoison::RwLock::new(2); | |
| 876 | ||
| 877 | let result = rwlock.with_mut(|value| { | |
| 878 | *value += 3; | |
| 879 | ||
| 880 | *value + 5 | |
| 881 | }); | |
| 882 | ||
| 883 | assert_eq!(*rwlock.read(), 5); | |
| 884 | assert_eq!(result, 10); | |
| 885 | } |
src/doc/rustc/src/platform-support/wasm32-unknown-unknown.md+61-1| ... | ... | @@ -157,7 +157,7 @@ $ cargo +nightly build -Zbuild-std=panic_abort,std --target wasm32-unknown-unkno |
| 157 | 157 | ``` |
| 158 | 158 | |
| 159 | 159 | Here the `mvp` "cpu" is a placeholder in LLVM for disabling all supported |
| 160 | features by default. Cargo's `-Zbuild-std` feature, a Nightly Rust feature, is | |
| 160 | features by default. Cargo's [`-Zbuild-std`] feature, a Nightly Rust feature, is | |
| 161 | 161 | then used to recompile the standard library in addition to your own code. This |
| 162 | 162 | will produce a binary that uses only the original WebAssembly features by |
| 163 | 163 | default and no proposals since its inception. |
| ... | ... | @@ -207,3 +207,63 @@ conditionally compile code instead. This is notably different to the way native |
| 207 | 207 | platforms such as x86\_64 work, and this is due to the fact that WebAssembly |
| 208 | 208 | binaries must only contain code the engine understands. Native binaries work so |
| 209 | 209 | long as the CPU doesn't execute unknown code dynamically at runtime. |
| 210 | ||
| 211 | ## Unwinding | |
| 212 | ||
| 213 | By default the `wasm32-unknown-unknown` target is compiled with `-Cpanic=abort`. | |
| 214 | Historically this was due to the fact that there was no way to catch panics in | |
| 215 | wasm, but since mid-2025 the WebAssembly [`exception-handling` | |
| 216 | proposal](https://github.com/WebAssembly/exception-handling) reached | |
| 217 | stabilization. LLVM has support for this proposal as well and when this is all | |
| 218 | combined together it's possible to enable `-Cpanic=unwind` on wasm targets. | |
| 219 | ||
| 220 | Compiling wasm targets with `-Cpanic=unwind` is not as easy as just passing | |
| 221 | `-Cpanic=unwind`, however: | |
| 222 | ||
| 223 | ```sh | |
| 224 | $ rustc foo.rs -Cpanic=unwind --target wasm32-unknown-unknown | |
| 225 | error: the crate `panic_unwind` does not have the panic strategy `unwind` | |
| 226 | ``` | |
| 227 | ||
| 228 | Notably the precompiled standard library that is shipped through Rustup is | |
| 229 | compiled with `-Cpanic=abort`, not `-Cpanic=unwind`. While this is the case | |
| 230 | you're going to be required to use Cargo's [`-Zbuild-std`] feature to build with | |
| 231 | unwinding support: | |
| 232 | ||
| 233 | ```sh | |
| 234 | $ RUSTFLAGS='-Cpanic=unwind' cargo +nightly build --target wasm32-unknown-unknown -Zbuild-std | |
| 235 | ``` | |
| 236 | ||
| 237 | Note, however, that as of 2025-10-03 LLVM is still using the "legacy exception | |
| 238 | instructions" by default, not the officially standard version of the | |
| 239 | exception-handling proposal: | |
| 240 | ||
| 241 | ```sh | |
| 242 | $ wasm-tools validate target/wasm32-unknown-unknown/debug/foo.wasm | |
| 243 | error: <sysroot>/library/std/src/sys/backtrace.rs:161:5 | |
| 244 | function `std::sys::backtrace::__rust_begin_short_backtrace` failed to validate | |
| 245 | ||
| 246 | Caused by: | |
| 247 | 0: func 2 failed to validate | |
| 248 | 1: legacy_exceptions feature required for try instruction (at offset 0x880) | |
| 249 | ``` | |
| 250 | ||
| 251 | Fixing this requires passing `-Cllvm-args=-wasm-use-legacy-eh=false` to the Rust | |
| 252 | compiler as well: | |
| 253 | ||
| 254 | ```sh | |
| 255 | $ RUSTFLAGS='-Cpanic=unwind -Cllvm-args=-wasm-use-legacy-eh=false' cargo +nightly build --target wasm32-unknown-unknown -Zbuild-std | |
| 256 | $ wasm-tools validate target/wasm32-unknown-unknown/debug/foo.wasm | |
| 257 | ``` | |
| 258 | ||
| 259 | At this time there are no concrete plans for adding new targets to the Rust | |
| 260 | compiler which have `-Cpanic=unwind` enabled-by-default. The most likely route | |
| 261 | to having this enabled is that in a few years when the `exception-handling` | |
| 262 | target feature is enabled by default in LLVM (due to browsers/runtime support | |
| 263 | propagating widely enough) the targets will switch to using `-Cpanic=unwind` by | |
| 264 | default. This is not for certain, however, and will likely be accompanied with | |
| 265 | either an MCP or an RFC about changing all wasm targets in the same manner. In | |
| 266 | the meantime using `-Cpanic=unwind` will require using [`-Zbuild-std`] and | |
| 267 | passing the appropriate flags to rustc. | |
| 268 | ||
| 269 | [`-Zbuild-std`]: ../../cargo/reference/unstable.html#build-std |
src/doc/rustc/src/platform-support/wasm32-wasip1.md+6| ... | ... | @@ -133,3 +133,9 @@ to Rust 1.80 the `target_env` condition was not set. |
| 133 | 133 | The default set of WebAssembly features enabled for compilation is currently the |
| 134 | 134 | same as [`wasm32-unknown-unknown`](./wasm32-unknown-unknown.md). See the |
| 135 | 135 | documentation there for more information. |
| 136 | ||
| 137 | ## Unwinding | |
| 138 | ||
| 139 | This target is compiled with `-Cpanic=abort` by default. For information on | |
| 140 | using `-Cpanic=unwind` see the [documentation about unwinding for | |
| 141 | `wasm32-unknown-unknown`](./wasm32-unknown-unknown.md#unwinding). |
src/doc/rustc/src/platform-support/wasm32-wasip2.md+6| ... | ... | @@ -67,3 +67,9 @@ It's recommended to conditionally compile code for this target with: |
| 67 | 67 | The default set of WebAssembly features enabled for compilation is currently the |
| 68 | 68 | same as [`wasm32-unknown-unknown`](./wasm32-unknown-unknown.md). See the |
| 69 | 69 | documentation there for more information. |
| 70 | ||
| 71 | ## Unwinding | |
| 72 | ||
| 73 | This target is compiled with `-Cpanic=abort` by default. For information on | |
| 74 | using `-Cpanic=unwind` see the [documentation about unwinding for | |
| 75 | `wasm32-unknown-unknown`](./wasm32-unknown-unknown.md#unwinding). |
src/doc/rustc/src/platform-support/wasm32v1-none.md+11| ... | ... | @@ -107,3 +107,14 @@ $ cargo +nightly build -Zbuild-std=panic_abort,std --target wasm32-unknown-unkno |
| 107 | 107 | Which not only rebuilds `std`, `core` and `alloc` (which is somewhat costly and annoying) but more importantly requires the use of nightly Rust toolchains (for the `-Zbuild-std` flag). This is very undesirable for the target audience, which consists of people targeting WebAssembly implementations that prioritize stability, simplicity and/or security over feature support. |
| 108 | 108 | |
| 109 | 109 | This `wasm32v1-none` target exists as an alternative option that works on stable Rust toolchains, without rebuilding the stdlib. |
| 110 | ||
| 111 | ## Unwinding | |
| 112 | ||
| 113 | This target is compiled with `-Cpanic=abort` by default. Using `-Cpanic=unwind` | |
| 114 | would require using the WebAssembly exception-handling proposal stabilized | |
| 115 | mid-2025, and if that's desired then you most likely don't want to use this | |
| 116 | target and instead want to use `wasm32-unknown-unknown` instead. It's unlikely | |
| 117 | that this target will ever support unwinding with the precompiled artifacts | |
| 118 | shipped through rustup. For documentation about using `-Zbuild-std` to enable | |
| 119 | using `-Cpanic=unwind` see the [documentation of | |
| 120 | `wasm32-unknown-unknown`](./wasm32-unknown-unknown.md#unwinding). |
src/tools/compiletest/src/directives.rs+58-122| ... | ... | @@ -15,6 +15,7 @@ use crate::directives::auxiliary::{AuxProps, parse_and_update_aux}; |
| 15 | 15 | use crate::directives::directive_names::{ |
| 16 | 16 | KNOWN_DIRECTIVE_NAMES, KNOWN_HTMLDOCCK_DIRECTIVE_NAMES, KNOWN_JSONDOCCK_DIRECTIVE_NAMES, |
| 17 | 17 | }; |
| 18 | use crate::directives::line::{DirectiveLine, line_directive}; | |
| 18 | 19 | use crate::directives::needs::CachedNeedsConditions; |
| 19 | 20 | use crate::edition::{Edition, parse_edition}; |
| 20 | 21 | use crate::errors::ErrorKind; |
| ... | ... | @@ -25,6 +26,7 @@ use crate::{fatal, help}; |
| 25 | 26 | pub(crate) mod auxiliary; |
| 26 | 27 | mod cfg; |
| 27 | 28 | mod directive_names; |
| 29 | mod line; | |
| 28 | 30 | mod needs; |
| 29 | 31 | #[cfg(test)] |
| 30 | 32 | mod tests; |
| ... | ... | @@ -824,70 +826,6 @@ impl TestProps { |
| 824 | 826 | } |
| 825 | 827 | } |
| 826 | 828 | |
| 827 | /// If the given line begins with the appropriate comment prefix for a directive, | |
| 828 | /// returns a struct containing various parts of the directive. | |
| 829 | fn line_directive<'line>( | |
| 830 | line_number: usize, | |
| 831 | original_line: &'line str, | |
| 832 | ) -> Option<DirectiveLine<'line>> { | |
| 833 | // Ignore lines that don't start with the comment prefix. | |
| 834 | let after_comment = | |
| 835 | original_line.trim_start().strip_prefix(COMPILETEST_DIRECTIVE_PREFIX)?.trim_start(); | |
| 836 | ||
| 837 | let revision; | |
| 838 | let raw_directive; | |
| 839 | ||
| 840 | if let Some(after_open_bracket) = after_comment.strip_prefix('[') { | |
| 841 | // A comment like `//@[foo]` only applies to revision `foo`. | |
| 842 | let Some((line_revision, after_close_bracket)) = after_open_bracket.split_once(']') else { | |
| 843 | panic!( | |
| 844 | "malformed condition directive: expected `{COMPILETEST_DIRECTIVE_PREFIX}[foo]`, found `{original_line}`" | |
| 845 | ) | |
| 846 | }; | |
| 847 | ||
| 848 | revision = Some(line_revision); | |
| 849 | raw_directive = after_close_bracket.trim_start(); | |
| 850 | } else { | |
| 851 | revision = None; | |
| 852 | raw_directive = after_comment; | |
| 853 | }; | |
| 854 | ||
| 855 | Some(DirectiveLine { line_number, revision, raw_directive }) | |
| 856 | } | |
| 857 | ||
| 858 | /// The (partly) broken-down contents of a line containing a test directive, | |
| 859 | /// which [`iter_directives`] passes to its callback function. | |
| 860 | /// | |
| 861 | /// For example: | |
| 862 | /// | |
| 863 | /// ```text | |
| 864 | /// //@ compile-flags: -O | |
| 865 | /// ^^^^^^^^^^^^^^^^^ raw_directive | |
| 866 | /// | |
| 867 | /// //@ [foo] compile-flags: -O | |
| 868 | /// ^^^ revision | |
| 869 | /// ^^^^^^^^^^^^^^^^^ raw_directive | |
| 870 | /// ``` | |
| 871 | struct DirectiveLine<'ln> { | |
| 872 | line_number: usize, | |
| 873 | /// Some test directives start with a revision name in square brackets | |
| 874 | /// (e.g. `[foo]`), and only apply to that revision of the test. | |
| 875 | /// If present, this field contains the revision name (e.g. `foo`). | |
| 876 | revision: Option<&'ln str>, | |
| 877 | /// The main part of the directive, after removing the comment prefix | |
| 878 | /// and the optional revision specifier. | |
| 879 | /// | |
| 880 | /// This is "raw" because the directive's name and colon-separated value | |
| 881 | /// (if present) have not yet been extracted or checked. | |
| 882 | raw_directive: &'ln str, | |
| 883 | } | |
| 884 | ||
| 885 | impl<'ln> DirectiveLine<'ln> { | |
| 886 | fn applies_to_test_revision(&self, test_revision: Option<&str>) -> bool { | |
| 887 | self.revision.is_none() || self.revision == test_revision | |
| 888 | } | |
| 889 | } | |
| 890 | ||
| 891 | 829 | pub(crate) struct CheckDirectiveResult<'ln> { |
| 892 | 830 | is_known_directive: bool, |
| 893 | 831 | trailing_directive: Option<&'ln str>, |
| ... | ... | @@ -897,9 +835,7 @@ fn check_directive<'a>( |
| 897 | 835 | directive_ln: &DirectiveLine<'a>, |
| 898 | 836 | mode: TestMode, |
| 899 | 837 | ) -> CheckDirectiveResult<'a> { |
| 900 | let &DirectiveLine { raw_directive: directive_ln, .. } = directive_ln; | |
| 901 | ||
| 902 | let (directive_name, post) = directive_ln.split_once([':', ' ']).unwrap_or((directive_ln, "")); | |
| 838 | let &DirectiveLine { name: directive_name, .. } = directive_ln; | |
| 903 | 839 | |
| 904 | 840 | let is_known_directive = KNOWN_DIRECTIVE_NAMES.contains(&directive_name) |
| 905 | 841 | || match mode { |
| ... | ... | @@ -908,20 +844,17 @@ fn check_directive<'a>( |
| 908 | 844 | _ => false, |
| 909 | 845 | }; |
| 910 | 846 | |
| 911 | let trailing = post.trim().split_once(' ').map(|(pre, _)| pre).unwrap_or(post); | |
| 912 | let trailing_directive = { | |
| 913 | // 1. is the directive name followed by a space? (to exclude `:`) | |
| 914 | directive_ln.get(directive_name.len()..).is_some_and(|s| s.starts_with(' ')) | |
| 915 | // 2. is what is after that directive also a directive (ex: "only-x86 only-arm") | |
| 916 | && KNOWN_DIRECTIVE_NAMES.contains(&trailing) | |
| 917 | } | |
| 918 | .then_some(trailing); | |
| 847 | // If it looks like the user tried to put two directives on the same line | |
| 848 | // (e.g. `//@ only-linux only-x86_64`), signal an error, because the | |
| 849 | // second "directive" would actually be ignored with no effect. | |
| 850 | let trailing_directive = directive_ln | |
| 851 | .remark_after_space() | |
| 852 | .map(|remark| remark.trim_start().split(' ').next().unwrap()) | |
| 853 | .filter(|token| KNOWN_DIRECTIVE_NAMES.contains(token)); | |
| 919 | 854 | |
| 920 | 855 | CheckDirectiveResult { is_known_directive, trailing_directive } |
| 921 | 856 | } |
| 922 | 857 | |
| 923 | const COMPILETEST_DIRECTIVE_PREFIX: &str = "//@"; | |
| 924 | ||
| 925 | 858 | fn iter_directives( |
| 926 | 859 | mode: TestMode, |
| 927 | 860 | poisoned: &mut bool, |
| ... | ... | @@ -939,15 +872,17 @@ fn iter_directives( |
| 939 | 872 | // FIXME(jieyouxu): I feel like there's a better way to do this, leaving for later. |
| 940 | 873 | if mode == TestMode::CoverageRun { |
| 941 | 874 | let extra_directives: &[&str] = &[ |
| 942 | "needs-profiler-runtime", | |
| 875 | "//@ needs-profiler-runtime", | |
| 943 | 876 | // FIXME(pietroalbini): this test currently does not work on cross-compiled targets |
| 944 | 877 | // because remote-test is not capable of sending back the *.profraw files generated by |
| 945 | 878 | // the LLVM instrumentation. |
| 946 | "ignore-cross-compile", | |
| 879 | "//@ ignore-cross-compile", | |
| 947 | 880 | ]; |
| 948 | 881 | // Process the extra implied directives, with a dummy line number of 0. |
| 949 | for raw_directive in extra_directives { | |
| 950 | it(DirectiveLine { line_number: 0, revision: None, raw_directive }); | |
| 882 | for directive_str in extra_directives { | |
| 883 | let directive_line = line_directive(0, directive_str) | |
| 884 | .unwrap_or_else(|| panic!("bad extra-directive line: {directive_str:?}")); | |
| 885 | it(directive_line); | |
| 951 | 886 | } |
| 952 | 887 | } |
| 953 | 888 | |
| ... | ... | @@ -977,7 +912,7 @@ fn iter_directives( |
| 977 | 912 | |
| 978 | 913 | error!( |
| 979 | 914 | "{testfile}:{line_number}: detected unknown compiletest test directive `{}`", |
| 980 | directive_line.raw_directive, | |
| 915 | directive_line.display(), | |
| 981 | 916 | ); |
| 982 | 917 | |
| 983 | 918 | return; |
| ... | ... | @@ -1073,13 +1008,9 @@ impl Config { |
| 1073 | 1008 | } |
| 1074 | 1009 | |
| 1075 | 1010 | fn parse_custom_normalization(&self, line: &DirectiveLine<'_>) -> Option<NormalizeRule> { |
| 1076 | let &DirectiveLine { raw_directive, .. } = line; | |
| 1077 | ||
| 1078 | // FIXME(Zalathar): Integrate name/value splitting into `DirectiveLine` | |
| 1079 | // instead of doing it here. | |
| 1080 | let (directive_name, raw_value) = raw_directive.split_once(':')?; | |
| 1011 | let &DirectiveLine { name, .. } = line; | |
| 1081 | 1012 | |
| 1082 | let kind = match directive_name { | |
| 1013 | let kind = match name { | |
| 1083 | 1014 | "normalize-stdout" => NormalizeKind::Stdout, |
| 1084 | 1015 | "normalize-stderr" => NormalizeKind::Stderr, |
| 1085 | 1016 | "normalize-stderr-32bit" => NormalizeKind::Stderr32bit, |
| ... | ... | @@ -1087,21 +1018,20 @@ impl Config { |
| 1087 | 1018 | _ => return None, |
| 1088 | 1019 | }; |
| 1089 | 1020 | |
| 1090 | let Some((regex, replacement)) = parse_normalize_rule(raw_value) else { | |
| 1091 | error!("couldn't parse custom normalization rule: `{raw_directive}`"); | |
| 1092 | help!("expected syntax is: `{directive_name}: \"REGEX\" -> \"REPLACEMENT\"`"); | |
| 1021 | let Some((regex, replacement)) = line.value_after_colon().and_then(parse_normalize_rule) | |
| 1022 | else { | |
| 1023 | error!("couldn't parse custom normalization rule: `{}`", line.display()); | |
| 1024 | help!("expected syntax is: `{name}: \"REGEX\" -> \"REPLACEMENT\"`"); | |
| 1093 | 1025 | panic!("invalid normalization rule detected"); |
| 1094 | 1026 | }; |
| 1095 | 1027 | Some(NormalizeRule { kind, regex, replacement }) |
| 1096 | 1028 | } |
| 1097 | 1029 | |
| 1098 | 1030 | fn parse_name_directive(&self, line: &DirectiveLine<'_>, directive: &str) -> bool { |
| 1099 | let &DirectiveLine { raw_directive: line, .. } = line; | |
| 1100 | ||
| 1101 | // Ensure the directive is a whole word. Do not match "ignore-x86" when | |
| 1102 | // the line says "ignore-x86_64". | |
| 1103 | line.starts_with(directive) | |
| 1104 | && matches!(line.as_bytes().get(directive.len()), None | Some(&b' ') | Some(&b':')) | |
| 1031 | // FIXME(Zalathar): Ideally, this should raise an error if a name-only | |
| 1032 | // directive is followed by a colon, since that's the wrong syntax. | |
| 1033 | // But we would need to fix tests that rely on the current behaviour. | |
| 1034 | line.name == directive | |
| 1105 | 1035 | } |
| 1106 | 1036 | |
| 1107 | 1037 | fn parse_name_value_directive( |
| ... | ... | @@ -1110,22 +1040,26 @@ impl Config { |
| 1110 | 1040 | directive: &str, |
| 1111 | 1041 | testfile: &Utf8Path, |
| 1112 | 1042 | ) -> Option<String> { |
| 1113 | let &DirectiveLine { line_number, raw_directive: line, .. } = line; | |
| 1114 | ||
| 1115 | let colon = directive.len(); | |
| 1116 | if line.starts_with(directive) && line.as_bytes().get(colon) == Some(&b':') { | |
| 1117 | let value = line[(colon + 1)..].to_owned(); | |
| 1118 | debug!("{}: {}", directive, value); | |
| 1119 | let value = expand_variables(value, self); | |
| 1120 | if value.is_empty() { | |
| 1121 | error!("{testfile}:{line_number}: empty value for directive `{directive}`"); | |
| 1122 | help!("expected syntax is: `{directive}: value`"); | |
| 1123 | panic!("empty directive value detected"); | |
| 1124 | } | |
| 1125 | Some(value) | |
| 1126 | } else { | |
| 1127 | None | |
| 1043 | let &DirectiveLine { line_number, .. } = line; | |
| 1044 | ||
| 1045 | if line.name != directive { | |
| 1046 | return None; | |
| 1047 | }; | |
| 1048 | ||
| 1049 | // FIXME(Zalathar): This silently discards directives with a matching | |
| 1050 | // name but no colon. Unfortunately, some directives (e.g. "pp-exact") | |
| 1051 | // currently rely on _not_ panicking here. | |
| 1052 | let value = line.value_after_colon()?; | |
| 1053 | debug!("{}: {}", directive, value); | |
| 1054 | let value = expand_variables(value.to_owned(), self); | |
| 1055 | ||
| 1056 | if value.is_empty() { | |
| 1057 | error!("{testfile}:{line_number}: empty value for directive `{directive}`"); | |
| 1058 | help!("expected syntax is: `{directive}: value`"); | |
| 1059 | panic!("empty directive value detected"); | |
| 1128 | 1060 | } |
| 1061 | ||
| 1062 | Some(value) | |
| 1129 | 1063 | } |
| 1130 | 1064 | |
| 1131 | 1065 | fn set_name_directive(&self, line: &DirectiveLine<'_>, directive: &str, value: &mut bool) { |
| ... | ... | @@ -1515,14 +1449,14 @@ pub(crate) fn make_test_description<R: Read>( |
| 1515 | 1449 | } |
| 1516 | 1450 | |
| 1517 | 1451 | fn ignore_cdb(config: &Config, line: &DirectiveLine<'_>) -> IgnoreDecision { |
| 1518 | let &DirectiveLine { raw_directive: line, .. } = line; | |
| 1519 | ||
| 1520 | 1452 | if config.debugger != Some(Debugger::Cdb) { |
| 1521 | 1453 | return IgnoreDecision::Continue; |
| 1522 | 1454 | } |
| 1523 | 1455 | |
| 1524 | 1456 | if let Some(actual_version) = config.cdb_version { |
| 1525 | if let Some(rest) = line.strip_prefix("min-cdb-version:").map(str::trim) { | |
| 1457 | if line.name == "min-cdb-version" | |
| 1458 | && let Some(rest) = line.value_after_colon().map(str::trim) | |
| 1459 | { | |
| 1526 | 1460 | let min_version = extract_cdb_version(rest).unwrap_or_else(|| { |
| 1527 | 1461 | panic!("couldn't parse version range: {:?}", rest); |
| 1528 | 1462 | }); |
| ... | ... | @@ -1540,14 +1474,14 @@ fn ignore_cdb(config: &Config, line: &DirectiveLine<'_>) -> IgnoreDecision { |
| 1540 | 1474 | } |
| 1541 | 1475 | |
| 1542 | 1476 | fn ignore_gdb(config: &Config, line: &DirectiveLine<'_>) -> IgnoreDecision { |
| 1543 | let &DirectiveLine { raw_directive: line, .. } = line; | |
| 1544 | ||
| 1545 | 1477 | if config.debugger != Some(Debugger::Gdb) { |
| 1546 | 1478 | return IgnoreDecision::Continue; |
| 1547 | 1479 | } |
| 1548 | 1480 | |
| 1549 | 1481 | if let Some(actual_version) = config.gdb_version { |
| 1550 | if let Some(rest) = line.strip_prefix("min-gdb-version:").map(str::trim) { | |
| 1482 | if line.name == "min-gdb-version" | |
| 1483 | && let Some(rest) = line.value_after_colon().map(str::trim) | |
| 1484 | { | |
| 1551 | 1485 | let (start_ver, end_ver) = extract_version_range(rest, extract_gdb_version) |
| 1552 | 1486 | .unwrap_or_else(|| { |
| 1553 | 1487 | panic!("couldn't parse version range: {:?}", rest); |
| ... | ... | @@ -1563,7 +1497,9 @@ fn ignore_gdb(config: &Config, line: &DirectiveLine<'_>) -> IgnoreDecision { |
| 1563 | 1497 | reason: format!("ignored when the GDB version is lower than {rest}"), |
| 1564 | 1498 | }; |
| 1565 | 1499 | } |
| 1566 | } else if let Some(rest) = line.strip_prefix("ignore-gdb-version:").map(str::trim) { | |
| 1500 | } else if line.name == "ignore-gdb-version" | |
| 1501 | && let Some(rest) = line.value_after_colon().map(str::trim) | |
| 1502 | { | |
| 1567 | 1503 | let (min_version, max_version) = extract_version_range(rest, extract_gdb_version) |
| 1568 | 1504 | .unwrap_or_else(|| { |
| 1569 | 1505 | panic!("couldn't parse version range: {:?}", rest); |
| ... | ... | @@ -1590,14 +1526,14 @@ fn ignore_gdb(config: &Config, line: &DirectiveLine<'_>) -> IgnoreDecision { |
| 1590 | 1526 | } |
| 1591 | 1527 | |
| 1592 | 1528 | fn ignore_lldb(config: &Config, line: &DirectiveLine<'_>) -> IgnoreDecision { |
| 1593 | let &DirectiveLine { raw_directive: line, .. } = line; | |
| 1594 | ||
| 1595 | 1529 | if config.debugger != Some(Debugger::Lldb) { |
| 1596 | 1530 | return IgnoreDecision::Continue; |
| 1597 | 1531 | } |
| 1598 | 1532 | |
| 1599 | 1533 | if let Some(actual_version) = config.lldb_version { |
| 1600 | if let Some(rest) = line.strip_prefix("min-lldb-version:").map(str::trim) { | |
| 1534 | if line.name == "min-lldb-version" | |
| 1535 | && let Some(rest) = line.value_after_colon().map(str::trim) | |
| 1536 | { | |
| 1601 | 1537 | let min_version = rest.parse().unwrap_or_else(|e| { |
| 1602 | 1538 | panic!("Unexpected format of LLDB version string: {}\n{:?}", rest, e); |
| 1603 | 1539 | }); |
src/tools/compiletest/src/directives/auxiliary.rs+1-3| ... | ... | @@ -50,9 +50,7 @@ pub(super) fn parse_and_update_aux( |
| 50 | 50 | testfile: &Utf8Path, |
| 51 | 51 | aux: &mut AuxProps, |
| 52 | 52 | ) { |
| 53 | let &DirectiveLine { raw_directive: ln, .. } = directive_line; | |
| 54 | ||
| 55 | if !(ln.starts_with("aux-") || ln.starts_with("proc-macro")) { | |
| 53 | if !(directive_line.name.starts_with("aux-") || directive_line.name == "proc-macro") { | |
| 56 | 54 | return; |
| 57 | 55 | } |
| 58 | 56 |
src/tools/compiletest/src/directives/cfg.rs+11-15| ... | ... | @@ -6,8 +6,8 @@ use crate::directives::{DirectiveLine, IgnoreDecision}; |
| 6 | 6 | const EXTRA_ARCHS: &[&str] = &["spirv"]; |
| 7 | 7 | |
| 8 | 8 | pub(super) fn handle_ignore(config: &Config, line: &DirectiveLine<'_>) -> IgnoreDecision { |
| 9 | let parsed = parse_cfg_name_directive(config, line, "ignore"); | |
| 10 | let &DirectiveLine { raw_directive: line, .. } = line; | |
| 9 | let parsed = parse_cfg_name_directive(config, line, "ignore-"); | |
| 10 | let line = line.display(); | |
| 11 | 11 | |
| 12 | 12 | match parsed.outcome { |
| 13 | 13 | MatchOutcome::NoMatch => IgnoreDecision::Continue, |
| ... | ... | @@ -24,8 +24,8 @@ pub(super) fn handle_ignore(config: &Config, line: &DirectiveLine<'_>) -> Ignore |
| 24 | 24 | } |
| 25 | 25 | |
| 26 | 26 | pub(super) fn handle_only(config: &Config, line: &DirectiveLine<'_>) -> IgnoreDecision { |
| 27 | let parsed = parse_cfg_name_directive(config, line, "only"); | |
| 28 | let &DirectiveLine { raw_directive: line, .. } = line; | |
| 27 | let parsed = parse_cfg_name_directive(config, line, "only-"); | |
| 28 | let line = line.display(); | |
| 29 | 29 | |
| 30 | 30 | match parsed.outcome { |
| 31 | 31 | MatchOutcome::Match => IgnoreDecision::Continue, |
| ... | ... | @@ -50,18 +50,14 @@ fn parse_cfg_name_directive<'a>( |
| 50 | 50 | line: &'a DirectiveLine<'a>, |
| 51 | 51 | prefix: &str, |
| 52 | 52 | ) -> ParsedNameDirective<'a> { |
| 53 | let &DirectiveLine { raw_directive: line, .. } = line; | |
| 54 | ||
| 55 | if !line.as_bytes().starts_with(prefix.as_bytes()) { | |
| 56 | return ParsedNameDirective::not_a_directive(); | |
| 57 | } | |
| 58 | if line.as_bytes().get(prefix.len()) != Some(&b'-') { | |
| 53 | let Some(name) = line.name.strip_prefix(prefix) else { | |
| 59 | 54 | return ParsedNameDirective::not_a_directive(); |
| 60 | } | |
| 61 | let line = &line[prefix.len() + 1..]; | |
| 55 | }; | |
| 62 | 56 | |
| 63 | let (name, comment) = | |
| 64 | line.split_once(&[':', ' ']).map(|(l, c)| (l, Some(c))).unwrap_or((line, None)); | |
| 57 | // FIXME(Zalathar): This currently allows either a space or a colon, and | |
| 58 | // treats any "value" after a colon as though it were a remark. | |
| 59 | // We should instead forbid the colon syntax for these directives. | |
| 60 | let comment = line.remark_after_space().or_else(|| line.value_after_colon()); | |
| 65 | 61 | |
| 66 | 62 | // Some of the matchers might be "" depending on what the target information is. To avoid |
| 67 | 63 | // problems we outright reject empty directives. |
| ... | ... | @@ -269,7 +265,7 @@ fn parse_cfg_name_directive<'a>( |
| 269 | 265 | message: "when performing tests on dist toolchain" |
| 270 | 266 | } |
| 271 | 267 | |
| 272 | if prefix == "ignore" && outcome == MatchOutcome::Invalid { | |
| 268 | if prefix == "ignore-" && outcome == MatchOutcome::Invalid { | |
| 273 | 269 | // Don't error out for ignore-tidy-* diretives, as those are not handled by compiletest. |
| 274 | 270 | if name.starts_with("tidy-") { |
| 275 | 271 | outcome = MatchOutcome::External; |
src/tools/compiletest/src/directives/line.rs created+108| ... | ... | @@ -0,0 +1,108 @@ |
| 1 | use std::fmt; | |
| 2 | ||
| 3 | const COMPILETEST_DIRECTIVE_PREFIX: &str = "//@"; | |
| 4 | ||
| 5 | /// If the given line begins with the appropriate comment prefix for a directive, | |
| 6 | /// returns a struct containing various parts of the directive. | |
| 7 | pub(crate) fn line_directive<'line>( | |
| 8 | line_number: usize, | |
| 9 | original_line: &'line str, | |
| 10 | ) -> Option<DirectiveLine<'line>> { | |
| 11 | // Ignore lines that don't start with the comment prefix. | |
| 12 | let after_comment = | |
| 13 | original_line.trim_start().strip_prefix(COMPILETEST_DIRECTIVE_PREFIX)?.trim_start(); | |
| 14 | ||
| 15 | let revision; | |
| 16 | let raw_directive; | |
| 17 | ||
| 18 | if let Some(after_open_bracket) = after_comment.strip_prefix('[') { | |
| 19 | // A comment like `//@[foo]` only applies to revision `foo`. | |
| 20 | let Some((line_revision, after_close_bracket)) = after_open_bracket.split_once(']') else { | |
| 21 | panic!( | |
| 22 | "malformed condition directive: expected `{COMPILETEST_DIRECTIVE_PREFIX}[foo]`, found `{original_line}`" | |
| 23 | ) | |
| 24 | }; | |
| 25 | ||
| 26 | revision = Some(line_revision); | |
| 27 | raw_directive = after_close_bracket.trim_start(); | |
| 28 | } else { | |
| 29 | revision = None; | |
| 30 | raw_directive = after_comment; | |
| 31 | }; | |
| 32 | ||
| 33 | // The directive name ends at the first occurrence of colon, space, or end-of-string. | |
| 34 | let name = raw_directive.split([':', ' ']).next().expect("split is never empty"); | |
| 35 | ||
| 36 | Some(DirectiveLine { line_number, revision, raw_directive, name }) | |
| 37 | } | |
| 38 | ||
| 39 | /// The (partly) broken-down contents of a line containing a test directive, | |
| 40 | /// which `iter_directives` passes to its callback function. | |
| 41 | /// | |
| 42 | /// For example: | |
| 43 | /// | |
| 44 | /// ```text | |
| 45 | /// //@ compile-flags: -O | |
| 46 | /// ^^^^^^^^^^^^^^^^^ raw_directive | |
| 47 | /// ^^^^^^^^^^^^^ name | |
| 48 | /// | |
| 49 | /// //@ [foo] compile-flags: -O | |
| 50 | /// ^^^ revision | |
| 51 | /// ^^^^^^^^^^^^^^^^^ raw_directive | |
| 52 | /// ^^^^^^^^^^^^^ name | |
| 53 | /// ``` | |
| 54 | pub(crate) struct DirectiveLine<'ln> { | |
| 55 | pub(crate) line_number: usize, | |
| 56 | ||
| 57 | /// Some test directives start with a revision name in square brackets | |
| 58 | /// (e.g. `[foo]`), and only apply to that revision of the test. | |
| 59 | /// If present, this field contains the revision name (e.g. `foo`). | |
| 60 | pub(crate) revision: Option<&'ln str>, | |
| 61 | ||
| 62 | /// The main part of the directive, after removing the comment prefix | |
| 63 | /// and the optional revision specifier. | |
| 64 | /// | |
| 65 | /// This is "raw" because the directive's name and colon-separated value | |
| 66 | /// (if present) have not yet been extracted or checked. | |
| 67 | raw_directive: &'ln str, | |
| 68 | ||
| 69 | /// Name of the directive. | |
| 70 | /// | |
| 71 | /// Invariant: `self.raw_directive.starts_with(self.name)` | |
| 72 | pub(crate) name: &'ln str, | |
| 73 | } | |
| 74 | ||
| 75 | impl<'ln> DirectiveLine<'ln> { | |
| 76 | pub(crate) fn applies_to_test_revision(&self, test_revision: Option<&str>) -> bool { | |
| 77 | self.revision.is_none() || self.revision == test_revision | |
| 78 | } | |
| 79 | ||
| 80 | /// Helper method used by `value_after_colon` and `remark_after_space`. | |
| 81 | /// Don't call this directly. | |
| 82 | fn rest_after_separator(&self, separator: u8) -> Option<&'ln str> { | |
| 83 | let n = self.name.len(); | |
| 84 | if self.raw_directive.as_bytes().get(n) != Some(&separator) { | |
| 85 | return None; | |
| 86 | } | |
| 87 | ||
| 88 | Some(&self.raw_directive[n + 1..]) | |
| 89 | } | |
| 90 | ||
| 91 | /// If this directive uses `name: value` syntax, returns the part after | |
| 92 | /// the colon character. | |
| 93 | pub(crate) fn value_after_colon(&self) -> Option<&'ln str> { | |
| 94 | self.rest_after_separator(b':') | |
| 95 | } | |
| 96 | ||
| 97 | /// If this directive uses `name remark` syntax, returns the part after | |
| 98 | /// the separating space. | |
| 99 | pub(crate) fn remark_after_space(&self) -> Option<&'ln str> { | |
| 100 | self.rest_after_separator(b' ') | |
| 101 | } | |
| 102 | ||
| 103 | /// Allows callers to print `raw_directive` if necessary, | |
| 104 | /// without accessing the field directly. | |
| 105 | pub(crate) fn display(&self) -> impl fmt::Display { | |
| 106 | self.raw_directive | |
| 107 | } | |
| 108 | } |
src/tools/compiletest/src/directives/needs.rs+4-11| ... | ... | @@ -181,17 +181,10 @@ pub(super) fn handle_needs( |
| 181 | 181 | }, |
| 182 | 182 | ]; |
| 183 | 183 | |
| 184 | let &DirectiveLine { raw_directive: ln, .. } = ln; | |
| 184 | let &DirectiveLine { name, .. } = ln; | |
| 185 | 185 | |
| 186 | let (name, rest) = match ln.split_once([':', ' ']) { | |
| 187 | Some((name, rest)) => (name, Some(rest)), | |
| 188 | None => (ln, None), | |
| 189 | }; | |
| 190 | ||
| 191 | // FIXME(jieyouxu): tighten up this parsing to reject using both `:` and ` ` as means to | |
| 192 | // delineate value. | |
| 193 | 186 | if name == "needs-target-has-atomic" { |
| 194 | let Some(rest) = rest else { | |
| 187 | let Some(rest) = ln.value_after_colon() else { | |
| 195 | 188 | return IgnoreDecision::Error { |
| 196 | 189 | message: "expected `needs-target-has-atomic` to have a comma-separated list of atomic widths".to_string(), |
| 197 | 190 | }; |
| ... | ... | @@ -233,7 +226,7 @@ pub(super) fn handle_needs( |
| 233 | 226 | |
| 234 | 227 | // FIXME(jieyouxu): share multi-value directive logic with `needs-target-has-atomic` above. |
| 235 | 228 | if name == "needs-crate-type" { |
| 236 | let Some(rest) = rest else { | |
| 229 | let Some(rest) = ln.value_after_colon() else { | |
| 237 | 230 | return IgnoreDecision::Error { |
| 238 | 231 | message: |
| 239 | 232 | "expected `needs-crate-type` to have a comma-separated list of crate types" |
| ... | ... | @@ -292,7 +285,7 @@ pub(super) fn handle_needs( |
| 292 | 285 | break; |
| 293 | 286 | } else { |
| 294 | 287 | return IgnoreDecision::Ignore { |
| 295 | reason: if let Some(comment) = rest { | |
| 288 | reason: if let Some(comment) = ln.remark_after_space() { | |
| 296 | 289 | format!("{} ({})", need.ignore_reason, comment.trim()) |
| 297 | 290 | } else { |
| 298 | 291 | need.ignore_reason.into() |
src/tools/compiletest/src/directives/tests.rs+6-5| ... | ... | @@ -3,12 +3,11 @@ use std::io::Read; |
| 3 | 3 | use camino::Utf8Path; |
| 4 | 4 | use semver::Version; |
| 5 | 5 | |
| 6 | use super::{ | |
| 6 | use crate::common::{Config, Debugger, TestMode}; | |
| 7 | use crate::directives::{ | |
| 7 | 8 | DirectivesCache, EarlyProps, Edition, EditionRange, extract_llvm_version, |
| 8 | extract_version_range, iter_directives, parse_normalize_rule, | |
| 9 | extract_version_range, iter_directives, line_directive, parse_edition, parse_normalize_rule, | |
| 9 | 10 | }; |
| 10 | use crate::common::{Config, Debugger, TestMode}; | |
| 11 | use crate::directives::parse_edition; | |
| 12 | 11 | use crate::executor::{CollectedTestDesc, ShouldPanic}; |
| 13 | 12 | |
| 14 | 13 | fn make_test_description<R: Read>( |
| ... | ... | @@ -955,7 +954,9 @@ fn test_needs_target_std() { |
| 955 | 954 | |
| 956 | 955 | fn parse_edition_range(line: &str) -> Option<EditionRange> { |
| 957 | 956 | let config = cfg().build(); |
| 958 | let line = super::DirectiveLine { line_number: 0, revision: None, raw_directive: line }; | |
| 957 | ||
| 958 | let line_with_comment = format!("//@ {line}"); | |
| 959 | let line = line_directive(0, &line_with_comment).unwrap(); | |
| 959 | 960 | |
| 960 | 961 | super::parse_edition_range(&config, &line, "tmp.rs".into()) |
| 961 | 962 | } |
tests/ui/fn/invalid-sugg-for-unused-fn-arg-147303.rs created+13| ... | ... | @@ -0,0 +1,13 @@ |
| 1 | // Regression test for <https://github.com/rust-lang/rust/issues/147303>. | |
| 2 | ||
| 3 | #![deny(unused_assignments, unused_variables)] | |
| 4 | ||
| 5 | mod m1 { | |
| 6 | const _MAX_FMTVER_X1X_EVENTNUM: i32 = 0; | |
| 7 | } | |
| 8 | ||
| 9 | mod m2 { | |
| 10 | fn fun(rough: i32) {} //~ERROR unused variable | |
| 11 | } | |
| 12 | ||
| 13 | fn main() {} |
tests/ui/fn/invalid-sugg-for-unused-fn-arg-147303.stderr created+14| ... | ... | @@ -0,0 +1,14 @@ |
| 1 | error: unused variable: `rough` | |
| 2 | --> $DIR/invalid-sugg-for-unused-fn-arg-147303.rs:10:12 | |
| 3 | | | |
| 4 | LL | fn fun(rough: i32) {} | |
| 5 | | ^^^^^ help: if this is intentional, prefix it with an underscore: `_rough` | |
| 6 | | | |
| 7 | note: the lint level is defined here | |
| 8 | --> $DIR/invalid-sugg-for-unused-fn-arg-147303.rs:3:29 | |
| 9 | | | |
| 10 | LL | #![deny(unused_assignments, unused_variables)] | |
| 11 | | ^^^^^^^^^^^^^^^^ | |
| 12 | ||
| 13 | error: aborting due to 1 previous error | |
| 14 |
tests/ui/lint/non-snake-case/lint-uppercase-variables.stderr+1-10| ... | ... | @@ -58,16 +58,7 @@ warning: unused variable: `Foo` |
| 58 | 58 | --> $DIR/lint-uppercase-variables.rs:33:17 |
| 59 | 59 | | |
| 60 | 60 | LL | fn in_param(Foo: foo::Foo) {} |
| 61 | | ^^^ | |
| 62 | | | |
| 63 | help: if this is intentional, prefix it with an underscore | |
| 64 | | | |
| 65 | LL | fn in_param(_Foo: foo::Foo) {} | |
| 66 | | + | |
| 67 | help: you might have meant to pattern match on the similarly named variant `Foo` | |
| 68 | | | |
| 69 | LL | fn in_param(foo::Foo::Foo: foo::Foo) {} | |
| 70 | | ++++++++++ | |
| 61 | | ^^^ help: if this is intentional, prefix it with an underscore: `_Foo` | |
| 71 | 62 | |
| 72 | 63 | error: structure field `X` should have a snake case name |
| 73 | 64 | --> $DIR/lint-uppercase-variables.rs:10:5 |