Merge from rustc

This commit is contained in:
The Miri Cronjob Bot 2025-06-06 05:02:49 +00:00
commit c44bc10b67
291 changed files with 4610 additions and 2821 deletions

View file

@ -77,11 +77,11 @@ fn main() {
struct NoIntoIter();
impl NoIntoIter {
fn iter(&self) -> slice::Iter<u8> {
fn iter(&self) -> slice::Iter<'_, u8> {
unimplemented!()
}
fn iter_mut(&mut self) -> slice::IterMut<u8> {
fn iter_mut(&mut self) -> slice::IterMut<'_, u8> {
unimplemented!()
}
}

View file

@ -77,11 +77,11 @@ fn main() {
struct NoIntoIter();
impl NoIntoIter {
fn iter(&self) -> slice::Iter<u8> {
fn iter(&self) -> slice::Iter<'_, u8> {
unimplemented!()
}
fn iter_mut(&mut self) -> slice::IterMut<u8> {
fn iter_mut(&mut self) -> slice::IterMut<'_, u8> {
unimplemented!()
}
}

View file

@ -8,7 +8,7 @@ fn main() {
struct Unrelated(&'static [u8]);
impl Unrelated {
fn next(&self) -> std::slice::Iter<u8> {
fn next(&self) -> std::slice::Iter<'_, u8> {
self.0.iter()
}
}

View file

@ -71,7 +71,7 @@ impl S {
struct S2([u8]);
impl S2 {
fn iter(&self) -> core::slice::Iter<u8> {
fn iter(&self) -> core::slice::Iter<'_, u8> {
self.0.iter()
}
}

View file

@ -49,7 +49,7 @@ struct Lt2<'a> {
impl<'a> Lt2<'a> {
// The lifetime is different, but thats irrelevant; see issue #734.
pub fn new(s: &str) -> Lt2 {
pub fn new(s: &str) -> Lt2<'_> {
unimplemented!()
}
}

View file

@ -10,7 +10,7 @@
clippy::unnecessary_wraps,
dyn_drop,
clippy::get_first,
elided_named_lifetimes
mismatched_lifetime_syntaxes,
)]
extern crate proc_macros;

View file

@ -10,7 +10,7 @@
clippy::unnecessary_wraps,
dyn_drop,
clippy::get_first,
elided_named_lifetimes
mismatched_lifetime_syntaxes,
)]
extern crate proc_macros;

View file

@ -312,7 +312,7 @@ mod issue_9218 {
// Inferred to be `&'a str`, afaik.
fn cow_good_ret_ty<'a>(input: &'a Cow<'a, str>) -> &str {
//~^ ERROR: elided lifetime has a name
//~^ ERROR: lifetime flowing from input to output with different syntax
todo!()
}
}

View file

@ -1,12 +1,3 @@
error: elided lifetime has a name
--> tests/ui/ptr_arg.rs:314:56
|
LL | fn cow_good_ret_ty<'a>(input: &'a Cow<'a, str>) -> &str {
| -- lifetime `'a` declared here ^ this elided lifetime gets resolved as `'a`
|
= note: `-D elided-named-lifetimes` implied by `-D warnings`
= help: to override `-D warnings` add `#[allow(elided_named_lifetimes)]`
error: writing `&Vec` instead of `&[_]` involves a new object where a slice will do
--> tests/ui/ptr_arg.rs:13:14
|
@ -240,5 +231,21 @@ error: writing `&String` instead of `&str` involves a new object where a slice w
LL | fn good(v1: &String, v2: &String) {
| ^^^^^^^ help: change this to: `&str`
error: lifetime flowing from input to output with different syntax can be confusing
--> tests/ui/ptr_arg.rs:314:36
|
LL | fn cow_good_ret_ty<'a>(input: &'a Cow<'a, str>) -> &str {
| ^^ ^^ ---- the lifetime gets resolved as `'a`
| | |
| | these lifetimes flow to the output
| these lifetimes flow to the output
|
= note: `-D mismatched-lifetime-syntaxes` implied by `-D warnings`
= help: to override `-D warnings` add `#[allow(mismatched_lifetime_syntaxes)]`
help: one option is to consistently use `'a`
|
LL | fn cow_good_ret_ty<'a>(input: &'a Cow<'a, str>) -> &'a str {
| ++
error: aborting due to 27 previous errors

View file

@ -191,7 +191,7 @@ struct CounterWrapper<'a> {
}
impl<'a> CounterWrapper<'a> {
fn new(counter: &Counter) -> CounterWrapper {
fn new(counter: &Counter) -> CounterWrapper<'_> {
counter.i.fetch_add(1, Ordering::Relaxed);
CounterWrapper { counter }
}
@ -204,7 +204,7 @@ impl<'a> Drop for CounterWrapper<'a> {
}
impl Counter {
fn temp_increment(&self) -> Vec<CounterWrapper> {
fn temp_increment(&self) -> Vec<CounterWrapper<'_>> {
vec![CounterWrapper::new(self), CounterWrapper::new(self)]
}
}
@ -480,7 +480,7 @@ impl StateWithBoxedMutexGuard {
fn new() -> StateWithBoxedMutexGuard {
StateWithBoxedMutexGuard { u: Mutex::new(42) }
}
fn lock(&self) -> Box<MutexGuard<u64>> {
fn lock(&self) -> Box<MutexGuard<'_, u64>> {
Box::new(self.u.lock().unwrap())
}
}
@ -507,7 +507,7 @@ impl StateStringWithBoxedMutexGuard {
s: Mutex::new("A String".to_owned()),
}
}
fn lock(&self) -> Box<MutexGuard<String>> {
fn lock(&self) -> Box<MutexGuard<'_, String>> {
Box::new(self.s.lock().unwrap())
}
}
@ -686,11 +686,11 @@ struct Guard<'a, T>(MutexGuard<'a, T>);
struct Ref<'a, T>(&'a T);
impl<'a, T> Guard<'a, T> {
fn guard(&self) -> &MutexGuard<T> {
fn guard(&self) -> &MutexGuard<'_, T> {
&self.0
}
fn guard_ref(&self) -> Ref<MutexGuard<T>> {
fn guard_ref(&self) -> Ref<'_, MutexGuard<'_, T>> {
Ref(&self.0)
}

View file

@ -41,6 +41,7 @@ fn good_return_explicit_lt_ref<'a>(foo: &'a Foo) -> &'a u32 {
&foo.0
}
#[allow(mismatched_lifetime_syntaxes)]
fn good_return_implicit_lt_struct(foo: &Foo) -> FooRef {
FooRef { foo }
}

View file

@ -41,6 +41,7 @@ fn good_return_explicit_lt_ref<'a>(foo: &'a Foo) -> &'a u32 {
&foo.0
}
#[allow(mismatched_lifetime_syntaxes)]
fn good_return_implicit_lt_struct(foo: &Foo) -> FooRef {
FooRef { foo }
}

View file

@ -1,5 +1,5 @@
error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte)
--> tests/ui/trivially_copy_pass_by_ref.rs:53:11
--> tests/ui/trivially_copy_pass_by_ref.rs:54:11
|
LL | fn bad(x: &u32, y: &Foo, z: &Baz) {}
| ^^^^ help: consider passing by value instead: `u32`
@ -11,103 +11,103 @@ LL | #![deny(clippy::trivially_copy_pass_by_ref)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte)
--> tests/ui/trivially_copy_pass_by_ref.rs:53:20
--> tests/ui/trivially_copy_pass_by_ref.rs:54:20
|
LL | fn bad(x: &u32, y: &Foo, z: &Baz) {}
| ^^^^ help: consider passing by value instead: `Foo`
error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte)
--> tests/ui/trivially_copy_pass_by_ref.rs:53:29
--> tests/ui/trivially_copy_pass_by_ref.rs:54:29
|
LL | fn bad(x: &u32, y: &Foo, z: &Baz) {}
| ^^^^ help: consider passing by value instead: `Baz`
error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte)
--> tests/ui/trivially_copy_pass_by_ref.rs:63:12
--> tests/ui/trivially_copy_pass_by_ref.rs:64:12
|
LL | fn bad(&self, x: &u32, y: &Foo, z: &Baz) {}
| ^^^^^ help: consider passing by value instead: `self`
error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte)
--> tests/ui/trivially_copy_pass_by_ref.rs:63:22
--> tests/ui/trivially_copy_pass_by_ref.rs:64:22
|
LL | fn bad(&self, x: &u32, y: &Foo, z: &Baz) {}
| ^^^^ help: consider passing by value instead: `u32`
error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte)
--> tests/ui/trivially_copy_pass_by_ref.rs:63:31
--> tests/ui/trivially_copy_pass_by_ref.rs:64:31
|
LL | fn bad(&self, x: &u32, y: &Foo, z: &Baz) {}
| ^^^^ help: consider passing by value instead: `Foo`
error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte)
--> tests/ui/trivially_copy_pass_by_ref.rs:63:40
--> tests/ui/trivially_copy_pass_by_ref.rs:64:40
|
LL | fn bad(&self, x: &u32, y: &Foo, z: &Baz) {}
| ^^^^ help: consider passing by value instead: `Baz`
error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte)
--> tests/ui/trivially_copy_pass_by_ref.rs:69:16
--> tests/ui/trivially_copy_pass_by_ref.rs:70:16
|
LL | fn bad2(x: &u32, y: &Foo, z: &Baz) {}
| ^^^^ help: consider passing by value instead: `u32`
error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte)
--> tests/ui/trivially_copy_pass_by_ref.rs:69:25
--> tests/ui/trivially_copy_pass_by_ref.rs:70:25
|
LL | fn bad2(x: &u32, y: &Foo, z: &Baz) {}
| ^^^^ help: consider passing by value instead: `Foo`
error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte)
--> tests/ui/trivially_copy_pass_by_ref.rs:69:34
--> tests/ui/trivially_copy_pass_by_ref.rs:70:34
|
LL | fn bad2(x: &u32, y: &Foo, z: &Baz) {}
| ^^^^ help: consider passing by value instead: `Baz`
error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte)
--> tests/ui/trivially_copy_pass_by_ref.rs:74:35
--> tests/ui/trivially_copy_pass_by_ref.rs:75:35
|
LL | fn bad_issue7518(self, other: &Self) {}
| ^^^^^ help: consider passing by value instead: `Self`
error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte)
--> tests/ui/trivially_copy_pass_by_ref.rs:87:16
--> tests/ui/trivially_copy_pass_by_ref.rs:88:16
|
LL | fn bad2(x: &u32, y: &Foo, z: &Baz) {}
| ^^^^ help: consider passing by value instead: `u32`
error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte)
--> tests/ui/trivially_copy_pass_by_ref.rs:87:25
--> tests/ui/trivially_copy_pass_by_ref.rs:88:25
|
LL | fn bad2(x: &u32, y: &Foo, z: &Baz) {}
| ^^^^ help: consider passing by value instead: `Foo`
error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte)
--> tests/ui/trivially_copy_pass_by_ref.rs:87:34
--> tests/ui/trivially_copy_pass_by_ref.rs:88:34
|
LL | fn bad2(x: &u32, y: &Foo, z: &Baz) {}
| ^^^^ help: consider passing by value instead: `Baz`
error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte)
--> tests/ui/trivially_copy_pass_by_ref.rs:94:33
--> tests/ui/trivially_copy_pass_by_ref.rs:95:33
|
LL | fn trait_method(&self, foo: &Foo);
| ^^^^ help: consider passing by value instead: `Foo`
error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte)
--> tests/ui/trivially_copy_pass_by_ref.rs:132:21
--> tests/ui/trivially_copy_pass_by_ref.rs:133:21
|
LL | fn foo_never(x: &i32) {
| ^^^^ help: consider passing by value instead: `i32`
error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte)
--> tests/ui/trivially_copy_pass_by_ref.rs:138:15
--> tests/ui/trivially_copy_pass_by_ref.rs:139:15
|
LL | fn foo(x: &i32) {
| ^^^^ help: consider passing by value instead: `i32`
error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte)
--> tests/ui/trivially_copy_pass_by_ref.rs:164:36
--> tests/ui/trivially_copy_pass_by_ref.rs:165:36
|
LL | fn unrelated_lifetimes<'a, 'b>(_x: &'a u32, y: &'b u32) -> &'b u32 {
| ^^^^^^^ help: consider passing by value instead: `u32`

View file

@ -69,7 +69,7 @@ mod lifetimes {
impl<'a> Foo<'a> {
// Cannot use `Self` as return type, because the function is actually `fn foo<'b>(s: &'b str) ->
// Foo<'b>`
fn foo(s: &str) -> Foo {
fn foo(s: &str) -> Foo<'_> {
Foo { foo_str: s }
}
// cannot replace with `Self`, because that's `Foo<'a>`

View file

@ -69,7 +69,7 @@ mod lifetimes {
impl<'a> Foo<'a> {
// Cannot use `Self` as return type, because the function is actually `fn foo<'b>(s: &'b str) ->
// Foo<'b>`
fn foo(s: &str) -> Foo {
fn foo(s: &str) -> Foo<'_> {
Foo { foo_str: s }
}
// cannot replace with `Self`, because that's `Foo<'a>`

View file

@ -4,7 +4,7 @@ use getopts::Options;
pub struct Config {
/// The directory documentation output was generated in
pub doc_dir: String,
/// The file documentation was generated for, with docck commands to check
/// The file documentation was generated for, with docck directives to check
pub template: String,
}

View file

@ -0,0 +1,232 @@
use std::borrow::Cow;
use serde_json::Value;
use crate::cache::Cache;
#[derive(Debug)]
pub struct Directive {
pub kind: DirectiveKind,
pub path: String,
pub lineno: usize,
}
#[derive(Debug)]
pub enum DirectiveKind {
/// `//@ has <path>`
///
/// Checks the path exists.
HasPath,
/// `//@ has <path> <value>`
///
/// Check one thing at the path is equal to the value.
HasValue { value: String },
/// `//@ !has <path>`
///
/// Checks the path doesn't exist.
HasNotPath,
/// `//@ !has <path> <value>`
///
/// Checks the path exists, but doesn't have the given value.
HasNotValue { value: String },
/// `//@ is <path> <value>`
///
/// Check the path is the given value.
Is { value: String },
/// `//@ is <path> <value> <value>...`
///
/// Check that the path matches to exactly every given value.
IsMany { values: Vec<String> },
/// `//@ !is <path> <value>`
///
/// Check the path isn't the given value.
IsNot { value: String },
/// `//@ count <path> <value>`
///
/// Check the path has the expected number of matches.
CountIs { expected: usize },
/// `//@ set <name> = <path>`
Set { variable: String },
}
impl DirectiveKind {
/// Returns both the kind and the path.
///
/// Returns `None` if the directive isn't from jsondocck (e.g. from compiletest).
pub fn parse<'a>(
directive_name: &str,
negated: bool,
args: &'a [String],
) -> Option<(Self, &'a str)> {
let kind = match (directive_name, negated) {
("count", false) => {
assert_eq!(args.len(), 2);
let expected = args[1].parse().expect("invalid number for `count`");
Self::CountIs { expected }
}
("ismany", false) => {
// FIXME: Make this >= 3, and migrate len(values)==1 cases to @is
assert!(args.len() >= 2, "Not enough args to `ismany`");
let values = args[1..].to_owned();
Self::IsMany { values }
}
("is", false) => {
assert_eq!(args.len(), 2);
Self::Is { value: args[1].clone() }
}
("is", true) => {
assert_eq!(args.len(), 2);
Self::IsNot { value: args[1].clone() }
}
("set", false) => {
assert_eq!(args.len(), 3);
assert_eq!(args[1], "=");
return Some((Self::Set { variable: args[0].clone() }, &args[2]));
}
("has", false) => match args {
[_path] => Self::HasPath,
[_path, value] => Self::HasValue { value: value.clone() },
_ => panic!("`//@ has` must have 2 or 3 arguments, but got {args:?}"),
},
("has", true) => match args {
[_path] => Self::HasNotPath,
[_path, value] => Self::HasNotValue { value: value.clone() },
_ => panic!("`//@ !has` must have 2 or 3 arguments, but got {args:?}"),
},
// Ignore compiletest directives, like //@ edition
(_, false) if KNOWN_DIRECTIVE_NAMES.contains(&directive_name) => {
return None;
}
_ => {
panic!("Invalid directive `//@ {}{directive_name}`", if negated { "!" } else { "" })
}
};
Some((kind, &args[0]))
}
}
impl Directive {
/// Performs the actual work of ensuring a directive passes.
pub fn check(&self, cache: &mut Cache) -> Result<(), String> {
let matches = cache.select(&self.path);
match &self.kind {
DirectiveKind::HasPath => {
if matches.is_empty() {
return Err("matched to no values".to_owned());
}
}
DirectiveKind::HasNotPath => {
if !matches.is_empty() {
return Err(format!("matched to {matches:?}, but wanted no matches"));
}
}
DirectiveKind::HasValue { value } => {
let want_value = string_to_value(value, cache);
if !matches.contains(&want_value.as_ref()) {
return Err(format!(
"matched to {matches:?}, which didn't contain {want_value:?}"
));
}
}
DirectiveKind::HasNotValue { value } => {
let wantnt_value = string_to_value(value, cache);
if matches.contains(&wantnt_value.as_ref()) {
return Err(format!(
"matched to {matches:?}, which contains unwanted {wantnt_value:?}"
));
} else if matches.is_empty() {
return Err(format!(
"got no matches, but expected some matched (not containing {wantnt_value:?}"
));
}
}
DirectiveKind::Is { value } => {
let want_value = string_to_value(value, cache);
let matched = get_one(&matches)?;
if matched != want_value.as_ref() {
return Err(format!("matched to {matched:?} but want {want_value:?}"));
}
}
DirectiveKind::IsNot { value } => {
let wantnt_value = string_to_value(value, cache);
let matched = get_one(&matches)?;
if matched == wantnt_value.as_ref() {
return Err(format!("got value {wantnt_value:?}, but want anything else"));
}
}
DirectiveKind::IsMany { values } => {
// Serde json doesn't implement Ord or Hash for Value, so we must
// use a Vec here. While in theory that makes setwize equality
// O(n^2), in practice n will never be large enough to matter.
let expected_values =
values.iter().map(|v| string_to_value(v, cache)).collect::<Vec<_>>();
if expected_values.len() != matches.len() {
return Err(format!(
"Expected {} values, but matched to {} values ({:?})",
expected_values.len(),
matches.len(),
matches
));
};
for got_value in matches {
if !expected_values.iter().any(|exp| &**exp == got_value) {
return Err(format!("has match {got_value:?}, which was not expected",));
}
}
}
DirectiveKind::CountIs { expected } => {
if *expected != matches.len() {
return Err(format!(
"matched to `{matches:?}` with length {}, but expected length {expected}",
matches.len(),
));
}
}
DirectiveKind::Set { variable } => {
let value = get_one(&matches)?;
let r = cache.variables.insert(variable.to_owned(), value.clone());
assert!(r.is_none(), "name collision: {variable:?} is duplicated");
}
}
Ok(())
}
}
fn get_one<'a>(matches: &[&'a Value]) -> Result<&'a Value, String> {
match matches {
[] => Err("matched to no values".to_owned()),
[matched] => Ok(matched),
_ => Err(format!("matched to multiple values {matches:?}, but want exactly 1")),
}
}
// FIXME: This setup is temporary until we figure out how to improve this situation.
// See <https://github.com/rust-lang/rust/issues/125813#issuecomment-2141953780>.
include!(concat!(env!("CARGO_MANIFEST_DIR"), "/../compiletest/src/directive-list.rs"));
fn string_to_value<'a>(s: &str, cache: &'a Cache) -> Cow<'a, Value> {
if s.starts_with("$") {
Cow::Borrowed(&cache.variables.get(&s[1..]).unwrap_or_else(|| {
// FIXME(adotinthevoid): Show line number
panic!("No variable: `{}`. Current state: `{:?}`", &s[1..], cache.variables)
}))
} else {
Cow::Owned(serde_json::from_str(s).expect(&format!("Cannot convert `{}` to json", s)))
}
}

View file

@ -1,7 +1,7 @@
use crate::Command;
use crate::Directive;
#[derive(Debug)]
pub struct CkError {
pub message: String,
pub command: Command,
pub directive: Directive,
}

View file

@ -1,17 +1,17 @@
use std::borrow::Cow;
use std::process::ExitCode;
use std::sync::LazyLock;
use std::{env, fs};
use regex::{Regex, RegexBuilder};
use serde_json::Value;
mod cache;
mod config;
mod directive;
mod error;
use cache::Cache;
use config::parse_config;
use directive::{Directive, DirectiveKind};
use error::CkError;
fn main() -> ExitCode {
@ -19,14 +19,14 @@ fn main() -> ExitCode {
let mut failed = Vec::new();
let mut cache = Cache::new(&config);
let Ok(commands) = get_commands(&config.template) else {
let Ok(directives) = get_directives(&config.template) else {
eprintln!("Jsondocck failed for {}", &config.template);
return ExitCode::FAILURE;
};
for command in commands {
if let Err(message) = check_command(&command, &mut cache) {
failed.push(CkError { command, message });
for directive in directives {
if let Err(message) = directive.check(&mut cache) {
failed.push(CkError { directive, message });
}
}
@ -34,130 +34,20 @@ fn main() -> ExitCode {
ExitCode::SUCCESS
} else {
for i in failed {
eprintln!("{}:{}, command failed", config.template, i.command.lineno);
eprintln!("{}:{}, directive failed", config.template, i.directive.lineno);
eprintln!("{}", i.message)
}
ExitCode::FAILURE
}
}
#[derive(Debug)]
pub struct Command {
kind: CommandKind,
path: String,
lineno: usize,
}
#[derive(Debug)]
enum CommandKind {
/// `//@ has <path>`
///
/// Checks the path exists.
HasPath,
/// `//@ has <path> <value>`
///
/// Check one thing at the path is equal to the value.
HasValue { value: String },
/// `//@ !has <path>`
///
/// Checks the path doesn't exist.
HasNotPath,
/// `//@ !has <path> <value>`
///
/// Checks the path exists, but doesn't have the given value.
HasNotValue { value: String },
/// `//@ is <path> <value>`
///
/// Check the path is the given value.
Is { value: String },
/// `//@ is <path> <value> <value>...`
///
/// Check that the path matches to exactly every given value.
IsMany { values: Vec<String> },
/// `//@ !is <path> <value>`
///
/// Check the path isn't the given value.
IsNot { value: String },
/// `//@ count <path> <value>`
///
/// Check the path has the expected number of matches.
CountIs { expected: usize },
/// `//@ set <name> = <path>`
Set { variable: String },
}
impl CommandKind {
/// Returns both the kind and the path.
///
/// Returns `None` if the command isn't from jsondocck (e.g. from compiletest).
fn parse<'a>(command_name: &str, negated: bool, args: &'a [String]) -> Option<(Self, &'a str)> {
let kind = match (command_name, negated) {
("count", false) => {
assert_eq!(args.len(), 2);
let expected = args[1].parse().expect("invalid number for `count`");
Self::CountIs { expected }
}
("ismany", false) => {
// FIXME: Make this >= 3, and migrate len(values)==1 cases to @is
assert!(args.len() >= 2, "Not enough args to `ismany`");
let values = args[1..].to_owned();
Self::IsMany { values }
}
("is", false) => {
assert_eq!(args.len(), 2);
Self::Is { value: args[1].clone() }
}
("is", true) => {
assert_eq!(args.len(), 2);
Self::IsNot { value: args[1].clone() }
}
("set", false) => {
assert_eq!(args.len(), 3);
assert_eq!(args[1], "=");
return Some((Self::Set { variable: args[0].clone() }, &args[2]));
}
("has", false) => match args {
[_path] => Self::HasPath,
[_path, value] => Self::HasValue { value: value.clone() },
_ => panic!("`//@ has` must have 2 or 3 arguments, but got {args:?}"),
},
("has", true) => match args {
[_path] => Self::HasNotPath,
[_path, value] => Self::HasNotValue { value: value.clone() },
_ => panic!("`//@ !has` must have 2 or 3 arguments, but got {args:?}"),
},
(_, false) if KNOWN_DIRECTIVE_NAMES.contains(&command_name) => {
return None;
}
_ => {
panic!("Invalid command `//@ {}{command_name}`", if negated { "!" } else { "" })
}
};
Some((kind, &args[0]))
}
}
static LINE_PATTERN: LazyLock<Regex> = LazyLock::new(|| {
RegexBuilder::new(
r#"
^\s*
//@\s+
(?P<negated>!?)
(?P<cmd>[A-Za-z0-9]+(?:-[A-Za-z0-9]+)*)
(?P<directive>[A-Za-z0-9]+(?:-[A-Za-z0-9]+)*)
(?P<args>.*)$
"#,
)
@ -180,16 +70,12 @@ static DEPRECATED_LINE_PATTERN: LazyLock<Regex> = LazyLock::new(|| {
});
fn print_err(msg: &str, lineno: usize) {
eprintln!("Invalid command: {} on line {}", msg, lineno)
eprintln!("Invalid directive: {} on line {}", msg, lineno)
}
// FIXME: This setup is temporary until we figure out how to improve this situation.
// See <https://github.com/rust-lang/rust/issues/125813#issuecomment-2141953780>.
include!(concat!(env!("CARGO_MANIFEST_DIR"), "/../compiletest/src/directive-list.rs"));
/// Get a list of commands from a file.
fn get_commands(template: &str) -> Result<Vec<Command>, ()> {
let mut commands = Vec::new();
/// Get a list of directives from a file.
fn get_directives(template: &str) -> Result<Vec<Directive>, ()> {
let mut directives = Vec::new();
let mut errors = false;
let file = fs::read_to_string(template).unwrap();
@ -197,7 +83,7 @@ fn get_commands(template: &str) -> Result<Vec<Command>, ()> {
let lineno = lineno + 1;
if DEPRECATED_LINE_PATTERN.is_match(line) {
print_err("Deprecated command syntax, replace `// @` with `//@ `", lineno);
print_err("Deprecated directive syntax, replace `// @` with `//@ `", lineno);
errors = true;
continue;
}
@ -215,115 +101,10 @@ fn get_commands(template: &str) -> Result<Vec<Command>, ()> {
continue;
};
if let Some((kind, path)) = CommandKind::parse(&cap["cmd"], negated, &args) {
commands.push(Command { kind, lineno, path: path.to_owned() })
if let Some((kind, path)) = DirectiveKind::parse(&cap["directive"], negated, &args) {
directives.push(Directive { kind, lineno, path: path.to_owned() })
}
}
if !errors { Ok(commands) } else { Err(()) }
}
/// Performs the actual work of ensuring a command passes.
fn check_command(command: &Command, cache: &mut Cache) -> Result<(), String> {
let matches = cache.select(&command.path);
match &command.kind {
CommandKind::HasPath => {
if matches.is_empty() {
return Err("matched to no values".to_owned());
}
}
CommandKind::HasNotPath => {
if !matches.is_empty() {
return Err(format!("matched to {matches:?}, but wanted no matches"));
}
}
CommandKind::HasValue { value } => {
let want_value = string_to_value(value, cache);
if !matches.contains(&want_value.as_ref()) {
return Err(format!("matched to {matches:?}, which didn't contain {want_value:?}"));
}
}
CommandKind::HasNotValue { value } => {
let wantnt_value = string_to_value(value, cache);
if matches.contains(&wantnt_value.as_ref()) {
return Err(format!(
"matched to {matches:?}, which contains unwanted {wantnt_value:?}"
));
} else if matches.is_empty() {
return Err(format!(
"got no matches, but expected some matched (not containing {wantnt_value:?}"
));
}
}
CommandKind::Is { value } => {
let want_value = string_to_value(value, cache);
let matched = get_one(&matches)?;
if matched != want_value.as_ref() {
return Err(format!("matched to {matched:?} but want {want_value:?}"));
}
}
CommandKind::IsNot { value } => {
let wantnt_value = string_to_value(value, cache);
let matched = get_one(&matches)?;
if matched == wantnt_value.as_ref() {
return Err(format!("got value {wantnt_value:?}, but want anything else"));
}
}
CommandKind::IsMany { values } => {
// Serde json doesn't implement Ord or Hash for Value, so we must
// use a Vec here. While in theory that makes setwize equality
// O(n^2), in practice n will never be large enough to matter.
let expected_values =
values.iter().map(|v| string_to_value(v, cache)).collect::<Vec<_>>();
if expected_values.len() != matches.len() {
return Err(format!(
"Expected {} values, but matched to {} values ({:?})",
expected_values.len(),
matches.len(),
matches
));
};
for got_value in matches {
if !expected_values.iter().any(|exp| &**exp == got_value) {
return Err(format!("has match {got_value:?}, which was not expected",));
}
}
}
CommandKind::CountIs { expected } => {
if *expected != matches.len() {
return Err(format!(
"matched to `{matches:?}` with length {}, but expected length {expected}",
matches.len(),
));
}
}
CommandKind::Set { variable } => {
let value = get_one(&matches)?;
let r = cache.variables.insert(variable.to_owned(), value.clone());
assert!(r.is_none(), "name collision: {variable:?} is duplicated");
}
}
Ok(())
}
fn get_one<'a>(matches: &[&'a Value]) -> Result<&'a Value, String> {
match matches {
[] => Err("matched to no values".to_owned()),
[matched] => Ok(matched),
_ => Err(format!("matched to multiple values {matches:?}, but want exactly 1")),
}
}
fn string_to_value<'a>(s: &str, cache: &'a Cache) -> Cow<'a, Value> {
if s.starts_with("$") {
Cow::Borrowed(&cache.variables.get(&s[1..]).unwrap_or_else(|| {
// FIXME(adotinthevoid): Show line number
panic!("No variable: `{}`. Current state: `{:?}`", &s[1..], cache.variables)
}))
} else {
Cow::Owned(serde_json::from_str(s).expect(&format!("Cannot convert `{}` to json", s)))
}
if !errors { Ok(directives) } else { Err(()) }
}

View file

@ -0,0 +1,25 @@
//! FIXME: This test should pass! However, `async fn` does not yet use `UnsafePinned`.
//! This is a regression test for <https://github.com/rust-lang/rust/issues/137750>:
//! `UnsafePinned` must include the effects of `UnsafeCell`.
//@revisions: stack tree
//@[tree]compile-flags: -Zmiri-tree-borrows
//@normalize-stderr-test: "\[0x[a-fx\d.]+\]" -> "[OFFSET]"
use core::future::Future;
use core::pin::{Pin, pin};
use core::task::{Context, Poll, Waker};
fn main() {
let mut f = pin!(async move {
let x = &mut 0u8;
core::future::poll_fn(move |_| {
*x = 1; //~ERROR: write access
Poll::<()>::Pending
})
.await
});
let mut cx = Context::from_waker(&Waker::noop());
assert_eq!(f.as_mut().poll(&mut cx), Poll::Pending);
let _: Pin<&_> = f.as_ref(); // Or: `f.as_mut().into_ref()`.
assert_eq!(f.as_mut().poll(&mut cx), Poll::Pending);
}

View file

@ -0,0 +1,43 @@
error: Undefined Behavior: attempting a write access using <TAG> at ALLOC[OFFSET], but that tag does not exist in the borrow stack for this location
--> tests/fail/async-shared-mutable.rs:LL:CC
|
LL | *x = 1;
| ^^^^^^
| |
| attempting a write access using <TAG> at ALLOC[OFFSET], but that tag does not exist in the borrow stack for this location
| this error occurs as part of an access at ALLOC[OFFSET]
|
= help: this indicates a potential bug in the program: it performed an invalid operation, but the Stacked Borrows rules it violated are still experimental
= help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/stacked-borrows.md for further information
help: <TAG> was created by a Unique retag at offsets [OFFSET]
--> tests/fail/async-shared-mutable.rs:LL:CC
|
LL | / core::future::poll_fn(move |_| {
LL | | *x = 1;
LL | | Poll::<()>::Pending
LL | | })
LL | | .await
| |______________^
help: <TAG> was later invalidated at offsets [OFFSET] by a SharedReadOnly retag
--> tests/fail/async-shared-mutable.rs:LL:CC
|
LL | let _: Pin<&_> = f.as_ref(); // Or: `f.as_mut().into_ref()`.
| ^^^^^^^^^^
= note: BACKTRACE (of the first span):
= note: inside closure at tests/fail/async-shared-mutable.rs:LL:CC
= note: inside `<std::future::PollFn<{closure@tests/fail/async-shared-mutable.rs:LL:CC}> as std::future::Future>::poll` at RUSTLIB/core/src/future/poll_fn.rs:LL:CC
note: inside closure
--> tests/fail/async-shared-mutable.rs:LL:CC
|
LL | .await
| ^^^^^
note: inside `main`
--> tests/fail/async-shared-mutable.rs:LL:CC
|
LL | assert_eq!(f.as_mut().poll(&mut cx), Poll::Pending);
| ^^^^^^^^^^^^^^^^^^^^^^^^
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
error: aborting due to 1 previous error

View file

@ -0,0 +1,47 @@
error: Undefined Behavior: write access through <TAG> at ALLOC[OFFSET] is forbidden
--> tests/fail/async-shared-mutable.rs:LL:CC
|
LL | *x = 1;
| ^^^^^^ write access through <TAG> at ALLOC[OFFSET] is forbidden
|
= help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental
= help: the accessed tag <TAG> has state Frozen which forbids this child write access
help: the accessed tag <TAG> was created here, in the initial state Reserved
--> tests/fail/async-shared-mutable.rs:LL:CC
|
LL | / core::future::poll_fn(move |_| {
LL | | *x = 1;
LL | | Poll::<()>::Pending
LL | | })
LL | | .await
| |______________^
help: the accessed tag <TAG> later transitioned to Active due to a child write access at offsets [OFFSET]
--> tests/fail/async-shared-mutable.rs:LL:CC
|
LL | *x = 1;
| ^^^^^^
= help: this transition corresponds to the first write to a 2-phase borrowed mutable reference
help: the accessed tag <TAG> later transitioned to Frozen due to a reborrow (acting as a foreign read access) at offsets [OFFSET]
--> tests/fail/async-shared-mutable.rs:LL:CC
|
LL | let _: Pin<&_> = f.as_ref(); // Or: `f.as_mut().into_ref()`.
| ^^^^^^^^^^
= help: this transition corresponds to a loss of write permissions
= note: BACKTRACE (of the first span):
= note: inside closure at tests/fail/async-shared-mutable.rs:LL:CC
= note: inside `<std::future::PollFn<{closure@tests/fail/async-shared-mutable.rs:LL:CC}> as std::future::Future>::poll` at RUSTLIB/core/src/future/poll_fn.rs:LL:CC
note: inside closure
--> tests/fail/async-shared-mutable.rs:LL:CC
|
LL | .await
| ^^^^^
note: inside `main`
--> tests/fail/async-shared-mutable.rs:LL:CC
|
LL | assert_eq!(f.as_mut().poll(&mut cx), Poll::Pending);
| ^^^^^^^^^^^^^^^^^^^^^^^^
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
error: aborting due to 1 previous error

View file

@ -0,0 +1,16 @@
//@revisions: stack tree
//@[tree]compile-flags: -Zmiri-tree-borrows
#![feature(unsafe_pinned)]
use std::pin::UnsafePinned;
fn mutate(x: &UnsafePinned<i32>) {
let ptr = x as *const _ as *mut i32;
unsafe { ptr.write(42) };
}
fn main() {
let x = UnsafePinned::new(0);
mutate(&x);
assert_eq!(x.into_inner(), 42);
}

View file

@ -19,11 +19,11 @@ fn fat_ptr_via_local(a: &[u8]) -> &[u8] {
x
}
fn fat_ptr_from_struct(s: FatPtrContainer) -> &[u8] {
fn fat_ptr_from_struct(s: FatPtrContainer<'_>) -> &[u8] {
s.ptr
}
fn fat_ptr_to_struct(a: &[u8]) -> FatPtrContainer {
fn fat_ptr_to_struct(a: &[u8]) -> FatPtrContainer<'_> {
FatPtrContainer { ptr: a }
}