diff --git a/src/validation/statement.rs b/src/validation/statement.rs index ad9e39077a9..dfaa9caa182 100644 --- a/src/validation/statement.rs +++ b/src/validation/statement.rs @@ -1,6 +1,6 @@ use std::{collections::HashSet, mem::discriminant}; -use plc_ast::control_statements::ForLoopStatement; +use plc_ast::control_statements::{ForLoopStatement, IfStatement}; use plc_ast::{ ast::{ flatten_expression_list, AstNode, AstStatement, DirectAccess, DirectAccessType, JumpStatement, @@ -287,6 +287,26 @@ fn validate_for_loop( // by a VAR_INPUT {ref} function call. } +fn validate_if_statement( + validator: &mut Validator, + context: &ValidationContext, + statement: &IfStatement, +) { + for block in &statement.blocks { + let kind = context.annotations.get_type_or_void(&block.condition, context.index); + + if !kind.get_type_information().is_bool() { + let slice = get_datatype_name_or_slice(validator.context, kind); + let message = format!("Expected a boolean, got `{slice}`"); + validator.push_diagnostic( + Diagnostic::error(message) + .with_location(block.condition.get_location()) + .with_error_code("E093"), + ) + } + } +} + fn validate_control_statement( validator: &mut Validator, control_statement: &AstControlStatement, @@ -294,6 +314,7 @@ fn validate_control_statement( ) { match control_statement { AstControlStatement::If(stmt) => { + validate_if_statement(validator, context, stmt); stmt.blocks.iter().for_each(|b| { visit_statement(validator, b.condition.as_ref(), context); b.body.iter().for_each(|s| visit_statement(validator, s, context)); diff --git a/src/validation/tests/statement_validation_tests.rs b/src/validation/tests/statement_validation_tests.rs index 01fe26b9fd0..6c6bc8f3860 100644 --- a/src/validation/tests/statement_validation_tests.rs +++ b/src/validation/tests/statement_validation_tests.rs @@ -1402,7 +1402,7 @@ fn for_loop_conditions_are_numerical() { FOR i := 100000 TO x BY y DO END_FOR - + END_PROGRAM ", ); @@ -1436,7 +1436,7 @@ fn for_loop_conditions_are_real_and_trigger_error() { FOR i := 10.0 TO x BY y DO END_FOR - + END_PROGRAM ", ); @@ -1468,3 +1468,57 @@ fn for_loop_conditions_are_real_and_trigger_error() { "###); } + +#[test] +fn if_statement_triggers_error_if_condition_is_not_boolean() { + let diagnostic = parse_and_validate_buffered( + " + FUNCTION main + VAR + x : BOOL; + y : DINT; + z : STRING; + END_VAR + + IF x THEN + ELSIF y THEN + ELSIF z THEN + ELSIF 0 THEN + ELSIF 1 THEN + + // These should be Ok + ELSIF (0 < 1) THEN + ELSIF (y < 0) THEN + ELSIF ((1 = 2) = 3) THEN + END_IF + END_FUNCTION + ", + ); + + assert_snapshot!(diagnostic, @r###" + error: Expected a boolean, got `DINT` + ┌─ :10:21 + │ + 10 │ ELSIF y THEN + │ ^ Expected a boolean, got `DINT` + + error: Expected a boolean, got `STRING` + ┌─ :11:21 + │ + 11 │ ELSIF z THEN + │ ^ Expected a boolean, got `STRING` + + error: Expected a boolean, got `DINT` + ┌─ :12:21 + │ + 12 │ ELSIF 0 THEN + │ ^ Expected a boolean, got `DINT` + + error: Expected a boolean, got `DINT` + ┌─ :13:21 + │ + 13 │ ELSIF 1 THEN + │ ^ Expected a boolean, got `DINT` + + "###); +}