From cd69f74005a7e71594f8f688972e12b32a4b1d4a Mon Sep 17 00:00:00 2001 From: Lucas Steuernagel Date: Tue, 17 Aug 2021 17:44:45 -0300 Subject: [PATCH] Add more tests for undefined variable detection --- src/codegen/statements.rs | 2 +- src/codegen/undefined_variable.rs | 3 + tests/undefined_variable_detection.rs | 159 +++++++++++++++++++++++++- 3 files changed, 159 insertions(+), 5 deletions(-) diff --git a/src/codegen/statements.rs b/src/codegen/statements.rs index be9e0a48a..f721bfd19 100644 --- a/src/codegen/statements.rs +++ b/src/codegen/statements.rs @@ -1216,7 +1216,7 @@ impl Type { )), None, )), - Type::InternalFunction { .. } | Type::ExternalFunction { .. } => None, + Type::InternalFunction { .. } | Type::Contract(_) | Type::ExternalFunction { .. } => None, Type::Array(_, dims) => { if dims[0].is_none() { Some(Expression::AllocDynamicArray( diff --git a/src/codegen/undefined_variable.rs b/src/codegen/undefined_variable.rs index 846ac5380..6806bb86c 100644 --- a/src/codegen/undefined_variable.rs +++ b/src/codegen/undefined_variable.rs @@ -96,6 +96,9 @@ pub fn find_undefined_variables_in_expression( false } + // This is a method call whose array will never be undefined + Expression::DynamicArrayLength(..) => false, + _ => true, } } diff --git a/tests/undefined_variable_detection.rs b/tests/undefined_variable_detection.rs index bdf80abfe..ba032497d 100644 --- a/tests/undefined_variable_detection.rs +++ b/tests/undefined_variable_detection.rs @@ -33,10 +33,10 @@ fn get_errors(ns: &Namespace) -> Vec<&Diagnostic> { vec } -fn contains_error_message(errors: &[&Diagnostic], message: &str) -> bool { +fn contains_error_message_and_notes(errors: &[&Diagnostic], message: &str, notes_no: usize) -> bool { for error in errors { if error.message == message { - return true; + return true && error.notes.len() == notes_no; } } @@ -273,8 +273,8 @@ fn for_loop() { let ns = parse_and_codegen(file); let errors = get_errors(&ns); assert_eq!(errors.len(), 2); - assert!(contains_error_message(&errors, "Variable 'p' is undefined")); - assert!(contains_error_message(&errors, "Variable 's' is undefined")); + assert!(contains_error_message_and_notes(&errors, "Variable 'p' is undefined", 1)); + assert!(contains_error_message_and_notes(&errors, "Variable 's' is undefined", 1)); let file = r#" contract testing { @@ -408,6 +408,157 @@ fn if_else_condition() { assert_eq!(errors.len(), 0); } +#[test] +fn array() { + let file = r#" + contract test { + + function testing(int x) public pure returns (int) { + int[] vec; + + return vec[0]; + } + } + "#; + let ns = parse_and_codegen(file); + let errors = get_errors(&ns); + assert_eq!(errors.len(), 1); + assert_eq!(errors[0].message, "Variable 'vec' is undefined"); + assert_eq!(errors[0].notes.len(), 1); + assert_eq!( + errors[0].notes[0].message, + "Variable read before being defined" + ); + + let file = r#" + contract test { + + function testing(int x) public pure returns (int) { + int[] vec; + + if(x > 0) { + vec.push(2); + } + + return vec[0]; + } + } + "#; + + let ns = parse_and_codegen(file); + let errors = get_errors(&ns); + assert_eq!(errors.len(), 0); +} + +#[test] +fn contract_and_enum() { + let file = r#" +contract other { + int public a; + + function testing() public returns (int) { + return 2; + } +} + + +contract test { + enum FreshJuiceSize{ SMALL, MEDIUM, LARGE } + function testing(int x) public returns (int) { + other o; + FreshJuiceSize choice; + if(x > 0 && o.testing() < 5) { + o = new other(); + } + + assert(choice == FreshJuiceSize.LARGE); + + return o.a(); + } +} + "#; + + let ns = parse_and_codegen(file); + let errors = get_errors(&ns); + assert!(contains_error_message_and_notes(&errors, "Variable 'o' is undefined", 2)); + assert!(contains_error_message_and_notes(&errors, "Variable 'choice' is undefined", 1)); +} + +#[test] +fn basic_types() { + let file = r#" + contract test { + + function testing(int x) public returns (address, int, uint, bool, bytes, int) { + address a; + int i; + uint u; + bool b; + bytes bt; + int[5] vec; + + while(x > 0) { + x--; + a = address(this); + i = -2; + u = 2; + b = true; + bt = hex"1234"; + vec[0] = 2; + } + return (a, i, u, b, bt, vec[1]); + } +} + "#; + let ns = parse_and_codegen(file); + let errors = get_errors(&ns); + assert!(contains_error_message_and_notes(&errors, "Variable 'bt' is undefined", 1)); + assert!(contains_error_message_and_notes(&errors, "Variable 'b' is undefined", 1)); + assert!(contains_error_message_and_notes(&errors, "Variable 'a' is undefined", 1)); + assert!(contains_error_message_and_notes(&errors, "Variable 'i' is undefined", 1)); + assert!(contains_error_message_and_notes(&errors, "Variable 'u' is undefined", 1)); + +} + +#[test] +fn nested_branches() { + let file = r#" + +contract test { + + function testing(int x) public returns (int) { + int i; + + while(x > 0) { + int b; + if(x > 5) { + b = 2; + } + + i = b; + } + int a; + if(x < 5) { + if(x < 2) { + a = 2; + } else { + a = 1; + } + } else if(x < 4) { + a = 5; + } + + return i + a; + } +} + "#; + let ns = parse_and_codegen(file); + let errors = get_errors(&ns); + assert!(contains_error_message_and_notes(&errors, "Variable 'b' is undefined", 1)); + assert!(contains_error_message_and_notes(&errors, "Variable 'a' is undefined", 1)); + assert!(contains_error_message_and_notes(&errors, "Variable 'i' is undefined", 2)); +} + //TODO: Fix this test // #[test]