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",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "indoc"
|
||||
version = "1.0.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e5a75aeaaef0ce18b58056d306c27b07436fbb34b8816c53094b76dd81803136"
|
||||
dependencies = [
|
||||
"unindent",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "indoc-impl"
|
||||
version = "0.3.6"
|
||||
|
@ -449,6 +458,7 @@ checksum = "0ee1c47aaa256ecabcaea351eae4a9b01ef39ed810004e298d2511ed284b1525"
|
|||
name = "nac3core"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"indoc 1.0.3",
|
||||
"inkwell",
|
||||
"num-bigint",
|
||||
"num-traits",
|
||||
|
@ -613,7 +623,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "bf6bbbe8f70d179260b3728e5d04eb012f4f0c7988e58c11433dd689cecaa72e"
|
||||
dependencies = [
|
||||
"ctor",
|
||||
"indoc",
|
||||
"indoc 0.3.6",
|
||||
"inventory",
|
||||
"libc",
|
||||
"parking_lot",
|
||||
|
|
|
@ -9,3 +9,7 @@ num-bigint = "0.3"
|
|||
num-traits = "0.2"
|
||||
inkwell = { git = "https://github.com/TheDan64/inkwell", branch = "master", features = ["llvm10-0"] }
|
||||
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)?;
|
||||
}
|
||||
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 } => {
|
||||
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())
|
||||
}
|
||||
}
|
||||
|
||||
#[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