blob: d5763b327868c5c1fa8b8ce379294a4b43ac94d7 [file] [log] [blame]
// 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