diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 3e0cc62a60..05c61bb5ae 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -209,6 +209,7 @@ set(TINT_TEST_SRCS ast/else_statement_test.cc ast/entry_point_test.cc ast/fallthrough_statement_test.cc + ast/function_test.cc ast/import_test.cc ast/int_literal_test.cc ast/location_decoration_test.cc diff --git a/src/ast/function.cc b/src/ast/function.cc index f53ba0b0bb..4d13d139cc 100644 --- a/src/ast/function.cc +++ b/src/ast/function.cc @@ -37,6 +37,15 @@ Function::Function(const Source& source, Function::~Function() = default; bool Function::IsValid() const { + for (const auto& param : params_) { + if (param == nullptr || !param->IsValid()) + return false; + } + for (const auto& stmt : body_) { + if (stmt == nullptr || !stmt->IsValid()) + return false; + } + if (name_.length() == 0) { return false; } @@ -48,21 +57,28 @@ bool Function::IsValid() const { void Function::to_str(std::ostream& out, size_t indent) const { make_indent(out, indent); - out << "function -> " << return_type_->type_name() << "{" << std::endl; - make_indent(out, indent + 2); - out << name_ << std::endl; + out << "Function " << name_ << " -> " << return_type_->type_name() + << std::endl; - for (const auto& param : params_) - param->to_str(out, indent + 2); + make_indent(out, indent); + out << "("; - make_indent(out, indent + 2); + if (params_.size() > 0) { + out << std::endl; + + for (const auto& param : params_) + param->to_str(out, indent + 2); + + make_indent(out, indent); + } + out << ")" << std::endl; + + make_indent(out, indent); out << "{" << std::endl; for (const auto& stmt : body_) - stmt->to_str(out, indent + 4); + stmt->to_str(out, indent + 2); - make_indent(out, indent + 2); - out << "}" << std::endl; make_indent(out, indent); out << "}" << std::endl; } diff --git a/src/ast/function_test.cc b/src/ast/function_test.cc new file mode 100644 index 0000000000..d2bd9aacf7 --- /dev/null +++ b/src/ast/function_test.cc @@ -0,0 +1,206 @@ +// Copyright 2020 The Tint Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "src/ast/function.h" + +#include "gtest/gtest.h" +#include "src/ast/nop_statement.h" +#include "src/ast/type/i32_type.h" +#include "src/ast/type/void_type.h" +#include "src/ast/variable.h" + +namespace tint { +namespace ast { + +using FunctionTest = testing::Test; + +TEST_F(FunctionTest, Creation) { + type::VoidType void_type; + type::I32Type i32; + + std::vector> params; + params.push_back( + std::make_unique("var", StorageClass::kNone, &i32)); + auto var_ptr = params[0].get(); + + Function f("func", std::move(params), &void_type); + EXPECT_EQ(f.name(), "func"); + ASSERT_EQ(f.params().size(), 1); + EXPECT_EQ(f.return_type(), &void_type); + EXPECT_EQ(f.params()[0].get(), var_ptr); +} + +TEST_F(FunctionTest, Creation_WithSource) { + type::VoidType void_type; + type::I32Type i32; + + std::vector> params; + params.push_back( + std::make_unique("var", StorageClass::kNone, &i32)); + + Function f(Source{20, 2}, "func", std::move(params), &void_type); + auto src = f.source(); + EXPECT_EQ(src.line, 20); + EXPECT_EQ(src.column, 2); +} + +TEST_F(FunctionTest, IsValid) { + type::VoidType void_type; + type::I32Type i32; + + std::vector> params; + params.push_back( + std::make_unique("var", StorageClass::kNone, &i32)); + + std::vector> body; + body.push_back(std::make_unique()); + + Function f("func", std::move(params), &void_type); + f.set_body(std::move(body)); + EXPECT_TRUE(f.IsValid()); +} + +TEST_F(FunctionTest, IsValid_EmptyName) { + type::VoidType void_type; + type::I32Type i32; + + std::vector> params; + params.push_back( + std::make_unique("var", StorageClass::kNone, &i32)); + + Function f("", std::move(params), &void_type); + EXPECT_FALSE(f.IsValid()); +} + +TEST_F(FunctionTest, IsValid_MissingReturnType) { + type::I32Type i32; + + std::vector> params; + params.push_back( + std::make_unique("var", StorageClass::kNone, &i32)); + + Function f("func", std::move(params), nullptr); + EXPECT_FALSE(f.IsValid()); +} + +TEST_F(FunctionTest, IsValid_NullParam) { + type::VoidType void_type; + type::I32Type i32; + + std::vector> params; + params.push_back( + std::make_unique("var", StorageClass::kNone, &i32)); + params.push_back(nullptr); + + Function f("func", std::move(params), &void_type); + EXPECT_FALSE(f.IsValid()); +} + +TEST_F(FunctionTest, IsValid_InvalidParam) { + type::VoidType void_type; + + std::vector> params; + params.push_back( + std::make_unique("var", StorageClass::kNone, nullptr)); + + Function f("func", std::move(params), &void_type); + EXPECT_FALSE(f.IsValid()); +} + +TEST_F(FunctionTest, IsValid_NullBodyStatement) { + type::VoidType void_type; + type::I32Type i32; + + std::vector> params; + params.push_back( + std::make_unique("var", StorageClass::kNone, &i32)); + + std::vector> body; + body.push_back(std::make_unique()); + body.push_back(nullptr); + + Function f("func", std::move(params), &void_type); + f.set_body(std::move(body)); + EXPECT_FALSE(f.IsValid()); +} + +TEST_F(FunctionTest, IsValid_InvalidBodyStatement) { + type::VoidType void_type; + type::I32Type i32; + + std::vector> params; + params.push_back( + std::make_unique("var", StorageClass::kNone, &i32)); + + std::vector> body; + body.push_back(std::make_unique()); + body.push_back(nullptr); + + Function f("func", std::move(params), &void_type); + f.set_body(std::move(body)); + EXPECT_FALSE(f.IsValid()); +} + +TEST_F(FunctionTest, ToStr) { + type::VoidType void_type; + type::I32Type i32; + + std::vector> body; + body.push_back(std::make_unique()); + + Function f("func", {}, &void_type); + f.set_body(std::move(body)); + + std::ostringstream out; + f.to_str(out, 2); + EXPECT_EQ(out.str(), R"( Function func -> __void + () + { + Nop{} + } +)"); +} + +TEST_F(FunctionTest, ToStr_WithParams) { + type::VoidType void_type; + type::I32Type i32; + + std::vector> params; + params.push_back( + std::make_unique("var", StorageClass::kNone, &i32)); + + std::vector> body; + body.push_back(std::make_unique()); + + Function f("func", std::move(params), &void_type); + f.set_body(std::move(body)); + + std::ostringstream out; + f.to_str(out, 2); + EXPECT_EQ(out.str(), R"( Function func -> __void + ( + Variable{ + var + none + __i32 + } + ) + { + Nop{} + } +)"); +} + +} // namespace ast +} // namespace tint