forked from M-Labs/nac3
added statement tests
This commit is contained in:
parent
007843c1ef
commit
b51168a5ab
|
@ -288,6 +288,15 @@ dependencies = [
|
||||||
"proc-macro-hack",
|
"proc-macro-hack",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "indoc"
|
||||||
|
version = "1.0.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e5a75aeaaef0ce18b58056d306c27b07436fbb34b8816c53094b76dd81803136"
|
||||||
|
dependencies = [
|
||||||
|
"unindent",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "indoc-impl"
|
name = "indoc-impl"
|
||||||
version = "0.3.6"
|
version = "0.3.6"
|
||||||
|
@ -449,6 +458,7 @@ checksum = "0ee1c47aaa256ecabcaea351eae4a9b01ef39ed810004e298d2511ed284b1525"
|
||||||
name = "nac3core"
|
name = "nac3core"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"indoc 1.0.3",
|
||||||
"inkwell",
|
"inkwell",
|
||||||
"num-bigint",
|
"num-bigint",
|
||||||
"num-traits",
|
"num-traits",
|
||||||
|
@ -613,7 +623,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "bf6bbbe8f70d179260b3728e5d04eb012f4f0c7988e58c11433dd689cecaa72e"
|
checksum = "bf6bbbe8f70d179260b3728e5d04eb012f4f0c7988e58c11433dd689cecaa72e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"ctor",
|
"ctor",
|
||||||
"indoc",
|
"indoc 0.3.6",
|
||||||
"inventory",
|
"inventory",
|
||||||
"libc",
|
"libc",
|
||||||
"parking_lot",
|
"parking_lot",
|
||||||
|
|
|
@ -9,3 +9,7 @@ num-bigint = "0.3"
|
||||||
num-traits = "0.2"
|
num-traits = "0.2"
|
||||||
inkwell = { git = "https://github.com/TheDan64/inkwell", branch = "master", features = ["llvm10-0"] }
|
inkwell = { git = "https://github.com/TheDan64/inkwell", branch = "master", features = ["llvm10-0"] }
|
||||||
rustpython-parser = { git = "https://github.com/RustPython/RustPython", branch = "master" }
|
rustpython-parser = { git = "https://github.com/RustPython/RustPython", branch = "master" }
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
indoc = "1.0"
|
||||||
|
|
||||||
|
|
|
@ -19,7 +19,9 @@ pub fn check_stmts<'b: 'a, 'a>(
|
||||||
check_aug_assign(ctx, &target, op, &value)?;
|
check_aug_assign(ctx, &target, op, &value)?;
|
||||||
}
|
}
|
||||||
StatementType::If { test, body, orelse } => {
|
StatementType::If { test, body, orelse } => {
|
||||||
check_if(ctx, test, body.as_slice(), orelse)?;
|
if check_if(ctx, test, body.as_slice(), orelse)? {
|
||||||
|
return Ok(true)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
StatementType::While { test, body, orelse } => {
|
StatementType::While { test, body, orelse } => {
|
||||||
check_while_stmt(ctx, test, body.as_slice(), orelse)?;
|
check_while_stmt(ctx, test, body.as_slice(), orelse)?;
|
||||||
|
@ -239,3 +241,333 @@ fn check_for_stmt<'b: 'a, 'a>(
|
||||||
Err("only list can be iterated over".to_string())
|
Err("only list can be iterated over".to_string())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
use super::*;
|
||||||
|
use crate::context::*;
|
||||||
|
use indoc::indoc;
|
||||||
|
use rustpython_parser::parser::parse_program;
|
||||||
|
|
||||||
|
fn get_inference_context(ctx: TopLevelContext) -> InferenceContext {
|
||||||
|
InferenceContext::new(ctx, Box::new(|_| Err("unbounded identifier".into())))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_assign() {
|
||||||
|
let ctx = basic_ctx();
|
||||||
|
let mut ctx = get_inference_context(ctx);
|
||||||
|
let ast = parse_program(indoc! {"
|
||||||
|
a = 1
|
||||||
|
b = a * 2
|
||||||
|
" })
|
||||||
|
.unwrap();
|
||||||
|
ctx.with_scope(|ctx| {
|
||||||
|
assert_eq!(Ok(false), check_stmts(ctx, ast.statements.as_slice()));
|
||||||
|
});
|
||||||
|
|
||||||
|
let ast = parse_program(indoc! {"
|
||||||
|
a = 1
|
||||||
|
b = b * 2
|
||||||
|
" })
|
||||||
|
.unwrap();
|
||||||
|
ctx.with_scope(|ctx| {
|
||||||
|
assert_eq!(
|
||||||
|
Err("unbounded identifier".to_string()),
|
||||||
|
check_stmts(ctx, ast.statements.as_slice())
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
let ast = parse_program(indoc! {"
|
||||||
|
b = a = 1
|
||||||
|
b = b * 2
|
||||||
|
" })
|
||||||
|
.unwrap();
|
||||||
|
ctx.with_scope(|ctx| {
|
||||||
|
assert_eq!(Ok(false), check_stmts(ctx, ast.statements.as_slice()));
|
||||||
|
});
|
||||||
|
|
||||||
|
let ast = parse_program(indoc! {"
|
||||||
|
b = a = 1
|
||||||
|
b = [a]
|
||||||
|
" })
|
||||||
|
.unwrap();
|
||||||
|
ctx.with_scope(|ctx| {
|
||||||
|
assert_eq!(
|
||||||
|
Err("conflicting type".to_string()),
|
||||||
|
check_stmts(ctx, ast.statements.as_slice())
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_if() {
|
||||||
|
let ctx = basic_ctx();
|
||||||
|
let mut ctx = get_inference_context(ctx);
|
||||||
|
|
||||||
|
let ast = parse_program(indoc! {"
|
||||||
|
a = 1
|
||||||
|
b = a * 2
|
||||||
|
if b > a:
|
||||||
|
c = 1
|
||||||
|
else:
|
||||||
|
c = 0
|
||||||
|
d = c
|
||||||
|
" })
|
||||||
|
.unwrap();
|
||||||
|
ctx.with_scope(|ctx| {
|
||||||
|
assert_eq!(Ok(false), check_stmts(ctx, ast.statements.as_slice()));
|
||||||
|
});
|
||||||
|
|
||||||
|
let ast = parse_program(indoc! {"
|
||||||
|
a = 1
|
||||||
|
b = a * 2
|
||||||
|
if b > a:
|
||||||
|
c = 1
|
||||||
|
else:
|
||||||
|
d = 0
|
||||||
|
d = c
|
||||||
|
" })
|
||||||
|
.unwrap();
|
||||||
|
ctx.with_scope(|ctx| {
|
||||||
|
assert_eq!(
|
||||||
|
Err("unbounded identifier".to_string()),
|
||||||
|
check_stmts(ctx, ast.statements.as_slice())
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
let ast = parse_program(indoc! {"
|
||||||
|
a = 1
|
||||||
|
b = a * 2
|
||||||
|
if b > a:
|
||||||
|
c = 1
|
||||||
|
d = c
|
||||||
|
" })
|
||||||
|
.unwrap();
|
||||||
|
ctx.with_scope(|ctx| {
|
||||||
|
assert_eq!(
|
||||||
|
Err("unbounded identifier".to_string()),
|
||||||
|
check_stmts(ctx, ast.statements.as_slice())
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
let ast = parse_program(indoc! {"
|
||||||
|
a = 1
|
||||||
|
b = a * 2
|
||||||
|
if a:
|
||||||
|
b = 0
|
||||||
|
" })
|
||||||
|
.unwrap();
|
||||||
|
ctx.with_scope(|ctx| {
|
||||||
|
assert_eq!(
|
||||||
|
Err("condition should be bool".to_string()),
|
||||||
|
check_stmts(ctx, ast.statements.as_slice())
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
let ast = parse_program(indoc! {"
|
||||||
|
a = 1
|
||||||
|
b = a * 2
|
||||||
|
if b > a:
|
||||||
|
c = 1
|
||||||
|
c = [1]
|
||||||
|
" })
|
||||||
|
.unwrap();
|
||||||
|
ctx.with_scope(|ctx| {
|
||||||
|
assert_eq!(Ok(false), check_stmts(ctx, ast.statements.as_slice()));
|
||||||
|
});
|
||||||
|
|
||||||
|
let ast = parse_program(indoc! {"
|
||||||
|
a = 1
|
||||||
|
b = a * 2
|
||||||
|
if b > a:
|
||||||
|
c = 1
|
||||||
|
else:
|
||||||
|
c = 0
|
||||||
|
c = [1]
|
||||||
|
" })
|
||||||
|
.unwrap();
|
||||||
|
ctx.with_scope(|ctx| {
|
||||||
|
assert_eq!(
|
||||||
|
Err("conflicting type".to_string()),
|
||||||
|
check_stmts(ctx, ast.statements.as_slice())
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_while() {
|
||||||
|
let ctx = basic_ctx();
|
||||||
|
let mut ctx = get_inference_context(ctx);
|
||||||
|
let ast = parse_program(indoc! {"
|
||||||
|
a = 1
|
||||||
|
b = 1
|
||||||
|
while a < 10:
|
||||||
|
a += 1
|
||||||
|
b *= a
|
||||||
|
" })
|
||||||
|
.unwrap();
|
||||||
|
ctx.with_scope(|ctx| {
|
||||||
|
assert_eq!(Ok(false), check_stmts(ctx, ast.statements.as_slice()));
|
||||||
|
});
|
||||||
|
|
||||||
|
let ast = parse_program(indoc! {"
|
||||||
|
a = 1
|
||||||
|
b = 1
|
||||||
|
while a < 10:
|
||||||
|
a += 1
|
||||||
|
b *= a
|
||||||
|
" })
|
||||||
|
.unwrap();
|
||||||
|
ctx.with_scope(|ctx| {
|
||||||
|
assert_eq!(Ok(false), check_stmts(ctx, ast.statements.as_slice()));
|
||||||
|
});
|
||||||
|
|
||||||
|
let ast = parse_program(indoc! {"
|
||||||
|
a = 1
|
||||||
|
b = 1
|
||||||
|
while a < 10:
|
||||||
|
a += 1
|
||||||
|
b *= a
|
||||||
|
else:
|
||||||
|
a += 1
|
||||||
|
" })
|
||||||
|
.unwrap();
|
||||||
|
ctx.with_scope(|ctx| {
|
||||||
|
assert_eq!(Ok(false), check_stmts(ctx, ast.statements.as_slice()));
|
||||||
|
});
|
||||||
|
|
||||||
|
let ast = parse_program(indoc! {"
|
||||||
|
a = 1
|
||||||
|
b = 1
|
||||||
|
while a:
|
||||||
|
a += 1
|
||||||
|
" })
|
||||||
|
.unwrap();
|
||||||
|
ctx.with_scope(|ctx| {
|
||||||
|
assert_eq!(
|
||||||
|
Err("condition should be bool".to_string()),
|
||||||
|
check_stmts(ctx, ast.statements.as_slice())
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
let ast = parse_program(indoc! {"
|
||||||
|
a = 1
|
||||||
|
b = 1
|
||||||
|
while a < 10:
|
||||||
|
a += 1
|
||||||
|
c = a*2
|
||||||
|
else:
|
||||||
|
c = a*2
|
||||||
|
b = c
|
||||||
|
" })
|
||||||
|
.unwrap();
|
||||||
|
ctx.with_scope(|ctx| {
|
||||||
|
assert_eq!(
|
||||||
|
Err("unbounded identifier".to_string()),
|
||||||
|
check_stmts(ctx, ast.statements.as_slice())
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_for() {
|
||||||
|
let ctx = basic_ctx();
|
||||||
|
let mut ctx = get_inference_context(ctx);
|
||||||
|
|
||||||
|
let ast = parse_program(indoc! {"
|
||||||
|
b = 1
|
||||||
|
for a in [0, 1, 2, 3, 4, 5]:
|
||||||
|
b *= a
|
||||||
|
" })
|
||||||
|
.unwrap();
|
||||||
|
ctx.with_scope(|ctx| {
|
||||||
|
assert_eq!(Ok(false), check_stmts(ctx, ast.statements.as_slice()));
|
||||||
|
});
|
||||||
|
|
||||||
|
let ast = parse_program(indoc! {"
|
||||||
|
b = 1
|
||||||
|
for a, a1 in [(0, 1), (2, 3), (4, 5)]:
|
||||||
|
b *= a
|
||||||
|
" })
|
||||||
|
.unwrap();
|
||||||
|
ctx.with_scope(|ctx| {
|
||||||
|
assert_eq!(Ok(false), check_stmts(ctx, ast.statements.as_slice()));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_return() {
|
||||||
|
let ctx = basic_ctx();
|
||||||
|
let mut ctx = get_inference_context(ctx);
|
||||||
|
|
||||||
|
let ast = parse_program(indoc! {"
|
||||||
|
b = 1
|
||||||
|
return
|
||||||
|
" })
|
||||||
|
.unwrap();
|
||||||
|
ctx.with_scope(|ctx| {
|
||||||
|
assert_eq!(Ok(true), check_stmts(ctx, ast.statements.as_slice()));
|
||||||
|
});
|
||||||
|
|
||||||
|
let ast = parse_program(indoc! {"
|
||||||
|
b = 1
|
||||||
|
if b > 0:
|
||||||
|
return
|
||||||
|
" })
|
||||||
|
.unwrap();
|
||||||
|
ctx.with_scope(|ctx| {
|
||||||
|
assert_eq!(Ok(false), check_stmts(ctx, ast.statements.as_slice()));
|
||||||
|
});
|
||||||
|
|
||||||
|
let ast = parse_program(indoc! {"
|
||||||
|
b = 1
|
||||||
|
if b > 0:
|
||||||
|
return
|
||||||
|
else:
|
||||||
|
return
|
||||||
|
" })
|
||||||
|
.unwrap();
|
||||||
|
ctx.with_scope(|ctx| {
|
||||||
|
assert_eq!(Ok(true), check_stmts(ctx, ast.statements.as_slice()));
|
||||||
|
});
|
||||||
|
|
||||||
|
let ast = parse_program(indoc! {"
|
||||||
|
b = 1
|
||||||
|
while b > 0:
|
||||||
|
return
|
||||||
|
else:
|
||||||
|
return
|
||||||
|
" })
|
||||||
|
.unwrap();
|
||||||
|
ctx.with_scope(|ctx| {
|
||||||
|
// with sophisticated analysis, this one should be Ok(true)
|
||||||
|
// but with our simple implementation, this is Ok(false)
|
||||||
|
// as we don't analyse the control flow
|
||||||
|
assert_eq!(Ok(false), check_stmts(ctx, ast.statements.as_slice()));
|
||||||
|
});
|
||||||
|
|
||||||
|
ctx.set_result(Some(ctx.get_primitive(INT32_TYPE)));
|
||||||
|
let ast = parse_program(indoc! {"
|
||||||
|
b = 1
|
||||||
|
return 1
|
||||||
|
" })
|
||||||
|
.unwrap();
|
||||||
|
ctx.with_scope(|ctx| {
|
||||||
|
assert_eq!(Ok(true), check_stmts(ctx, ast.statements.as_slice()));
|
||||||
|
});
|
||||||
|
|
||||||
|
ctx.set_result(Some(ctx.get_primitive(INT32_TYPE)));
|
||||||
|
let ast = parse_program(indoc! {"
|
||||||
|
b = 1
|
||||||
|
return [1]
|
||||||
|
" })
|
||||||
|
.unwrap();
|
||||||
|
ctx.with_scope(|ctx| {
|
||||||
|
assert_eq!(
|
||||||
|
Err("return type mismatch".to_string()),
|
||||||
|
check_stmts(ctx, ast.statements.as_slice())
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue