diff --git a/src/ExpressionBuilder.cpp b/src/ExpressionBuilder.cpp index e07181a9..205d412f 100644 --- a/src/ExpressionBuilder.cpp +++ b/src/ExpressionBuilder.cpp @@ -341,17 +341,19 @@ void ExpressionBuilder::expr_call_end(uint32_t n) switch (id.get_type().get_kind()) { case FUNCTION_EXTERNAL: case FUNCTION: - if (expr.size() != id.get_type().size()) { - handle_error(TypeException{"$Wrong_number_of_arguments"}); - } + if (expr.size() < id.get_type().size()) + handle_error(TypeException{"$Too_few_arguments_for_function_call"}); + if (expr.size() > id.get_type().size()) + handle_error(TypeException{"$Too_many_arguments_for_function_call"}); e = expression_t::create_nary(id.get_type().get_kind() == FUNCTION ? FUN_CALL : FUN_CALL_EXT, expr, position, id.get_type()[0]); break; case PROCESS_SET: - if (expr.size() - 1 != id.get_type().size()) { - handle_error(TypeException{"$Wrong_number_of_arguments"}); - } + if (expr.size() - 1 < id.get_type().size()) + handle_error(TypeException{"$Too_few_arguments_for_template_instantiation"}); + if (expr.size() - 1 > id.get_type().size()) + handle_error(TypeException{"$Too_many_arguments_for_template_instantiation"}); instance = static_cast(id.get_symbol().get_data()); /* Process set lookups are represented as expressions indexing diff --git a/src/typechecker.cpp b/src/typechecker.cpp index 553f3463..07ea9a5d 100644 --- a/src/typechecker.cpp +++ b/src/typechecker.cpp @@ -2129,12 +2129,16 @@ bool TypeChecker::checkExpression(expression_t expr) checkExpression(expr[0]); bool result = true; - type_t type = expr[0].get_type(); - size_t parameters = type.size() - 1; - for (uint32_t i = 0; i < parameters; i++) { - type_t parameter = type[i + 1]; - expression_t argument = expr[i + 1]; - result &= checkParameterCompatible(parameter, argument); + type_t fn_type = expr[0].get_type(); // [ret_type, arg1_type, ..., argn_type] + size_t param_count = fn_type.size() - 1; + for (uint32_t i = 1; i < param_count; ++i) { + if (i >= expr.get_size()) { + handleError(expr, "$Too_few_function_arguments"); + break; + } + type_t param_type = fn_type[i]; + expression_t argument = expr[i]; + result &= checkParameterCompatible(param_type, argument); } return result; } diff --git a/test/models/function_calls.xml b/test/models/function_calls.xml new file mode 100644 index 00000000..3846e805 --- /dev/null +++ b/test/models/function_calls.xml @@ -0,0 +1,49 @@ + + + + int v; + +bool enabled(int i) { + return (i>0); +} + + + system P; + + + simulate[<=5;3] { v, enabled(1) } : 2 : enabled(1) + Good call, should pass + + + simulate[<=5;3] { v, enabled(1) } : 2 : enabled() + Call is missing argument, should give meaningful error and not crash. + + + simulate[<=5;3] { v, enabled() } : 2 : enabled(1) + Call is missing argument, should give meaningful error and not crash. + + + simulate[<=5;3] { v, enabled() } : 2 : enabled() + Both calls are missing arguments, should give error message and not crash + + + simulate[<=5;3] { v, non_existent() } : 2 : non_existent() + Chould give meaningfull error message and not crash + + + diff --git a/test/test_typechecker.cpp b/test/test_typechecker.cpp index eb924a93..f29a977b 100644 --- a/test/test_typechecker.cpp +++ b/test/test_typechecker.cpp @@ -473,4 +473,104 @@ TEST_CASE("Nested structs") CHECK(warns.size() == 0); auto errs = doc->get_errors(); CHECK(errs.size() == 1); -} \ No newline at end of file +} + +TEST_CASE("Function calls in queries") +{ + auto doc = read_document("function_calls.xml"); + const auto& errs = doc->get_errors(); + REQUIRE_MESSAGE(errs.empty(), errs.front().msg); + auto builder = std::make_unique(*doc); + const auto& queries = doc->get_queries(); + REQUIRE(queries.size() == 5); + SUBCASE("Correct") + { + const auto& query = *queries.begin(); + builder->parse(query.formula.c_str(), query.location, query.options); + REQUIRE_MESSAGE(errs.empty(), errs.front().msg); + /* + REQUIRE_MESSAGE(props.size() == prop_count + 1, "Should contain one more property"); + const auto& expr = std::next(props.begin(), prop_count)->intermediate; + REQUIRE(expr.get_size() == 7); + CHECK(expr.get(0).get_value() == 3); ///< max runs + CHECK(expr.get(1).get_value() == 1); ///< bound kind of time + CHECK(expr.get(2).get_value() == 5); ///< time bound + // CHECK(expr.get(3)); + // CHECK(expr.get(4)); + // CHECK(expr.get(5)); + CHECK(expr.get(6).get_value() == 2); ///< number of satisfying runs + */ + } + SUBCASE("Predicate misses argument") + { + const auto& query = *std::next(queries.begin(), 1); + builder->parse(query.formula.c_str(), query.location, query.options); + CHECK_MESSAGE(errs.empty(), errs.front().msg); + /* + REQUIRE_MESSAGE(props.size() == prop_count + 1, "Should contain one more property"); + const auto& expr = std::next(props.begin(), prop_count)->intermediate; + REQUIRE(expr.get_size() == 7); + CHECK(expr.get(0).get_value() == 3); ///< max runs + CHECK(expr.get(1).get_value() == 1); ///< bound kind of time + CHECK(expr.get(2).get_value() == 5); ///< time bound + // CHECK(expr.get(3)); + // CHECK(expr.get(4)); + // CHECK(expr.get(5)); + CHECK(expr.get(6).get_value() == 2); ///< number of satisfying runs + */ + } + SUBCASE("Monitored expression misses argument") + { + const auto& query = *std::next(queries.begin(), 2); + builder->parse(query.formula.c_str(), query.location, query.options); + CHECK_MESSAGE(errs.empty(), errs.front().msg); + /* + REQUIRE_MESSAGE(props.size() == prop_count + 1, "Should contain one more property"); + const auto& expr = std::next(props.begin(), prop_count)->intermediate; + REQUIRE(expr.get_size() == 7); + CHECK(expr.get(0).get_value() == 3); ///< max runs + CHECK(expr.get(1).get_value() == 1); ///< bound kind of time + CHECK(expr.get(2).get_value() == 5); ///< time bound + // CHECK(expr.get(3)); + // CHECK(expr.get(4)); + // CHECK(expr.get(5)); + CHECK(expr.get(6).get_value() == 2); ///< number of satisfying runs + */ + } + SUBCASE("Missing arguments in both calls") + { + const auto& query = *std::next(queries.begin(), 3); + builder->parse(query.formula.c_str(), query.location, query.options); + CHECK_MESSAGE(errs.empty(), errs.front().msg); + /* + REQUIRE_MESSAGE(props.size() == prop_count + 1, "Should contain one more property"); + const auto& expr = std::next(props.begin(), prop_count)->intermediate; + REQUIRE(expr.get_size() == 7); + CHECK(expr.get(0).get_value() == 3); ///< max runs + CHECK(expr.get(1).get_value() == 1); ///< bound kind of time + CHECK(expr.get(2).get_value() == 5); ///< time bound + // CHECK(expr.get(3)); + // CHECK(expr.get(4)); + // CHECK(expr.get(5)); + CHECK(expr.get(6).get_value() == 2); ///< number of satisfying runs + */ + } + SUBCASE("Non-existent fn") + { + const auto& query = *std::next(queries.begin(), 4); + builder->parse(query.formula.c_str(), query.location, query.options); + CHECK_MESSAGE(errs.empty(), errs.front().msg); + /* + REQUIRE_MESSAGE(props.size() == prop_count + 1, "Should contain one more property"); + const auto& expr = std::next(props.begin(), prop_count)->intermediate; + REQUIRE(expr.get_size() == 7); + CHECK(expr.get(0).get_value() == 3); ///< max runs + CHECK(expr.get(1).get_value() == 1); ///< bound kind of time + CHECK(expr.get(2).get_value() == 5); ///< time bound + // CHECK(expr.get(3)); + // CHECK(expr.get(4)); + // CHECK(expr.get(5)); + CHECK(expr.get(6).get_value() == 2); ///< number of satisfying runs + */ + } +}