Merge from rustc
This commit is contained in:
commit
c44bc10b67
291 changed files with 4610 additions and 2821 deletions
|
|
@ -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!()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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!()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -49,7 +49,7 @@ struct Lt2<'a> {
|
|||
|
||||
impl<'a> Lt2<'a> {
|
||||
// The lifetime is different, but that’s irrelevant; see issue #734.
|
||||
pub fn new(s: &str) -> Lt2 {
|
||||
pub fn new(s: &str) -> Lt2<'_> {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@
|
|||
clippy::unnecessary_wraps,
|
||||
dyn_drop,
|
||||
clippy::get_first,
|
||||
elided_named_lifetimes
|
||||
mismatched_lifetime_syntaxes,
|
||||
)]
|
||||
|
||||
extern crate proc_macros;
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@
|
|||
clippy::unnecessary_wraps,
|
||||
dyn_drop,
|
||||
clippy::get_first,
|
||||
elided_named_lifetimes
|
||||
mismatched_lifetime_syntaxes,
|
||||
)]
|
||||
|
||||
extern crate proc_macros;
|
||||
|
|
|
|||
|
|
@ -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!()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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 }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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`
|
||||
|
|
|
|||
|
|
@ -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>`
|
||||
|
|
|
|||
|
|
@ -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>`
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
}
|
||||
|
||||
|
|
|
|||
232
src/tools/jsondocck/src/directive.rs
Normal file
232
src/tools/jsondocck/src/directive.rs
Normal 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)))
|
||||
}
|
||||
}
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
use crate::Command;
|
||||
use crate::Directive;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct CkError {
|
||||
pub message: String,
|
||||
pub command: Command,
|
||||
pub directive: Directive,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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(()) }
|
||||
}
|
||||
|
|
|
|||
25
src/tools/miri/tests/fail/async-shared-mutable.rs
Normal file
25
src/tools/miri/tests/fail/async-shared-mutable.rs
Normal 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);
|
||||
}
|
||||
43
src/tools/miri/tests/fail/async-shared-mutable.stack.stderr
Normal file
43
src/tools/miri/tests/fail/async-shared-mutable.stack.stderr
Normal 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
|
||||
|
||||
47
src/tools/miri/tests/fail/async-shared-mutable.tree.stderr
Normal file
47
src/tools/miri/tests/fail/async-shared-mutable.tree.stderr
Normal 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
|
||||
|
||||
16
src/tools/miri/tests/pass/both_borrows/unsafe_pinned.rs
Normal file
16
src/tools/miri/tests/pass/both_borrows/unsafe_pinned.rs
Normal 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);
|
||||
}
|
||||
|
|
@ -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 }
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue