From cc62e0d7bfb985ee5cd81459957ca79159c38761 Mon Sep 17 00:00:00 2001 From: Stephane Del Pino <stephane.delpino44@gmail.com> Date: Wed, 15 Jan 2020 11:04:53 +0100 Subject: [PATCH] Check if test arguments are not build on non natural conversions Actually, it is now forbidden to write code such as `` if(1) { // do something } `` This rule also applies to `for`-loops, `while`-loops and `do while`-loops. Related to issue #16 --- src/language/ASTNodeDataTypeBuilder.cpp | 37 +++++++++++++------------ tests/test_DoWhileProcessor.cpp | 23 +++++++++++++++ tests/test_ForProcessor.cpp | 23 +++++++++++++++ tests/test_IfProcessor.cpp | 24 ++++++++++++++++ tests/test_WhileProcessor.cpp | 23 +++++++++++++++ 5 files changed, 112 insertions(+), 18 deletions(-) diff --git a/src/language/ASTNodeDataTypeBuilder.cpp b/src/language/ASTNodeDataTypeBuilder.cpp index 68bb5a920..a320695ac 100644 --- a/src/language/ASTNodeDataTypeBuilder.cpp +++ b/src/language/ASTNodeDataTypeBuilder.cpp @@ -1,5 +1,7 @@ #include <ASTNodeDataTypeBuilder.hpp> +#include <ASTNodeNaturalConversionChecker.hpp> + #include <PEGGrammar.hpp> #include <PugsAssert.hpp> #include <SymbolTable.hpp> @@ -65,6 +67,15 @@ ASTNodeDataTypeBuilder::_buildNodeDataTypes(ASTNode& n) const for (auto& child : n.children) { this->_buildNodeDataTypes(*child); } + + if (n.is_type<language::for_statement>()) { + const ASTNode& test_node = *n.children[1]; + + if (not n.children[1]->is_type<language::for_test>()) { + ASTNodeNaturalConversionChecker{test_node, test_node.m_data_type, ASTNodeDataType::bool_t}; + } // in the case of empty for_test (not simplified node), nothing to check! + } + n.m_data_type = ASTNodeDataType::void_t; } else { if (n.has_content()) { @@ -255,26 +266,16 @@ ASTNodeDataTypeBuilder::_buildNodeDataTypes(ASTNode& n) const n.m_data_type = ASTNodeDataType::void_t; } else if (n.is_type<language::if_statement>() or n.is_type<language::while_statement>()) { n.m_data_type = ASTNodeDataType::void_t; - if ((n.children[0]->m_data_type > ASTNodeDataType::double_t) or - (n.children[0]->m_data_type < ASTNodeDataType::bool_t)) { - const ASTNodeDataType type_0 = n.children[0]->m_data_type; - std::ostringstream message; - message << "Cannot convert data type to boolean value\n" - << "note: incompatible operand '" << n.children[0]->string() << "' of type " << dataTypeName(type_0) - << std::ends; - throw parse_error(message.str(), n.children[0]->begin()); - } + + const ASTNode& test_node = *n.children[0]; + ASTNodeNaturalConversionChecker{test_node, test_node.m_data_type, ASTNodeDataType::bool_t}; + } else if (n.is_type<language::do_while_statement>()) { n.m_data_type = ASTNodeDataType::void_t; - if ((n.children[1]->m_data_type > ASTNodeDataType::double_t) or - (n.children[1]->m_data_type < ASTNodeDataType::bool_t)) { - const ASTNodeDataType type_0 = n.children[1]->m_data_type; - std::ostringstream message; - message << "Cannot convert data type to boolean value\n" - << "note: incompatible operand '" << n.children[1]->string() << "' of type " << dataTypeName(type_0) - << std::ends; - throw parse_error(message.str(), n.children[1]->begin()); - } + + const ASTNode& test_node = *n.children[1]; + ASTNodeNaturalConversionChecker{test_node, test_node.m_data_type, ASTNodeDataType::bool_t}; + } else if (n.is_type<language::unary_not>() or n.is_type<language::lesser_op>() or n.is_type<language::lesser_or_eq_op>() or n.is_type<language::greater_op>() or n.is_type<language::greater_or_eq_op>() or n.is_type<language::eqeq_op>() or diff --git a/tests/test_DoWhileProcessor.cpp b/tests/test_DoWhileProcessor.cpp index 135c8d221..d68b2f9e4 100644 --- a/tests/test_DoWhileProcessor.cpp +++ b/tests/test_DoWhileProcessor.cpp @@ -48,6 +48,16 @@ REQUIRE(value == expected_value); \ } +#define CHECK_DO_WHILE_PROCESSOR_THROWS_WITH(data, error_message) \ + { \ + string_input input{data, "test.pgs"}; \ + auto ast = ASTBuilder::build(input); \ + \ + ASTSymbolTableBuilder{*ast}; \ + \ + REQUIRE_THROWS_WITH(ASTNodeDataTypeBuilder{*ast}, error_message); \ + } + TEST_CASE("DoWhileProcessor", "[language]") { SECTION("simple loop") @@ -90,4 +100,17 @@ do { )"; CHECK_WHILE_PROCESSOR_RESULT(data, "i", 12ul); } + + SECTION("errors") + { + SECTION("bad test type") + { + std::string_view data = R"( +do { +} while(1); +)"; + + CHECK_DO_WHILE_PROCESSOR_THROWS_WITH(data, "invalid implicit conversion: Z -> B"); + } + } } diff --git a/tests/test_ForProcessor.cpp b/tests/test_ForProcessor.cpp index 7b4faa855..51195d9b3 100644 --- a/tests/test_ForProcessor.cpp +++ b/tests/test_ForProcessor.cpp @@ -48,6 +48,16 @@ REQUIRE(value == expected_value); \ } +#define CHECK_FOR_PROCESSOR_THROWS_WITH(data, error_message) \ + { \ + string_input input{data, "test.pgs"}; \ + auto ast = ASTBuilder::build(input); \ + \ + ASTSymbolTableBuilder{*ast}; \ + \ + REQUIRE_THROWS_WITH(ASTNodeDataTypeBuilder{*ast}, error_message); \ + } + TEST_CASE("ForProcessor", "[language]") { SECTION("simple for") @@ -84,4 +94,17 @@ for(N l=0; l<10; ++l) { )"; CHECK_FOR_PROCESSOR_RESULT(data, "i", 42ul); } + + SECTION("errors") + { + SECTION("bad test type") + { + std::string_view data = R"( +for(N l=0; l; ++l) { +} +)"; + + CHECK_FOR_PROCESSOR_THROWS_WITH(data, "invalid implicit conversion: N -> B"); + } + } } diff --git a/tests/test_IfProcessor.cpp b/tests/test_IfProcessor.cpp index 9df57fc07..492d9b7b3 100644 --- a/tests/test_IfProcessor.cpp +++ b/tests/test_IfProcessor.cpp @@ -48,6 +48,16 @@ REQUIRE(value == expected_value); \ } +#define CHECK_IF_PROCESSOR_THROWS_WITH(data, error_message) \ + { \ + string_input input{data, "test.pgs"}; \ + auto ast = ASTBuilder::build(input); \ + \ + ASTSymbolTableBuilder{*ast}; \ + \ + REQUIRE_THROWS_WITH(ASTNodeDataTypeBuilder{*ast}, error_message); \ + } + TEST_CASE("IfProcessor", "[language]") { SECTION("simple if(true)") @@ -97,4 +107,18 @@ if(false) { )"; CHECK_IF_PROCESSOR_RESULT(data, "i", 2ul); } + + SECTION("errors") + { + SECTION("bad test type") + { + std::string_view data = R"( +if (1.2) { +} + +)"; + + CHECK_IF_PROCESSOR_THROWS_WITH(data, "invalid implicit conversion: R -> B"); + } + } } diff --git a/tests/test_WhileProcessor.cpp b/tests/test_WhileProcessor.cpp index d9200a1f5..98e46e73e 100644 --- a/tests/test_WhileProcessor.cpp +++ b/tests/test_WhileProcessor.cpp @@ -48,6 +48,16 @@ REQUIRE(value == expected_value); \ } +#define CHECK_WHILE_PROCESSOR_THROWS_WITH(data, error_message) \ + { \ + string_input input{data, "test.pgs"}; \ + auto ast = ASTBuilder::build(input); \ + \ + ASTSymbolTableBuilder{*ast}; \ + \ + REQUIRE_THROWS_WITH(ASTNodeDataTypeBuilder{*ast}, error_message); \ + } + TEST_CASE("WhileProcessor", "[language]") { SECTION("simple loop") @@ -90,4 +100,17 @@ while(i<10) { )"; CHECK_WHILE_PROCESSOR_RESULT(data, "i", 12ul); } + + SECTION("errors") + { + SECTION("bad test type") + { + std::string_view data = R"( +while(1) { +} +)"; + + CHECK_WHILE_PROCESSOR_THROWS_WITH(data, "invalid implicit conversion: Z -> B"); + } + } } -- GitLab