diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 4d23bf6..dfabebf 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -61,7 +61,7 @@ jobs: - name: Run grcov run: | mkdir ./target/debug/coverage/ - grcov . -s . -b ./target/debug/ -o ./target/debug/coverage/ --ignore-not-existing --excl-line="grcov-excl-line|#\\[derive\\(|//!|///" --ignore="*.cargo/*" --ignore="src/lib.rs" + grcov . -s . -b ./target/debug/ -o ./target/debug/coverage/ --ignore-not-existing --excl-line="grcov-excl-line|#\\[derive\\(|//!|///" --ignore="*.cargo/*" --ignore="src/lib.rs" --ignore="tests/*" - name: Upload coverage reports to Codecov uses: codecov/codecov-action@v3 env: diff --git a/Cargo.toml b/Cargo.toml index e6495b4..385ccb7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "semantic-analyzer" -version = "0.2.5" +version = "0.2.6" authors = ["Evgeny Ukhanov "] description = "Semantic analyzer library for compilers written in Rust for semantic analysis of programming languages AST" keywords = ["compiler", "semantic-analisis", "semantic-alalyzer", "compiler-design", "semantic"] diff --git a/src/semantic.rs b/src/semantic.rs index 256c1f6..8f53c42 100644 --- a/src/semantic.rs +++ b/src/semantic.rs @@ -10,7 +10,7 @@ //! - `Context` - stack for `Block state` of each functions body state. //! - `Errors` - semantic analyzes errors.z -use crate::ast::{self, GetLocation, GetName, MAX_PRIORITY_LEVEL_FOR_EXPRESSIONS}; +use crate::ast::{self, CodeLocation, GetLocation, GetName, MAX_PRIORITY_LEVEL_FOR_EXPRESSIONS}; use crate::types::block_state::BlockState; use crate::types::expression::{ Expression, ExpressionResult, ExpressionResultValue, ExpressionStructValue, @@ -290,6 +290,13 @@ impl State { let mut return_is_called = false; // Fetch function elements and gather errors for body in &data.body { + if return_is_called { + self.add_error(error::StateErrorResult::new( + error::StateErrorKind::ForbiddenCodeAfterReturnDeprecated, + format!("{body:?}"), + CodeLocation::new(1, 1), + )); + } match body { ast::BodyStatement::LetBinding(bind) => { self.let_binding(bind, &body_state); @@ -599,14 +606,24 @@ impl State { /// Analyze body for ant if condition: /// - if, else, if-else /// NOTE: label_end - is always already exists + /// ## Return + /// Return body statement "return" status pub fn if_condition_body( &mut self, body: &[ast::IfBodyStatement<'_>], if_body_state: &Rc>, label_end: &LabelName, label_loop: Option<(&LabelName, &LabelName)>, - ) { + ) -> bool { + let mut return_is_called = false; for body in body.iter() { + if return_is_called { + self.add_error(error::StateErrorResult::new( + error::StateErrorKind::ForbiddenCodeAfterReturnDeprecated, + format!("{body:?}"), + CodeLocation::new(1, 1), + )); + } match body { ast::IfBodyStatement::LetBinding(bind) => { self.let_binding(bind, if_body_state); @@ -636,15 +653,19 @@ impl State { // return if_body_state.borrow_mut().context.jump_function_return(res); if_body_state.borrow_mut().set_return(); + return_is_called = true; }; } } } + return_is_called } /// # If-condition loop body /// Analyze body for ant if condition: /// - if, else, if-else + /// ## Return + /// Return body statement "return" status pub fn if_condition_loop_body( &mut self, body: &[ast::IfLoopBodyStatement<'_>], @@ -652,8 +673,33 @@ impl State { label_if_end: &LabelName, label_loop_start: &LabelName, label_loop_end: &LabelName, - ) { + ) -> bool { + let mut return_is_called = false; + let mut break_is_called = false; + let mut continue_is_called = false; for body in body.iter() { + if return_is_called { + self.add_error(error::StateErrorResult::new( + error::StateErrorKind::ForbiddenCodeAfterReturnDeprecated, + format!("{body:?}"), + CodeLocation::new(1, 1), + )); + } + if break_is_called { + self.add_error(error::StateErrorResult::new( + error::StateErrorKind::ForbiddenCodeAfterBreakDeprecated, + format!("{body:?}"), + CodeLocation::new(1, 1), + )); + } + if continue_is_called { + self.add_error(error::StateErrorResult::new( + error::StateErrorKind::ForbiddenCodeAfterContinueDeprecated, + format!("{body:?}"), + CodeLocation::new(1, 1), + )); + } + match body { ast::IfLoopBodyStatement::LetBinding(bind) => { self.let_binding(bind, if_body_state); @@ -683,9 +729,11 @@ impl State { // return if_body_state.borrow_mut().context.jump_function_return(res); if_body_state.borrow_mut().set_return(); + return_is_called = true; } } ast::IfLoopBodyStatement::Continue => { + continue_is_called = true; // Skip next loop step and jump to the start // of loop if_body_state @@ -694,6 +742,7 @@ impl State { .jump_to(label_loop_start.clone()); } ast::IfLoopBodyStatement::Break => { + break_is_called = true; // Break loop and jump to the end of loop if_body_state .borrow_mut() @@ -702,6 +751,7 @@ impl State { } } } + return_is_called } /// # If conditions calculations @@ -833,11 +883,12 @@ impl State { //== If condition main body // Set if-begin label if_body_state.borrow_mut().context.set_label(label_if_begin); - // Analyze if-conditions body kind - match &data.body { + // Analyze if-conditions body kind. + // Return flag for current body state, excluding children return claims + let return_is_called = match &data.body { ast::IfBodyStatements::If(body) => { // Analyze if-statement body - self.if_condition_body(body, &if_body_state, &label_if_end, label_loop); + self.if_condition_body(body, &if_body_state, &label_if_end, label_loop) } ast::IfBodyStatements::Loop(body) => { // It's special case for the Loop, when `label_loop` should always be set. @@ -851,12 +902,12 @@ impl State { &label_if_end, label_loop_start, label_loop_end, - ); + ) } - } + }; // Codegen for jump to if-end statement - return to program flow. // If return is set do not add jump-to-end label. - if !if_body_state.borrow().manual_return { + if !return_is_called { if_body_state .borrow_mut() .context @@ -878,15 +929,10 @@ impl State { .borrow_mut() .set_child(if_else_body_state.clone()); - match else_body { + let return_is_called = match else_body { ast::IfBodyStatements::If(body) => { // Analyze if-statement body - self.if_condition_body( - body, - &if_else_body_state, - &label_if_end, - label_loop, - ); + self.if_condition_body(body, &if_else_body_state, &label_if_end, label_loop) } ast::IfBodyStatements::Loop(body) => { let (label_loop_start, label_loop_end) = @@ -898,13 +944,13 @@ impl State { &label_if_end, label_loop_start, label_loop_end, - ); + ) } - } + }; // Codegen for jump to if-end statement -return to program flow // If return is set do not add jump-to-end label. - if !if_body_state.borrow().manual_return { + if !return_is_called { if_body_state .borrow_mut() .context @@ -966,7 +1012,32 @@ impl State { .context .set_label(label_loop_begin.clone()); + let mut return_is_called = false; + let mut break_is_called = false; + let mut continue_is_called = false; for body in data.iter() { + if return_is_called { + self.add_error(error::StateErrorResult::new( + error::StateErrorKind::ForbiddenCodeAfterReturnDeprecated, + format!("{body:?}"), + CodeLocation::new(1, 1), + )); + } + if break_is_called { + self.add_error(error::StateErrorResult::new( + error::StateErrorKind::ForbiddenCodeAfterBreakDeprecated, + format!("{body:?}"), + CodeLocation::new(1, 1), + )); + } + if continue_is_called { + self.add_error(error::StateErrorResult::new( + error::StateErrorKind::ForbiddenCodeAfterContinueDeprecated, + format!("{body:?}"), + CodeLocation::new(1, 1), + )); + } + match body { ast::LoopBodyStatement::LetBinding(bind) => { self.let_binding(bind, &loop_body_state); @@ -997,6 +1068,7 @@ impl State { .context .jump_function_return(res); loop_body_state.borrow_mut().set_return(); + return_is_called = true; } } ast::LoopBodyStatement::Break => { @@ -1005,6 +1077,7 @@ impl State { .borrow_mut() .context .jump_to(label_loop_end.clone()); + break_is_called = true; } ast::LoopBodyStatement::Continue => { // Skip next loop step and jump to the start @@ -1013,21 +1086,25 @@ impl State { .borrow_mut() .context .jump_to(label_loop_begin.clone()); + continue_is_called = true; } } } - // Because it's loop jump to loop begin - loop_body_state - .borrow_mut() - .context - .jump_to(label_loop_begin.clone()); + // If return is called do not set loop-specific instructions + if !return_is_called { + // Because it's loop jump to loop begin + loop_body_state + .borrow_mut() + .context + .jump_to(label_loop_begin.clone()); - // Loop ending - loop_body_state - .borrow_mut() - .context - .set_label(label_loop_end); + // Loop ending + loop_body_state + .borrow_mut() + .context + .set_label(label_loop_end); + } } #[allow(clippy::doc_markdown)] diff --git a/src/types/error.rs b/src/types/error.rs index fb8149e..6f9cfb3 100644 --- a/src/types/error.rs +++ b/src/types/error.rs @@ -27,6 +27,9 @@ pub enum StateErrorKind { WrongReturnType, ConditionExpressionWrongType, ConditionExpressionNotSupported, + ForbiddenCodeAfterReturnDeprecated, + ForbiddenCodeAfterContinueDeprecated, + ForbiddenCodeAfterBreakDeprecated, } /// State error location. Useful to determine location of error diff --git a/tests/binding_tests.rs b/tests/binding_tests.rs index 4decb15..5a311be 100644 --- a/tests/binding_tests.rs +++ b/tests/binding_tests.rs @@ -45,8 +45,12 @@ fn binding_wrong_expression() { value: Box::new(expr), }; t.state.binding(&binding, &block_state); - assert!(t.check_errors_len(1)); - assert!(t.check_error(StateErrorKind::ValueNotFound)); + assert!(t.check_errors_len(1), "Errors: {:?}", t.state.errors.len()); + assert!( + t.check_error(StateErrorKind::ValueNotFound), + "Errors: {:?}", + t.state.errors[0] + ); } #[test] @@ -62,8 +66,12 @@ fn binding_value_not_exist() { value: Box::new(expr), }; t.state.binding(&binding, &block_state); - assert!(t.check_errors_len(1)); - assert!(t.check_error(StateErrorKind::ValueNotFound)); + assert!(t.check_errors_len(1), "Errors: {:?}", t.state.errors.len()); + assert!( + t.check_error(StateErrorKind::ValueNotFound), + "Errors: {:?}", + t.state.errors[0] + ); } #[test] @@ -113,8 +121,12 @@ fn binding_value_not_mutable() { value: Box::new(expr), }; t.state.binding(&binding, &block_state); - assert!(t.check_errors_len(1)); - assert!(t.check_error(StateErrorKind::ValueIsNotMutable)); + assert!(t.check_errors_len(1), "Errors: {:?}", t.state.errors.len()); + assert!( + t.check_error(StateErrorKind::ValueIsNotMutable), + "Errors: {:?}", + t.state.errors[0] + ); } #[test] diff --git a/tests/const_tests.rs b/tests/const_tests.rs index 3f2df2f..dd6c7df 100644 --- a/tests/const_tests.rs +++ b/tests/const_tests.rs @@ -120,8 +120,12 @@ fn const_declaration() { ); t.state.constant(&const_statement); - assert!(t.check_errors_len(1)); - assert!(t.check_error(StateErrorKind::ConstantAlreadyExist)); + assert!(t.check_errors_len(1), "Errors: {:?}", t.state.errors.len()); + assert!( + t.check_error(StateErrorKind::ConstantAlreadyExist), + "Errors: {:?}", + t.state.errors[0] + ); let state = t.state.global.context.clone().get(); assert_eq!(state.len(), 1); } @@ -153,8 +157,12 @@ fn const_declaration_with_operations() { }; assert_eq!(const_statement.location(), CodeLocation::new(1, 0)); t.state.constant(&const_statement); - assert!(t.check_errors_len(1)); - assert!(t.check_error(StateErrorKind::ConstantNotFound)); + assert!(t.check_errors_len(1), "Errors: {:?}", t.state.errors.len()); + assert!( + t.check_error(StateErrorKind::ConstantNotFound), + "Errors: {:?}", + t.state.errors[0] + ); assert!(!t .state .global diff --git a/tests/expressions_tests.rs b/tests/expressions_tests.rs index 9778986..a63c0d8 100644 --- a/tests/expressions_tests.rs +++ b/tests/expressions_tests.rs @@ -366,10 +366,14 @@ fn expression_value_name_not_found() { }; let res = t.state.expression(&expr, &block_state); assert!(res.is_none()); - assert!(t.check_error(StateErrorKind::ValueNotFound)); + assert!( + t.check_error(StateErrorKind::ValueNotFound), + "Errors: {:?}", + t.state.errors[0] + ); let state = block_state.borrow().context.clone().get(); assert!(state.is_empty()); - assert!(t.check_errors_len(1)); + assert!(t.check_errors_len(1), "Errors: {:?}", t.state.errors.len()); } #[test] @@ -529,8 +533,12 @@ fn expression_struct_value_not_found() { }; let res = t.state.expression(&expr, &block_state); assert!(res.is_none()); - assert!(t.check_errors_len(1)); - assert!(t.check_error(StateErrorKind::ValueNotFound)); + assert!(t.check_errors_len(1), "Errors: {:?}", t.state.errors.len()); + assert!( + t.check_error(StateErrorKind::ValueNotFound), + "Errors: {:?}", + t.state.errors[0] + ); } #[test] @@ -558,8 +566,12 @@ fn expression_struct_value_wrong_struct_type() { .insert("x".into(), val.clone()); let res = t.state.expression(&expr, &block_state); assert!(res.is_none()); - assert!(t.check_errors_len(1)); - assert!(t.check_error(StateErrorKind::ValueNotStruct)); + assert!(t.check_errors_len(1), "Errors: {:?}", t.state.errors.len()); + assert!( + t.check_error(StateErrorKind::ValueNotStruct), + "Errors: {:?}", + t.state.errors[0] + ); } #[test] @@ -595,8 +607,12 @@ fn expression_struct_value_type_not_found() { .insert("x".into(), val.clone()); let res = t.state.expression(&expr, &block_state); assert!(res.is_none()); - assert!(t.check_errors_len(1)); - assert!(t.check_error(StateErrorKind::TypeNotFound)); + assert!(t.check_errors_len(1), "Errors: {:?}", t.state.errors.len()); + assert!( + t.check_error(StateErrorKind::TypeNotFound), + "Errors: {:?}", + t.state.errors[0] + ); } #[test] @@ -643,8 +659,12 @@ fn expression_struct_value_wrong_expression_type() { assert!(t.is_empty_error()); let res = t.state.expression(&expr, &block_state); assert!(res.is_none()); - assert!(t.check_errors_len(1)); - assert!(t.check_error(StateErrorKind::WrongExpressionType)); + assert!(t.check_errors_len(1), "Errors: {:?}", t.state.errors.len()); + assert!( + t.check_error(StateErrorKind::WrongExpressionType), + "Errors: {:?}", + t.state.errors[0] + ); } #[test] @@ -691,8 +711,12 @@ fn expression_struct_value_wrong_struct_attribute() { assert!(t.is_empty_error()); let res = t.state.expression(&expr, &block_state); assert!(res.is_none()); - assert!(t.check_errors_len(1)); - assert!(t.check_error(StateErrorKind::ValueNotStructField)); + assert!(t.check_errors_len(1), "Errors: {:?}", t.state.errors.len()); + assert!( + t.check_error(StateErrorKind::ValueNotStructField), + "Errors: {:?}", + t.state.errors[0] + ); } #[test] @@ -777,8 +801,12 @@ fn expression_func_call() { }; let res = t.state.expression(&expr, &block_state); assert!(res.is_none()); - assert!(t.check_errors_len(1)); - assert!(t.check_error(StateErrorKind::FunctionNotFound)); + assert!(t.check_errors_len(1), "Errors: {:?}", t.state.errors.len()); + assert!( + t.check_error(StateErrorKind::FunctionNotFound), + "Errors: {:?}", + t.state.errors[0] + ); t.clean_errors(); // Declare function @@ -892,8 +920,12 @@ fn expression_operation_wrong_type() { operation: Some((ast::ExpressionOperations::Plus, Box::new(next_expr))), }; let res = t.state.expression(&expr, &block_state); - assert!(t.check_errors_len(1)); - assert!(t.check_error(StateErrorKind::WrongExpressionType)); + assert!(t.check_errors_len(1), "Errors: {:?}", t.state.errors.len()); + assert!( + t.check_error(StateErrorKind::WrongExpressionType), + "Errors: {:?}", + t.state.errors[0] + ); assert!(res.is_none()); let state = block_state.borrow().context.clone().get(); assert!(state.is_empty()); diff --git a/tests/function_call_tests.rs b/tests/function_call_tests.rs index 2c6beca..bc91591 100644 --- a/tests/function_call_tests.rs +++ b/tests/function_call_tests.rs @@ -52,8 +52,12 @@ fn func_call_not_declared_func() { }; let res = t.state.function_call(&fn_call, &block_state); assert!(res.is_none()); - assert!(t.check_errors_len(1)); - assert!(t.check_error(StateErrorKind::FunctionNotFound)); + assert!(t.check_errors_len(1), "Errors: {:?}", t.state.errors.len()); + assert!( + t.check_error(StateErrorKind::FunctionNotFound), + "Errors: {:?}", + t.state.errors[0] + ); } #[test] @@ -84,8 +88,12 @@ fn func_call_wrong_type() { }; let res = t.state.function_call(&fn_call, &block_state).unwrap(); assert_eq!(res, Type::Primitive(PrimitiveTypes::I16)); - assert!(t.check_errors_len(1)); - assert!(t.check_error(StateErrorKind::FunctionParameterTypeWrong)); + assert!(t.check_errors_len(1), "Errors: {:?}", t.state.errors.len()); + assert!( + t.check_error(StateErrorKind::FunctionParameterTypeWrong), + "Errors: {:?}", + t.state.errors[0] + ); } #[test] diff --git a/tests/function_declaration_tests.rs b/tests/function_declaration_tests.rs index 36778ed..9d7eceb 100644 --- a/tests/function_declaration_tests.rs +++ b/tests/function_declaration_tests.rs @@ -35,8 +35,12 @@ fn function_declaration_without_body() { ); t.state.function_declaration(&fn_statement); - assert!(t.check_errors_len(1)); - assert!(t.check_error(StateErrorKind::FunctionAlreadyExist)); + assert!(t.check_errors_len(1), "Errors: {:?}", t.state.errors.len()); + assert!( + t.check_error(StateErrorKind::FunctionAlreadyExist), + "Errors: {:?}", + t.state.errors[0] + ); } #[test] @@ -59,8 +63,12 @@ fn function_declaration_wrong_type() { body: vec![], }; t.state.function_declaration(&fn_statement); - assert!(t.check_errors_len(1)); - assert!(t.check_error(StateErrorKind::TypeNotFound)); + assert!(t.check_errors_len(1), "Errors: {:?}", t.state.errors.len()); + assert!( + t.check_error(StateErrorKind::TypeNotFound), + "Errors: {:?}", + t.state.errors[0] + ); assert!(!t .state @@ -79,7 +87,7 @@ fn function_declaration_wrong_type() { ..fn_statement }; t.state.function_declaration(&fn_statement2); - assert!(t.check_errors_len(2)); + assert!(t.check_errors_len(2), "Errors: {:?}", t.state.errors.len()); assert!(t.check_error_index(1, StateErrorKind::TypeNotFound)); t.clean_errors(); diff --git a/tests/if_tests.rs b/tests/if_tests.rs index c759060..580cc20 100644 --- a/tests/if_tests.rs +++ b/tests/if_tests.rs @@ -306,8 +306,12 @@ fn check_if_and_else_if_statement_duplicate() { else_if_statement: Some(Box::new(if_else_stmt)), }; t.state.if_condition(&if_stmt, &block_state, None, None); - t.check_errors_len(1); - t.check_error(StateErrorKind::IfElseDuplicated); + assert!(t.check_errors_len(1), "Errors: {:?}", t.state.errors.len()); + assert!( + t.check_error(StateErrorKind::IfElseDuplicated), + "Errors: {:?}", + t.state.errors[0] + ); } #[test] @@ -384,8 +388,12 @@ fn if_condition_calculation_simple() { label_if_end: label_if_else, } ); - assert!(t.check_errors_len(1)); - assert!(t.check_error(StateErrorKind::ValueNotFound)); + assert!(t.check_errors_len(1), "Errors: {:?}", t.state.errors.len()); + assert!( + t.check_error(StateErrorKind::ValueNotFound), + "Errors: {:?}", + t.state.errors[0] + ); } #[test] @@ -549,8 +557,12 @@ fn if_condition_when_left_expr_return_error() { &label_if_end, false, ); - assert!(t.check_errors_len(1)); - assert!(t.check_error(StateErrorKind::FunctionNotFound)); + assert!(t.check_errors_len(1), "Errors: {:?}", t.state.errors.len()); + assert!( + t.check_error(StateErrorKind::FunctionNotFound), + "Errors: {:?}", + t.state.errors[0] + ); } #[test] @@ -587,8 +599,12 @@ fn if_condition_left_expr_and_right_expr_different_type() { &label_if_end, false, ); - assert!(t.check_errors_len(1)); - assert!(t.check_error(StateErrorKind::ConditionExpressionWrongType)); + assert!(t.check_errors_len(1), "Errors: {:?}", t.state.errors.len()); + assert!( + t.check_error(StateErrorKind::ConditionExpressionWrongType), + "Errors: {:?}", + t.state.errors[0] + ); } #[test] @@ -646,8 +662,12 @@ fn if_condition_primitive_type_only_check() { &label_if_end, false, ); - assert!(t.check_errors_len(1)); - assert!(t.check_error(StateErrorKind::ConditionExpressionNotSupported)); + assert!(t.check_errors_len(1), "Errors: {:?}", t.state.errors.len()); + assert!( + t.check_error(StateErrorKind::ConditionExpressionNotSupported), + "Errors: {:?}", + t.state.errors[0] + ); } #[test] @@ -1142,8 +1162,6 @@ fn if_loop_body_statements() { expression_value: ast::ExpressionValue::PrimitiveValue(ast::PrimitiveValue::Bool(true)), operation: None, }); - let if_body_break = ast::IfLoopBodyStatement::Break; - let if_body_continue = ast::IfLoopBodyStatement::Continue; let label_loop_begin: LabelName = String::from("loop_begin").into(); let label_loop_end: LabelName = String::from("loop_end").into(); @@ -1156,8 +1174,6 @@ fn if_loop_body_statements() { if_body_fn_call, if_body_if, if_body_loop, - if_body_break, - if_body_continue, if_body_return, ]), else_statement: None, @@ -1182,7 +1198,7 @@ fn if_loop_body_statements() { assert_eq!(ctx.borrow().children.len(), 2); let stm_ctx = ctx.borrow().context.clone().get(); - assert_eq!(stm_ctx.len(), 9); + assert_eq!(stm_ctx.len(), 7); assert_eq!( stm_ctx[0], SemanticStackContext::IfConditionExpression { @@ -1245,18 +1261,6 @@ fn if_loop_body_statements() { ); assert_eq!( stm_ctx[5], - SemanticStackContext::JumpTo { - label: String::from("loop_end").into() - } - ); - assert_eq!( - stm_ctx[6], - SemanticStackContext::JumpTo { - label: String::from("loop_begin").into() - } - ); - assert_eq!( - stm_ctx[7], SemanticStackContext::JumpFunctionReturn { expr_result: ExpressionResult { expr_type: Type::Primitive(PrimitiveTypes::Bool), @@ -1265,7 +1269,7 @@ fn if_loop_body_statements() { } ); assert_eq!( - stm_ctx[8], + stm_ctx[6], SemanticStackContext::SetLabel { label: String::from("if_end").into() } @@ -1354,3 +1358,295 @@ fn if_loop_body_statements() { } ); } + +#[test] +fn if_loop_body_instructions_after_return() { + let block_state = Rc::new(RefCell::new(BlockState::new(None))); + let mut t = SemanticTest::new(); + let if_expr = ast::Expression { + expression_value: ast::ExpressionValue::PrimitiveValue(ast::PrimitiveValue::U64(1)), + operation: None, + }; + + let if_body_let_binding = ast::IfLoopBodyStatement::LetBinding(ast::LetBinding { + name: ast::ValueName::new(Ident::new("x")), + mutable: true, + value_type: None, + value: Box::new(ast::Expression { + expression_value: ast::ExpressionValue::PrimitiveValue(ast::PrimitiveValue::Bool( + false, + )), + operation: None, + }), + }); + let if_body_return = ast::IfLoopBodyStatement::Return(ast::Expression { + expression_value: ast::ExpressionValue::PrimitiveValue(ast::PrimitiveValue::Bool(true)), + operation: None, + }); + let label_loop_begin: LabelName = String::from("loop_begin").into(); + let label_loop_end: LabelName = String::from("loop_end").into(); + + let if_stmt = ast::IfStatement { + condition: ast::IfCondition::Single(if_expr), + body: ast::IfBodyStatements::Loop(vec![if_body_return, if_body_let_binding]), + else_statement: None, + else_if_statement: None, + }; + + t.state.if_condition( + &if_stmt, + &block_state, + None, + Some((&label_loop_begin, &label_loop_end)), + ); + + assert!(t.check_errors_len(1), "Errors: {:?}", t.state.errors.len()); + assert!( + t.check_error(StateErrorKind::ForbiddenCodeAfterReturnDeprecated), + "Errors: {:?}", + t.state.errors[0] + ); +} + +#[test] +fn else_if_loop_body_instructions_after_return() { + let block_state = Rc::new(RefCell::new(BlockState::new(None))); + let mut t = SemanticTest::new(); + let if_expr = ast::Expression { + expression_value: ast::ExpressionValue::PrimitiveValue(ast::PrimitiveValue::U64(1)), + operation: None, + }; + + let if_body_let_binding = ast::IfLoopBodyStatement::LetBinding(ast::LetBinding { + name: ast::ValueName::new(Ident::new("x")), + mutable: true, + value_type: None, + value: Box::new(ast::Expression { + expression_value: ast::ExpressionValue::PrimitiveValue(ast::PrimitiveValue::Bool( + false, + )), + operation: None, + }), + }); + let if_body_return = ast::IfLoopBodyStatement::Return(ast::Expression { + expression_value: ast::ExpressionValue::PrimitiveValue(ast::PrimitiveValue::Bool(true)), + operation: None, + }); + let label_loop_begin: LabelName = String::from("loop_begin").into(); + let label_loop_end: LabelName = String::from("loop_end").into(); + + let if_stmt = ast::IfStatement { + condition: ast::IfCondition::Single(if_expr), + body: ast::IfBodyStatements::Loop(vec![if_body_return.clone()]), + else_statement: Some(ast::IfBodyStatements::Loop(vec![ + if_body_return.clone(), + if_body_let_binding, + ])), + else_if_statement: None, + }; + + t.state.if_condition( + &if_stmt, + &block_state, + None, + Some((&label_loop_begin, &label_loop_end)), + ); + + assert!(t.check_errors_len(1), "Errors: {:?}", t.state.errors.len()); + assert!( + t.check_error(StateErrorKind::ForbiddenCodeAfterReturnDeprecated), + "Errors: {:?}", + t.state.errors[0] + ); +} + +#[test] +fn if_body_instructions_after_return() { + let block_state = Rc::new(RefCell::new(BlockState::new(None))); + let mut t = SemanticTest::new(); + let if_expr = ast::Expression { + expression_value: ast::ExpressionValue::PrimitiveValue(ast::PrimitiveValue::U64(1)), + operation: None, + }; + + let if_body_let_binding = ast::IfBodyStatement::LetBinding(ast::LetBinding { + name: ast::ValueName::new(Ident::new("x")), + mutable: true, + value_type: None, + value: Box::new(ast::Expression { + expression_value: ast::ExpressionValue::PrimitiveValue(ast::PrimitiveValue::Bool( + false, + )), + operation: None, + }), + }); + let if_body_return = ast::IfBodyStatement::Return(ast::Expression { + expression_value: ast::ExpressionValue::PrimitiveValue(ast::PrimitiveValue::Bool(true)), + operation: None, + }); + let label_loop_begin: LabelName = String::from("loop_begin").into(); + let label_loop_end: LabelName = String::from("loop_end").into(); + + let if_stmt = ast::IfStatement { + condition: ast::IfCondition::Single(if_expr), + body: ast::IfBodyStatements::If(vec![if_body_return, if_body_let_binding]), + else_statement: None, + else_if_statement: None, + }; + + t.state.if_condition( + &if_stmt, + &block_state, + None, + Some((&label_loop_begin, &label_loop_end)), + ); + + assert!(t.check_errors_len(1), "Errors: {:?}", t.state.errors.len()); + assert!( + t.check_error(StateErrorKind::ForbiddenCodeAfterReturnDeprecated), + "Errors: {:?}", + t.state.errors[0] + ); +} + +#[test] +fn if_else_body_instructions_after_return() { + let block_state = Rc::new(RefCell::new(BlockState::new(None))); + let mut t = SemanticTest::new(); + let if_expr = ast::Expression { + expression_value: ast::ExpressionValue::PrimitiveValue(ast::PrimitiveValue::U64(1)), + operation: None, + }; + + let if_body_let_binding = ast::IfBodyStatement::LetBinding(ast::LetBinding { + name: ast::ValueName::new(Ident::new("x")), + mutable: true, + value_type: None, + value: Box::new(ast::Expression { + expression_value: ast::ExpressionValue::PrimitiveValue(ast::PrimitiveValue::Bool( + false, + )), + operation: None, + }), + }); + let if_body_return = ast::IfBodyStatement::Return(ast::Expression { + expression_value: ast::ExpressionValue::PrimitiveValue(ast::PrimitiveValue::Bool(true)), + operation: None, + }); + let label_loop_begin: LabelName = String::from("loop_begin").into(); + let label_loop_end: LabelName = String::from("loop_end").into(); + + let if_stmt = ast::IfStatement { + condition: ast::IfCondition::Single(if_expr), + body: ast::IfBodyStatements::If(vec![if_body_return.clone()]), + else_statement: Some(ast::IfBodyStatements::If(vec![ + if_body_return, + if_body_let_binding, + ])), + else_if_statement: None, + }; + + t.state.if_condition( + &if_stmt, + &block_state, + None, + Some((&label_loop_begin, &label_loop_end)), + ); + + assert!(t.check_errors_len(1), "Errors: {:?}", t.state.errors.len()); + assert!( + t.check_error(StateErrorKind::ForbiddenCodeAfterReturnDeprecated), + "Errors: {:?}", + t.state.errors[0] + ); +} + +#[test] +fn if_loop_body_instructions_after_break() { + let block_state = Rc::new(RefCell::new(BlockState::new(None))); + let mut t = SemanticTest::new(); + let if_expr = ast::Expression { + expression_value: ast::ExpressionValue::PrimitiveValue(ast::PrimitiveValue::U64(1)), + operation: None, + }; + let if_body_let_binding = ast::IfLoopBodyStatement::LetBinding(ast::LetBinding { + name: ast::ValueName::new(Ident::new("x")), + mutable: true, + value_type: None, + value: Box::new(ast::Expression { + expression_value: ast::ExpressionValue::PrimitiveValue(ast::PrimitiveValue::Bool( + false, + )), + operation: None, + }), + }); + let if_body_break = ast::IfLoopBodyStatement::Break; + + let label_loop_begin: LabelName = String::from("loop_begin").into(); + let label_loop_end: LabelName = String::from("loop_end").into(); + + let if_stmt = ast::IfStatement { + condition: ast::IfCondition::Single(if_expr), + body: ast::IfBodyStatements::Loop(vec![if_body_break, if_body_let_binding]), + else_statement: None, + else_if_statement: None, + }; + + t.state.if_condition( + &if_stmt, + &block_state, + None, + Some((&label_loop_begin, &label_loop_end)), + ); + assert!(t.check_errors_len(1), "Errors: {:?}", t.state.errors.len()); + assert!( + t.check_error(StateErrorKind::ForbiddenCodeAfterBreakDeprecated), + "Errors: {:?}", + t.state.errors[0] + ); +} + +#[test] +fn if_loop_body_instructions_after_continue() { + let block_state = Rc::new(RefCell::new(BlockState::new(None))); + let mut t = SemanticTest::new(); + let if_expr = ast::Expression { + expression_value: ast::ExpressionValue::PrimitiveValue(ast::PrimitiveValue::U64(1)), + operation: None, + }; + let if_body_let_binding = ast::IfLoopBodyStatement::LetBinding(ast::LetBinding { + name: ast::ValueName::new(Ident::new("x")), + mutable: true, + value_type: None, + value: Box::new(ast::Expression { + expression_value: ast::ExpressionValue::PrimitiveValue(ast::PrimitiveValue::Bool( + false, + )), + operation: None, + }), + }); + let if_body_continue = ast::IfLoopBodyStatement::Continue; + + let label_loop_begin: LabelName = String::from("loop_begin").into(); + let label_loop_end: LabelName = String::from("loop_end").into(); + + let if_stmt = ast::IfStatement { + condition: ast::IfCondition::Single(if_expr), + body: ast::IfBodyStatements::Loop(vec![if_body_continue, if_body_let_binding]), + else_statement: None, + else_if_statement: None, + }; + + t.state.if_condition( + &if_stmt, + &block_state, + None, + Some((&label_loop_begin, &label_loop_end)), + ); + assert!(t.check_errors_len(1), "Errors: {:?}", t.state.errors.len()); + assert!( + t.check_error(StateErrorKind::ForbiddenCodeAfterContinueDeprecated), + "Errors: {:?}", + t.state.errors[0] + ); +} diff --git a/tests/let_binding_tests.rs b/tests/let_binding_tests.rs index 75204dd..2ec6bc7 100644 --- a/tests/let_binding_tests.rs +++ b/tests/let_binding_tests.rs @@ -54,8 +54,12 @@ fn let_binding_wrong_expression() { value: Box::new(expr), }; t.state.let_binding(&let_binding, &block_state); - assert!(t.check_errors_len(1)); - assert!(t.check_error(StateErrorKind::ValueNotFound)); + assert!(t.check_errors_len(1), "Errors: {:?}", t.state.errors.len()); + assert!( + t.check_error(StateErrorKind::ValueNotFound), + "Errors: {:?}", + t.state.errors[0] + ); } #[test] @@ -73,8 +77,12 @@ fn let_binding_wrong_type() { value: Box::new(expr), }; t.state.let_binding(&let_binding, &block_state); - assert!(t.check_errors_len(1)); - assert!(t.check_error(StateErrorKind::WrongLetType)); + assert!(t.check_errors_len(1), "Errors: {:?}", t.state.errors.len()); + assert!( + t.check_error(StateErrorKind::WrongLetType), + "Errors: {:?}", + t.state.errors[0] + ); } #[test] diff --git a/tests/loop_tests.rs b/tests/loop_tests.rs index 5bfe69a..2d8c6b3 100644 --- a/tests/loop_tests.rs +++ b/tests/loop_tests.rs @@ -3,6 +3,7 @@ use semantic_analyzer::ast; use semantic_analyzer::ast::Ident; use semantic_analyzer::types::block_state::BlockState; use semantic_analyzer::types::condition::LoopBodyStatement; +use semantic_analyzer::types::error::StateErrorKind; use semantic_analyzer::types::expression::{ExpressionResult, ExpressionResultValue}; use semantic_analyzer::types::semantic::SemanticStackContext; use semantic_analyzer::types::types::{PrimitiveTypes, Type}; @@ -58,7 +59,7 @@ fn loop_transform() { ast::LoopBodyStatement::Break, ast::LoopBodyStatement::Continue, ]; - // For grcov®® + // For grcov format!("{loop_stmts:#?}"); for loop_stmt in loop_stmts { let loop_stmt_into: LoopBodyStatement = loop_stmt.into(); @@ -139,12 +140,6 @@ fn loop_statements() { parameters: vec![], }, )]); - let loop_body_return = ast::LoopBodyStatement::Return(ast::Expression { - expression_value: ast::ExpressionValue::PrimitiveValue(ast::PrimitiveValue::Bool(true)), - operation: None, - }); - let loop_body_break = ast::LoopBodyStatement::Break; - let loop_body_continue = ast::LoopBodyStatement::Continue; let loop_stmt = [ loop_body_let_binding, @@ -152,9 +147,6 @@ fn loop_statements() { loop_body_fn_call, loop_body_if, loop_body_loop, - loop_body_break, - loop_body_continue, - loop_body_return, ]; t.state.loop_statement(&loop_stmt, &block_state); @@ -168,7 +160,7 @@ fn loop_statements() { assert_eq!(ctx.borrow().children.len(), 2); let stm_ctx = ctx.borrow().context.clone().get(); - assert_eq!(stm_ctx.len(), 10); + assert_eq!(stm_ctx.len(), 7); assert_eq!( stm_ctx[0], SemanticStackContext::JumpTo { @@ -226,33 +218,12 @@ fn loop_statements() { ); assert_eq!( stm_ctx[5], - SemanticStackContext::JumpTo { - label: String::from("loop_end").into() - } - ); - assert_eq!( - stm_ctx[6], SemanticStackContext::JumpTo { label: String::from("loop_begin").into() } ); assert_eq!( - stm_ctx[7], - SemanticStackContext::JumpFunctionReturn { - expr_result: ExpressionResult { - expr_type: Type::Primitive(PrimitiveTypes::Bool), - expr_value: ExpressionResultValue::PrimitiveValue(PrimitiveValue::Bool(true)), - } - } - ); - assert_eq!( - stm_ctx[8], - SemanticStackContext::JumpTo { - label: String::from("loop_begin").into() - } - ); - assert_eq!( - stm_ctx[9], + stm_ctx[6], SemanticStackContext::SetLabel { label: String::from("loop_end").into() } @@ -349,3 +320,166 @@ fn loop_statements() { assert!(t.is_empty_error()); } + +#[test] +fn loop_statements_instructions_after_return() { + let block_state = Rc::new(RefCell::new(BlockState::new(None))); + let mut t = SemanticTest::new(); + + let loop_body_let_binding = ast::LoopBodyStatement::LetBinding(ast::LetBinding { + name: ast::ValueName::new(Ident::new("x")), + mutable: true, + value_type: None, + value: Box::new(ast::Expression { + expression_value: ast::ExpressionValue::PrimitiveValue(ast::PrimitiveValue::Bool( + false, + )), + operation: None, + }), + }); + let loop_body_return = ast::LoopBodyStatement::Return(ast::Expression { + expression_value: ast::ExpressionValue::PrimitiveValue(ast::PrimitiveValue::Bool(true)), + operation: None, + }); + + let loop_stmt = [loop_body_return, loop_body_let_binding]; + t.state.loop_statement(&loop_stmt, &block_state); + assert!(t.check_errors_len(1), "Errors: {:?}", t.state.errors.len()); + assert!( + t.check_error(StateErrorKind::ForbiddenCodeAfterReturnDeprecated), + "Errors: {:?}", + t.state.errors[0] + ); +} + +#[test] +fn loop_statements_instructions_after_break() { + let block_state = Rc::new(RefCell::new(BlockState::new(None))); + let mut t = SemanticTest::new(); + + let loop_body_let_binding = ast::LoopBodyStatement::LetBinding(ast::LetBinding { + name: ast::ValueName::new(Ident::new("x")), + mutable: true, + value_type: None, + value: Box::new(ast::Expression { + expression_value: ast::ExpressionValue::PrimitiveValue(ast::PrimitiveValue::Bool( + false, + )), + operation: None, + }), + }); + let loop_body_break = ast::LoopBodyStatement::Break; + + let loop_stmt = [loop_body_break, loop_body_let_binding]; + t.state.loop_statement(&loop_stmt, &block_state); + assert!(t.check_errors_len(1), "Errors: {:?}", t.state.errors.len()); + assert!( + t.check_error(StateErrorKind::ForbiddenCodeAfterBreakDeprecated), + "Errors: {:?}", + t.state.errors[0] + ); +} + +#[test] +fn loop_statements_instructions_after_continue() { + let block_state = Rc::new(RefCell::new(BlockState::new(None))); + let mut t = SemanticTest::new(); + + let loop_body_let_binding = ast::LoopBodyStatement::LetBinding(ast::LetBinding { + name: ast::ValueName::new(Ident::new("x")), + mutable: true, + value_type: None, + value: Box::new(ast::Expression { + expression_value: ast::ExpressionValue::PrimitiveValue(ast::PrimitiveValue::Bool( + false, + )), + operation: None, + }), + }); + let loop_body_continue = ast::LoopBodyStatement::Continue; + + let loop_stmt = [loop_body_continue, loop_body_let_binding]; + t.state.loop_statement(&loop_stmt, &block_state); + assert!(t.check_errors_len(1), "Errors: {:?}", t.state.errors.len()); + assert!( + t.check_error(StateErrorKind::ForbiddenCodeAfterContinueDeprecated), + "Errors: {:?}", + t.state.errors[0] + ); +} + +#[test] +fn loop_statements_with_return_invocation() { + let block_state = Rc::new(RefCell::new(BlockState::new(None))); + let mut t = SemanticTest::new(); + + let loop_body_let_binding = ast::LoopBodyStatement::LetBinding(ast::LetBinding { + name: ast::ValueName::new(Ident::new("x")), + mutable: true, + value_type: None, + value: Box::new(ast::Expression { + expression_value: ast::ExpressionValue::PrimitiveValue(ast::PrimitiveValue::Bool( + false, + )), + operation: None, + }), + }); + let loop_body_return = ast::LoopBodyStatement::Return(ast::Expression { + expression_value: ast::ExpressionValue::PrimitiveValue(ast::PrimitiveValue::Bool(true)), + operation: None, + }); + + let loop_stmt = [loop_body_let_binding, loop_body_return]; + t.state.loop_statement(&loop_stmt, &block_state); + + assert!(block_state.borrow().parent.is_none()); + assert_eq!(block_state.borrow().context.clone().get().len(), 0); + assert!(block_state.borrow().parent.is_none()); + assert_eq!(block_state.borrow().children.len(), 1); + + let ctx = block_state.borrow().children[0].clone(); + assert!(ctx.borrow().parent.is_some()); + assert!(ctx.borrow().children.is_empty()); + + let stm_ctx = ctx.borrow().context.clone().get(); + assert_eq!(stm_ctx.len(), 4); + assert_eq!( + stm_ctx[0], + SemanticStackContext::JumpTo { + label: String::from("loop_begin").into() + } + ); + assert_eq!( + stm_ctx[1], + SemanticStackContext::SetLabel { + label: String::from("loop_begin").into() + } + ); + assert_eq!( + stm_ctx[2], + SemanticStackContext::LetBinding { + let_decl: Value { + inner_name: "x.0".into(), + inner_type: Type::Primitive(PrimitiveTypes::Bool), + mutable: true, + alloca: false, + malloc: false + }, + expr_result: ExpressionResult { + expr_type: Type::Primitive(PrimitiveTypes::Bool), + expr_value: ExpressionResultValue::PrimitiveValue(PrimitiveValue::Bool(false)), + }, + } + ); + assert_eq!( + stm_ctx[3], + SemanticStackContext::JumpFunctionReturn { + expr_result: ExpressionResult { + expr_type: Type::Primitive(PrimitiveTypes::Bool), + expr_value: ExpressionResultValue::PrimitiveValue(PrimitiveValue::Bool(true)), + } + } + ); + + assert!(t.is_empty_error()); +} diff --git a/tests/main_tests.rs b/tests/main_tests.rs index 36338f1..663a378 100644 --- a/tests/main_tests.rs +++ b/tests/main_tests.rs @@ -349,8 +349,10 @@ fn double_return() { let fn_stm = ast::MainStatement::Function(fn1); let main_stm: ast::Main = vec![fn_stm]; t.state.run(&main_stm); - assert!(t.check_errors_len(1)); - assert!(t.check_error(StateErrorKind::ReturnAlreadyCalled)); + println!("{:#?}", t.state.errors); + assert!(t.check_errors_len(2), "Errors: {:?}", t.state.errors.len()); + assert!(t.check_error_index(0, StateErrorKind::ForbiddenCodeAfterReturnDeprecated)); + assert!(t.check_error_index(1, StateErrorKind::ReturnAlreadyCalled)); } #[test] @@ -369,8 +371,12 @@ fn wrong_return_type() { let fn_stm = ast::MainStatement::Function(fn1); let main_stm: ast::Main = vec![fn_stm]; t.state.run(&main_stm); - assert!(t.check_errors_len(1)); - assert!(t.check_error(StateErrorKind::WrongReturnType)); + assert!(t.check_errors_len(1), "Errors: {:?}", t.state.errors.len()); + assert!( + t.check_error(StateErrorKind::WrongReturnType), + "Errors: {:?}", + t.state.errors[0] + ); } #[test] diff --git a/tests/types_tests.rs b/tests/types_tests.rs index 80045ad..cace94b 100644 --- a/tests/types_tests.rs +++ b/tests/types_tests.rs @@ -346,8 +346,12 @@ fn types_declaration() { ); t.state.types(&type_decl2.clone()); - assert!(t.check_errors_len(1)); - assert!(t.check_error(StateErrorKind::TypeAlreadyExist)); + assert!(t.check_errors_len(1), "Errors: {:?}", t.state.errors.len()); + assert!( + t.check_error(StateErrorKind::TypeAlreadyExist), + "Errors: {:?}", + t.state.errors[0] + ); let state = t.state.global.context.clone().get(); assert_eq!(state.len(), 2); }