| // Copyright 2017-2020 The Verible 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 "verilog/CST/dimensions.h" |
| |
| #include <stddef.h> |
| |
| #include <algorithm> |
| #include <memory> |
| #include <vector> |
| |
| #include "gmock/gmock.h" |
| #include "gtest/gtest.h" |
| #include "common/analysis/matcher/matcher.h" |
| #include "common/analysis/matcher/matcher_builders.h" |
| #include "common/analysis/syntax_tree_search.h" |
| #include "common/text/concrete_syntax_leaf.h" |
| #include "common/text/concrete_syntax_tree.h" |
| #include "common/text/symbol.h" |
| #include "common/text/text_structure.h" |
| #include "common/text/token_info.h" |
| #include "common/text/tree_utils.h" |
| #include "common/util/casts.h" |
| #include "common/util/logging.h" |
| #include "verilog/CST/verilog_matchers.h" // IWYU pragma: keep |
| #include "verilog/analysis/verilog_analyzer.h" |
| |
| #undef ASSERT_OK |
| #define ASSERT_OK(value) ASSERT_TRUE((value).ok()) |
| |
| namespace verilog { |
| namespace { |
| |
| using verible::down_cast; |
| using verible::SyntaxTreeLeaf; |
| |
| struct MatchTestCase { |
| const char* code; |
| int expect_packed_matches; |
| int expect_unpacked_matches; |
| }; |
| |
| // These test cases check for the correct number of occurences of |
| // packed and unpacked dimensions. |
| static const MatchTestCase kMatchTestCases[] = { |
| {"", 0, 0}, |
| // package_or_generate_item_declaration level tests |
| {"wire w;", 0, 0}, |
| {"wire [1:0] w;", 1, 0}, |
| {"wire [9:0] w;", 1, 0}, |
| {"wire [0:4] w;", 1, 0}, |
| {"wire [1:0][3:0] w;", 1, 0}, // 2 dimensional, but 1 set of packed |
| {"wire w [0:1];", 0, 1}, |
| {"wire w [1:0];", 0, 1}, |
| {"wire w [1:0][7:0];", 0, 1}, // 2 dimensional, but 1 set of unpacked |
| {"wire [2:0] w [2];", 1, 1}, |
| {"wire [2:0][1:0] w [4][2];", 1, 1}, |
| {"wire [1:0] w; wire [1:0] x;", 2, 0}, // separate declarations |
| {"wire w [1:0]; wire x [1:0];", 0, 2}, // separate declarations |
| |
| // Test different data_declaration types. |
| {"logic l;", 0, 0}, |
| {"logic [1:0] l;", 1, 0}, |
| {"logic l [1:0];", 0, 1}, |
| {"bit b;", 0, 0}, |
| {"bit [1:0] b;", 1, 0}, |
| {"bit b [1:0];", 0, 1}, |
| {"reg r;", 0, 0}, |
| {"reg [1:0] r;", 1, 0}, |
| {"reg r [1:0];", 0, 1}, |
| {"mytype m;", 0, 0}, |
| {"mytype [1:0] m;", 1, 0}, |
| {"mytype m [1:0];", 0, 1}, |
| {"mypkg::mytype m;", 0, 0}, |
| {"mypkg::mytype [1:0] m;", 1, 0}, |
| {"mypkg::mytype m [1:0];", 0, 1}, |
| {"signed m;", 0, 0}, |
| {"signed [1:0] m;", 1, 0}, |
| {"signed m [1:0];", 0, 1}, |
| {"unsigned m;", 0, 0}, |
| {"unsigned [1:0] m;", 1, 0}, |
| {"unsigned m [1:0];", 0, 1}, |
| {"event e;", 0, 0}, |
| {"event [1:0] e;", 1, 0}, |
| {"event e [1:0];", 0, 1}, |
| |
| // Test unnamed struct members |
| {"struct { logic l; } s;", 0, 0}, |
| {"struct { logic [2:0] l; } s;", 1, 0}, |
| {"struct { logic l [2:0]; } s;", 0, 1}, |
| // Test typedef struct members |
| {"typedef struct { logic l; } s_s;", 0, 0}, |
| {"typedef struct { logic [2:0] l; } s_s;", 1, 0}, |
| {"typedef struct { logic l [2:0]; } s_s;", 0, 1}, |
| |
| // Test class fields |
| {"class c; bit b; endclass", 0, 0}, |
| {"class c; bit [1:0] b; endclass", 1, 0}, |
| {"class c; bit b [0:1]; endclass", 0, 1}, |
| {"class c; bit [2:0] b [0:1]; endclass", 1, 1}, |
| |
| // Test module ports |
| {"module m(input wire foo); endmodule", 0, 0}, |
| {"module m(input wire [2:0] foo); endmodule", 1, 0}, |
| {"module m(input wire foo [2:0]); endmodule", 0, 1}, |
| {"module m(input wire [2:0] foo [2:0]); endmodule", 1, 1}, |
| {"module m(output reg foo); endmodule", 0, 0}, |
| {"module m(output reg [2:0] foo); endmodule", 1, 0}, |
| {"module m(output reg foo [2:0]); endmodule", 0, 1}, |
| {"module m(output reg [2:0] foo [2:0]); endmodule", 1, 1}, |
| |
| // Test module local declarations |
| {"module m; wire foo; endmodule", 0, 0}, |
| {"module m; wire [4:0] foo; endmodule", 1, 0}, |
| {"module m; wire foo[5]; endmodule", 0, 1}, |
| {"module m; wire [4:0] foo[5]; endmodule", 1, 1}, |
| {"module m; submod foo; endmodule", 0, 0}, |
| {"module m; submod foo[5]; endmodule", 0, 1}, |
| |
| // Test function ports |
| {"function void f(bit foo); endfunction", 0, 0}, |
| {"function void f(bit [2:0] foo); endfunction", 1, 0}, |
| {"function void f(bit foo [2:0]); endfunction", 0, 1}, |
| |
| // Test function locals |
| {"function void f; bit foo; endfunction", 0, 0}, |
| {"function void f; bit [3:0] foo; endfunction", 1, 0}, |
| {"function void f; bit foo [3:0]; endfunction", 0, 1}, |
| |
| // Test function return types |
| {"function bit foo; endfunction", 0, 0}, |
| {"function bit [2:0] foo; endfunction", 1, 0}, |
| |
| // Test task ports |
| {"task automatic t(bit foo); endtask", 0, 0}, |
| {"task automatic t(bit [2:0] foo); endtask", 1, 0}, |
| {"task automatic t(bit foo [2:0]); endtask", 0, 1}, |
| |
| // Test task locals |
| {"task automatic t; bit foo; endtask", 0, 0}, |
| {"task automatic t; bit [2:0] foo; endtask", 1, 0}, |
| {"task automatic t; bit foo [2:0]; endtask", 0, 1}, |
| |
| // Test parameters |
| {"parameter int p = 0;", 0, 0}, |
| {"parameter int [3:0] p = 0;", 1, 0}, |
| {"parameter int p [3:0] = 0;", 0, 1}, |
| {"localparam int p = 0;", 0, 0}, |
| {"localparam int [3:0] p = 0;", 1, 0}, |
| {"localparam int p [3:0] = 0;", 0, 1}, |
| {"parameter int p = q[0];", 0, 0}, // selection is not declaration |
| {"parameter int p = q[1:0];", 0, 0}, // selection is not declaration |
| {"parameter int p = 0, q = 1;", 0, 0}, // multiple assignments |
| {"parameter int [1:0] p = 0, q = 1;", 1, 0}, // multiple assignments |
| // TODO(b/132818394): parse unpacked dimensions in subsequent initializers |
| // {"parameter int p[0:1] = {0,2}, q[0:1] = {1,3};", 0, 2}, |
| }; |
| |
| static size_t ExtractNumDimensions(const verible::Symbol* root) { |
| if (root == nullptr) return 0; |
| const auto matches = FindAllDeclarationDimensions(*root); |
| if (matches.empty()) return 0; |
| // Only extract from the first match. |
| if (matches[0].match == nullptr) return 0; |
| const auto& s = *matches[0].match; |
| return (down_cast<const verible::SyntaxTreeNode&>(s).children()).size(); |
| } |
| |
| // Test that number of sets of packed dimensions found is correct. |
| TEST(FindAllPackedDimensionsTest, MatchCounts) { |
| for (const auto& test : kMatchTestCases) { |
| VerilogAnalyzer analyzer(test.code, ""); |
| ASSERT_OK(analyzer.Analyze()) << "Failed test code: " << test.code; |
| const auto& root = analyzer.Data().SyntaxTree(); |
| const auto packed_dimensions = |
| FindAllPackedDimensions(*ABSL_DIE_IF_NULL(root)); |
| const int nonempty_packed_dimensions = |
| std::count_if(packed_dimensions.begin(), packed_dimensions.end(), |
| [](const verible::TreeSearchMatch& m) { |
| return ExtractNumDimensions(m.match) > 0; |
| }); |
| EXPECT_EQ(nonempty_packed_dimensions, test.expect_packed_matches) |
| << "Failed test code: " << test.code; |
| } |
| } |
| |
| // Test that number of sets of unpacked dimensions found is correct. |
| TEST(FindAllUnpackedDimensionsTest, MatchCounts) { |
| for (const auto& test : kMatchTestCases) { |
| VerilogAnalyzer analyzer(test.code, ""); |
| ASSERT_OK(analyzer.Analyze()) << "Failed test code: " << test.code; |
| const auto& root = analyzer.Data().SyntaxTree(); |
| const auto unpacked_dimensions = |
| FindAllUnpackedDimensions(*ABSL_DIE_IF_NULL(root)); |
| const int nonempty_unpacked_dimensions = |
| std::count_if(unpacked_dimensions.begin(), unpacked_dimensions.end(), |
| [](const verible::TreeSearchMatch& m) { |
| return ExtractNumDimensions(m.match) > 0; |
| }); |
| EXPECT_EQ(nonempty_unpacked_dimensions, test.expect_unpacked_matches) |
| << "Failed test code: " << test.code; |
| } |
| } |
| |
| struct DimensionTestCase { |
| const char* code; |
| int expect_dimensions; |
| }; |
| |
| // Test that dimensionality counts are correct. |
| TEST(ExtractNumDimensionsTest, DimensionCounts) { |
| // In each of these cases, there should be exactly one set of dimensions. |
| const DimensionTestCase kDimensionTestCases[] = { |
| {"wire w;", 0}, {"wire [] w;", 1}, |
| {"wire [1:0] w;", 1}, {"wire [1:0][1:0] w;", 2}, |
| {"wire w [0:1];", 1}, {"wire w [0:1][0:3];", 2}, |
| {"wire w [2];", 1}, {"wire w [3][5];", 2}, |
| {"wire w [];", 1}, |
| }; |
| for (const auto& test : kDimensionTestCases) { |
| VerilogAnalyzer analyzer(test.code, ""); |
| ASSERT_OK(analyzer.Analyze()) << "Failed test code: " << test.code; |
| const auto& root = analyzer.Data().SyntaxTree(); |
| EXPECT_EQ(ExtractNumDimensions(root.get()), test.expect_dimensions) |
| << "Failed test code: " << test.code; |
| } |
| } |
| |
| struct RangeTestCase { |
| const char* code; |
| const char* expect_left; |
| const char* expect_right; |
| }; |
| |
| // Each of these test cases should have exactly one ranged-dimension. |
| static const RangeTestCase kRangeTestCases[] = { |
| {"wire [a:b] w;", "a", "b"}, |
| {"wire w [c:d];", "c", "d"}, |
| {"wire w [c1:d1][e];", "c1", "d1"}, |
| {"wire w [f][c2:d2];", "c2", "d2"}, |
| }; |
| |
| // Test that left-expression of dimension range is extracted correctly. |
| TEST(GetDimensionRangeLeftBoundTest, CheckBounds) { |
| for (const auto& test : kRangeTestCases) { |
| VerilogAnalyzer analyzer(test.code, ""); |
| ASSERT_OK(analyzer.Analyze()) << "Failed test code: " << test.code; |
| const auto& root = analyzer.Data().SyntaxTree(); |
| const auto range_matches = |
| SearchSyntaxTree(*ABSL_DIE_IF_NULL(root), NodekDimensionRange()); |
| ASSERT_EQ(range_matches.size(), 1); |
| const auto* left = GetDimensionRangeLeftBound(*range_matches.front().match); |
| const SyntaxTreeLeaf* left_leaf = verible::GetLeftmostLeaf(*left); |
| EXPECT_EQ(ABSL_DIE_IF_NULL(left_leaf)->get().text(), test.expect_left); |
| } |
| } |
| |
| // Test that right-expression of dimension range is extracted correctly. |
| TEST(GetDimensionRangeRightBoundTest, CheckBounds) { |
| for (const auto& test : kRangeTestCases) { |
| VerilogAnalyzer analyzer(test.code, ""); |
| ASSERT_OK(analyzer.Analyze()) << "Failed test code: " << test.code; |
| const auto& root = analyzer.Data().SyntaxTree(); |
| const auto range_matches = |
| SearchSyntaxTree(*ABSL_DIE_IF_NULL(root), NodekDimensionRange()); |
| ASSERT_EQ(range_matches.size(), 1); |
| const auto* right = |
| GetDimensionRangeRightBound(*range_matches.front().match); |
| const SyntaxTreeLeaf* right_leaf = verible::GetLeftmostLeaf(*right); |
| EXPECT_EQ(ABSL_DIE_IF_NULL(right_leaf)->get().text(), test.expect_right); |
| } |
| } |
| |
| } // namespace |
| } // namespace verilog |