Skip to content

Commit

Permalink
added validation for function return types (#273)
Browse files Browse the repository at this point in the history
  • Loading branch information
99NIMI committed Oct 13, 2021
1 parent b1e7694 commit c20b8a2
Show file tree
Hide file tree
Showing 5 changed files with 192 additions and 3 deletions.
13 changes: 13 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ use inkwell::targets::{
};
use lexer::IdProvider;
use std::{fs::File, io::Read};
use typesystem::DataTypeInformation;
use validation::Validator;

use crate::ast::CompilationUnit;
Expand Down Expand Up @@ -81,6 +82,7 @@ pub enum ErrNo {
// pou related
pou__missing_return_type,
pou__unexpected_return_type,
pou__unsupported_return_type,
pou__empty_variable_block,

//variable related
Expand Down Expand Up @@ -140,6 +142,17 @@ impl Diagnostic {
}
}

pub fn unsupported_return_type(
data_type: &DataTypeInformation,
range: SourceRange,
) -> Diagnostic {
Diagnostic::SyntaxError {
message: format!("Data Type {:?} not supported!", data_type),
range,
err_no: ErrNo::pou__unsupported_return_type,
}
}

pub fn function_return_missing(range: SourceRange) -> Diagnostic {
Diagnostic::SyntaxError {
message: "Function Return type missing".into(),
Expand Down
2 changes: 1 addition & 1 deletion src/validation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ impl Validator {
}

pub fn visit_pou(&mut self, pou: &Pou, context: &ValidationContext) {
self.pou_validator.validate_pou(pou);
self.pou_validator.validate_pou(pou, context);

for block in &pou.variable_blocks {
self.visit_variable_container(context, block);
Expand Down
29 changes: 27 additions & 2 deletions src/validation/pou_validator.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use crate::{ast::Pou, Diagnostic};
use super::ValidationContext;
use crate::{ast::Pou, typesystem::DataTypeInformation, Diagnostic, PouType};

/// validates POUs
pub struct PouValidator {
Expand All @@ -12,5 +13,29 @@ impl PouValidator {
}
}

pub fn validate_pou(&mut self, _pou: &Pou) {}
pub fn validate_pou(&mut self, pou: &Pou, context: &ValidationContext) {
if pou.pou_type == PouType::Function {
self.validate_function(pou, context);
};
}

pub fn validate_function(&mut self, pou: &Pou, context: &ValidationContext) {
let return_type = context.index.find_return_type(&pou.name);
if let Some(data_type) = return_type {
let type_info = data_type.get_type_information();
let location = pou.return_type.to_owned().unwrap().get_location();
match type_info {
DataTypeInformation::Enum { .. } => self
.diagnostics
.push(Diagnostic::unsupported_return_type(type_info, location)),
DataTypeInformation::Struct { .. } => self
.diagnostics
.push(Diagnostic::unsupported_return_type(type_info, location)),
_ => {}
}
} else {
self.diagnostics
.push(Diagnostic::function_return_missing(pou.location.to_owned()));
}
}
}
2 changes: 2 additions & 0 deletions src/validation/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ mod variable_validation_tests;

mod statement_validation_tests;

mod pou_validation_tests;

pub fn parse_and_validate(src: &str) -> Vec<Diagnostic> {
let mut idx = Index::new();
let (mut ast, _) = parse(lex(src));
Expand Down
149 changes: 149 additions & 0 deletions src/validation/tests/pou_validation_tests.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
use crate::{
typesystem::{DataTypeInformation, StructSource},
validation::tests::parse_and_validate,
Diagnostic,
};

// supported return types
#[test]
fn function_array_return_supported() {
//GIVEN FUNCTION returning an ARRAY
//WHEN parse_and_validate is done
let diagnostics =
parse_and_validate("FUNCTION foo : ARRAY[0..3] OF INT VAR_INPUT END_VAR END_FUNCTION");
//THEN there shouldn't be any diagnostics -> valid return type
assert_eq!(diagnostics, vec![]);
}

#[test]
fn function_subrange_return_supported() {
//GIVEN FUNCTION returning a SubRange
//WHEN parse_and_validate is done
let diagnostics =
parse_and_validate("FUNCTION foo : INT(0..10) OF INT VAR_INPUT END_VAR END_FUNCTION");
//THEN there shouldn't be any diagnostics -> valid return type
assert_eq!(diagnostics, vec![]);
}

#[test]
fn function_pointer_return_supported() {
//GIVEN FUNCTION returning a POINTER
//WHEN parse_and_validate is done
let diagnostics =
parse_and_validate("FUNCTION foo : REF_TO INT OF INT VAR_INPUT END_VAR END_FUNCTION");
//THEN there shouldn't be any diagnostics -> valid return type
assert_eq!(diagnostics, vec![]);
}

// STRING types
#[test]
fn function_string_return_supported() {
//GIVEN FUNCTION returning a STRING
//WHEN parse_and_validate is done
let diagnostics = parse_and_validate("FUNCTION foo : STRING VAR_INPUT END_VAR END_FUNCTION");
//THEN there shouldn't be any diagnostics -> valid return type
assert_eq!(diagnostics, vec![]);
}

#[test]
fn function_string_len_return_supported() {
//GIVEN FUNCTION returning a STRING[10]
//WHEN parse_and_validate is done
let diagnostics =
parse_and_validate("FUNCTION foo : STRING[10] VAR_INPUT END_VAR END_FUNCTION");
//THEN there shouldn't be any diagnostics -> valid return type
assert_eq!(diagnostics, vec![]);
}

#[test]
fn function_wstring_return_supported() {
//GIVEN FUNCTION returning a WSTRING
//WHEN parse_and_validate is done
let diagnostics = parse_and_validate("FUNCTION foo : WSTRING VAR_INPUT END_VAR END_FUNCTION");
//THEN there shouldn't be any diagnostics -> valid return type
assert_eq!(diagnostics, vec![]);
}

#[test]
fn function_wstring_len_return_supported() {
//GIVEN FUNCTION returning a WSTRING[10]
//WHEN parse_and_validate is done
let diagnostics =
parse_and_validate("FUNCTION foo : WSTRING[10] VAR_INPUT END_VAR END_FUNCTION");
//THEN there shouldn't be any diagnostics -> valid return type
assert_eq!(diagnostics, vec![]);
}

// SCALAR types
#[test]
fn function_int_return_supported() {
//GIVEN FUNCTION returning an INT
//WHEN parse_and_validate is done
let diagnostics = parse_and_validate("FUNCTION foo : INT VAR_INPUT END_VAR END_FUNCTION");
//THEN there shouldn't be any diagnostics -> valid return type
assert_eq!(diagnostics, vec![]);
}

#[test]
fn function_bool_return_supported() {
//GIVEN FUNCTION returning a BOOL
//WHEN parse_and_validate is done
let diagnostics = parse_and_validate("FUNCTION foo : BOOL VAR_INPUT END_VAR END_FUNCTION");
//THEN there shouldn't be any diagnostics -> valid return type
assert_eq!(diagnostics, vec![]);
}

// unsupported return types
#[test]
fn function_no_return_unsupported() {
// GIVEN FUNCTION with no return type
// WHEN parse_and_validate is done
let diagnostics = parse_and_validate("FUNCTION foo VAR_INPUT END_VAR END_FUNCTION");
// THEN there should be one diagnostic -> missing return type
assert_eq!(
diagnostics,
vec![Diagnostic::function_return_missing((0..43).into())]
);
}

#[test]
fn function_enum_return_unsupported() {
// GIVEN FUNCTION returning an ENUM
// WHEN parse_and_validate is done
let diagnostics = parse_and_validate(
"FUNCTION foo : (green, yellow, red) foo VAR_INPUT END_VAR END_FUNCTION",
);
// THEN there should be one diagnostic -> unsupported return type
assert_eq!(
diagnostics,
vec![Diagnostic::unsupported_return_type(
&DataTypeInformation::Enum {
name: "__foo_return".into(),
elements: vec!["green".into(), "yellow".into(), "red".into()]
},
(15..35).into()
)]
);
}

#[test]
fn function_struct_return_unsupported() {
// GIVEN FUNCTION returning a STRUCT
// WHEN parse_and_validate is done
let diagnostics = parse_and_validate(
"FUNCTION foo : STRUCT x : INT; y : INT; END_STRUCT foo VAR_INPUT END_VAR END_FUNCTION",
);
// THEN there should be one diagnostic -> unsupported return type
assert_eq!(
diagnostics,
vec![Diagnostic::unsupported_return_type(
&DataTypeInformation::Struct {
member_names: vec!["x".into(), "y".into()],
name: "__foo_return".into(),
source: StructSource::OriginalDeclaration,
varargs: None
},
(15..50).into()
)]
);
}

0 comments on commit c20b8a2

Please sign in to comment.