blob: be33cc33d63aee4f036e8a1054d2a45dcd3b3b1b [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.
// Unit tests for package-related concrete-syntax-tree functions.
//
// Testing strategy:
// The point of these tests is to validate the structure that is assumed
// about package declaration nodes and the structure that is actually
// created by the parser, so test *should* use the parser-generated
// syntax trees, as opposed to hand-crafted/mocked syntax trees.
#include "verilog/CST/package.h"
#include <memory>
#include <vector>
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "absl/status/status.h"
#include "common/analysis/syntax_tree_search.h"
#include "common/analysis/syntax_tree_search_test_utils.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/util/casts.h"
#include "common/util/logging.h"
#include "verilog/CST/match_test_utils.h"
#include "verilog/analysis/verilog_analyzer.h"
#undef EXPECT_OK
#define EXPECT_OK(value) EXPECT_TRUE((value).ok())
#undef ASSERT_OK
#define ASSERT_OK(value) ASSERT_TRUE((value).ok())
namespace verilog {
namespace {
using verible::down_cast;
using verible::SyntaxTreeNode;
using verible::SyntaxTreeSearchTestCase;
using verible::TextStructureView;
using verible::TreeSearchMatch;
TEST(FindAllPackageDeclarationsTest, VariousTests) {
constexpr int kTag = 1;
const SyntaxTreeSearchTestCase testcases[] = {
{""},
{"module m;\nendmodule\n"},
{"class c;\nendclass\n"},
{"function f;\nendfunction\n"},
{{kTag, "package p; \n endpackage"}, "\n"},
{"\n", {kTag, "package p2; endpackage"}},
{{kTag, "package p; \n endpackage"},
" task sleep; ",
" endtask\n",
" class myclass;\n",
"endclass\n",
{kTag, "package p; \n endpackage"}},
{{kTag, "package p1; \n endpackage"},
"\n",
"module m; \n",
"const foo bar; \n",
"endmodule \n",
{kTag, "package p2; \n endpackage"}},
{"function f; ",
"endfunction ",
"module m; \n",
"const foo bar; \n",
"endmodule \n",
{kTag, "package p; \n endpackage"}},
{"`include \"stuff.svh\"\n",
"`expand_stuff()\n",
"`expand_with_semi(name);\n",
"`expand_more(name)\n",
{kTag, "package p; \n endpackage"}},
{{kTag, "package p; \n endpackage"},
"`undef FOOOBAR\n",
{kTag, "package p; \n endpackage"}},
{{kTag, "package p; \n endpackage"},
" let Peace = Love;\n",
{kTag, "package p; \n endpackage"},
" let Five() = Two + Two + One;\n",
" let Min(a,b) = (a < b) ? a : b;\n",
" let Max(a,b=1) = (a > b) ? a : b;\n",
{kTag, "package p; \n endpackage"},
" let Max(untyped a, bit b=1) = (a > b) ? a : b;\n"},
{"localparam real foo = 3.14;\n",
"localparam shortreal foo = 159.265;\n",
"localparam realtime foo = 358.979ns;\n",
{kTag, "package p; \n endpackage"},
" localparam real foo = 323.846;\n"},
{{kTag, "package p; \n endpackage"},
" import $unit::arnold;\n",
" import $unit::*;\n",
{kTag, "package p; \n endpackage"}},
{{kTag, "package p; \n endpackage"},
" export bar::baz;\n",
" export bar::*;\n",
{kTag, "package p; \n endpackage"}},
{{kTag, "package p; \n endpackage"},
" parameter reg[BITS:0] MR0 = '0;\n"},
{{kTag, "package p; \n endpackage"},
" bind scope_x type_y z (.*);\n",
" bind scope_x type_y z1(.*), z2(.*);\n",
" bind module_scope : inst_x type_y inst_z(.*);\n",
{kTag, "package p; \n endpackage"}},
{{kTag, "package p; \n endpackage"},
"`ifndef DEBUGGER\n",
"`endif\n",
" int num_packets;\n",
"`ifdef DEBUGGER\n",
"`endif\n",
" int router_size;\n"},
{{kTag, "package p; \n endpackage"},
" int num_packets;\n",
"`ifdef DEBUGGER\n",
"`elsif BORED\n",
"`else\n",
" string source_name;\n",
" string dest_name;\n",
"`endif\n",
" int router_size;\n",
{kTag, "package p; \n endpackage"}},
{{kTag, "package p; \n endpackage"},
" virtual a_if b_if;\n",
"virtual a_if b_if, c_if;\n",
{kTag, "package p; \n endpackage"},
" virtual a_if b_if;\n",
{kTag, "package p; \n endpackage"}},
{{kTag, "package p; \n endpackage"},
"`ifdef DEBUGGER\n",
"`endif\n",
{kTag, "package p; \n endpackage"},
"`ifdef DEBUGGER\n",
"`ifdef VERBOSE\n",
"`endif\n",
"`endif\n",
{kTag, "package p; \n endpackage"}}};
for (const auto& test : testcases) {
const absl::string_view code(test.code);
VerilogAnalyzer analyzer(code, "test-file");
const auto code_copy = analyzer.Data().Contents();
ASSERT_OK(analyzer.Analyze()) << "failed on:\n" << code;
const auto& root = analyzer.Data().SyntaxTree();
const auto decls = FindAllPackageDeclarations(*ABSL_DIE_IF_NULL(root));
std::ostringstream diffs;
EXPECT_TRUE(test.ExactMatchFindings(decls, code_copy, &diffs))
<< "failed on:\n"
<< code << "\ndiffs:\n"
<< diffs.str();
}
}
TEST(GetPackageNameTokenTest, VariousPackageTokenTests) {
constexpr int kTag = 1;
const SyntaxTreeSearchTestCase testcases[] = {
{""},
{"package ", {kTag, "foo"}, "; \n endpackage"},
{"package ", {kTag, "bar"}, "; \n endpackage"},
{"package ", {kTag, "foo"}, "; \n endpackage", "\n"},
{"package ",
{kTag, "p1"},
"; \n endpackage",
" task sleep; ",
" endtask\n",
" class myclass;\n",
"endclass\n",
"package ",
{kTag, "p2"},
"; \n endpackage"},
{"package ",
{kTag, "p1"},
"; \n endpackage",
"`ifdef DEBUGGER\n",
"`endif\n",
"package ",
{kTag, "p2"},
"; \n endpackage",
"`ifdef DEBUGGER\n",
"`ifdef VERBOSE\n",
"`endif\n",
"`endif\n",
"package ",
{kTag, "p3"},
"; \n endpackage"},
{"package ",
{kTag, "p1"},
"; \n endpackage",
" virtual a_if b_if;\n",
"virtual a_if b_if, c_if;\n",
"package ",
{kTag, "p2"},
"; \n endpackage",
" virtual a_if b_if;\n",
"package ",
{kTag, "p3"},
"; \n endpackage"},
{"package ",
{kTag, "p1"},
"; \n endpackage",
" int num_packets;\n",
"`ifdef DEBUGGER\n",
"`elsif BORED\n",
"`else\n",
" string source_name;\n",
" string dest_name;\n",
"`endif\n",
" int router_size;\n",
"package ",
{kTag, "p2"},
"; \n endpackage"},
{"package ",
{kTag, "p1"},
"; \n endpackage",
" bind scope_x type_y z (.*);\n",
" bind scope_x type_y z1(.*), z2(.*);\n",
" bind module_scope : inst_x type_y inst_z(.*);\n",
"package ",
{kTag, "p2"},
"; \n endpackage"},
{"package ",
{kTag, "p1"},
"; \n endpackage",
" import $unit::arnold;\n",
" import $unit::*;\n",
"package ",
{kTag, "p2"},
"; \n endpackage"},
{"package ",
{kTag, "p1"},
"; \n endpackage",
" export bar::baz;\n",
" export bar::*;\n",
"package ",
{kTag, "p2"},
"; \n endpackage"},
{"package ",
{kTag, "p1"},
"; \n endpackage",
" parameter reg[BITS:0] MR0 = '0;\n"},
{"`include \"stuff.svh\"\n",
"`expand_stuff()\n",
"`expand_with_semi(name);\n",
"`expand_more(name)\n",
"package ",
{kTag, "p2"},
"; \n endpackage"},
{"package ",
{kTag, "p1"},
"; \n endpackage",
"`undef FOOOBAR\n",
"package ",
{kTag, "p2"},
"; \n endpackage"},
{"package ",
{kTag, "p1"},
"; \n endpackage",
" let Peace = Love;\n",
"package ",
{kTag, "p2"},
"; \n endpackage",
" let Five() = Two + Two + One;\n",
" let Min(a,b) = (a < b) ? a : b;\n",
" let Max(a,b=1) = (a > b) ? a : b;\n",
"package ",
{kTag, "p3"},
"; \n endpackage",
" let Max(untyped a, bit b=1) = (a > b) ? a : b;\n"},
{"localparam real foo = 3.14;\n",
"localparam shortreal foo = 159.265;\n",
"localparam realtime foo = 358.979ns;\n",
"package ",
{kTag, "p1"},
"; \n endpackage",
" localparam real foo = 323.846;\n"}};
for (const auto& test : testcases) {
const absl::string_view code(test.code);
VerilogAnalyzer analyzer(code, "test-file");
const auto code_copy = analyzer.Data().Contents();
ASSERT_OK(analyzer.Analyze()) << "failed on:\n" << code;
const auto& root = analyzer.Data().SyntaxTree();
const auto declarations =
FindAllPackageDeclarations(*ABSL_DIE_IF_NULL(root));
std::vector<TreeSearchMatch> declIdentifiers;
for (const auto& decl : declarations) {
const auto& packageToken = GetPackageNameLeaf(*decl.match);
declIdentifiers.push_back(TreeSearchMatch{&packageToken, {}});
}
std::ostringstream diffs;
EXPECT_TRUE(test.ExactMatchFindings(declIdentifiers, code_copy, &diffs))
<< "failed on:\n"
<< code << "\ndiffs:\n"
<< diffs.str();
}
}
TEST(GetPackageNameTokenTest, RootIsNotAPackage) {
VerilogAnalyzer analyzer("package foo; endpackage", "");
EXPECT_OK(analyzer.Analyze());
const auto& root = analyzer.Data().SyntaxTree();
// Root node is a description list, not a package.
EXPECT_DEATH(GetPackageNameToken(*ABSL_DIE_IF_NULL(root)),
"kDescriptionList vs. kPackageDeclaration");
}
TEST(GetPackageNameTokenTest, ValidPackage) {
VerilogAnalyzer analyzer("package foo; endpackage", "");
EXPECT_OK(analyzer.Analyze());
const auto& root = analyzer.Data().SyntaxTree();
const auto package_declarations = FindAllPackageDeclarations(*root);
EXPECT_EQ(package_declarations.size(), 1);
const auto& package_node =
down_cast<const SyntaxTreeNode&>(*package_declarations.front().match);
// Root node is a description list, not a package.
const auto& token = GetPackageNameToken(package_node);
EXPECT_EQ(token.text(), "foo");
}
TEST(GetPackageNameTest, GetPackageEndLabelName) {
constexpr int kTag = 1;
const SyntaxTreeSearchTestCase testcases[] = {
{""},
{"package foo;\n endpackage"},
{"package foo;\n endpackage: ", {kTag, "foo"}},
{"package foo;\n function int f();\n return 10;\n endfunction: f\n class "
"c; endclass: c\n "
"endpackage: ",
{kTag, "foo"}},
};
for (const auto& test : testcases) {
const absl::string_view code(test.code);
VerilogAnalyzer analyzer(code, "test-file");
const auto code_copy = analyzer.Data().Contents();
ASSERT_OK(analyzer.Analyze()) << "failed on:\n" << code;
const auto& root = analyzer.Data().SyntaxTree();
const auto declarations =
FindAllPackageDeclarations(*ABSL_DIE_IF_NULL(root));
std::vector<TreeSearchMatch> names;
for (const auto& decl : declarations) {
const auto* package_name = GetPackageNameEndLabel(*decl.match);
if (package_name == nullptr) continue;
names.push_back(TreeSearchMatch{package_name, {}});
}
std::ostringstream diffs;
EXPECT_TRUE(test.ExactMatchFindings(names, code_copy, &diffs))
<< "failed on:\n"
<< code << "\ndiffs:\n"
<< diffs.str();
}
}
TEST(GetPackageBodyTest, GetPackageItemList) {
constexpr int kTag = 1;
const SyntaxTreeSearchTestCase testcases[] = {
{""},
{"package foo;\n endpackage"},
{"package foo;\n endpackage: foo"},
{"package foo;\n",
{kTag,
"function int f();\n return 10;\n endfunction: f\n class "
"c; endclass: c"},
"\n ",
"endpackage: foo"},
};
for (const auto& test : testcases) {
const absl::string_view code(test.code);
VerilogAnalyzer analyzer(code, "test-file");
const auto code_copy = analyzer.Data().Contents();
ASSERT_OK(analyzer.Analyze()) << "failed on:\n" << code;
const auto& root = analyzer.Data().SyntaxTree();
const auto declarations =
FindAllPackageDeclarations(*ABSL_DIE_IF_NULL(root));
std::vector<TreeSearchMatch> lists;
for (const auto& decl : declarations) {
const auto* package_item_list = GetPackageItemList(*decl.match);
if (package_item_list == nullptr) continue;
lists.push_back(TreeSearchMatch{package_item_list, {}});
}
std::ostringstream diffs;
EXPECT_TRUE(test.ExactMatchFindings(lists, code_copy, &diffs))
<< "failed on:\n"
<< code << "\ndiffs:\n"
<< diffs.str();
}
}
TEST(PackageImportTest, GetImportedPackageName) {
constexpr int kTag = 1; // value doesn't matter
const SyntaxTreeSearchTestCase kTestCases[] = {
{""},
{"module m; endmodule\n"},
{"package pkg; endpackage\nmodule m();\n import ",
{kTag, "pkg"},
"::*;\nendmodule"},
{"package pkg;\n int my_int;\nendpackage\nmodule m\nimport ",
{kTag, "pkg"},
"::*;\nimport ",
{kTag, "pkg"},
"::my_int;\n();\nendmodule"},
};
for (const auto& test : kTestCases) {
TestVerilogSyntaxRangeMatches(
__FUNCTION__, test, [](const TextStructureView& text_structure) {
const auto& root = text_structure.SyntaxTree();
const auto decls = FindAllPackageImportItems(*ABSL_DIE_IF_NULL(root));
std::vector<TreeSearchMatch> names;
for (const auto& decl : decls) {
const auto& name = GetImportedPackageName(*decl.match);
names.emplace_back(TreeSearchMatch{&name, {/* ignored context */}});
}
return names;
});
}
}
TEST(PackageImportTest, GetImportedItemName) {
constexpr int kTag = 1; // value doesn't matter
const SyntaxTreeSearchTestCase kTestCases[] = {
{""},
{"module m; endmodule\n"},
{"package pkg; endpackage\nmodule m();\n import pkg::*;\nendmodule"},
{"package pkg;\n int my_int;\nendpackage\nmodule m();\n import "
"pkg::*;\nimport pkg::",
{kTag, "my_int"},
";\nendmodule"},
{"package pkg;\n int my_int;\nclass "
"my_class;\nendclass\nendpackage\nmodule m();\n import "
"pkg::*;\nimport pkg::",
{kTag, "my_int"},
";\nimport pkg::",
{kTag, "my_class"},
";\nendmodule"},
{"package pkg;\n int my_int;\nclass "
"my_class;\nendclass\nendpackage\nmodule m\n import "
"pkg::*;\nimport pkg::",
{kTag, "my_int"},
";\nimport pkg::",
{kTag, "my_class"},
";\n();\n\nendmodule"},
};
for (const auto& test : kTestCases) {
TestVerilogSyntaxRangeMatches(
__FUNCTION__, test, [](const TextStructureView& text_structure) {
const auto& root = text_structure.SyntaxTree();
const auto decls = FindAllPackageImportItems(*ABSL_DIE_IF_NULL(root));
std::vector<TreeSearchMatch> names;
for (const auto& decl : decls) {
const auto* name =
GeImportedItemNameFromPackageImportItem(*decl.match);
if (name == nullptr) continue;
names.emplace_back(TreeSearchMatch{name, {/* ignored context*/}});
}
return names;
});
}
}
} // namespace
} // namespace verilog