// Copyright 2021 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/tint/ast/traverse_expressions.h" #include "gmock/gmock.h" #include "src/tint/ast/test_helper.h" using ::testing::ElementsAre; using namespace tint::number_suffixes; // NOLINT namespace tint::ast { namespace { using TraverseExpressionsTest = TestHelper; TEST_F(TraverseExpressionsTest, DescendIndexAccessor) { std::vector e = {Expr(1_i), Expr(1_i), Expr(1_i), Expr(1_i)}; std::vector i = {IndexAccessor(e[0], e[1]), IndexAccessor(e[2], e[3])}; auto* root = IndexAccessor(i[0], i[1]); { std::vector l2r; TraverseExpressions(root, Diagnostics(), [&](const ast::Expression* expr) { l2r.push_back(expr); return ast::TraverseAction::Descend; }); EXPECT_THAT(l2r, ElementsAre(root, i[0], e[0], e[1], i[1], e[2], e[3])); } { std::vector r2l; TraverseExpressions(root, Diagnostics(), [&](const ast::Expression* expr) { r2l.push_back(expr); return ast::TraverseAction::Descend; }); EXPECT_THAT(r2l, ElementsAre(root, i[1], e[3], e[2], i[0], e[1], e[0])); } } TEST_F(TraverseExpressionsTest, DescendBinaryExpression) { std::vector e = {Expr(1_i), Expr(1_i), Expr(1_i), Expr(1_i)}; std::vector i = {Add(e[0], e[1]), Sub(e[2], e[3])}; auto* root = Mul(i[0], i[1]); { std::vector l2r; TraverseExpressions(root, Diagnostics(), [&](const ast::Expression* expr) { l2r.push_back(expr); return ast::TraverseAction::Descend; }); EXPECT_THAT(l2r, ElementsAre(root, i[0], e[0], e[1], i[1], e[2], e[3])); } { std::vector r2l; TraverseExpressions(root, Diagnostics(), [&](const ast::Expression* expr) { r2l.push_back(expr); return ast::TraverseAction::Descend; }); EXPECT_THAT(r2l, ElementsAre(root, i[1], e[3], e[2], i[0], e[1], e[0])); } } TEST_F(TraverseExpressionsTest, Depth) { std::vector e = {Expr(1_i), Expr(1_i), Expr(1_i), Expr(1_i)}; std::vector i = {Add(e[0], e[1]), Sub(e[2], e[3])}; auto* root = Mul(i[0], i[1]); size_t j = 0; size_t depths[] = {0, 1, 2, 2, 1, 2, 2}; { TraverseExpressions( // root, Diagnostics(), [&](const ast::Expression* expr, size_t depth) { (void)expr; EXPECT_THAT(depth, depths[j++]); return ast::TraverseAction::Descend; }); } } TEST_F(TraverseExpressionsTest, DescendBitcastExpression) { auto* e = Expr(1_i); auto* b0 = Bitcast(e); auto* b1 = Bitcast(b0); auto* b2 = Bitcast(b1); auto* root = Bitcast(b2); { utils::Vector l2r; TraverseExpressions(root, Diagnostics(), [&](const ast::Expression* expr) { l2r.Push(expr); return ast::TraverseAction::Descend; }); EXPECT_THAT(l2r, ElementsAre(root, b2, b1, b0, e)); } { utils::Vector r2l; TraverseExpressions(root, Diagnostics(), [&](const ast::Expression* expr) { r2l.Push(expr); return ast::TraverseAction::Descend; }); EXPECT_THAT(r2l, ElementsAre(root, b2, b1, b0, e)); } } TEST_F(TraverseExpressionsTest, DescendCallExpression) { utils::Vector e{Expr(1_i), Expr(1_i), Expr(1_i), Expr(1_i)}; utils::Vector c{Call("a", e[0], e[1]), Call("b", e[2], e[3])}; auto* root = Call("c", c[0], c[1]); { utils::Vector l2r; TraverseExpressions(root, Diagnostics(), [&](const ast::Expression* expr) { l2r.Push(expr); return ast::TraverseAction::Descend; }); EXPECT_THAT(l2r, ElementsAre(root, c[0], e[0], e[1], c[1], e[2], e[3])); } { utils::Vector r2l; TraverseExpressions(root, Diagnostics(), [&](const ast::Expression* expr) { r2l.Push(expr); return ast::TraverseAction::Descend; }); EXPECT_THAT(r2l, ElementsAre(root, c[1], e[3], e[2], c[0], e[1], e[0])); } } // TODO(crbug.com/tint/1257): Test ignores member accessor 'member' field. // Replace with the test below when fixed. TEST_F(TraverseExpressionsTest, DescendMemberIndexExpression) { auto* e = Expr(1_i); auto* m = MemberAccessor(e, Expr("a")); auto* root = MemberAccessor(m, Expr("b")); { std::vector l2r; TraverseExpressions(root, Diagnostics(), [&](const ast::Expression* expr) { l2r.push_back(expr); return ast::TraverseAction::Descend; }); EXPECT_THAT(l2r, ElementsAre(root, m, e)); } { std::vector r2l; TraverseExpressions(root, Diagnostics(), [&](const ast::Expression* expr) { r2l.push_back(expr); return ast::TraverseAction::Descend; }); EXPECT_THAT(r2l, ElementsAre(root, m, e)); } } // TODO(crbug.com/tint/1257): The correct test for DescendMemberIndexExpression. TEST_F(TraverseExpressionsTest, DISABLED_DescendMemberIndexExpression) { auto* e = Expr(1_i); std::vector i = {Expr("a"), Expr("b")}; auto* m = MemberAccessor(e, i[0]); auto* root = MemberAccessor(m, i[1]); { std::vector l2r; TraverseExpressions(root, Diagnostics(), [&](const ast::Expression* expr) { l2r.push_back(expr); return ast::TraverseAction::Descend; }); EXPECT_THAT(l2r, ElementsAre(root, m, e, i[0], i[1])); } { std::vector r2l; TraverseExpressions(root, Diagnostics(), [&](const ast::Expression* expr) { r2l.push_back(expr); return ast::TraverseAction::Descend; }); EXPECT_THAT(r2l, ElementsAre(root, i[1], m, i[0], e)); } } TEST_F(TraverseExpressionsTest, DescendUnaryExpression) { auto* e = Expr(1_i); auto* u0 = AddressOf(e); auto* u1 = Deref(u0); auto* u2 = AddressOf(u1); auto* root = Deref(u2); { std::vector l2r; TraverseExpressions(root, Diagnostics(), [&](const ast::Expression* expr) { l2r.push_back(expr); return ast::TraverseAction::Descend; }); EXPECT_THAT(l2r, ElementsAre(root, u2, u1, u0, e)); } { std::vector r2l; TraverseExpressions(root, Diagnostics(), [&](const ast::Expression* expr) { r2l.push_back(expr); return ast::TraverseAction::Descend; }); EXPECT_THAT(r2l, ElementsAre(root, u2, u1, u0, e)); } } TEST_F(TraverseExpressionsTest, Skip) { std::vector e = {Expr(1_i), Expr(1_i), Expr(1_i), Expr(1_i)}; std::vector i = {IndexAccessor(e[0], e[1]), IndexAccessor(e[2], e[3])}; auto* root = IndexAccessor(i[0], i[1]); std::vector order; TraverseExpressions( root, Diagnostics(), [&](const ast::Expression* expr) { order.push_back(expr); return expr == i[0] ? ast::TraverseAction::Skip : ast::TraverseAction::Descend; }); EXPECT_THAT(order, ElementsAre(root, i[0], i[1], e[2], e[3])); } TEST_F(TraverseExpressionsTest, Stop) { std::vector e = {Expr(1_i), Expr(1_i), Expr(1_i), Expr(1_i)}; std::vector i = {IndexAccessor(e[0], e[1]), IndexAccessor(e[2], e[3])}; auto* root = IndexAccessor(i[0], i[1]); std::vector order; TraverseExpressions( root, Diagnostics(), [&](const ast::Expression* expr) { order.push_back(expr); return expr == i[0] ? ast::TraverseAction::Stop : ast::TraverseAction::Descend; }); EXPECT_THAT(order, ElementsAre(root, i[0])); } } // namespace } // namespace tint::ast