Get rid of unsafe code in the interpreter (#303)

* Get rid of unsafe code unless really necessary

* Add lint levels where appropriate

Some crates (air-beautifier, air-testing-framework) have lot of
rust_2018_idioms violations, that will be resolved later.
This commit is contained in:
Ivan Boldyrev
2022-09-05 20:03:30 +03:00
committed by GitHub
parent 1cb6901caa
commit 619e8829a9
19 changed files with 113 additions and 48 deletions

View File

@ -14,6 +14,7 @@
* limitations under the License. * limitations under the License.
*/ */
#![forbid(unsafe_code)]
#![warn(rust_2018_idioms)] #![warn(rust_2018_idioms)]
#![deny( #![deny(
dead_code, dead_code,

View File

@ -14,6 +14,18 @@
* limitations under the License. * limitations under the License.
*/ */
#![forbid(unsafe_code)]
#![warn(rust_2018_idioms)]
#![deny(
dead_code,
nonstandard_style,
unused_imports,
unused_mut,
unused_variables,
unused_unsafe,
unreachable_patterns
)]
mod call_request_parameters; mod call_request_parameters;
mod call_service_result; mod call_service_result;
mod outcome; mod outcome;

View File

@ -18,6 +18,8 @@ use super::*;
use air_lambda_parser::LambdaAST; use air_lambda_parser::LambdaAST;
use air_lambda_parser::ValueAccessor; use air_lambda_parser::ValueAccessor;
use std::convert::TryFrom;
impl<'i> ScalarWithLambda<'i> { impl<'i> ScalarWithLambda<'i> {
pub fn new(name: &'i str, lambda: Option<LambdaAST<'i>>, position: usize) -> Self { pub fn new(name: &'i str, lambda: Option<LambdaAST<'i>>, position: usize) -> Self {
Self { Self {
@ -27,16 +29,15 @@ impl<'i> ScalarWithLambda<'i> {
} }
} }
// it's unsafe method that should be used only for tests
pub(crate) fn from_raw_lambda( pub(crate) fn from_raw_lambda(
name: &'i str, name: &'i str,
lambda: Vec<ValueAccessor<'i>>, lambda: Vec<ValueAccessor<'i>>,
position: usize, position: usize,
) -> Self { ) -> Self {
let lambda = unsafe { LambdaAST::new_unchecked(lambda) }; let lambda = LambdaAST::try_from(lambda).ok();
Self { Self {
name, name,
lambda: Some(lambda), lambda,
position, position,
} }
} }
@ -51,17 +52,16 @@ impl<'i> StreamWithLambda<'i> {
} }
} }
// it's unsafe method that should be used only for tests
#[allow(dead_code)] #[allow(dead_code)]
pub(crate) fn from_raw_lambda( pub(crate) fn from_raw_lambda(
name: &'i str, name: &'i str,
lambda: Vec<ValueAccessor<'i>>, lambda: Vec<ValueAccessor<'i>>,
position: usize, position: usize,
) -> Self { ) -> Self {
let lambda = unsafe { LambdaAST::new_unchecked(lambda) }; let lambda = LambdaAST::try_from(lambda).ok();
Self { Self {
name, name,
lambda: Some(lambda), lambda,
position, position,
} }
} }
@ -154,7 +154,6 @@ impl<'i> VariableWithLambda<'i> {
} }
} }
// This function is unsafe and lambda must be non-empty, although it's used only for tests
#[allow(dead_code)] #[allow(dead_code)]
pub(crate) fn from_raw_lambda_scalar( pub(crate) fn from_raw_lambda_scalar(
name: &'i str, name: &'i str,
@ -165,7 +164,6 @@ impl<'i> VariableWithLambda<'i> {
Self::Scalar(scalar) Self::Scalar(scalar)
} }
// This function is unsafe and lambda must be non-empty, although it's used only for tests
#[allow(dead_code)] #[allow(dead_code)]
pub(crate) fn from_raw_lambda_stream( pub(crate) fn from_raw_lambda_stream(
name: &'i str, name: &'i str,

View File

@ -14,6 +14,7 @@
* limitations under the License. * limitations under the License.
*/ */
#![forbid(unsafe_code)]
#![deny( #![deny(
dead_code, dead_code,
nonstandard_style, nonstandard_style,

View File

@ -25,6 +25,8 @@ use air_lambda_parser::ValueAccessor;
use fstrings::f; use fstrings::f;
use fstrings::format_args_f; use fstrings::format_args_f;
use std::convert::TryFrom;
fn run_lexer(input: &str) -> Vec<Spanned<Token<'_>, usize, LexerError>> { fn run_lexer(input: &str) -> Vec<Spanned<Token<'_>, usize, LexerError>> {
let lexer = AIRLexer::new(input); let lexer = AIRLexer::new(input);
lexer.collect() lexer.collect()
@ -306,14 +308,13 @@ fn lambda() {
0, 0,
Token::ScalarWithLambda { Token::ScalarWithLambda {
name: "value", name: "value",
lambda: unsafe { lambda: LambdaAST::try_from(vec![
LambdaAST::new_unchecked(vec![
ValueAccessor::FieldAccessByName { ValueAccessor::FieldAccessByName {
field_name: "field", field_name: "field",
}, },
ValueAccessor::ArrayAccess { idx: 1 }, ValueAccessor::ArrayAccess { idx: 1 },
]) ])
}, .unwrap(),
position: 0, position: 0,
}, },
LAMBDA.len(), LAMBDA.len(),
@ -413,11 +414,12 @@ fn last_error() {
fn last_error_instruction() { fn last_error_instruction() {
const LAST_ERROR: &str = r#"%last_error%.$.instruction"#; const LAST_ERROR: &str = r#"%last_error%.$.instruction"#;
let token = Token::LastErrorWithLambda(unsafe { let token = Token::LastErrorWithLambda(
LambdaAST::new_unchecked(vec![ValueAccessor::FieldAccessByName { LambdaAST::try_from(vec![ValueAccessor::FieldAccessByName {
field_name: "instruction", field_name: "instruction",
}]) }])
}); .unwrap(),
);
lexer_test(LAST_ERROR, Single(Ok((0, token, LAST_ERROR.len())))); lexer_test(LAST_ERROR, Single(Ok((0, token, LAST_ERROR.len()))));
} }
@ -426,11 +428,12 @@ fn last_error_instruction() {
fn last_error_message() { fn last_error_message() {
const LAST_ERROR: &str = r#"%last_error%.$.message"#; const LAST_ERROR: &str = r#"%last_error%.$.message"#;
let token = Token::LastErrorWithLambda(unsafe { let token = Token::LastErrorWithLambda(
LambdaAST::new_unchecked(vec![ValueAccessor::FieldAccessByName { LambdaAST::try_from(vec![ValueAccessor::FieldAccessByName {
field_name: "message", field_name: "message",
}]) }])
}); .unwrap(),
);
lexer_test(LAST_ERROR, Single(Ok((0, token, LAST_ERROR.len())))); lexer_test(LAST_ERROR, Single(Ok((0, token, LAST_ERROR.len()))));
} }
@ -438,11 +441,12 @@ fn last_error_message() {
fn last_error_peer_id() { fn last_error_peer_id() {
const LAST_ERROR: &str = r#"%last_error%.$.peer_id"#; const LAST_ERROR: &str = r#"%last_error%.$.peer_id"#;
let token = Token::LastErrorWithLambda(unsafe { let token = Token::LastErrorWithLambda(
LambdaAST::new_unchecked(vec![ValueAccessor::FieldAccessByName { LambdaAST::try_from(vec![ValueAccessor::FieldAccessByName {
field_name: "peer_id", field_name: "peer_id",
}]) }])
}); .unwrap(),
);
lexer_test(LAST_ERROR, Single(Ok((0, token, LAST_ERROR.len())))); lexer_test(LAST_ERROR, Single(Ok((0, token, LAST_ERROR.len()))));
} }
@ -450,11 +454,12 @@ fn last_error_peer_id() {
fn last_error_non_standard_field() { fn last_error_non_standard_field() {
const LAST_ERROR: &str = r#"%last_error%.$.asdasd"#; const LAST_ERROR: &str = r#"%last_error%.$.asdasd"#;
let token = Token::LastErrorWithLambda(unsafe { let token = Token::LastErrorWithLambda(
LambdaAST::new_unchecked(vec![ValueAccessor::FieldAccessByName { LambdaAST::try_from(vec![ValueAccessor::FieldAccessByName {
field_name: "asdasd", field_name: "asdasd",
}]) }])
}); .unwrap(),
);
lexer_test(LAST_ERROR, Single(Ok((0, token, LAST_ERROR.len())))); lexer_test(LAST_ERROR, Single(Ok((0, token, LAST_ERROR.len()))));
} }

View File

@ -22,6 +22,8 @@ use air_lambda_ast::{LambdaAST, ValueAccessor};
use fstrings::f; use fstrings::f;
use fstrings::format_args_f; use fstrings::format_args_f;
use std::convert::TryFrom;
#[test] #[test]
fn ap_with_literal() { fn ap_with_literal() {
let source_code = r#" let source_code = r#"
@ -75,11 +77,12 @@ fn ap_with_last_error() {
let actual = parse(source_code); let actual = parse(source_code);
let expected = ap( let expected = ap(
ApArgument::LastError(Some(unsafe { ApArgument::LastError(Some(
LambdaAST::new_unchecked(vec![ValueAccessor::FieldAccessByName { LambdaAST::try_from(vec![ValueAccessor::FieldAccessByName {
field_name: "message", field_name: "message",
}]) }])
})), .unwrap(),
)),
ApResult::Stream(Stream::new("$stream", 37)), ApResult::Stream(Stream::new("$stream", 37)),
); );
@ -175,7 +178,7 @@ fn ap_with_canon_stream_with_lambda() {
let expected = ap( let expected = ap(
ApArgument::CanonStream(CanonStreamWithLambda::new( ApArgument::CanonStream(CanonStreamWithLambda::new(
canon_stream, canon_stream,
Some(unsafe { LambdaAST::new_unchecked(vec![ValueAccessor::ArrayAccess { idx: 0 }]) }), Some(LambdaAST::try_from(vec![ValueAccessor::ArrayAccess { idx: 0 }]).unwrap()),
13, 13,
)), )),
ApResult::Scalar(Scalar::new(scalar, 33)), ApResult::Scalar(Scalar::new(scalar, 33)),

View File

@ -24,6 +24,7 @@ use fstrings::f;
use fstrings::format_args_f; use fstrings::format_args_f;
use lalrpop_util::ParseError; use lalrpop_util::ParseError;
use std::convert::TryFrom;
use std::rc::Rc; use std::rc::Rc;
#[test] #[test]
@ -471,12 +472,11 @@ fn canon_stream_with_lambda_in_triplet() {
let expected = call( let expected = call(
CallInstrValue::Variable(VariableWithLambda::canon_stream_wl( CallInstrValue::Variable(VariableWithLambda::canon_stream_wl(
canon_stream, canon_stream,
unsafe { LambdaAST::try_from(vec![
LambdaAST::new_unchecked(vec![
ValueAccessor::ArrayAccess { idx: 0 }, ValueAccessor::ArrayAccess { idx: 0 },
ValueAccessor::FieldAccessByName { field_name: "path" }, ValueAccessor::FieldAccessByName { field_name: "path" },
]) ])
}, .unwrap(),
19, 19,
)), )),
CallInstrValue::Literal(service_id), CallInstrValue::Literal(service_id),

View File

@ -21,6 +21,8 @@ use crate::ast::ScalarWithLambda;
use air_lambda_ast::LambdaAST; use air_lambda_ast::LambdaAST;
use air_lambda_ast::ValueAccessor; use air_lambda_ast::ValueAccessor;
use std::convert::TryFrom;
#[test] #[test]
fn parse_fail_last_error() { fn parse_fail_last_error() {
let source_code = r#" let source_code = r#"
@ -59,11 +61,12 @@ fn parse_fail_scalar_with_lambda() {
let instruction = parse(source_code); let instruction = parse(source_code);
let expected = fail_scalar(ScalarWithLambda::new( let expected = fail_scalar(ScalarWithLambda::new(
"scalar", "scalar",
Some(unsafe { Some(
LambdaAST::new_unchecked(vec![ValueAccessor::FieldAccessByName { LambdaAST::try_from(vec![ValueAccessor::FieldAccessByName {
field_name: "field_accessor", field_name: "field_accessor",
}]) }])
}), .unwrap(),
),
18, 18,
)); ));
assert_eq!(instruction, expected) assert_eq!(instruction, expected)

View File

@ -22,6 +22,8 @@ use air_lambda_ast::{LambdaAST, ValueAccessor};
use fstrings::f; use fstrings::f;
use fstrings::format_args_f; use fstrings::format_args_f;
use std::convert::TryFrom;
#[test] #[test]
fn parse_match() { fn parse_match() {
let source_code = r#" let source_code = r#"
@ -52,7 +54,7 @@ fn parse_match_with_canon_stream() {
let expected = match_( let expected = match_(
Value::Variable(VariableWithLambda::canon_stream_wl( Value::Variable(VariableWithLambda::canon_stream_wl(
canon_stream, canon_stream,
unsafe { LambdaAST::new_unchecked(vec![ValueAccessor::ArrayAccess { idx: 0 }]) }, LambdaAST::try_from(vec![ValueAccessor::ArrayAccess { idx: 0 }]).unwrap(),
16, 16,
)), )),
Value::Variable(VariableWithLambda::scalar("v2", 36)), Value::Variable(VariableWithLambda::scalar("v2", 36)),

View File

@ -14,6 +14,18 @@
* limitations under the License. * limitations under the License.
*/ */
#![forbid(unsafe_code)]
#![warn(rust_2018_idioms)]
#![deny(
dead_code,
nonstandard_style,
unused_imports,
unused_mut,
unused_variables,
unused_unsafe,
unreachable_patterns
)]
mod instructions_tracker; mod instructions_tracker;
pub use instructions_tracker::*; pub use instructions_tracker::*;

View File

@ -14,6 +14,7 @@
* limitations under the License. * limitations under the License.
*/ */
#![forbid(unsafe_code)]
#![warn(rust_2018_idioms)] #![warn(rust_2018_idioms)]
#![deny( #![deny(
dead_code, dead_code,

View File

@ -14,6 +14,18 @@
* limitations under the License. * limitations under the License.
*/ */
#![forbid(unsafe_code)]
#![warn(rust_2018_idioms)]
#![deny(
dead_code,
nonstandard_style,
unused_imports,
unused_mut,
unused_variables,
unused_unsafe,
unreachable_patterns
)]
mod call_request_parameters; mod call_request_parameters;
mod call_service_result; mod call_service_result;
mod interpreter_outcome; mod interpreter_outcome;

View File

@ -14,6 +14,8 @@
* limitations under the License. * limitations under the License.
*/ */
#![forbid(unsafe_code)]
#![warn(rust_2018_idioms)]
#![deny( #![deny(
dead_code, dead_code,
nonstandard_style, nonstandard_style,

View File

@ -14,6 +14,7 @@
* limitations under the License. * limitations under the License.
*/ */
#![forbid(unsafe_code)]
#![deny( #![deny(
dead_code, dead_code,
nonstandard_style, nonstandard_style,

View File

@ -23,6 +23,8 @@ use crate::ValueAccessor;
use va_lambda::LambdaParser; use va_lambda::LambdaParser;
use std::convert::TryFrom;
// Caching parser to cache internal regexes, which are expensive to instantiate // Caching parser to cache internal regexes, which are expensive to instantiate
// See also https://github.com/lalrpop/lalrpop/issues/269 // See also https://github.com/lalrpop/lalrpop/issues/269
thread_local!(static PARSER: LambdaParser = LambdaParser::new()); thread_local!(static PARSER: LambdaParser = LambdaParser::new());
@ -43,10 +45,5 @@ pub fn parse(lambda: &str) -> LambdaParserResult<'_, LambdaAST> {
} }
fn try_to_lambda(accessors: Vec<ValueAccessor>) -> LambdaParserResult<'_, LambdaAST> { fn try_to_lambda(accessors: Vec<ValueAccessor>) -> LambdaParserResult<'_, LambdaAST> {
if accessors.is_empty() { LambdaAST::try_from(accessors).or(Err(LambdaParserError::EmptyLambda))
return Err(LambdaParserError::EmptyLambda);
}
let ast = unsafe { LambdaAST::new_unchecked(accessors) };
Ok(ast)
} }

View File

@ -14,6 +14,7 @@
* limitations under the License. * limitations under the License.
*/ */
#![forbid(unsafe_code)]
#![warn(rust_2018_idioms)] #![warn(rust_2018_idioms)]
#![deny( #![deny(
dead_code, dead_code,

View File

@ -14,6 +14,7 @@
* limitations under the License. * limitations under the License.
*/ */
#![forbid(unsafe_code)]
#![warn(rust_2018_idioms)] #![warn(rust_2018_idioms)]
#![deny( #![deny(
dead_code, dead_code,

View File

@ -14,6 +14,7 @@
* limitations under the License. * limitations under the License.
*/ */
#![forbid(unsafe_code)]
#![warn(rust_2018_idioms)] #![warn(rust_2018_idioms)]
#![deny( #![deny(
dead_code, dead_code,

View File

@ -14,6 +14,18 @@
* limitations under the License. * limitations under the License.
*/ */
#![forbid(unsafe_code)]
#![warn(rust_2018_idioms)]
#![deny(
dead_code,
nonstandard_style,
unused_imports,
unused_mut,
unused_variables,
unused_unsafe,
unreachable_patterns
)]
use serde::Deserialize; use serde::Deserialize;
use serde::Serialize; use serde::Serialize;
use std::borrow::Cow; use std::borrow::Cow;