Merge pull request #541 from schungx/master

Minor fixes.
This commit is contained in:
Stephen Chung 2022-03-18 17:46:03 +08:00 committed by GitHub
commit 0603b2b1cc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 123 additions and 137 deletions

1
.gitattributes vendored Normal file
View File

@ -0,0 +1 @@
*.rhai linguist-language=JavaScript

View File

@ -264,7 +264,7 @@ impl Module {
// NB: sub-modules must have their new items for exporting generated in depth-first order
// to avoid issues caused by re-parsing them
let inner_modules = sub_modules
.drain(..)
.into_iter()
.try_fold::<_, _, Result<_, syn::Error>>(Vec::new(), |mut acc, m| {
acc.push(m.generate_inner()?);
Ok(acc)

View File

@ -1,8 +1,8 @@
error[E0599]: the method `map` exists for type `bool`, but its trait bounds were not satisfied
error[E0599]: `bool` is not an iterator
--> ui_tests/export_fn_raw_return.rs:10:33
|
10 | pub fn test_fn(input: Point) -> bool {
| ^^^^ method cannot be called on `bool` due to unsatisfied trait bounds
| ^^^^ `bool` is not an iterator
|
= note: the following trait bounds were not satisfied:
`bool: std::iter::Iterator`

View File

@ -1,8 +1,8 @@
error[E0599]: the method `map` exists for type `bool`, but its trait bounds were not satisfied
error[E0599]: `bool` is not an iterator
--> ui_tests/export_mod_raw_return.rs:12:33
|
12 | pub fn test_fn(input: Point) -> bool {
| ^^^^ method cannot be called on `bool` due to unsatisfied trait bounds
| ^^^^ `bool` is not an iterator
|
= note: the following trait bounds were not satisfied:
`bool: std::iter::Iterator`

View File

@ -1,6 +1,8 @@
error[E0277]: the trait bound `NonClonable: Clone` is not satisfied
--> ui_tests/non_clonable.rs:11:23
|
10 | #[export_fn]
| ------------ in this procedural macro expansion
11 | pub fn test_fn(input: NonClonable) -> bool {
| ^^^^^^^^^^^ the trait `Clone` is not implemented for `NonClonable`
|
@ -9,3 +11,4 @@ note: required by a bound in `rhai::Dynamic::cast`
|
| pub fn cast<T: Any + Clone>(self) -> T {
| ^^^^^ required by this bound in `rhai::Dynamic::cast`
= note: this error originates in the attribute macro `export_fn` (in Nightly builds, run with -Z macro-backtrace for more info)

View File

@ -1,6 +1,8 @@
error[E0277]: the trait bound `NonClonable: Clone` is not satisfied
--> ui_tests/non_clonable_second.rs:11:27
|
10 | #[export_fn]
| ------------ in this procedural macro expansion
11 | pub fn test_fn(a: u32, b: NonClonable) -> bool {
| ^^^^^^^^^^^ the trait `Clone` is not implemented for `NonClonable`
|
@ -9,3 +11,4 @@ note: required by a bound in `rhai::Dynamic::cast`
|
| pub fn cast<T: Any + Clone>(self) -> T {
| ^^^^^ required by this bound in `rhai::Dynamic::cast`
= note: this error originates in the attribute macro `export_fn` (in Nightly builds, run with -Z macro-backtrace for more info)

View File

@ -69,12 +69,8 @@ impl Engine {
let typ = self.map_type_name(result.type_name());
result.try_cast().ok_or_else(|| {
ERR::ErrorMismatchOutputType(
self.map_type_name(type_name::<T>()).into(),
typ.into(),
Position::NONE,
)
.into()
let t = self.map_type_name(type_name::<T>()).into();
ERR::ErrorMismatchOutputType(t, typ.into(), Position::NONE).into()
})
}
/// Call a script function defined in an [`AST`] with multiple [`Dynamic`] arguments and the

View File

@ -195,12 +195,8 @@ impl Engine {
let typ = self.map_type_name(result.type_name());
result.try_cast::<T>().ok_or_else(|| {
ERR::ErrorMismatchOutputType(
self.map_type_name(type_name::<T>()).into(),
typ.into(),
Position::NONE,
)
.into()
let t = self.map_type_name(type_name::<T>()).into();
ERR::ErrorMismatchOutputType(t, typ.into(), Position::NONE).into()
})
}
/// Evaluate an [`AST`] with own scope.

View File

@ -125,7 +125,7 @@ impl Engine {
#[inline(never)]
#[must_use]
pub(crate) fn make_type_mismatch_err<T>(&self, typ: &str, pos: Position) -> RhaiError {
ERR::ErrorMismatchDataType(self.map_type_name(type_name::<T>()).into(), typ.into(), pos)
.into()
let t = self.map_type_name(type_name::<T>()).into();
ERR::ErrorMismatchDataType(t, typ.into(), pos).into()
}
}

View File

@ -37,6 +37,7 @@ struct ArgBackup<'a> {
impl<'a> ArgBackup<'a> {
/// Create a new `ArgBackup`.
#[inline(always)]
pub fn new() -> Self {
Self {
orig_mut: None,
@ -60,8 +61,8 @@ impl<'a> ArgBackup<'a> {
/// # Panics
///
/// Panics when `args` is empty.
#[inline]
fn change_first_arg_to_copy(&mut self, args: &mut FnCallArgs<'a>) {
#[inline(always)]
pub fn change_first_arg_to_copy(&mut self, args: &mut FnCallArgs<'a>) {
// Clone the original value.
self.value_copy = args[0].clone();
@ -86,7 +87,7 @@ impl<'a> ArgBackup<'a> {
/// If `change_first_arg_to_copy` has been called, this function **MUST** be called _BEFORE_
/// exiting the current scope. Otherwise it is undefined behavior as the shorter lifetime will leak.
#[inline(always)]
fn restore_first_arg(mut self, args: &mut FnCallArgs<'a>) {
pub fn restore_first_arg(mut self, args: &mut FnCallArgs<'a>) {
if let Some(p) = self.orig_mut.take() {
args[0] = p;
}
@ -94,7 +95,7 @@ impl<'a> ArgBackup<'a> {
}
impl Drop for ArgBackup<'_> {
#[inline]
#[inline(always)]
fn drop(&mut self) {
// Panic if the shorter lifetime leaks.
assert!(
@ -382,15 +383,12 @@ impl Engine {
let mut _result = if let Some(FnResolutionCacheEntry { func, source }) = func {
assert!(func.is_native());
let mut backup = ArgBackup::new();
// Calling pure function but the first argument is a reference?
let mut backup: Option<ArgBackup> = None;
if is_ref_mut && func.is_pure() && !args.is_empty() {
// Clone the first argument
backup = Some(ArgBackup::new());
backup
.as_mut()
.expect("`Some`")
.change_first_arg_to_copy(args);
backup.change_first_arg_to_copy(args);
}
let source = match (source.as_str(), parent_source.as_str()) {
@ -420,9 +418,7 @@ impl Engine {
};
// Restore the original reference
if let Some(bk) = backup {
bk.restore_first_arg(args)
}
backup.restore_first_arg(args);
result
} else {
@ -470,11 +466,8 @@ impl Engine {
KEYWORD_PRINT => {
if let Some(ref print) = self.print {
let text = result.into_immutable_string().map_err(|typ| {
ERR::ErrorMismatchOutputType(
self.map_type_name(type_name::<ImmutableString>()).into(),
typ.into(),
pos,
)
let t = self.map_type_name(type_name::<ImmutableString>()).into();
ERR::ErrorMismatchOutputType(t, typ.into(), pos)
})?;
(print(&text).into(), false)
} else {
@ -484,11 +477,8 @@ impl Engine {
KEYWORD_DEBUG => {
if let Some(ref debug) = self.debug {
let text = result.into_immutable_string().map_err(|typ| {
ERR::ErrorMismatchOutputType(
self.map_type_name(type_name::<ImmutableString>()).into(),
typ.into(),
pos,
)
let t = self.map_type_name(type_name::<ImmutableString>()).into();
ERR::ErrorMismatchOutputType(t, typ.into(), pos)
})?;
let source = match global.source.as_str() {
"" => None,
@ -511,15 +501,10 @@ impl Engine {
crate::engine::FN_IDX_GET => {
assert!(args.len() == 2);
Err(ERR::ErrorIndexingType(
format!(
"{} [{}]",
self.map_type_name(args[0].type_name()),
self.map_type_name(args[1].type_name())
),
pos,
)
.into())
let t0 = self.map_type_name(args[0].type_name());
let t1 = self.map_type_name(args[1].type_name());
Err(ERR::ErrorIndexingType(format!("{} [{}]", t0, t1), pos).into())
}
// index setter function not found?
@ -527,16 +512,11 @@ impl Engine {
crate::engine::FN_IDX_SET => {
assert!(args.len() == 3);
Err(ERR::ErrorIndexingType(
format!(
"{} [{}] = {}",
self.map_type_name(args[0].type_name()),
self.map_type_name(args[1].type_name()),
self.map_type_name(args[2].type_name())
),
pos,
)
.into())
let t0 = self.map_type_name(args[0].type_name());
let t1 = self.map_type_name(args[1].type_name());
let t2 = self.map_type_name(args[2].type_name());
Err(ERR::ErrorIndexingType(format!("{} [{}] = {}", t0, t1, t2), pos).into())
}
// Getter function not found?
@ -544,11 +524,13 @@ impl Engine {
_ if name.starts_with(crate::engine::FN_GET) => {
assert!(args.len() == 1);
let prop = &name[crate::engine::FN_GET.len()..];
let t0 = self.map_type_name(args[0].type_name());
Err(ERR::ErrorDotExpr(
format!(
"Unknown property '{}' - a getter is not registered for type '{}'",
&name[crate::engine::FN_GET.len()..],
self.map_type_name(args[0].type_name())
prop, t0
),
pos,
)
@ -560,12 +542,14 @@ impl Engine {
_ if name.starts_with(crate::engine::FN_SET) => {
assert!(args.len() == 2);
let prop = &name[crate::engine::FN_SET.len()..];
let t0 = self.map_type_name(args[0].type_name());
let t1 = self.map_type_name(args[1].type_name());
Err(ERR::ErrorDotExpr(
format!(
"No writable property '{}' - a setter is not registered for type '{}' to handle '{}'",
&name[crate::engine::FN_SET.len()..],
self.map_type_name(args[0].type_name()),
self.map_type_name(args[1].type_name()),
prop, t0, t1
),
pos,
)
@ -612,8 +596,11 @@ impl Engine {
level: usize,
) -> RhaiResultOf<(Dynamic, bool)> {
fn no_method_err(name: &str, pos: Position) -> RhaiResultOf<(Dynamic, bool)> {
let msg = format!("'{0}' should not be called this way. Try {0}(...);", name);
Err(ERR::ErrorRuntime(msg.into(), pos).into())
Err(ERR::ErrorRuntime(
(format!("'{0}' should not be called this way. Try {0}(...);", name)).into(),
pos,
)
.into())
}
// Check for data race.
@ -627,10 +614,8 @@ impl Engine {
match fn_name {
// Handle type_of()
KEYWORD_TYPE_OF if args.len() == 1 => {
return Ok((
self.map_type_name(args[0].type_name()).to_string().into(),
false,
))
let typ = self.map_type_name(args[0].type_name()).to_string().into();
return Ok((typ, false));
}
// Handle is_def_fn()
@ -711,7 +696,7 @@ impl Engine {
// Method call of script function - map first argument to `this`
let (first_arg, rest_args) = args.split_first_mut().unwrap();
let result = self.call_script_fn(
self.call_script_fn(
scope,
global,
state,
@ -722,19 +707,14 @@ impl Engine {
true,
pos,
level,
);
result?
)
} else {
// Normal call of script function
let mut backup = ArgBackup::new();
// The first argument is a reference?
let mut backup: Option<ArgBackup> = None;
if is_ref_mut && !args.is_empty() {
backup = Some(ArgBackup::new());
backup
.as_mut()
.expect("`Some`")
.change_first_arg_to_copy(args);
backup.change_first_arg_to_copy(args);
}
let result = self.call_script_fn(
@ -742,17 +722,15 @@ impl Engine {
);
// Restore the original reference
if let Some(bk) = backup {
bk.restore_first_arg(args)
}
backup.restore_first_arg(args);
result?
result
};
// Restore the original source
mem::swap(&mut global.source, &mut source);
return Ok((result, false));
return Ok((result?, false));
}
// Native function call
@ -827,16 +805,12 @@ impl Engine {
KEYWORD_FN_PTR_CALL => {
if !call_args.is_empty() {
if !call_args[0].is::<FnPtr>() {
return Err(self.make_type_mismatch_err::<FnPtr>(
self.map_type_name(call_args[0].type_name()),
*call_arg_pos,
));
let typ = self.map_type_name(call_args[0].type_name());
return Err(self.make_type_mismatch_err::<FnPtr>(typ, *call_arg_pos));
}
} else {
return Err(self.make_type_mismatch_err::<FnPtr>(
self.map_type_name(target.type_name()),
pos,
));
let typ = self.map_type_name(target.type_name());
return Err(self.make_type_mismatch_err::<FnPtr>(typ, pos));
}
// FnPtr call on object
@ -866,10 +840,8 @@ impl Engine {
}
KEYWORD_FN_PTR_CURRY => {
if !target.is::<FnPtr>() {
return Err(self.make_type_mismatch_err::<FnPtr>(
self.map_type_name(target.type_name()),
pos,
));
let typ = self.map_type_name(target.type_name());
return Err(self.make_type_mismatch_err::<FnPtr>(typ, pos));
}
let fn_ptr = target.read_lock::<FnPtr>().expect("`FnPtr`");
@ -1016,10 +988,8 @@ impl Engine {
self.get_arg_value(scope, global, state, lib, this_ptr, arg, level)?;
if !arg_value.is::<FnPtr>() {
return Err(self.make_type_mismatch_err::<FnPtr>(
self.map_type_name(arg_value.type_name()),
arg_pos,
));
let typ = self.map_type_name(arg_value.type_name());
return Err(self.make_type_mismatch_err::<FnPtr>(typ, arg_pos));
}
let fn_ptr = arg_value.cast::<FnPtr>();
@ -1066,10 +1036,8 @@ impl Engine {
self.get_arg_value(scope, global, state, lib, this_ptr, first, level)?;
if !arg_value.is::<FnPtr>() {
return Err(self.make_type_mismatch_err::<FnPtr>(
self.map_type_name(arg_value.type_name()),
arg_pos,
));
let typ = self.map_type_name(arg_value.type_name());
return Err(self.make_type_mismatch_err::<FnPtr>(typ, arg_pos));
}
let (name, fn_curry) = arg_value.cast::<FnPtr>().take_data();

View File

@ -277,12 +277,8 @@ impl<'a> NativeCallContext<'a> {
let typ = self.engine().map_type_name(result.type_name());
result.try_cast().ok_or_else(|| {
ERR::ErrorMismatchOutputType(
self.engine().map_type_name(type_name::<T>()).into(),
typ.into(),
Position::NONE,
)
.into()
let t = self.engine().map_type_name(type_name::<T>()).into();
ERR::ErrorMismatchOutputType(t, typ.into(), Position::NONE).into()
})
}
/// Call a function inside the call context.
@ -348,7 +344,7 @@ impl<'a> NativeCallContext<'a> {
}
}
/// Consume a [`Shared`] resource and return a mutable reference to the wrapped value.
/// Return a mutable reference to the wrapped value of a [`Shared`] resource.
/// If the resource is shared (i.e. has other outstanding references), a cloned copy is used.
#[inline(always)]
#[must_use]
@ -357,6 +353,14 @@ pub fn shared_make_mut<T: Clone>(value: &mut Shared<T>) -> &mut T {
Shared::make_mut(value)
}
/// Return a mutable reference to the wrapped value of a [`Shared`] resource.
#[inline(always)]
#[must_use]
#[allow(dead_code)]
pub fn shared_get_mut<T: Clone>(value: &mut Shared<T>) -> Option<&mut T> {
Shared::get_mut(value)
}
/// Consume a [`Shared`] resource if is unique (i.e. not shared), or clone it otherwise.
#[inline]
#[must_use]

View File

@ -1085,7 +1085,8 @@ fn optimize_expr(expr: &mut Expr, state: &mut OptimizerState, chaining: bool) {
match x.name.as_str() {
KEYWORD_TYPE_OF if arg_values.len() == 1 => {
state.set_dirty();
*expr = Expr::from_dynamic(state.engine.map_type_name(arg_values[0].type_name()).into(), *pos);
let typ = state.engine.map_type_name(arg_values[0].type_name()).into();
*expr = Expr::from_dynamic(typ, *pos);
return;
}
#[cfg(not(feature = "no_closure"))]

View File

@ -193,7 +193,11 @@ mod string_functions {
/// Clear the string, making it empty.
pub fn clear(string: &mut ImmutableString) {
if !string.is_empty() {
string.make_mut().clear();
if let Some(s) = string.get_mut() {
s.clear();
} else {
*string = ImmutableString::new();
}
}
}
/// Cut off the string at the specified number of characters.
@ -220,8 +224,8 @@ mod string_functions {
let copy = string.make_mut();
copy.clear();
copy.extend(chars.into_iter().take(len as usize));
} else if !string.is_empty() {
string.make_mut().clear();
} else {
clear(string);
}
}
/// Remove whitespace characters from both ends of the string.
@ -231,13 +235,23 @@ mod string_functions {
/// ```rhai
/// let text = " hello ";
///
/// print(text.trim()); // prints "hello"
/// text.trim();
///
/// print(text); // prints "hello"
/// ```
pub fn trim(string: &mut ImmutableString) {
if let Some(s) = string.get_mut() {
let trimmed = s.trim();
if trimmed != s {
*s = trimmed.into();
}
} else {
let trimmed = string.trim();
if trimmed.len() < string.len() {
*string = trimmed.to_string().into();
if trimmed != string {
*string = trimmed.into();
}
}
}
/// Remove the last character from the string and return it.
@ -313,7 +327,7 @@ mod string_functions {
/// ```
#[rhai_fn(pure)]
pub fn to_upper(string: &mut ImmutableString) -> ImmutableString {
if string.is_empty() || string.chars().all(char::is_uppercase) {
if string.chars().all(char::is_uppercase) {
string.clone()
} else {
string.to_uppercase().into()
@ -366,7 +380,7 @@ mod string_functions {
/// print(text); // prints "hello, world!";
/// ```
pub fn make_lower(string: &mut ImmutableString) {
if !string.is_empty() && string.chars().any(|ch| !ch.is_lowercase()) {
if string.chars().any(|ch| !ch.is_lowercase()) {
*string = string.to_lowercase().into();
}
}

View File

@ -162,12 +162,8 @@ impl FnPtr {
let typ = engine.map_type_name(result.type_name());
result.try_cast().ok_or_else(|| {
ERR::ErrorMismatchOutputType(
engine.map_type_name(type_name::<T>()).into(),
typ.into(),
Position::NONE,
)
.into()
let t = engine.map_type_name(type_name::<T>()).into();
ERR::ErrorMismatchOutputType(t, typ.into(), Position::NONE).into()
})
}
/// Call the function pointer with curried arguments (if any).
@ -190,12 +186,8 @@ impl FnPtr {
let typ = context.engine().map_type_name(result.type_name());
result.try_cast().ok_or_else(|| {
ERR::ErrorMismatchOutputType(
context.engine().map_type_name(type_name::<T>()).into(),
typ.into(),
Position::NONE,
)
.into()
let t = context.engine().map_type_name(type_name::<T>()).into();
ERR::ErrorMismatchOutputType(t, typ.into(), Position::NONE).into()
})
}
/// Call the function pointer with curried arguments (if any).

View File

@ -1,6 +1,6 @@
//! The `ImmutableString` type.
use crate::func::native::{shared_make_mut, shared_take};
use crate::func::native::{shared_get_mut, shared_make_mut, shared_take};
use crate::{Shared, SmartString};
#[cfg(feature = "no_std")]
use std::prelude::v1::*;
@ -580,6 +580,7 @@ impl ImmutableString {
Self(SmartString::new_const().into())
}
/// Consume the [`ImmutableString`] and convert it into a [`String`].
///
/// If there are other references to the same string, a cloned copy is returned.
#[inline]
pub fn into_owned(mut self) -> String {
@ -588,10 +589,17 @@ impl ImmutableString {
}
/// Make sure that the [`ImmutableString`] is unique (i.e. no other outstanding references).
/// Then return a mutable reference to the [`SmartString`].
///
/// If there are other references to the same string, a cloned copy is used.
#[inline(always)]
pub(crate) fn make_mut(&mut self) -> &mut SmartString {
shared_make_mut(&mut self.0)
}
/// Return a mutable reference to the [`SmartString`] wrapped by the [`ImmutableString`].
#[inline(always)]
pub(crate) fn get_mut(&mut self) -> Option<&mut SmartString> {
shared_get_mut(&mut self.0)
}
/// Returns `true` if the two [`ImmutableString`]'s point to the same allocation.
///
/// # Example