From b51168a5abbea6fad87516a984b19e19b5926b3b Mon Sep 17 00:00:00 2001 From: pca006132 Date: Tue, 5 Jan 2021 13:35:44 +0800 Subject: [PATCH] added statement tests --- Cargo.lock | 12 +- nac3core/Cargo.toml | 4 + nac3core/src/statement_check.rs | 334 +++++++++++++++++++++++++++++++- 3 files changed, 348 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index f16dee50..95b40fd4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -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", diff --git a/nac3core/Cargo.toml b/nac3core/Cargo.toml index 62af7c29..ae548be5 100644 --- a/nac3core/Cargo.toml +++ b/nac3core/Cargo.toml @@ -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" + diff --git a/nac3core/src/statement_check.rs b/nac3core/src/statement_check.rs index f480adba..9506763a 100644 --- a/nac3core/src/statement_check.rs +++ b/nac3core/src/statement_check.rs @@ -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()) + ); + }); + } +}