blob: cec3c880d440aaf475992ae2a9de77fbc08cae9e [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 "verible/verilog/analysis/symbol-table.h"
#include <algorithm>
#include <filesystem>
#include <iostream>
#include <iterator>
#include <memory>
#include <sstream>
#include <string>
#include <string_view>
#include <vector>
#include "absl/base/attributes.h"
#include "absl/status/status.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "verible/common/text/tree-utils.h"
#include "verible/common/util/file-util.h"
#include "verible/common/util/logging.h"
#include "verible/common/util/range.h"
#include "verible/common/util/tree-operations.h"
#include "verible/verilog/analysis/verilog-filelist.h"
#include "verible/verilog/analysis/verilog-project.h"
namespace verilog {
// Directly test some SymbolTable internals.
class SymbolTable::Tester : public SymbolTable {
public:
explicit Tester(VerilogProject *project) : SymbolTable(project) {}
using SymbolTable::MutableRoot;
};
namespace {
using testing::ElementsAreArray;
using testing::HasSubstr;
using verible::file::Basename;
using verible::file::testing::ScopedTestFile;
// An in-memory source file that doesn't require file-system access,
// nor create temporary files.
using TestVerilogSourceFile = InMemoryVerilogSourceFile;
struct ScopePathPrinter {
const SymbolTableNode &node;
};
static std::ostream &operator<<(std::ostream &stream,
const ScopePathPrinter &p) {
return SymbolTableNodeFullPath(stream, p.node);
}
// Assert that map/set element exists at 'key', and assigns it to 'dest'
// 'key' must be printable for failure diagnostic message.
// Defined as a macro for meaningful line numbers on failure.
#define ASSIGN_MUST_FIND(dest, map, key) \
const auto found_##dest(map.find(key)); /* iterator */ \
ASSERT_NE(found_##dest, map.end()) \
<< "No element at \"" << key << "\" in " #map; \
const auto &dest ABSL_ATTRIBUTE_UNUSED( \
found_##dest->second); /* mapped_type */
// Assert that container is not empty, and reference its first element.
// Works on any container type with .begin().
// Defined as a macro for meaningful line numbers on failure.
#define ASSIGN_MUST_HAVE_FIRST_ELEMENT(dest, container) \
ASSERT_FALSE(container.empty()); \
const auto &dest(*container.begin());
// Assert that container has exactly one-element, and reference it.
// Works on any container with .size() and .begin().
// Defined as a macro for meaningful line numbers on failure.
#define ASSIGN_MUST_HAVE_UNIQUE(dest, container) \
ASSERT_EQ(container.size(), 1); \
const auto &dest(*container.begin());
// Shorthand for asserting that a symbol table lookup from
// (const SymbolTableNode& scope) using (std::string_view key) must succeed,
// and is captured as (const SymbolTableNode& dest).
// Most of the time, the tester is not interested in the found_* iterator.
// This also defines 'dest##_info' as the SymbolInfo value attached to the
// 'dest' SymbolTableNode. Defined as a macro so that failure gives meaningful
// line numbers, and this allows ASSERT_NE to early exit.
#define MUST_ASSIGN_LOOKUP_SYMBOL(dest, scope, key) \
const auto found_##dest = (scope).Find(key); \
ASSERT_NE(found_##dest, (scope).end()) \
<< "No symbol at \"" << key << "\" in " << ScopePathPrinter{scope}; \
EXPECT_EQ(found_##dest->first, key); \
const SymbolTableNode &dest(found_##dest->second); \
const SymbolInfo &dest##_info ABSL_ATTRIBUTE_UNUSED(dest.Value())
// For SymbolInfo::references_map_view_type only: Assert that there is exactly
// one element at 'key' in 'map' and assign it to 'dest' (DependentReferences).
// 'map' should come from SymbolInfo::LocalReferencesMapViewForTesting().
#define ASSIGN_MUST_FIND_EXACTLY_ONE_REF(dest, map, key) \
ASSIGN_MUST_FIND(dest##_candidates, map, key); /* set of candidates */ \
ASSIGN_MUST_HAVE_UNIQUE(dest, dest##_candidates);
// Expect sequence of statuses to be empty, or print first (non-ok) status.
#define EXPECT_EMPTY_STATUSES(diagnostics) \
EXPECT_EQ(diagnostics.size(), 0) << "First unexpected diagnostic:\n" \
<< diagnostics.front().message()
TEST(SymbolMetaTypePrintTest, Print) {
std::ostringstream stream;
stream << SymbolMetaType::kClass;
EXPECT_EQ(stream.str(), "class");
}
TEST(SymbolTableNodeFullPathTest, Print) {
using KV = SymbolTableNode::key_value_type;
const SymbolTableNode root(
SymbolInfo{},
KV("AA", SymbolTableNode(SymbolInfo{}, KV("BB", SymbolTableNode{}))));
{
std::ostringstream stream;
SymbolTableNodeFullPath(stream, root);
EXPECT_EQ(stream.str(), "$root");
}
{
std::ostringstream stream;
SymbolTableNodeFullPath(stream, root.begin()->second);
EXPECT_EQ(stream.str(), "$root::AA");
}
{
std::ostringstream stream;
SymbolTableNodeFullPath(stream, root.begin()->second.begin()->second);
EXPECT_EQ(stream.str(), "$root::AA::BB");
}
}
TEST(ReferenceComponentTest, MatchesMetatypeTest) {
{ // kUnspecified matches all metatypes
const ReferenceComponent component{
.identifier = "",
.ref_type = ReferenceType::kUnqualified,
.required_metatype = SymbolMetaType::kUnspecified};
for (const auto &other :
{SymbolMetaType::kUnspecified, SymbolMetaType::kParameter,
SymbolMetaType::kFunction, SymbolMetaType::kTask}) {
const auto status = component.MatchesMetatype(other);
EXPECT_TRUE(status.ok()) << status.message();
}
}
{ // kCallable matches only kFunction and kTask
const ReferenceComponent component{
.identifier = "",
.ref_type = ReferenceType::kUnqualified,
.required_metatype = SymbolMetaType::kCallable};
for (const auto &other :
{SymbolMetaType::kFunction, SymbolMetaType::kTask}) {
const auto status = component.MatchesMetatype(other);
EXPECT_TRUE(status.ok()) << status.message();
}
for (const auto &other : {SymbolMetaType::kModule, SymbolMetaType::kPackage,
SymbolMetaType::kClass}) {
const auto status = component.MatchesMetatype(other);
EXPECT_FALSE(status.ok())
<< component.required_metatype << " vs. " << other;
}
}
{ // kClass matches only kClass and kTypeAlias
const ReferenceComponent component{
.identifier = "",
.ref_type = ReferenceType::kUnqualified,
.required_metatype = SymbolMetaType::kClass};
for (const auto &other :
{SymbolMetaType::kClass, SymbolMetaType::kTypeAlias}) {
const auto status = component.MatchesMetatype(other);
EXPECT_TRUE(status.ok()) << status.message();
}
for (const auto &other :
{SymbolMetaType::kModule, SymbolMetaType::kPackage,
SymbolMetaType::kFunction, SymbolMetaType::kTask}) {
const auto status = component.MatchesMetatype(other);
EXPECT_FALSE(status.ok())
<< component.required_metatype << " vs. " << other;
}
}
{ // all other types must be matched exactly
const ReferenceComponent component{
.identifier = "",
.ref_type = ReferenceType::kUnqualified,
.required_metatype = SymbolMetaType::kFunction};
for (const auto &other :
{SymbolMetaType::kUnspecified, SymbolMetaType::kParameter,
SymbolMetaType::kModule, SymbolMetaType::kTask,
SymbolMetaType::kClass}) {
const auto status = component.MatchesMetatype(other);
EXPECT_FALSE(status.ok())
<< component.required_metatype << " vs. " << other;
}
}
}
TEST(ReferenceNodeFullPathTest, Print) {
using Node = ReferenceComponentNode;
using Data = ReferenceComponent;
const Node root(
Data{.identifier = "xx",
.ref_type = ReferenceType::kUnqualified,
.required_metatype = SymbolMetaType::kClass},
Node(Data{.identifier = "yy", .ref_type = ReferenceType::kDirectMember},
Node(Data{.identifier = "zz",
.ref_type = ReferenceType::kMemberOfTypeOfParent})));
{
std::ostringstream stream;
ReferenceNodeFullPath(stream, root);
EXPECT_EQ(stream.str(), "@xx[class]");
}
{
std::ostringstream stream;
ReferenceNodeFullPath(stream, root.Children().front());
EXPECT_EQ(stream.str(), "@xx[class]::yy");
}
{
std::ostringstream stream;
ReferenceNodeFullPath(stream, root.Children().front().Children().front());
EXPECT_EQ(stream.str(), "@xx[class]::yy.zz");
}
}
TEST(DependentReferencesTest, PrintEmpty) {
DependentReferences dep_refs;
std::ostringstream stream;
stream << dep_refs;
EXPECT_EQ(stream.str(), "(empty-ref)");
}
TEST(DependentReferencesTest, PrintOnlyRootNodeUnresolved) {
const DependentReferences dep_refs{std::make_unique<ReferenceComponentNode>(
ReferenceComponent{.identifier = "foo",
.ref_type = ReferenceType::kUnqualified,
.required_metatype = SymbolMetaType::kUnspecified,
.resolved_symbol = nullptr})};
std::ostringstream stream;
stream << dep_refs;
EXPECT_EQ(stream.str(), "{ (@foo -> <unresolved>) }");
}
TEST(DependentReferencesTest, PrintNonRootResolved) {
// Synthesize a symbol table.
using KV = SymbolTableNode::key_value_type;
SymbolTableNode root(
SymbolInfo{SymbolMetaType::kRoot},
KV{"p_pkg",
SymbolTableNode(SymbolInfo{SymbolMetaType::kPackage},
KV{"c_class", SymbolTableNode(SymbolInfo{
SymbolMetaType::kClass})})});
// Bookmark symbol table nodes.
MUST_ASSIGN_LOOKUP_SYMBOL(p_pkg, root, "p_pkg");
MUST_ASSIGN_LOOKUP_SYMBOL(c_class, p_pkg, "c_class");
// Construct references already resolved to above nodes.
const DependentReferences dep_refs{std::make_unique<ReferenceComponentNode>(
ReferenceComponent{.identifier = "p_pkg",
.ref_type = ReferenceType::kUnqualified,
.required_metatype = SymbolMetaType::kPackage,
.resolved_symbol = &p_pkg},
ReferenceComponentNode(
ReferenceComponent{.identifier = "c_class",
.ref_type = ReferenceType::kDirectMember,
.required_metatype = SymbolMetaType::kClass,
.resolved_symbol = &c_class}))};
// Print and compare.
std::ostringstream stream;
stream << dep_refs;
EXPECT_EQ(stream.str(),
R"({ (@p_pkg[package] -> $root::p_pkg)
{ (::c_class[class] -> $root::p_pkg::c_class) }
})");
}
TEST(SymbolTablePrintTest, PrintClass) {
TestVerilogSourceFile src("foobar.sv",
"module ss;\n"
"endmodule\n"
"module tt;\n"
" ss qq();\n"
"endmodule\n");
const auto status = src.Parse();
ASSERT_TRUE(status.ok());
SymbolTable symbol_table(nullptr);
EXPECT_EQ(symbol_table.Project(), nullptr);
const auto build_diagnostics = BuildSymbolTable(src, &symbol_table);
EXPECT_EMPTY_STATUSES(build_diagnostics);
{
std::ostringstream stream;
symbol_table.PrintSymbolDefinitions(stream);
EXPECT_EQ(stream.str(), R"({ (
metatype: <root>
)
ss: { (
metatype: module
file: foobar.sv
) }
tt: { (
metatype: module
file: foobar.sv
)
qq: { (
metatype: data/net/var/instance
file: foobar.sv
type-info { source: "ss", type ref: { (@ss -> <unresolved>) } }
) }
}
})");
}
{
std::ostringstream stream;
symbol_table.PrintSymbolReferences(stream);
EXPECT_EQ(stream.str(), R"({ (refs: )
ss: { (refs: ) }
tt: { (refs:
{ (@ss -> <unresolved>) }
{ (@qq[data/net/var/instance] -> $root::tt::qq) }
)
qq: { (refs: ) }
}
})");
}
{ // Resolve symbols.
std::vector<absl::Status> resolve_diagnostics;
symbol_table.Resolve(&resolve_diagnostics); // nothing to resolve
EXPECT_EMPTY_STATUSES(resolve_diagnostics);
}
{ // <unresolved> should now become "$root::ss"
std::ostringstream stream;
symbol_table.PrintSymbolDefinitions(stream);
EXPECT_EQ(stream.str(), R"({ (
metatype: <root>
)
ss: { (
metatype: module
file: foobar.sv
) }
tt: { (
metatype: module
file: foobar.sv
)
qq: { (
metatype: data/net/var/instance
file: foobar.sv
type-info { source: "ss", type ref: { (@ss -> $root::ss) } }
) }
}
})");
}
{ // <unresolved> should now become "$root::ss"
std::ostringstream stream;
symbol_table.PrintSymbolReferences(stream);
EXPECT_EQ(stream.str(), R"({ (refs: )
ss: { (refs: ) }
tt: { (refs:
{ (@ss -> $root::ss) }
{ (@qq[data/net/var/instance] -> $root::tt::qq) }
)
qq: { (refs: ) }
}
})");
}
}
class BuildSymbolTableTest : public ::testing::Test {
protected:
void SetUp() final {
sources_dir = verible::file::JoinPath(
::testing::TempDir(),
::testing::UnitTest::GetInstance()->current_test_info()->name());
const absl::Status status = verible::file::CreateDir(sources_dir);
ASSERT_TRUE(status.ok()) << status;
}
void TearDown() final { std::filesystem::remove(sources_dir); }
std::string sources_dir;
};
TEST_F(BuildSymbolTableTest, IntegrityCheckResolvedSymbol) {
const auto test_func = []() {
SymbolTable::Tester symbol_table_1(nullptr), symbol_table_2(nullptr);
SymbolTableNode &root1(symbol_table_1.MutableRoot());
SymbolTableNode &root2(symbol_table_2.MutableRoot());
// Deliberately point from one symbol table to the other.
// To avoid an use-after-free AddressSanitizer error,
// mind the destruction ordering here:
// symbol_table1 will outlive symbol_table_2, so give symbol_table_2 a
// pointer to symbol_table_1.
root2.Value().local_references_to_bind.emplace_back(
std::make_unique<ReferenceComponentNode>(ReferenceComponent{
.identifier = "foo",
.ref_type = ReferenceType::kUnqualified,
.required_metatype = SymbolMetaType::kUnspecified,
.resolved_symbol = &root1}));
// CheckIntegrity() will fail on destruction of symbol_table_2.
};
EXPECT_DEATH(test_func(),
"Resolved symbols must point to a node in the same SymbolTable");
}
TEST_F(BuildSymbolTableTest, IntegrityCheckDeclaredType) {
const auto test_func = []() {
SymbolTable::Tester symbol_table_1(nullptr), symbol_table_2(nullptr);
SymbolTableNode &root1(symbol_table_1.MutableRoot());
SymbolTableNode &root2(symbol_table_2.MutableRoot());
// Deliberately point from one symbol table to the other.
// To avoid an use-after-free AddressSanitizer error,
// mind the destruction ordering here:
// symbol_table1 will outlive symbol_table_2, so give symbol_table_2 a
// pointer to symbol_table_1.
root1.Value().local_references_to_bind.emplace_back(
std::make_unique<ReferenceComponentNode>(ReferenceComponent{
.identifier = "foo",
.ref_type = ReferenceType::kUnqualified,
.required_metatype = SymbolMetaType::kUnspecified,
.resolved_symbol = &root1}));
root2.Value().declared_type.user_defined_type =
root1.Value().local_references_to_bind.front().components.get();
// CheckIntegrity() will fail on destruction of symbol_table_2.
};
EXPECT_DEATH(test_func(),
"Resolved symbols must point to a node in the same SymbolTable");
}
TEST_F(BuildSymbolTableTest, InvalidSyntax) {
constexpr std::string_view invalid_codes[] = {
"module;\nendmodule\n",
};
for (const auto &code : invalid_codes) {
TestVerilogSourceFile src("foobar.sv", code);
const auto status = src.Parse();
EXPECT_FALSE(status.ok());
SymbolTable symbol_table(nullptr);
EXPECT_EQ(symbol_table.Project(), nullptr);
{ // Attempt to build symbol table after parse failure.
const auto build_diagnostics = BuildSymbolTable(src, &symbol_table);
EXPECT_TRUE(symbol_table.Root().Children().empty());
EXPECT_EMPTY_STATUSES(build_diagnostics);
}
{ // Attempt to resolve empty symbol table and references.
std::vector<absl::Status> resolve_diagnostics;
symbol_table.Resolve(&resolve_diagnostics); // nothing to resolve
EXPECT_EMPTY_STATUSES(resolve_diagnostics);
}
}
}
TEST_F(BuildSymbolTableTest, AvoidCrashFromFuzzer) {
// All that matters is that these test cases do not trigger crashes.
constexpr std::string_view codes[] = {
// some of these test cases come from fuzz testing
// and may contain syntax errors
"`e(C*C);\n", // expect two distinct reference trees
"`e(C::D * C.m + 12);\n", // expect two reference trees
"n#7;\n",
"c#1;;=P;\n",
};
for (const auto &code : codes) {
TestVerilogSourceFile src("foobar.sv", code);
const auto status = src.Parse(); // don't care if code is valid or not
SymbolTable symbol_table(nullptr);
EXPECT_EQ(symbol_table.Project(), nullptr);
{ // Attempt to build symbol table.
const auto build_diagnostics = BuildSymbolTable(src, &symbol_table);
// don't care about diagnostics
}
{ // Attempt to resolve empty symbol table and references.
std::vector<absl::Status> resolve_diagnostics;
symbol_table.Resolve(&resolve_diagnostics);
// don't care about diagnostics
}
}
}
TEST_F(BuildSymbolTableTest, ModuleDeclarationSingleEmpty) {
TestVerilogSourceFile src("foobar.sv", "module m;\nendmodule\n");
const auto status = src.Parse();
ASSERT_TRUE(status.ok()) << status.message();
SymbolTable symbol_table(nullptr);
const SymbolTableNode &root_symbol(symbol_table.Root());
const auto build_diagnostics = BuildSymbolTable(src, &symbol_table);
MUST_ASSIGN_LOOKUP_SYMBOL(module_node, root_symbol, "m");
EXPECT_EQ(module_node_info.metatype, SymbolMetaType::kModule);
EXPECT_EQ(module_node_info.file_origin, &src);
EXPECT_EQ(module_node_info.declared_type.syntax_origin,
nullptr); // there is no module meta-type
EXPECT_EMPTY_STATUSES(build_diagnostics);
{
std::vector<absl::Status> resolve_diagnostics;
symbol_table.Resolve(&resolve_diagnostics); // nothing to resolve
EXPECT_EMPTY_STATUSES(resolve_diagnostics);
}
}
TEST_F(BuildSymbolTableTest, ModuleDeclarationLocalNetsVariables) {
TestVerilogSourceFile src("foobar.sv",
"module m;\n"
" wire w1, w2;\n"
" logic l1, l2;\n"
"endmodule\n");
const auto status = src.Parse();
ASSERT_TRUE(status.ok()) << status.message();
SymbolTable symbol_table(nullptr);
const SymbolTableNode &root_symbol(symbol_table.Root());
const auto build_diagnostics = BuildSymbolTable(src, &symbol_table);
MUST_ASSIGN_LOOKUP_SYMBOL(module_node, root_symbol, "m");
EXPECT_EQ(module_node_info.metatype, SymbolMetaType::kModule);
EXPECT_EQ(module_node_info.file_origin, &src);
EXPECT_EQ(module_node_info.declared_type.syntax_origin,
nullptr); // there is no module meta-type
EXPECT_EMPTY_STATUSES(build_diagnostics);
static constexpr std::string_view members[] = {"w1", "w2", "l1", "l2"};
for (const auto &member : members) {
MUST_ASSIGN_LOOKUP_SYMBOL(member_node, module_node, member);
EXPECT_EQ(member_node_info.metatype,
SymbolMetaType::kDataNetVariableInstance);
EXPECT_EQ(member_node_info.declared_type.user_defined_type,
nullptr); // types are primitive
}
{
std::vector<absl::Status> resolve_diagnostics;
symbol_table.Resolve(&resolve_diagnostics); // nothing to resolve
EXPECT_EMPTY_STATUSES(resolve_diagnostics);
}
}
TEST_F(BuildSymbolTableTest, ModuleDeclarationLocalDuplicateNets) {
TestVerilogSourceFile src("foobar.sv",
"module m;\n"
" wire y1;\n"
" logic y1;\n" // y1 already declared
"endmodule\n");
const auto status = src.Parse();
ASSERT_TRUE(status.ok()) << status.message();
SymbolTable symbol_table(nullptr);
const SymbolTableNode &root_symbol(symbol_table.Root());
const auto build_diagnostics = BuildSymbolTable(src, &symbol_table);
MUST_ASSIGN_LOOKUP_SYMBOL(module_node, root_symbol, "m");
EXPECT_EQ(module_node_info.metatype, SymbolMetaType::kModule);
EXPECT_EQ(module_node_info.file_origin, &src);
EXPECT_EQ(module_node_info.declared_type.syntax_origin,
nullptr); // there is no module meta-type
ASSIGN_MUST_HAVE_UNIQUE(err_status, build_diagnostics);
EXPECT_EQ(err_status.code(), absl::StatusCode::kAlreadyExists);
EXPECT_THAT(err_status.message(),
HasSubstr("\"y1\" is already defined in the $root::m scope"));
{
std::vector<absl::Status> resolve_diagnostics;
symbol_table.Resolve(&resolve_diagnostics); // nothing to resolve
EXPECT_EMPTY_STATUSES(resolve_diagnostics);
}
}
TEST_F(BuildSymbolTableTest, ModuleDeclarationConditionalGenerateAnonymous) {
constexpr std::string_view source_variants[] = {
// with begin/end
"module m;\n"
" if (1) begin\n"
" wire x;\n"
" end else if (2) begin\n"
" wire y;\n"
" end else begin\n"
" wire z;\n"
" end\n"
"endmodule\n",
// without begin/end
"module m;\n"
" if (1)\n"
" wire x;\n"
" else if (2)\n"
" wire y;\n"
" else\n"
" wire z;\n"
"endmodule\n",
};
for (const auto &code : source_variants) {
TestVerilogSourceFile src("foobar.sv", code);
const auto status = src.Parse();
ASSERT_TRUE(status.ok()) << status.message();
SymbolTable symbol_table(nullptr);
const SymbolTableNode &root_symbol(symbol_table.Root());
const auto build_diagnostics = BuildSymbolTable(src, &symbol_table);
EXPECT_EMPTY_STATUSES(build_diagnostics);
MUST_ASSIGN_LOOKUP_SYMBOL(module_node, root_symbol, "m");
EXPECT_EQ(module_node_info.metatype, SymbolMetaType::kModule);
EXPECT_EQ(module_node_info.file_origin, &src);
EXPECT_EQ(module_node_info.declared_type.syntax_origin,
nullptr); // there is no module meta-type
ASSERT_EQ(module_node.Children().size(), 3);
auto iter = module_node.Children().begin();
{
const SymbolTableNode &gen_block(iter->second); // anonymous "...-0"
const SymbolInfo &gen_block_info(gen_block.Value());
EXPECT_EQ(gen_block_info.metatype, SymbolMetaType::kGenerate);
MUST_ASSIGN_LOOKUP_SYMBOL(wire_x, gen_block, "x");
EXPECT_EQ(wire_x_info.metatype, SymbolMetaType::kDataNetVariableInstance);
++iter;
}
{
const SymbolTableNode &gen_block(iter->second); // anonymous "...-1"
const SymbolInfo &gen_block_info(gen_block.Value());
EXPECT_EQ(gen_block_info.metatype, SymbolMetaType::kGenerate);
MUST_ASSIGN_LOOKUP_SYMBOL(wire_y, gen_block, "y");
EXPECT_EQ(wire_y_info.metatype, SymbolMetaType::kDataNetVariableInstance);
++iter;
}
{
const SymbolTableNode &gen_block(iter->second); // anonymous "...-2"
const SymbolInfo &gen_block_info(gen_block.Value());
EXPECT_EQ(gen_block_info.metatype, SymbolMetaType::kGenerate);
MUST_ASSIGN_LOOKUP_SYMBOL(wire_z, gen_block, "z");
EXPECT_EQ(wire_z_info.metatype, SymbolMetaType::kDataNetVariableInstance);
++iter;
}
{
std::vector<absl::Status> resolve_diagnostics;
symbol_table.Resolve(&resolve_diagnostics); // nothing to resolve
EXPECT_EMPTY_STATUSES(resolve_diagnostics);
}
}
}
TEST_F(BuildSymbolTableTest, ModuleDeclarationConditionalGenerateLabeled) {
TestVerilogSourceFile src("foobar.sv",
"module m;\n"
" if (1) begin : cc\n"
" wire x;\n"
" end else if (2) begin : bb\n"
" wire y;\n"
" end else begin : aa\n"
" wire z;\n"
" end\n"
"endmodule\n");
const auto status = src.Parse();
ASSERT_TRUE(status.ok()) << status.message();
SymbolTable symbol_table(nullptr);
const SymbolTableNode &root_symbol(symbol_table.Root());
const auto build_diagnostics = BuildSymbolTable(src, &symbol_table);
EXPECT_EMPTY_STATUSES(build_diagnostics);
MUST_ASSIGN_LOOKUP_SYMBOL(module_node, root_symbol, "m");
EXPECT_EQ(module_node_info.metatype, SymbolMetaType::kModule);
EXPECT_EQ(module_node_info.file_origin, &src);
EXPECT_EQ(module_node_info.declared_type.syntax_origin,
nullptr); // there is no module meta-type
ASSERT_EQ(module_node.Children().size(), 3);
{
MUST_ASSIGN_LOOKUP_SYMBOL(gen_block, module_node, "aa");
EXPECT_EQ(gen_block_info.metatype, SymbolMetaType::kGenerate);
MUST_ASSIGN_LOOKUP_SYMBOL(wire_z, gen_block, "z");
EXPECT_EQ(wire_z_info.metatype, SymbolMetaType::kDataNetVariableInstance);
}
{
MUST_ASSIGN_LOOKUP_SYMBOL(gen_block, module_node, "bb");
EXPECT_EQ(gen_block_info.metatype, SymbolMetaType::kGenerate);
MUST_ASSIGN_LOOKUP_SYMBOL(wire_y, gen_block, "y");
EXPECT_EQ(wire_y_info.metatype, SymbolMetaType::kDataNetVariableInstance);
}
{
MUST_ASSIGN_LOOKUP_SYMBOL(gen_block, module_node, "cc");
EXPECT_EQ(gen_block_info.metatype, SymbolMetaType::kGenerate);
MUST_ASSIGN_LOOKUP_SYMBOL(wire_x, gen_block, "x");
EXPECT_EQ(wire_x_info.metatype, SymbolMetaType::kDataNetVariableInstance);
}
{
std::vector<absl::Status> resolve_diagnostics;
symbol_table.Resolve(&resolve_diagnostics); // nothing to resolve
EXPECT_EMPTY_STATUSES(resolve_diagnostics);
}
}
TEST_F(BuildSymbolTableTest, ModuleDeclarationWithPorts) {
TestVerilogSourceFile src("foobar.sv",
"module m (\n"
" input wire clk,\n"
" output reg q\n"
");\n"
"endmodule\n");
const auto status = src.Parse();
ASSERT_TRUE(status.ok()) << status.message();
SymbolTable symbol_table(nullptr);
const SymbolTableNode &root_symbol(symbol_table.Root());
const auto build_diagnostics = BuildSymbolTable(src, &symbol_table);
EXPECT_EMPTY_STATUSES(build_diagnostics);
MUST_ASSIGN_LOOKUP_SYMBOL(module_node, root_symbol, "m");
EXPECT_EQ(module_node_info.metatype, SymbolMetaType::kModule);
EXPECT_EQ(module_node_info.file_origin, &src);
EXPECT_EQ(module_node_info.declared_type.syntax_origin,
nullptr); // there is no module meta-type
static constexpr std::string_view members[] = {"clk", "q"};
for (const auto &member : members) {
MUST_ASSIGN_LOOKUP_SYMBOL(member_node, module_node, member);
EXPECT_EQ(member_node_info.metatype,
SymbolMetaType::kDataNetVariableInstance);
EXPECT_EQ(member_node_info.declared_type.user_defined_type,
nullptr); // types are primitive
}
{
std::vector<absl::Status> resolve_diagnostics;
symbol_table.Resolve(&resolve_diagnostics); // nothing to resolve
EXPECT_EMPTY_STATUSES(resolve_diagnostics);
}
}
TEST_F(BuildSymbolTableTest, ModuleDeclarationMultiple) {
TestVerilogSourceFile src("foobar.sv",
"module m1;\nendmodule\n"
"module m2;\nendmodule\n");
const auto status = src.Parse();
ASSERT_TRUE(status.ok()) << status.message();
SymbolTable symbol_table(nullptr);
const SymbolTableNode &root_symbol(symbol_table.Root());
const auto build_diagnostics = BuildSymbolTable(src, &symbol_table);
EXPECT_EMPTY_STATUSES(build_diagnostics);
const std::string_view expected_modules[] = {"m1", "m2"};
for (const auto &expected_module : expected_modules) {
MUST_ASSIGN_LOOKUP_SYMBOL(module_node, root_symbol, expected_module);
EXPECT_EQ(module_node_info.metatype, SymbolMetaType::kModule);
EXPECT_EQ(module_node_info.file_origin, &src);
EXPECT_EQ(module_node_info.declared_type.syntax_origin,
nullptr); // there is no module meta-type
}
{
std::vector<absl::Status> resolve_diagnostics;
symbol_table.Resolve(&resolve_diagnostics); // nothing to resolve
EXPECT_EMPTY_STATUSES(resolve_diagnostics);
}
}
TEST_F(BuildSymbolTableTest, ModuleDeclarationDuplicate) {
TestVerilogSourceFile src("foobar.sv",
"module mm;\nendmodule\n"
"module mm;\nendmodule\n");
const auto status = src.Parse();
ASSERT_TRUE(status.ok()) << status.message();
SymbolTable symbol_table(nullptr);
const SymbolTableNode &root_symbol(symbol_table.Root());
const auto build_diagnostics = BuildSymbolTable(src, &symbol_table);
MUST_ASSIGN_LOOKUP_SYMBOL(module_node, root_symbol, "mm");
EXPECT_EQ(module_node_info.metatype, SymbolMetaType::kModule);
EXPECT_EQ(module_node_info.file_origin, &src);
EXPECT_EQ(module_node_info.declared_type.syntax_origin,
nullptr); // there is no module meta-type
ASSIGN_MUST_HAVE_UNIQUE(err, build_diagnostics);
EXPECT_EQ(err.code(), absl::StatusCode::kAlreadyExists);
EXPECT_THAT(err.message(),
HasSubstr("\"mm\" is already defined in the $root scope"));
{
std::vector<absl::Status> resolve_diagnostics;
symbol_table.Resolve(&resolve_diagnostics); // nothing to resolve
EXPECT_EMPTY_STATUSES(resolve_diagnostics);
}
}
TEST_F(BuildSymbolTableTest, ModuleDeclarationDuplicateSeparateFiles) {
TestVerilogSourceFile src("foobar.sv", "module mm;\nendmodule\n");
TestVerilogSourceFile src2("foobar-2.sv", "module mm;\nendmodule\n");
const auto status = src.Parse();
ASSERT_TRUE(status.ok()) << status.message();
const auto status2 = src2.Parse();
ASSERT_TRUE(status2.ok()) << status2.message();
SymbolTable symbol_table(nullptr);
const SymbolTableNode &root_symbol(symbol_table.Root());
const auto build_diagnostics1 = BuildSymbolTable(src, &symbol_table);
const auto build_diagnostics = BuildSymbolTable(src2, &symbol_table);
MUST_ASSIGN_LOOKUP_SYMBOL(module_node, root_symbol, "mm");
EXPECT_EQ(module_node_info.metatype, SymbolMetaType::kModule);
EXPECT_EQ(module_node_info.file_origin, &src);
EXPECT_EQ(module_node_info.declared_type.syntax_origin,
nullptr); // there is no module meta-type
ASSIGN_MUST_HAVE_UNIQUE(err, build_diagnostics);
EXPECT_EQ(err.code(), absl::StatusCode::kAlreadyExists);
EXPECT_THAT(err.message(),
HasSubstr("\"mm\" is already defined in the $root scope"));
{
std::vector<absl::Status> resolve_diagnostics;
symbol_table.Resolve(&resolve_diagnostics); // nothing to resolve
EXPECT_EMPTY_STATUSES(resolve_diagnostics);
}
}
TEST_F(BuildSymbolTableTest, ModuleDeclarationNested) {
TestVerilogSourceFile src("foobar.sv",
"module m_outer;\n"
" module m_inner;\n"
" endmodule\n"
"endmodule\n");
const auto status = src.Parse();
ASSERT_TRUE(status.ok()) << status.message();
SymbolTable symbol_table(nullptr);
const SymbolTableNode &root_symbol(symbol_table.Root());
const auto build_diagnostics = BuildSymbolTable(src, &symbol_table);
EXPECT_EMPTY_STATUSES(build_diagnostics);
MUST_ASSIGN_LOOKUP_SYMBOL(outer_module_node, root_symbol, "m_outer");
{
EXPECT_EQ(outer_module_node_info.metatype, SymbolMetaType::kModule);
EXPECT_EQ(outer_module_node_info.file_origin, &src);
EXPECT_EQ(outer_module_node_info.declared_type.syntax_origin,
nullptr); // there is no module meta-type
}
{
MUST_ASSIGN_LOOKUP_SYMBOL(inner_module_node, outer_module_node, "m_inner");
EXPECT_EQ(inner_module_node_info.metatype, SymbolMetaType::kModule);
EXPECT_EQ(inner_module_node_info.file_origin, &src);
EXPECT_EQ(inner_module_node_info.declared_type.syntax_origin,
nullptr); // there is no module meta-type
}
{
std::vector<absl::Status> resolve_diagnostics;
symbol_table.Resolve(&resolve_diagnostics); // nothing to resolve
EXPECT_EMPTY_STATUSES(resolve_diagnostics);
}
}
TEST_F(BuildSymbolTableTest, ModuleDeclarationNestedDuplicate) {
TestVerilogSourceFile src("foobar.sv",
"module outer;\n"
" module mm;\nendmodule\n"
" module mm;\nendmodule\n" // dupe
"endmodule\n");
const auto status = src.Parse();
ASSERT_TRUE(status.ok()) << status.message();
SymbolTable symbol_table(nullptr);
const SymbolTableNode &root_symbol(symbol_table.Root());
const auto build_diagnostics = BuildSymbolTable(src, &symbol_table);
MUST_ASSIGN_LOOKUP_SYMBOL(module_node, root_symbol, "outer");
EXPECT_EQ(module_node_info.metatype, SymbolMetaType::kModule);
ASSIGN_MUST_HAVE_UNIQUE(err, build_diagnostics);
EXPECT_EQ(err.code(), absl::StatusCode::kAlreadyExists);
EXPECT_THAT(err.message(),
HasSubstr("\"mm\" is already defined in the $root::outer scope"));
{
std::vector<absl::Status> resolve_diagnostics;
symbol_table.Resolve(&resolve_diagnostics); // nothing to resolve
EXPECT_EMPTY_STATUSES(resolve_diagnostics);
}
}
TEST_F(BuildSymbolTableTest, ModuleInstance) {
// The following code variants should yield the same symbol table results:
static constexpr std::string_view source_variants[] = {
// pp defined earlier in file
"module pp;\n"
"endmodule\n"
"module qq;\n"
" pp rr();\n" // instance
"endmodule\n",
// pp defined later in file
"module qq;\n"
" pp rr();\n" // instance
"endmodule\n"
"module pp;\n"
"endmodule\n",
};
for (const auto &code : source_variants) {
VLOG(1) << "code:\n" << code;
TestVerilogSourceFile src("foobar.sv", code);
const auto status = src.Parse();
ASSERT_TRUE(status.ok()) << status.message();
SymbolTable symbol_table(nullptr);
const SymbolTableNode &root_symbol(symbol_table.Root());
const auto build_diagnostics = BuildSymbolTable(src, &symbol_table);
EXPECT_EMPTY_STATUSES(build_diagnostics);
// Goal: resolve the reference of "pp" to this definition node.
MUST_ASSIGN_LOOKUP_SYMBOL(pp, root_symbol, "pp");
// Inspect inside the "qq" module definition.
MUST_ASSIGN_LOOKUP_SYMBOL(qq, root_symbol, "qq");
// "rr" is an instance of type "pp"
MUST_ASSIGN_LOOKUP_SYMBOL(rr, qq, "rr");
{
EXPECT_EQ(qq_info.file_origin, &src);
ASSERT_EQ(qq_info.local_references_to_bind.size(), 2);
const auto ref_map(qq_info.LocalReferencesMapViewForTesting());
{
ASSIGN_MUST_FIND_EXACTLY_ONE_REF(pp_type, ref_map, "pp");
const ReferenceComponentNode *ref_node = pp_type->LastTypeComponent();
ASSERT_NE(ref_node, nullptr);
const ReferenceComponent &ref(ref_node->Value());
EXPECT_EQ(ref.identifier, "pp");
EXPECT_TRUE(verible::IsSubRange(ref.identifier,
src.GetTextStructure()->Contents()));
EXPECT_EQ(ref.ref_type, ReferenceType::kUnqualified);
EXPECT_EQ(ref.required_metatype, SymbolMetaType::kUnspecified);
EXPECT_EQ(ref.resolved_symbol, nullptr);
}
{ // self-reference to "rr" instance
ASSIGN_MUST_FIND_EXACTLY_ONE_REF(rr_self_ref, ref_map, "rr");
EXPECT_TRUE(is_leaf(*rr_self_ref->components)); // no named ports
// self-reference is already bound.
EXPECT_EQ(rr_self_ref->components->Value().resolved_symbol, &rr);
}
}
EXPECT_TRUE(rr_info.local_references_to_bind.empty());
EXPECT_NE(rr_info.declared_type.user_defined_type, nullptr);
{
const ReferenceComponent &pp_type(
rr_info.declared_type.user_defined_type->Value());
EXPECT_EQ(pp_type.identifier, "pp");
EXPECT_EQ(pp_type.resolved_symbol, nullptr); // nothing resolved yet
EXPECT_EQ(pp_type.ref_type, ReferenceType::kUnqualified);
EXPECT_EQ(pp_type.required_metatype, SymbolMetaType::kUnspecified);
}
EXPECT_EQ(rr_info.file_origin, &src);
// Resolve symbols.
std::vector<absl::Status> resolve_diagnostics;
symbol_table.Resolve(&resolve_diagnostics);
EXPECT_EMPTY_STATUSES(resolve_diagnostics);
// Verify that typeof(rr) successfully resolved to module pp.
EXPECT_EQ(rr_info.declared_type.user_defined_type->Value().resolved_symbol,
&pp);
}
}
TEST_F(BuildSymbolTableTest, ModuleInstanceUndefined) {
TestVerilogSourceFile src("foobar.sv",
"module qq;\n"
" pp rr();\n" // instance, pp undefined
"endmodule\n");
const auto status = src.Parse();
ASSERT_TRUE(status.ok()) << status.message();
SymbolTable symbol_table(nullptr);
const SymbolTableNode &root_symbol(symbol_table.Root());
const auto build_diagnostics = BuildSymbolTable(src, &symbol_table);
EXPECT_EMPTY_STATUSES(build_diagnostics);
// Inspect inside the "qq" module definition.
MUST_ASSIGN_LOOKUP_SYMBOL(qq, root_symbol, "qq");
{
EXPECT_EQ(qq_info.file_origin, &src);
// There is only one reference to the "pp" module type.
ASSERT_EQ(qq_info.local_references_to_bind.size(), 2);
const auto ref_map(qq_info.LocalReferencesMapViewForTesting());
ASSIGN_MUST_FIND_EXACTLY_ONE_REF(pp_type, ref_map, "pp");
{ // verify that a reference to "pp" was established
const ReferenceComponentNode *ref_node = pp_type->LastTypeComponent();
ASSERT_NE(ref_node, nullptr);
const ReferenceComponent &ref(ref_node->Value());
EXPECT_EQ(ref.identifier, "pp");
EXPECT_TRUE(verible::IsSubRange(ref.identifier,
src.GetTextStructure()->Contents()));
EXPECT_EQ(ref.ref_type, ReferenceType::kUnqualified);
EXPECT_EQ(ref.required_metatype, SymbolMetaType::kUnspecified);
EXPECT_EQ(ref.resolved_symbol, nullptr);
}
}
// "rr" is an instance of type "pp" (which is undefined)
MUST_ASSIGN_LOOKUP_SYMBOL(rr, qq, "rr");
EXPECT_TRUE(rr_info.local_references_to_bind.empty());
EXPECT_NE(rr_info.declared_type.user_defined_type, nullptr);
{
const ReferenceComponent &pp_type(
rr_info.declared_type.user_defined_type->Value());
EXPECT_EQ(pp_type.identifier, "pp");
EXPECT_EQ(pp_type.resolved_symbol, nullptr); // nothing resolved yet
EXPECT_EQ(pp_type.ref_type, ReferenceType::kUnqualified);
EXPECT_EQ(pp_type.required_metatype, SymbolMetaType::kUnspecified);
}
EXPECT_EQ(rr_info.file_origin, &src);
{
// Resolve symbols. Expect one unresolved symbol.
std::vector<absl::Status> resolve_diagnostics;
symbol_table.Resolve(&resolve_diagnostics);
ASSIGN_MUST_HAVE_UNIQUE(err_status, resolve_diagnostics);
EXPECT_EQ(err_status.code(), absl::StatusCode::kNotFound);
EXPECT_THAT(err_status.message(),
HasSubstr("Unable to resolve symbol \"pp\""));
// Verify that typeof(rr) failed to resolve "pp".
EXPECT_EQ(rr_info.declared_type.user_defined_type->Value().resolved_symbol,
nullptr);
}
}
TEST_F(BuildSymbolTableTest, ModuleInstanceTwoInSameDecl) {
static constexpr std::string_view source_variants[] = {
// The following all yield equivalent symbol tables bindings.
"module pp;\n"
"endmodule\n"
"module qq;\n"
" pp r1(), r2();\n" // instances
"endmodule\n",
"module qq;\n"
" pp r1(), r2();\n" // instances
"endmodule\n"
"module pp;\n"
"endmodule\n",
// swap r1, r2 order
"module pp;\n"
"endmodule\n"
"module qq;\n"
" pp r2(), r1();\n" // instances
"endmodule\n",
"module qq;\n"
" pp r2(), r1();\n" // instances
"endmodule\n"
"module pp;\n"
"endmodule\n",
};
for (const auto &code : source_variants) {
TestVerilogSourceFile src("foobar.sv", code);
const auto status = src.Parse();
ASSERT_TRUE(status.ok()) << status.message();
SymbolTable symbol_table(nullptr);
const SymbolTableNode &root_symbol(symbol_table.Root());
const auto build_diagnostics = BuildSymbolTable(src, &symbol_table);
EXPECT_EMPTY_STATUSES(build_diagnostics);
MUST_ASSIGN_LOOKUP_SYMBOL(pp, root_symbol, "pp");
// Inspect inside the "qq" module definition.
MUST_ASSIGN_LOOKUP_SYMBOL(qq, root_symbol, "qq");
{
EXPECT_EQ(qq_info.file_origin, &src);
// There is only one type reference of interest, the "pp" module type.
// The other two are instance self-references.
ASSERT_EQ(qq_info.local_references_to_bind.size(), 3);
const auto ref_map(qq_info.LocalReferencesMapViewForTesting());
ASSIGN_MUST_FIND_EXACTLY_ONE_REF(pp_type, ref_map, "pp");
const ReferenceComponentNode *ref_node = pp_type->LastTypeComponent();
ASSERT_NE(ref_node, nullptr);
const ReferenceComponent &ref(ref_node->Value());
EXPECT_EQ(ref.identifier, "pp");
EXPECT_TRUE(verible::IsSubRange(ref.identifier,
src.GetTextStructure()->Contents()));
EXPECT_EQ(ref.ref_type, ReferenceType::kUnqualified);
EXPECT_EQ(ref.required_metatype, SymbolMetaType::kUnspecified);
EXPECT_EQ(ref.resolved_symbol, nullptr);
}
// "r1" and "r2" are both instances of type "pp"
static constexpr std::string_view pp_instances[] = {"r1", "r2"};
for (const auto &pp_inst : pp_instances) {
MUST_ASSIGN_LOOKUP_SYMBOL(rr, qq, pp_inst);
EXPECT_TRUE(rr_info.local_references_to_bind.empty());
EXPECT_NE(rr_info.declared_type.user_defined_type, nullptr);
{
const ReferenceComponent &pp_type(
rr_info.declared_type.user_defined_type->Value());
EXPECT_EQ(pp_type.identifier, "pp");
EXPECT_EQ(pp_type.resolved_symbol, nullptr); // nothing resolved yet
EXPECT_EQ(pp_type.ref_type, ReferenceType::kUnqualified);
EXPECT_EQ(pp_type.required_metatype, SymbolMetaType::kUnspecified);
}
EXPECT_EQ(rr_info.file_origin, &src);
}
{
std::vector<absl::Status> resolve_diagnostics;
symbol_table.Resolve(&resolve_diagnostics); // nothing to resolve
EXPECT_EMPTY_STATUSES(resolve_diagnostics);
for (const auto &pp_inst : pp_instances) {
MUST_ASSIGN_LOOKUP_SYMBOL(rr, qq, pp_inst);
EXPECT_TRUE(rr_info.local_references_to_bind.empty());
// Verify that typeof(r1,r2) successfully resolved to module pp.
EXPECT_EQ(
rr_info.declared_type.user_defined_type->Value().resolved_symbol,
&pp);
}
}
}
}
TEST_F(BuildSymbolTableTest, ModuleInstancePositionalPortConnection) {
TestVerilogSourceFile src("foobar.sv",
"module m (\n"
" input wire clk,\n"
" output reg q\n"
");\n"
"endmodule\n"
"module rr;\n"
" wire c, d;\n"
" m m_inst(c, d);"
// one type reference, two net references
"endmodule\n");
const auto status = src.Parse();
ASSERT_TRUE(status.ok()) << status.message();
SymbolTable symbol_table(nullptr);
const SymbolTableNode &root_symbol(symbol_table.Root());
const auto build_diagnostics = BuildSymbolTable(src, &symbol_table);
EXPECT_EMPTY_STATUSES(build_diagnostics);
MUST_ASSIGN_LOOKUP_SYMBOL(m_node, root_symbol, "m");
EXPECT_EQ(m_node_info.metatype, SymbolMetaType::kModule);
EXPECT_EQ(m_node_info.file_origin, &src);
EXPECT_EQ(m_node_info.declared_type.syntax_origin,
nullptr); // there is no module meta-type
MUST_ASSIGN_LOOKUP_SYMBOL(clk_node, m_node, "clk");
EXPECT_EQ(clk_node_info.metatype, SymbolMetaType::kDataNetVariableInstance);
EXPECT_EQ(clk_node_info.declared_type.user_defined_type,
nullptr); // types are primitive
MUST_ASSIGN_LOOKUP_SYMBOL(q_node, m_node, "q");
EXPECT_EQ(q_node_info.metatype, SymbolMetaType::kDataNetVariableInstance);
EXPECT_EQ(q_node_info.declared_type.user_defined_type,
nullptr); // types are primitive
MUST_ASSIGN_LOOKUP_SYMBOL(rr_node, root_symbol, "rr");
// Inspect local references to wires "c" and "d".
ASSERT_EQ(rr_node_info.local_references_to_bind.size(), 4);
const auto ref_map(rr_node_info.LocalReferencesMapViewForTesting());
ASSIGN_MUST_FIND_EXACTLY_ONE_REF(c_ref, ref_map, "c");
ASSIGN_MUST_FIND_EXACTLY_ONE_REF(d_ref, ref_map, "d");
EXPECT_EQ(c_ref->LastLeaf()->Value().identifier, "c");
EXPECT_EQ(c_ref->LastLeaf()->Value().resolved_symbol, nullptr);
EXPECT_EQ(d_ref->LastLeaf()->Value().identifier, "d");
EXPECT_EQ(d_ref->LastLeaf()->Value().resolved_symbol, nullptr);
// Get the local symbol definitions for wires "c" and "d".
MUST_ASSIGN_LOOKUP_SYMBOL(c_node, rr_node, "c");
MUST_ASSIGN_LOOKUP_SYMBOL(d_node, rr_node, "d");
{
std::vector<absl::Status> resolve_diagnostics;
symbol_table.Resolve(&resolve_diagnostics);
EXPECT_EMPTY_STATUSES(resolve_diagnostics);
// Expect to resolve local references to wires "c" and "d".
EXPECT_EQ(c_ref->LastLeaf()->Value().resolved_symbol, &c_node);
EXPECT_EQ(d_ref->LastLeaf()->Value().resolved_symbol, &d_node);
}
}
TEST_F(BuildSymbolTableTest, ModuleInstanceNamedPortConnection) {
TestVerilogSourceFile src("foobar.sv",
"module m (\n"
" input wire clk,\n"
" output reg q\n"
");\n"
"endmodule\n"
"module rr;\n"
" wire c, d;\n"
" m m_inst(.clk(c), .q(d));"
// one type reference, two local net references
// two named port references
"endmodule\n");
const auto status = src.Parse();
ASSERT_TRUE(status.ok()) << status.message();
SymbolTable symbol_table(nullptr);
const SymbolTableNode &root_symbol(symbol_table.Root());
const auto build_diagnostics = BuildSymbolTable(src, &symbol_table);
EXPECT_EMPTY_STATUSES(build_diagnostics);
MUST_ASSIGN_LOOKUP_SYMBOL(m_node, root_symbol, "m");
EXPECT_EQ(m_node_info.metatype, SymbolMetaType::kModule);
EXPECT_EQ(m_node_info.file_origin, &src);
EXPECT_EQ(m_node_info.declared_type.syntax_origin,
nullptr); // there is no module meta-type
MUST_ASSIGN_LOOKUP_SYMBOL(clk_node, m_node, "clk");
EXPECT_EQ(clk_node_info.metatype, SymbolMetaType::kDataNetVariableInstance);
EXPECT_EQ(clk_node_info.declared_type.user_defined_type,
nullptr); // types are primitive
MUST_ASSIGN_LOOKUP_SYMBOL(q_node, m_node, "q");
EXPECT_EQ(q_node_info.metatype, SymbolMetaType::kDataNetVariableInstance);
EXPECT_EQ(q_node_info.declared_type.user_defined_type,
nullptr); // types are primitive
MUST_ASSIGN_LOOKUP_SYMBOL(rr_node, root_symbol, "rr");
// Inspect local references to wires "c" and "d".
ASSERT_EQ(rr_node_info.local_references_to_bind.size(), 4);
const auto ref_map(rr_node_info.LocalReferencesMapViewForTesting());
ASSIGN_MUST_FIND_EXACTLY_ONE_REF(c_ref, ref_map, "c");
ASSIGN_MUST_FIND_EXACTLY_ONE_REF(d_ref, ref_map, "d");
ASSIGN_MUST_FIND_EXACTLY_ONE_REF(m_inst_ref, ref_map, "m_inst");
EXPECT_EQ(c_ref->LastLeaf()->Value().identifier, "c");
EXPECT_EQ(c_ref->LastLeaf()->Value().resolved_symbol, nullptr);
EXPECT_EQ(d_ref->LastLeaf()->Value().identifier, "d");
EXPECT_EQ(d_ref->LastLeaf()->Value().resolved_symbol, nullptr);
const ReferenceComponentNode &m_inst_ref_root(*m_inst_ref->components);
ASSERT_EQ(m_inst_ref_root.Children().size(), 2);
const ReferenceComponentMap port_refs(
ReferenceComponentNodeMapView(m_inst_ref_root));
ASSIGN_MUST_FIND(clk_ref, port_refs, "clk");
const ReferenceComponent &clk_ref_comp(clk_ref->Value());
EXPECT_EQ(clk_ref_comp.identifier, "clk");
EXPECT_EQ(clk_ref_comp.ref_type, ReferenceType::kMemberOfTypeOfParent);
EXPECT_EQ(clk_ref_comp.required_metatype,
SymbolMetaType::kDataNetVariableInstance);
EXPECT_EQ(clk_ref_comp.resolved_symbol, nullptr); // not yet resolved
ASSIGN_MUST_FIND(q_ref, port_refs, "q");
const ReferenceComponent &q_ref_comp(q_ref->Value());
EXPECT_EQ(q_ref_comp.identifier, "q");
EXPECT_EQ(q_ref_comp.ref_type, ReferenceType::kMemberOfTypeOfParent);
EXPECT_EQ(q_ref_comp.required_metatype,
SymbolMetaType::kDataNetVariableInstance);
EXPECT_EQ(q_ref_comp.resolved_symbol, nullptr); // not yet resolved
// Get the local symbol definitions for wires "c" and "d".
MUST_ASSIGN_LOOKUP_SYMBOL(c_node, rr_node, "c");
MUST_ASSIGN_LOOKUP_SYMBOL(d_node, rr_node, "d");
{
std::vector<absl::Status> resolve_diagnostics;
symbol_table.Resolve(&resolve_diagnostics);
EXPECT_EMPTY_STATUSES(resolve_diagnostics);
// Expect to resolve local references to wires c and d
EXPECT_EQ(c_ref->LastLeaf()->Value().resolved_symbol, &c_node);
EXPECT_EQ(d_ref->LastLeaf()->Value().resolved_symbol, &d_node);
// Expect to resolved named port references to "clk" and "q".
EXPECT_EQ(clk_ref_comp.resolved_symbol, &clk_node);
EXPECT_EQ(q_ref_comp.resolved_symbol, &q_node);
}
}
TEST_F(BuildSymbolTableTest,
ModuleInstanceNamedPortConnectionResolveLocallyOnly) {
// Similar to ModuleInstanceNamedPortConnection, but will not resolve
// non-local references.
TestVerilogSourceFile src("foobar.sv",
"module m (\n"
" input wire clk,\n"
" output reg q\n"
");\n"
"endmodule\n"
"module rr;\n"
" wire c, d;\n"
" m m_inst(.clk(c), .q(d));"
// one type reference, two local net references
// two named port references
"endmodule\n");
const auto status = src.Parse();
ASSERT_TRUE(status.ok()) << status.message();
SymbolTable symbol_table(nullptr);
const SymbolTableNode &root_symbol(symbol_table.Root());
const auto build_diagnostics = BuildSymbolTable(src, &symbol_table);
EXPECT_EMPTY_STATUSES(build_diagnostics);
MUST_ASSIGN_LOOKUP_SYMBOL(m_node, root_symbol, "m");
EXPECT_EQ(m_node_info.metatype, SymbolMetaType::kModule);
EXPECT_EQ(m_node_info.file_origin, &src);
EXPECT_EQ(m_node_info.declared_type.syntax_origin,
nullptr); // there is no module meta-type
MUST_ASSIGN_LOOKUP_SYMBOL(clk_node, m_node, "clk");
EXPECT_EQ(clk_node_info.metatype, SymbolMetaType::kDataNetVariableInstance);
EXPECT_EQ(clk_node_info.declared_type.user_defined_type,
nullptr); // types are primitive
MUST_ASSIGN_LOOKUP_SYMBOL(q_node, m_node, "q");
EXPECT_EQ(q_node_info.metatype, SymbolMetaType::kDataNetVariableInstance);
EXPECT_EQ(q_node_info.declared_type.user_defined_type,
nullptr); // types are primitive
MUST_ASSIGN_LOOKUP_SYMBOL(rr_node, root_symbol, "rr");
// Inspect local references to wires "c" and "d".
ASSERT_EQ(rr_node_info.local_references_to_bind.size(), 4);
const auto ref_map(rr_node_info.LocalReferencesMapViewForTesting());
ASSIGN_MUST_FIND_EXACTLY_ONE_REF(c_ref, ref_map, "c");
ASSIGN_MUST_FIND_EXACTLY_ONE_REF(d_ref, ref_map, "d");
ASSIGN_MUST_FIND_EXACTLY_ONE_REF(m_inst_ref, ref_map, "m_inst");
// Initially not resolved, but will be resolved below.
EXPECT_EQ(c_ref->LastLeaf()->Value().identifier, "c");
EXPECT_EQ(c_ref->LastLeaf()->Value().resolved_symbol, nullptr);
EXPECT_EQ(d_ref->LastLeaf()->Value().identifier, "d");
EXPECT_EQ(d_ref->LastLeaf()->Value().resolved_symbol, nullptr);
const ReferenceComponentNode &m_inst_ref_root(*m_inst_ref->components);
ASSERT_EQ(m_inst_ref_root.Children().size(), 2);
const ReferenceComponentMap port_refs(
ReferenceComponentNodeMapView(m_inst_ref_root));
ASSIGN_MUST_FIND(clk_ref, port_refs, "clk");
const ReferenceComponent &clk_ref_comp(clk_ref->Value());
EXPECT_EQ(clk_ref_comp.identifier, "clk");
EXPECT_EQ(clk_ref_comp.ref_type, ReferenceType::kMemberOfTypeOfParent);
EXPECT_EQ(clk_ref_comp.required_metatype,
SymbolMetaType::kDataNetVariableInstance);
// "clk" is a non-local reference that will not even be resolved below
EXPECT_EQ(clk_ref_comp.resolved_symbol, nullptr);
ASSIGN_MUST_FIND(q_ref, port_refs, "q");
const ReferenceComponent &q_ref_comp(q_ref->Value());
EXPECT_EQ(q_ref_comp.identifier, "q");
EXPECT_EQ(q_ref_comp.ref_type, ReferenceType::kMemberOfTypeOfParent);
EXPECT_EQ(q_ref_comp.required_metatype,
SymbolMetaType::kDataNetVariableInstance);
// "q" is a non-local reference that will not even be resolved below
EXPECT_EQ(q_ref_comp.resolved_symbol, nullptr);
// Get the local symbol definitions for wires "c" and "d".
MUST_ASSIGN_LOOKUP_SYMBOL(c_node, rr_node, "c");
MUST_ASSIGN_LOOKUP_SYMBOL(d_node, rr_node, "d");
// Running this twice changes nothing and is safe.
for (int i = 0; i < 2; ++i) {
symbol_table.ResolveLocallyOnly();
// Expect to resolve local references to wires c and d
EXPECT_EQ(c_ref->LastLeaf()->Value().resolved_symbol, &c_node);
EXPECT_EQ(d_ref->LastLeaf()->Value().resolved_symbol, &d_node);
// Expect to named port references to "clk" and "q" to remain unresolved.
EXPECT_EQ(clk_ref_comp.resolved_symbol, nullptr);
EXPECT_EQ(q_ref_comp.resolved_symbol, nullptr);
}
}
TEST_F(BuildSymbolTableTest, ModuleInstancePositionalParameterAssignment) {
TestVerilogSourceFile src("foobar.sv",
"module m #(\n"
" int N = 1\n"
");\n"
"endmodule\n"
"module rr;\n"
" m #(3) m_inst();"
// one type reference to "m"
// one instance self-reference
"endmodule\n");
const auto status = src.Parse();
ASSERT_TRUE(status.ok()) << status.message();
SymbolTable symbol_table(nullptr);
const SymbolTableNode &root_symbol(symbol_table.Root());
const auto build_diagnostics = BuildSymbolTable(src, &symbol_table);
EXPECT_EMPTY_STATUSES(build_diagnostics);
MUST_ASSIGN_LOOKUP_SYMBOL(m_node, root_symbol, "m");
EXPECT_EQ(m_node_info.metatype, SymbolMetaType::kModule);
EXPECT_EQ(m_node_info.file_origin, &src);
EXPECT_EQ(m_node_info.declared_type.syntax_origin,
nullptr); // there is no module meta-type
MUST_ASSIGN_LOOKUP_SYMBOL(n_param, m_node, "N");
EXPECT_EQ(n_param_info.metatype, SymbolMetaType::kParameter);
EXPECT_EQ(n_param_info.declared_type.user_defined_type,
nullptr); // types are primitive
MUST_ASSIGN_LOOKUP_SYMBOL(rr_node, root_symbol, "rr");
MUST_ASSIGN_LOOKUP_SYMBOL(m_inst_node, rr_node, "m_inst");
// Inspect local references to "m" and "m_inst".
ASSERT_EQ(rr_node_info.local_references_to_bind.size(), 2);
const auto ref_map(rr_node_info.LocalReferencesMapViewForTesting());
ASSIGN_MUST_FIND_EXACTLY_ONE_REF(m_ref, ref_map, "m");
ASSIGN_MUST_FIND_EXACTLY_ONE_REF(m_inst_ref, ref_map, "m_inst");
EXPECT_EQ(m_ref->LastLeaf()->Value().identifier, "m");
EXPECT_EQ(m_ref->LastLeaf()->Value().resolved_symbol, nullptr);
EXPECT_EQ(m_inst_ref->LastLeaf()->Value().identifier, "m_inst");
EXPECT_EQ(m_inst_ref->LastLeaf()->Value().resolved_symbol, &m_inst_node);
{
std::vector<absl::Status> resolve_diagnostics;
symbol_table.Resolve(&resolve_diagnostics);
EXPECT_EMPTY_STATUSES(resolve_diagnostics);
// Expect to resolve local references to "m" and "m_inst".
EXPECT_EQ(m_ref->LastLeaf()->Value().resolved_symbol, &m_node);
EXPECT_EQ(m_inst_ref->LastLeaf()->Value().resolved_symbol, &m_inst_node);
}
}
TEST_F(BuildSymbolTableTest, ModuleInstanceNamedParameterAssignment) {
TestVerilogSourceFile src("foobar.sv",
"module m #(\n"
" int N = 0,\n"
" int P = 1\n"
");\n"
"endmodule\n"
"module rr;\n"
" m #(.N(2), .P(3)) m_inst();"
// one type reference, one instance self-reference
// two named param rereference
"endmodule\n");
const auto status = src.Parse();
ASSERT_TRUE(status.ok()) << status.message();
SymbolTable symbol_table(nullptr);
const SymbolTableNode &root_symbol(symbol_table.Root());
const auto build_diagnostics = BuildSymbolTable(src, &symbol_table);
EXPECT_EMPTY_STATUSES(build_diagnostics);
MUST_ASSIGN_LOOKUP_SYMBOL(m_node, root_symbol, "m");
EXPECT_EQ(m_node_info.metatype, SymbolMetaType::kModule);
EXPECT_EQ(m_node_info.file_origin, &src);
EXPECT_EQ(m_node_info.declared_type.syntax_origin,
nullptr); // there is no module meta-type
MUST_ASSIGN_LOOKUP_SYMBOL(n_param, m_node, "N");
EXPECT_EQ(n_param_info.metatype, SymbolMetaType::kParameter);
EXPECT_EQ(n_param_info.declared_type.user_defined_type,
nullptr); // types are primitive
MUST_ASSIGN_LOOKUP_SYMBOL(p_param, m_node, "P");
EXPECT_EQ(p_param_info.metatype, SymbolMetaType::kParameter);
EXPECT_EQ(p_param_info.declared_type.user_defined_type,
nullptr); // types are primitive
MUST_ASSIGN_LOOKUP_SYMBOL(rr_node, root_symbol, "rr");
MUST_ASSIGN_LOOKUP_SYMBOL(m_inst_node, rr_node, "m_inst");
const auto ref_map(rr_node_info.LocalReferencesMapViewForTesting());
ASSIGN_MUST_FIND_EXACTLY_ONE_REF(m_type_ref, ref_map, "m");
const ReferenceComponentNode &m_type_ref_root(*m_type_ref->components);
ASSERT_EQ(m_type_ref_root.Children().size(), 2);
const ReferenceComponentMap param_refs(
ReferenceComponentNodeMapView(m_type_ref_root));
ASSIGN_MUST_FIND(n_ref, param_refs, "N");
const ReferenceComponent &n_ref_comp(n_ref->Value());
EXPECT_EQ(n_ref_comp.identifier, "N");
EXPECT_EQ(n_ref_comp.ref_type, ReferenceType::kDirectMember);
EXPECT_EQ(n_ref_comp.required_metatype, SymbolMetaType::kParameter);
EXPECT_EQ(n_ref_comp.resolved_symbol, nullptr); // not yet resolved
ASSIGN_MUST_FIND(p_ref, param_refs, "P");
const ReferenceComponent &p_ref_comp(p_ref->Value());
EXPECT_EQ(p_ref_comp.identifier, "P");
EXPECT_EQ(p_ref_comp.ref_type, ReferenceType::kDirectMember);
EXPECT_EQ(p_ref_comp.required_metatype, SymbolMetaType::kParameter);
EXPECT_EQ(p_ref_comp.resolved_symbol, nullptr); // not yet resolved
{
std::vector<absl::Status> resolve_diagnostics;
symbol_table.Resolve(&resolve_diagnostics);
EXPECT_EMPTY_STATUSES(resolve_diagnostics);
// Expect ".N" and ".P" to resolve to formal parameters of "m".
EXPECT_EQ(n_ref_comp.resolved_symbol, &n_param);
EXPECT_EQ(p_ref_comp.resolved_symbol, &p_param);
}
}
TEST_F(BuildSymbolTableTest, TimerAsModuleNameRegressionIssue917) {
TestVerilogSourceFile src("foobar.sv",
"module foo;\n"
" timer #(.N(1)) t;\n"
"endmodule\n");
const auto status = src.Parse();
ASSERT_TRUE(status.ok()) << status.message();
SymbolTable symbol_table(nullptr);
const SymbolTableNode &root_symbol(symbol_table.Root());
const auto build_diagnostics = BuildSymbolTable(src, &symbol_table);
EXPECT_EMPTY_STATUSES(build_diagnostics);
MUST_ASSIGN_LOOKUP_SYMBOL(foo_node, root_symbol, "foo");
MUST_ASSIGN_LOOKUP_SYMBOL(timer_instance_node, foo_node, "t");
}
TEST_F(BuildSymbolTableTest, ModuleInstanceNamedPortIsParameter) {
TestVerilogSourceFile src("foobar.sv",
"module m #(\n"
" int N = 0\n"
") (\n"
" input wire clk\n"
");\n"
"endmodule\n"
"module rr;\n"
" m #(.clk(2)) m_inst();"
// error: clk is a net-port, not parameter
"endmodule\n");
const auto status = src.Parse();
ASSERT_TRUE(status.ok()) << status.message();
SymbolTable symbol_table(nullptr);
const SymbolTableNode &root_symbol(symbol_table.Root());
const auto build_diagnostics = BuildSymbolTable(src, &symbol_table);
EXPECT_EMPTY_STATUSES(build_diagnostics);
MUST_ASSIGN_LOOKUP_SYMBOL(m_node, root_symbol, "m");
EXPECT_EQ(m_node_info.metatype, SymbolMetaType::kModule);
EXPECT_EQ(m_node_info.file_origin, &src);
EXPECT_EQ(m_node_info.declared_type.syntax_origin,
nullptr); // there is no module meta-type
MUST_ASSIGN_LOOKUP_SYMBOL(n_param, m_node, "N");
EXPECT_EQ(n_param_info.metatype, SymbolMetaType::kParameter);
EXPECT_EQ(n_param_info.declared_type.user_defined_type,
nullptr); // types are primitive
MUST_ASSIGN_LOOKUP_SYMBOL(clk_port, m_node, "clk");
EXPECT_EQ(clk_port_info.metatype, SymbolMetaType::kDataNetVariableInstance);
EXPECT_EQ(clk_port_info.declared_type.user_defined_type,
nullptr); // type is primitive
MUST_ASSIGN_LOOKUP_SYMBOL(rr_node, root_symbol, "rr");
MUST_ASSIGN_LOOKUP_SYMBOL(m_inst_node, rr_node, "m_inst");
const auto ref_map(rr_node_info.LocalReferencesMapViewForTesting());
ASSIGN_MUST_FIND_EXACTLY_ONE_REF(m_type_ref, ref_map, "m");
const ReferenceComponentNode &m_type_ref_root(*m_type_ref->components);
ASSERT_EQ(m_type_ref_root.Children().size(), 1);
const ReferenceComponentMap param_refs(
ReferenceComponentNodeMapView(m_type_ref_root));
ASSIGN_MUST_FIND(clk_ref, param_refs, "clk");
const ReferenceComponent &clk_ref_comp(clk_ref->Value());
EXPECT_EQ(clk_ref_comp.identifier, "clk");
EXPECT_EQ(clk_ref_comp.ref_type, ReferenceType::kDirectMember);
EXPECT_EQ(clk_ref_comp.required_metatype, SymbolMetaType::kParameter);
EXPECT_EQ(clk_ref_comp.resolved_symbol, nullptr); // not yet resolved
{
std::vector<absl::Status> resolve_diagnostics;
symbol_table.Resolve(&resolve_diagnostics);
// Expect ".clk" to fail to resolve.
ASSIGN_MUST_HAVE_UNIQUE(err, resolve_diagnostics);
EXPECT_EQ(err.code(), absl::StatusCode::kInvalidArgument);
EXPECT_THAT(err.message(),
HasSubstr("Expecting reference \"clk\" to resolve to a "
"parameter, but found a data/net/var/instance"));
EXPECT_EQ(clk_ref_comp.resolved_symbol, nullptr); // still unresolved
}
}
TEST_F(BuildSymbolTableTest, ModuleInstanceNamedParameterIsPort) {
TestVerilogSourceFile src("foobar.sv",
"module m #(\n"
" int N = 0\n"
") (\n"
" input wire clk\n"
");\n"
"endmodule\n"
"module rr;\n"
" m m_inst(.N(1));"
// error: N is a parameter, not net-port
"endmodule\n");
const auto status = src.Parse();
ASSERT_TRUE(status.ok()) << status.message();
SymbolTable symbol_table(nullptr);
const SymbolTableNode &root_symbol(symbol_table.Root());
const auto build_diagnostics = BuildSymbolTable(src, &symbol_table);
EXPECT_EMPTY_STATUSES(build_diagnostics);
MUST_ASSIGN_LOOKUP_SYMBOL(m_node, root_symbol, "m");
EXPECT_EQ(m_node_info.metatype, SymbolMetaType::kModule);
EXPECT_EQ(m_node_info.file_origin, &src);
EXPECT_EQ(m_node_info.declared_type.syntax_origin,
nullptr); // there is no module meta-type
MUST_ASSIGN_LOOKUP_SYMBOL(n_param, m_node, "N");
EXPECT_EQ(n_param_info.metatype, SymbolMetaType::kParameter);
EXPECT_EQ(n_param_info.declared_type.user_defined_type,
nullptr); // type is primitive
MUST_ASSIGN_LOOKUP_SYMBOL(clk_port, m_node, "clk");
EXPECT_EQ(clk_port_info.metatype, SymbolMetaType::kDataNetVariableInstance);
EXPECT_EQ(clk_port_info.declared_type.user_defined_type,
nullptr); // type is primitive
MUST_ASSIGN_LOOKUP_SYMBOL(rr_node, root_symbol, "rr");
MUST_ASSIGN_LOOKUP_SYMBOL(m_inst_node, rr_node, "m_inst");
const auto ref_map(rr_node_info.LocalReferencesMapViewForTesting());
ASSIGN_MUST_FIND_EXACTLY_ONE_REF(m_inst_ref, ref_map, "m_inst");
const ReferenceComponentNode &m_inst_ref_root(*m_inst_ref->components);
ASSERT_EQ(m_inst_ref_root.Children().size(), 1);
const ReferenceComponentMap port_refs(
ReferenceComponentNodeMapView(m_inst_ref_root));
ASSIGN_MUST_FIND(n_ref, port_refs, "N");
const ReferenceComponent &n_ref_comp(n_ref->Value());
EXPECT_EQ(n_ref_comp.identifier, "N");
EXPECT_EQ(n_ref_comp.ref_type, ReferenceType::kMemberOfTypeOfParent);
EXPECT_EQ(n_ref_comp.required_metatype,
SymbolMetaType::kDataNetVariableInstance);
EXPECT_EQ(n_ref_comp.resolved_symbol, nullptr); // not yet resolved
{
std::vector<absl::Status> resolve_diagnostics;
symbol_table.Resolve(&resolve_diagnostics);
// Expect ".N" to fail to resolve.
ASSIGN_MUST_HAVE_UNIQUE(err, resolve_diagnostics);
EXPECT_EQ(err.code(), absl::StatusCode::kInvalidArgument);
EXPECT_THAT(err.message(),
HasSubstr("Expecting reference \"N\" to resolve to a "
"data/net/var/instance, but found a parameter"));
EXPECT_EQ(n_ref_comp.resolved_symbol, nullptr); // still unresolved
}
}
TEST_F(BuildSymbolTableTest, ModuleInstanceNamedPortConnectionNonexistentPort) {
TestVerilogSourceFile src("foobar.sv",
"module m (\n"
" input wire clk,\n"
" output reg q\n"
");\n"
"endmodule\n"
"module rr;\n"
" wire c;\n"
" m m_inst(.clk(c), .p(c));"
// one type reference, two local net references
// two named port references, "p" does not exist
"endmodule\n");
const auto status = src.Parse();
ASSERT_TRUE(status.ok()) << status.message();
SymbolTable symbol_table(nullptr);
const SymbolTableNode &root_symbol(symbol_table.Root());
const auto build_diagnostics = BuildSymbolTable(src, &symbol_table);
EXPECT_EMPTY_STATUSES(build_diagnostics);
MUST_ASSIGN_LOOKUP_SYMBOL(m_node, root_symbol, "m");
MUST_ASSIGN_LOOKUP_SYMBOL(clk_node, m_node, "clk");
MUST_ASSIGN_LOOKUP_SYMBOL(q_node, m_node, "q");
MUST_ASSIGN_LOOKUP_SYMBOL(rr_node, root_symbol, "rr");
const auto ref_map(rr_node_info.LocalReferencesMapViewForTesting());
ASSIGN_MUST_FIND_EXACTLY_ONE_REF(m_inst_ref, ref_map, "m_inst");
ASSERT_NE(m_inst_ref, nullptr);
const ReferenceComponentNode &m_inst_ref_root(*m_inst_ref->components);
ASSERT_EQ(m_inst_ref_root.Children().size(), 2);
const ReferenceComponentMap port_refs(
ReferenceComponentNodeMapView(m_inst_ref_root));
ASSIGN_MUST_FIND(clk_ref, port_refs, "clk");
const ReferenceComponent &clk_ref_comp(clk_ref->Value());
EXPECT_EQ(clk_ref_comp.identifier, "clk");
EXPECT_EQ(clk_ref_comp.ref_type, ReferenceType::kMemberOfTypeOfParent);
EXPECT_EQ(clk_ref_comp.required_metatype,
SymbolMetaType::kDataNetVariableInstance);
EXPECT_EQ(clk_ref_comp.resolved_symbol, nullptr); // not yet resolved
ASSIGN_MUST_FIND(p_ref, port_refs, "p");
const ReferenceComponent &p_ref_comp(p_ref->Value());
EXPECT_EQ(p_ref_comp.identifier, "p");
EXPECT_EQ(p_ref_comp.ref_type, ReferenceType::kMemberOfTypeOfParent);
EXPECT_EQ(p_ref_comp.required_metatype,
SymbolMetaType::kDataNetVariableInstance);
EXPECT_EQ(p_ref_comp.resolved_symbol, nullptr); // not yet resolved
// Get the local symbol definitions for wire "c".
MUST_ASSIGN_LOOKUP_SYMBOL(c_node, rr_node, "c");
{
std::vector<absl::Status> resolve_diagnostics;
symbol_table.Resolve(&resolve_diagnostics);
ASSIGN_MUST_HAVE_UNIQUE(err, resolve_diagnostics);
EXPECT_EQ(err.code(), absl::StatusCode::kNotFound);
EXPECT_THAT(
err.message(),
HasSubstr("No member symbol \"p\" in parent scope (module) m."));
// Expect to resolved named port reference to "clk", but not "p".
EXPECT_EQ(clk_ref_comp.resolved_symbol, &clk_node);
EXPECT_EQ(p_ref_comp.resolved_symbol, nullptr); // failed to resolve
}
}
TEST_F(BuildSymbolTableTest, ModuleInstanceNamedParameterNonexistentError) {
TestVerilogSourceFile src("foobar.sv",
"module m #(\n"
" int N = 0,\n"
" int P = 1\n"
");\n"
"endmodule\n"
"module rr;\n"
" m #(.N(2), .Q(3)) m_inst();"
// one type reference, one instance self-reference
// two named param rereference (one error)
"endmodule\n");
const auto status = src.Parse();
ASSERT_TRUE(status.ok()) << status.message();
SymbolTable symbol_table(nullptr);
const SymbolTableNode &root_symbol(symbol_table.Root());
const auto build_diagnostics = BuildSymbolTable(src, &symbol_table);
EXPECT_EMPTY_STATUSES(build_diagnostics);
MUST_ASSIGN_LOOKUP_SYMBOL(m_node, root_symbol, "m");
EXPECT_EQ(m_node_info.metatype, SymbolMetaType::kModule);
EXPECT_EQ(m_node_info.file_origin, &src);
EXPECT_EQ(m_node_info.declared_type.syntax_origin,
nullptr); // there is no module meta-type
MUST_ASSIGN_LOOKUP_SYMBOL(n_param, m_node, "N");
EXPECT_EQ(n_param_info.metatype, SymbolMetaType::kParameter);
EXPECT_EQ(n_param_info.declared_type.user_defined_type,
nullptr); // types are primitive
MUST_ASSIGN_LOOKUP_SYMBOL(p_param, m_node, "P");
EXPECT_EQ(p_param_info.metatype, SymbolMetaType::kParameter);
EXPECT_EQ(p_param_info.declared_type.user_defined_type,
nullptr); // types are primitive
MUST_ASSIGN_LOOKUP_SYMBOL(rr_node, root_symbol, "rr");
MUST_ASSIGN_LOOKUP_SYMBOL(m_inst_node, rr_node, "m_inst");
const auto ref_map(rr_node_info.LocalReferencesMapViewForTesting());
ASSIGN_MUST_FIND_EXACTLY_ONE_REF(m_type_ref, ref_map, "m");
const ReferenceComponentNode &m_type_ref_root(*m_type_ref->components);
ASSERT_EQ(m_type_ref_root.Children().size(), 2);
const ReferenceComponentMap param_refs(
ReferenceComponentNodeMapView(m_type_ref_root));
ASSIGN_MUST_FIND(n_ref, param_refs, "N");
const ReferenceComponent &n_ref_comp(n_ref->Value());
EXPECT_EQ(n_ref_comp.identifier, "N");
EXPECT_EQ(n_ref_comp.ref_type, ReferenceType::kDirectMember);
EXPECT_EQ(n_ref_comp.required_metatype, SymbolMetaType::kParameter);
EXPECT_EQ(n_ref_comp.resolved_symbol, nullptr); // not yet resolved
ASSIGN_MUST_FIND(q_ref, param_refs, "Q");
const ReferenceComponent &q_ref_comp(q_ref->Value());
EXPECT_EQ(q_ref_comp.identifier, "Q");
EXPECT_EQ(q_ref_comp.ref_type, ReferenceType::kDirectMember);
EXPECT_EQ(q_ref_comp.required_metatype, SymbolMetaType::kParameter);
EXPECT_EQ(q_ref_comp.resolved_symbol, nullptr); // not yet resolved
{
std::vector<absl::Status> resolve_diagnostics;
symbol_table.Resolve(&resolve_diagnostics);
ASSIGN_MUST_HAVE_UNIQUE(err, resolve_diagnostics);
EXPECT_EQ(err.code(), absl::StatusCode::kNotFound);
// Expect only ".N" to resolve to formal parameters of "m".
EXPECT_EQ(n_ref_comp.resolved_symbol, &n_param);
EXPECT_EQ(q_ref_comp.resolved_symbol, nullptr);
}
}
TEST_F(BuildSymbolTableTest, OneGlobalIntParameter) {
TestVerilogSourceFile src("foobar.sv", "localparam int mint = 1;\n");
const auto status = src.Parse();
ASSERT_TRUE(status.ok()) << status.message();
SymbolTable symbol_table(nullptr);
const SymbolTableNode &root_symbol(symbol_table.Root());
const auto build_diagnostics = BuildSymbolTable(src, &symbol_table);
EXPECT_EMPTY_STATUSES(build_diagnostics);
MUST_ASSIGN_LOOKUP_SYMBOL(mint_param, root_symbol, "mint");
EXPECT_EQ(mint_param_info.metatype, SymbolMetaType::kParameter);
EXPECT_EQ(mint_param_info.file_origin, &src);
ASSERT_NE(mint_param_info.declared_type.syntax_origin, nullptr);
EXPECT_EQ(
verible::StringSpanOfSymbol(*mint_param_info.declared_type.syntax_origin),
"int");
{
std::vector<absl::Status> resolve_diagnostics;
symbol_table.Resolve(&resolve_diagnostics); // nothing to resolve
EXPECT_EMPTY_STATUSES(resolve_diagnostics);
}
}
TEST_F(BuildSymbolTableTest, OneGlobalUndefinedTypeParameter) {
TestVerilogSourceFile src("foobar.sv", "localparam foo_t gun = 1;\n");
const auto status = src.Parse();
ASSERT_TRUE(status.ok()) << status.message();
SymbolTable symbol_table(nullptr);
const SymbolTableNode &root_symbol(symbol_table.Root());
const auto build_diagnostics = BuildSymbolTable(src, &symbol_table);
EXPECT_EMPTY_STATUSES(build_diagnostics);
MUST_ASSIGN_LOOKUP_SYMBOL(gun_param, root_symbol, "gun");
EXPECT_EQ(gun_param_info.metatype, SymbolMetaType::kParameter);
EXPECT_EQ(gun_param_info.file_origin, &src);
ASSERT_NE(gun_param_info.declared_type.syntax_origin, nullptr);
EXPECT_EQ(
verible::StringSpanOfSymbol(*gun_param_info.declared_type.syntax_origin),
"foo_t");
{
std::vector<absl::Status> resolve_diagnostics;
symbol_table.Resolve(&resolve_diagnostics); // nothing to resolve
ASSIGN_MUST_HAVE_UNIQUE(err_status, resolve_diagnostics);
EXPECT_EQ(err_status.code(), absl::StatusCode::kNotFound);
EXPECT_THAT(err_status.message(),
HasSubstr("Unable to resolve symbol \"foo_t\""));
EXPECT_EQ(
gun_param_info.declared_type.user_defined_type->Value().resolved_symbol,
nullptr); // not resolved
}
}
TEST_F(BuildSymbolTableTest, ReferenceOneParameterExpression) {
TestVerilogSourceFile src("foobar.sv",
"localparam int mint = 1;\n"
"localparam int tea = mint;\n");
const auto status = src.Parse();
ASSERT_TRUE(status.ok()) << status.message();
SymbolTable symbol_table(nullptr);
const SymbolTableNode &root_symbol(symbol_table.Root());
const auto build_diagnostics = BuildSymbolTable(src, &symbol_table);
EXPECT_EMPTY_STATUSES(build_diagnostics);
MUST_ASSIGN_LOOKUP_SYMBOL(tea, root_symbol, "tea");
EXPECT_EQ(tea_info.metatype, SymbolMetaType::kParameter);
MUST_ASSIGN_LOOKUP_SYMBOL(mint, root_symbol, "mint");
EXPECT_EQ(mint_info.metatype, SymbolMetaType::kParameter);
EXPECT_EQ(mint_info.file_origin, &src);
ASSERT_NE(mint_info.declared_type.syntax_origin, nullptr);
EXPECT_EQ(verible::StringSpanOfSymbol(*mint_info.declared_type.syntax_origin),
"int");
// There should be one reference: "mint" (line 2)
EXPECT_EQ(root_symbol.Value().local_references_to_bind.size(), 1);
const auto ref_map(root_symbol.Value().LocalReferencesMapViewForTesting());
ASSIGN_MUST_FIND_EXACTLY_ONE_REF(ref, ref_map, "mint");
const ReferenceComponent &ref_comp(ref->components->Value());
EXPECT_TRUE(is_leaf(*ref->components));
EXPECT_EQ(ref_comp.identifier, "mint");
EXPECT_EQ(ref_comp.ref_type, ReferenceType::kUnqualified);
EXPECT_EQ(ref_comp.required_metatype, SymbolMetaType::kUnspecified);
EXPECT_EQ(ref_comp.resolved_symbol,
nullptr); // have not tried to resolve yet
{ // resolve symbols
std::vector<absl::Status> resolve_diagnostics;
symbol_table.Resolve(&resolve_diagnostics);
EXPECT_EMPTY_STATUSES(resolve_diagnostics);
EXPECT_EQ(ref_comp.resolved_symbol, &mint); // resolved
}
}
TEST_F(BuildSymbolTableTest, OneUnresolvedReferenceInExpression) {
TestVerilogSourceFile src("foobar.sv", "localparam int mint = spice;\n");
const auto status = src.Parse();
ASSERT_TRUE(status.ok()) << status.message();
SymbolTable symbol_table(nullptr);
const SymbolTableNode &root_symbol(symbol_table.Root());
const auto build_diagnostics = BuildSymbolTable(src, &symbol_table);
EXPECT_EMPTY_STATUSES(build_diagnostics);
MUST_ASSIGN_LOOKUP_SYMBOL(mint, root_symbol, "mint");
EXPECT_EQ(mint_info.metatype, SymbolMetaType::kParameter);
EXPECT_EQ(mint_info.file_origin, &src);
ASSERT_NE(mint_info.declared_type.syntax_origin, nullptr);
EXPECT_EQ(verible::StringSpanOfSymbol(*mint_info.declared_type.syntax_origin),
"int");
// There should be one reference: "spice" (line 2)
EXPECT_EQ(root_symbol.Value().local_references_to_bind.size(), 1);
const auto ref_map(root_symbol.Value().LocalReferencesMapViewForTesting());
ASSIGN_MUST_FIND_EXACTLY_ONE_REF(ref, ref_map, "spice");
const ReferenceComponent &ref_comp(ref->components->Value());
EXPECT_TRUE(is_leaf(*ref->components));
EXPECT_EQ(ref_comp.identifier, "spice");
EXPECT_EQ(ref_comp.ref_type, ReferenceType::kUnqualified);
EXPECT_EQ(ref_comp.required_metatype, SymbolMetaType::kUnspecified);
EXPECT_EQ(ref_comp.resolved_symbol,
nullptr); // have not tried to resolve yet
{ // resolve symbols
std::vector<absl::Status> resolve_diagnostics;
symbol_table.Resolve(&resolve_diagnostics);
ASSIGN_MUST_HAVE_UNIQUE(err_status, resolve_diagnostics);
EXPECT_EQ(err_status.code(), absl::StatusCode::kNotFound);
EXPECT_THAT(err_status.message(),
HasSubstr("Unable to resolve symbol \"spice\""));
EXPECT_EQ(ref_comp.resolved_symbol, nullptr); // still resolved
}
}
TEST_F(BuildSymbolTableTest, PackageDeclarationSingle) {
TestVerilogSourceFile src("foobar.sv", "package my_pkg;\nendpackage\n");
const auto status = src.Parse();
ASSERT_TRUE(status.ok()) << status.message();
SymbolTable symbol_table(nullptr);
const SymbolTableNode &root_symbol(symbol_table.Root());
const auto build_diagnostics = BuildSymbolTable(src, &symbol_table);
EXPECT_EMPTY_STATUSES(build_diagnostics);
MUST_ASSIGN_LOOKUP_SYMBOL(my_pkg, root_symbol, "my_pkg");
EXPECT_EQ(my_pkg_info.metatype, SymbolMetaType::kPackage);
EXPECT_EQ(my_pkg_info.file_origin, &src);
EXPECT_EQ(my_pkg_info.declared_type.syntax_origin,
nullptr); // there is no module meta-type
{
std::vector<absl::Status> resolve_diagnostics;
symbol_table.Resolve(&resolve_diagnostics); // nothing to resolve
EXPECT_EMPTY_STATUSES(resolve_diagnostics);
}
}
TEST_F(BuildSymbolTableTest, ReferenceOneParameterFromPackageToRoot) {
TestVerilogSourceFile src("foobar.sv",
"localparam int mint = 1;\n"
"package p;\n"
"localparam int tea = mint;\n" // reference
"endpackage\n");
const auto status = src.Parse();
ASSERT_TRUE(status.ok()) << status.message();
SymbolTable symbol_table(nullptr);
const SymbolTableNode &root_symbol(symbol_table.Root());
const auto build_diagnostics = BuildSymbolTable(src, &symbol_table);
EXPECT_EMPTY_STATUSES(build_diagnostics);
MUST_ASSIGN_LOOKUP_SYMBOL(p_pkg, root_symbol, "p");
EXPECT_EQ(p_pkg_info.metatype, SymbolMetaType::kPackage);
ASSERT_EQ(p_pkg_info.local_references_to_bind.size(), 1);
const auto ref_map(p_pkg_info.LocalReferencesMapViewForTesting());
ASSIGN_MUST_FIND_EXACTLY_ONE_REF(ref, ref_map, "mint");
const ReferenceComponent &mint_ref(ref->components->Value());
EXPECT_EQ(mint_ref.identifier, "mint");
EXPECT_EQ(mint_ref.resolved_symbol, nullptr); // not yet resolved
MUST_ASSIGN_LOOKUP_SYMBOL(tea, p_pkg, "tea"); // p::tea
EXPECT_EQ(tea_info.metatype, SymbolMetaType::kParameter);
MUST_ASSIGN_LOOKUP_SYMBOL(mint, root_symbol, "mint");
EXPECT_EQ(mint_info.metatype, SymbolMetaType::kParameter);
EXPECT_EQ(mint_info.file_origin, &src);
ASSERT_NE(mint_info.declared_type.syntax_origin, nullptr);
EXPECT_EQ(verible::StringSpanOfSymbol(*mint_info.declared_type.syntax_origin),
"int");
{ // resolve symbols
std::vector<absl::Status> resolve_diagnostics;
symbol_table.Resolve(&resolve_diagnostics);
EXPECT_EMPTY_STATUSES(resolve_diagnostics);
EXPECT_EQ(mint_ref.resolved_symbol, &mint); // resolved "mint"
}
}
TEST_F(BuildSymbolTableTest, ReferenceOneParameterFromRootToPackage) {
TestVerilogSourceFile src(
"foobar.sv",
"package p;\n"
"localparam int mint = 1;\n"
"endpackage\n"
"localparam int tea = p::mint;\n" // qualified reference
);
const auto status = src.Parse();
ASSERT_TRUE(status.ok()) << status.message();
SymbolTable symbol_table(nullptr);
const SymbolTableNode &root_symbol(symbol_table.Root());
const auto build_diagnostics = BuildSymbolTable(src, &symbol_table);
EXPECT_EMPTY_STATUSES(build_diagnostics);
MUST_ASSIGN_LOOKUP_SYMBOL(p_pkg, root_symbol, "p");
EXPECT_EQ(p_pkg_info.metatype, SymbolMetaType::kPackage);
ASSERT_EQ(root_symbol.Value().local_references_to_bind.size(), 1);
// p_mint_ref is the reference chain for "p::mint".
const auto ref_map(root_symbol.Value().LocalReferencesMapViewForTesting());
ASSIGN_MUST_FIND_EXACTLY_ONE_REF(p_mint_ref, ref_map, "p");
const ReferenceComponent &p_ref(p_mint_ref->components->Value());
EXPECT_EQ(p_ref.identifier, "p");
EXPECT_EQ(p_ref.resolved_symbol, nullptr); // not yet resolved
const ReferenceComponent &mint_ref(p_mint_ref->LastLeaf()->Value());
EXPECT_EQ(mint_ref.identifier, "mint");
EXPECT_EQ(mint_ref.resolved_symbol, nullptr); // not yet resolved
MUST_ASSIGN_LOOKUP_SYMBOL(tea, root_symbol, "tea");
EXPECT_EQ(tea_info.metatype, SymbolMetaType::kParameter);
MUST_ASSIGN_LOOKUP_SYMBOL(mint, p_pkg, "mint"); // p::mint
EXPECT_EQ(mint_info.metatype, SymbolMetaType::kParameter);
EXPECT_EQ(mint_info.file_origin, &src);
ASSERT_NE(mint_info.declared_type.syntax_origin, nullptr);
EXPECT_EQ(verible::StringSpanOfSymbol(*mint_info.declared_type.syntax_origin),
"int");
{ // resolve symbols
std::vector<absl::Status> resolve_diagnostics;
symbol_table.Resolve(&resolve_diagnostics);
EXPECT_EMPTY_STATUSES(resolve_diagnostics);
EXPECT_EQ(p_ref.resolved_symbol, &p_pkg); // resolved "p"
EXPECT_EQ(mint_ref.resolved_symbol, &mint); // resolved "p::mint"
}
}
TEST_F(BuildSymbolTableTest,
ReferenceOneParameterFromRootToPackageNoSuchMember) {
TestVerilogSourceFile src("foobar.sv",
"package p;\n"
"localparam int mint = 1;\n"
"endpackage\n"
"localparam int tea = p::zzz;\n" // expect fail
);
const auto status = src.Parse();
ASSERT_TRUE(status.ok()) << status.message();
SymbolTable symbol_table(nullptr);
const SymbolTableNode &root_symbol(symbol_table.Root());
const auto build_diagnostics = BuildSymbolTable(src, &symbol_table);
EXPECT_EMPTY_STATUSES(build_diagnostics);
MUST_ASSIGN_LOOKUP_SYMBOL(p_pkg, root_symbol, "p");
EXPECT_EQ(p_pkg_info.metatype, SymbolMetaType::kPackage);
ASSERT_EQ(root_symbol.Value().local_references_to_bind.size(), 1);
// p_mint_ref is the reference chain for "p::mint".
const auto ref_map(root_symbol.Value().LocalReferencesMapViewForTesting());
ASSIGN_MUST_FIND_EXACTLY_ONE_REF(p_mint_ref, ref_map, "p");
const ReferenceComponent &p_ref(p_mint_ref->components->Value());
EXPECT_EQ(p_ref.identifier, "p");
EXPECT_EQ(p_ref.resolved_symbol, nullptr); // not yet resolved
const ReferenceComponent &zzz_ref(p_mint_ref->LastLeaf()->Value());
EXPECT_EQ(zzz_ref.identifier, "zzz");
EXPECT_EQ(zzz_ref.resolved_symbol, nullptr); // not yet resolved
MUST_ASSIGN_LOOKUP_SYMBOL(tea, root_symbol, "tea");
EXPECT_EQ(tea_info.metatype, SymbolMetaType::kParameter);
MUST_ASSIGN_LOOKUP_SYMBOL(mint, p_pkg, "mint");
EXPECT_EQ(mint_info.metatype, SymbolMetaType::kParameter);
EXPECT_EQ(mint_info.file_origin, &src);
ASSERT_NE(mint_info.declared_type.syntax_origin, nullptr);
EXPECT_EQ(verible::StringSpanOfSymbol(*mint_info.declared_type.syntax_origin),
"int");
// resolving twice should not change results
for (int i = 0; i < 2; ++i) { // resolve symbols
std::vector<absl::Status> resolve_diagnostics;
symbol_table.Resolve(&resolve_diagnostics);
ASSIGN_MUST_HAVE_UNIQUE(err_status, resolve_diagnostics);
EXPECT_EQ(err_status.code(), absl::StatusCode::kNotFound);
EXPECT_EQ(p_ref.resolved_symbol, &p_pkg); // resolved "p"
EXPECT_EQ(zzz_ref.resolved_symbol, nullptr); // unresolved "p::zzz"
}
}
TEST_F(BuildSymbolTableTest, ModuleDeclarationWithParameters) {
TestVerilogSourceFile src("foobar.sv",
"module m #(\n"
" int W = 2,\n"
" bar B = W\n"
");\n"
"endmodule\n");
const auto status = src.Parse();
ASSERT_TRUE(status.ok()) << status.message();
SymbolTable symbol_table(nullptr);
const SymbolTableNode &root_symbol(symbol_table.Root());
const auto build_diagnostics = BuildSymbolTable(src, &symbol_table);
EXPECT_EMPTY_STATUSES(build_diagnostics);
MUST_ASSIGN_LOOKUP_SYMBOL(module_node, root_symbol, "m");
EXPECT_EQ(module_node_info.metatype, SymbolMetaType::kModule);
EXPECT_EQ(module_node_info.file_origin, &src);
EXPECT_EQ(module_node_info.declared_type.syntax_origin,
nullptr); // there is no module meta-type
MUST_ASSIGN_LOOKUP_SYMBOL(w_param, module_node, "W");
EXPECT_EQ(w_param_info.metatype, SymbolMetaType::kParameter);
const ReferenceComponentNode *w_type_ref =
w_param_info.declared_type.user_defined_type;
EXPECT_EQ(w_type_ref, nullptr); // int is primitive type
MUST_ASSIGN_LOOKUP_SYMBOL(b_param, module_node, "B");
EXPECT_EQ(b_param_info.metatype, SymbolMetaType::kParameter);
const ReferenceComponentNode *b_type_ref =
b_param_info.declared_type.user_defined_type;
ASSERT_NE(b_type_ref, nullptr);
const ReferenceComponent &b_type_ref_comp(b_type_ref->Value());
EXPECT_EQ(b_type_ref_comp.ref_type, ReferenceType::kUnqualified);
EXPECT_EQ(b_type_ref_comp.required_metatype, SymbolMetaType::kUnspecified);
EXPECT_EQ(b_type_ref_comp.identifier, "bar");
ASSERT_EQ(module_node_info.local_references_to_bind.size(), 2);
const auto ref_map(module_node_info.LocalReferencesMapViewForTesting());
ASSIGN_MUST_FIND_EXACTLY_ONE_REF(w_ref, ref_map, "W");
const ReferenceComponent &w_ref_comp(w_ref->components->Value());
EXPECT_EQ(w_ref_comp.ref_type, ReferenceType::kUnqualified);
EXPECT_EQ(w_ref_comp.identifier, "W");
EXPECT_EQ(w_ref_comp.resolved_symbol, nullptr); // not yet resolved
ASSIGN_MUST_FIND_EXACTLY_ONE_REF(bar_ref, ref_map, "bar");
const ReferenceComponent &bar_ref_comp(bar_ref->components->Value());
EXPECT_EQ(bar_ref_comp.ref_type, ReferenceType::kUnqualified);
EXPECT_EQ(bar_ref_comp.identifier, "bar");
EXPECT_EQ(bar_ref_comp.resolved_symbol, nullptr); // not yet resolved
{
std::vector<absl::Status> resolve_diagnostics;
symbol_table.Resolve(&resolve_diagnostics);
ASSIGN_MUST_HAVE_UNIQUE(error, resolve_diagnostics);
// type reference 'bar' is unresolved
EXPECT_EQ(error.code(), absl::StatusCode::kNotFound);
EXPECT_THAT(error.message(), HasSubstr("Unable to resolve symbol \"bar\""));
EXPECT_EQ(w_ref_comp.resolved_symbol, &w_param); // resolved successfully
EXPECT_EQ(bar_ref_comp.resolved_symbol, nullptr); // failed to resolve
}
}
TEST_F(BuildSymbolTableTest, ModuleDeclarationLocalsDependOnParameter) {
TestVerilogSourceFile src("foobar.sv",
"module m #(\n"
" parameter int N = 2\n"
") (\n"
" input logic [N-1:0] ins,\n" // ref
" output reg [0:N-1] outs\n" // ref
");\n"
" wire [N][N] arr[N][N];\n" // 4 refs
"endmodule\n");
const auto status = src.Parse();
ASSERT_TRUE(status.ok()) << status.message();
SymbolTable symbol_table(nullptr);
const SymbolTableNode &root_symbol(symbol_table.Root());
const auto build_diagnostics = BuildSymbolTable(src, &symbol_table);
EXPECT_EMPTY_STATUSES(build_diagnostics);
MUST_ASSIGN_LOOKUP_SYMBOL(module_m, root_symbol, "m");
EXPECT_EQ(module_m_info.metatype, SymbolMetaType::kModule);
EXPECT_EQ(module_m_info.file_origin, &src);
EXPECT_EQ(module_m_info.declared_type.syntax_origin,
nullptr); // there is no module meta-type
MUST_ASSIGN_LOOKUP_SYMBOL(n_param, module_m, "N");
EXPECT_EQ(n_param_info.metatype, SymbolMetaType::kParameter);
const ReferenceComponentNode *n_type_ref =
n_param_info.declared_type.user_defined_type;
EXPECT_EQ(n_type_ref, nullptr); // int is primitive type
EXPECT_EQ(module_m_info.local_references_to_bind.size(), 6);
const auto ref_map(module_m_info.LocalReferencesMapViewForTesting());
ASSIGN_MUST_FIND(n_refs, ref_map, "N");
ASSERT_EQ(n_refs.size(), 6); // all references to "N" parameter
for (const auto &n_ref : n_refs) {
const ReferenceComponent &n_ref_comp(n_ref->components->Value());
EXPECT_EQ(n_ref_comp.ref_type, ReferenceType::kUnqualified);
EXPECT_EQ(n_ref_comp.required_metatype, SymbolMetaType::kUnspecified);
EXPECT_EQ(n_ref_comp.identifier, "N");
EXPECT_EQ(n_ref_comp.resolved_symbol, nullptr); // not yet resolved
}
{
std::vector<absl::Status> resolve_diagnostics;
symbol_table.Resolve(&resolve_diagnostics);
EXPECT_EMPTY_STATUSES(resolve_diagnostics);
// All references to "N" resolved.
for (const auto &n_ref : n_refs) {
const ReferenceComponent &n_ref_comp(n_ref->components->Value());
EXPECT_EQ(n_ref_comp.resolved_symbol, &n_param); // resolved successfully
}
}
}
TEST_F(BuildSymbolTableTest, ModuleSingleImplicitDeclaration) {
TestVerilogSourceFile src("foo.sv",
"module m;"
"assign a = 1'b0;"
"endmodule\n");
const auto status = src.Parse();
ASSERT_TRUE(status.ok()) << status.message();
SymbolTable symbol_table(nullptr);
const SymbolTableNode &root_symbol(symbol_table.Root());
const auto build_diagnostics = BuildSymbolTable(src, &symbol_table);
EXPECT_EMPTY_STATUSES(build_diagnostics);
MUST_ASSIGN_LOOKUP_SYMBOL(module_m, root_symbol, "m");
EXPECT_EQ(module_m_info.metatype, SymbolMetaType::kModule);
EXPECT_EQ(module_m_info.file_origin, &src);
EXPECT_EQ(module_m_info.declared_type.syntax_origin,
nullptr); // there is no module meta-type
MUST_ASSIGN_LOOKUP_SYMBOL(a_variable, module_m, "a");
EXPECT_EQ(a_variable_info.metatype, SymbolMetaType::kDataNetVariableInstance);
const ReferenceComponentNode *a_type_ref =
a_variable_info.declared_type.user_defined_type;
EXPECT_EQ(a_type_ref, nullptr); // implicit type is primitive type
EXPECT_TRUE(a_variable_info.declared_type.implicit);
EXPECT_EQ(module_m_info.local_references_to_bind.size(), 1);
const auto ref_map(module_m_info.LocalReferencesMapViewForTesting());
ASSIGN_MUST_FIND(a_refs, ref_map, "a");
ASSERT_EQ(a_refs.size(), 1); // all references to "a" parameter
for (const auto &a_ref : a_refs) {
const ReferenceComponent &a_ref_comp(a_ref->components->Value());
EXPECT_EQ(a_ref_comp.ref_type, ReferenceType::kUnqualified);
EXPECT_EQ(a_ref_comp.required_metatype, SymbolMetaType::kUnspecified);
EXPECT_EQ(a_ref_comp.identifier, "a");
EXPECT_EQ(a_ref_comp.resolved_symbol, &a_variable); // pre-resolved
}
{
std::vector<absl::Status> resolve_diagnostics;
// Resolve mustn't break anything
symbol_table.Resolve(&resolve_diagnostics);
EXPECT_EMPTY_STATUSES(resolve_diagnostics);
// All references to "a" resolved.
for (const auto &a_ref : a_refs) {
const ReferenceComponent &a_ref_comp(a_ref->components->Value());
EXPECT_EQ(a_ref_comp.resolved_symbol,
&a_variable); // resolved successfully
}
}
}
TEST_F(BuildSymbolTableTest, ModuleReferenceToImplicitDeclaration) {
TestVerilogSourceFile src("foo.sv",
"module m;"
"assign a = 1'b0;"
"assign a = 1'b1;"
"endmodule\n");
const auto status = src.Parse();
ASSERT_TRUE(status.ok()) << status.message();
SymbolTable symbol_table(nullptr);
const SymbolTableNode &root_symbol(symbol_table.Root());
const auto build_diagnostics = BuildSymbolTable(src, &symbol_table);
EXPECT_EMPTY_STATUSES(build_diagnostics);
MUST_ASSIGN_LOOKUP_SYMBOL(module_m, root_symbol, "m");
EXPECT_EQ(module_m_info.metatype, SymbolMetaType::kModule);
EXPECT_EQ(module_m_info.file_origin, &src);
EXPECT_EQ(module_m_info.declared_type.syntax_origin,
nullptr); // there is no module meta-type
MUST_ASSIGN_LOOKUP_SYMBOL(a_variable, module_m, "a");
EXPECT_EQ(a_variable_info.metatype, SymbolMetaType::kDataNetVariableInstance);
const ReferenceComponentNode *a_type_ref =
a_variable_info.declared_type.user_defined_type;
EXPECT_EQ(a_type_ref, nullptr); // implicit type is primitive type
EXPECT_TRUE(a_variable_info.declared_type.implicit);
EXPECT_EQ(module_m_info.local_references_to_bind.size(), 2);
const auto ref_map(module_m_info.LocalReferencesMapViewForTesting());
ASSIGN_MUST_FIND(a_refs, ref_map, "a");
ASSERT_EQ(a_refs.size(), 2); // all references to "a" parameter
{
const auto &a_ref = *a_refs.begin();
const ReferenceComponent &a_ref_comp(a_ref->components->Value());
EXPECT_EQ(a_ref_comp.ref_type, ReferenceType::kUnqualified);
EXPECT_EQ(a_ref_comp.required_metatype, SymbolMetaType::kUnspecified);
EXPECT_EQ(a_ref_comp.identifier, "a");
EXPECT_EQ(a_ref_comp.resolved_symbol, &a_variable); // pre-resolved
}
{
const auto &a_ref = *std::next(a_refs.begin());
const ReferenceComponent &a_ref_comp(a_ref->components->Value());
EXPECT_EQ(a_ref_comp.ref_type, ReferenceType::kUnqualified);
EXPECT_EQ(a_ref_comp.required_metatype, SymbolMetaType::kUnspecified);
EXPECT_EQ(a_ref_comp.identifier, "a");
EXPECT_EQ(a_ref_comp.resolved_symbol, nullptr); // pre-resolved
}
{
std::vector<absl::Status> resolve_diagnostics;
symbol_table.Resolve(&resolve_diagnostics);
EXPECT_EMPTY_STATUSES(resolve_diagnostics);
// All references to "a" resolved.
for (const auto &a_ref : a_refs) {
const ReferenceComponent &a_ref_comp(a_ref->components->Value());
EXPECT_EQ(a_ref_comp.resolved_symbol,
&a_variable); // resolved successfully
}
}
}
TEST_F(BuildSymbolTableTest, ModuleReferenceToImplicitDeclarationInSubScope) {
TestVerilogSourceFile src("foo.sv",
"module m;"
" assign a = 1'b0;"
" module n;"
" assign a = 1'b1;"
" endmodule;"
"endmodule\n");
const auto status = src.Parse();
ASSERT_TRUE(status.ok()) << status.message();
SymbolTable symbol_table(nullptr);
const SymbolTableNode &root_symbol(symbol_table.Root());
const auto build_diagnostics = BuildSymbolTable(src, &symbol_table);
EXPECT_EMPTY_STATUSES(build_diagnostics);
MUST_ASSIGN_LOOKUP_SYMBOL(module_m, root_symbol, "m");
EXPECT_EQ(module_m_info.metatype, SymbolMetaType::kModule);
EXPECT_EQ(module_m_info.file_origin, &src);
EXPECT_EQ(module_m_info.declared_type.syntax_origin,
nullptr); // there is no module meta-type
MUST_ASSIGN_LOOKUP_SYMBOL(a_variable, module_m, "a");
EXPECT_EQ(a_variable_info.metatype, SymbolMetaType::kDataNetVariableInstance);
const ReferenceComponentNode *a_type_ref =
a_variable_info.declared_type.user_defined_type;
EXPECT_EQ(a_type_ref, nullptr); // implicit type is primitive type
EXPECT_TRUE(a_variable_info.declared_type.implicit);
EXPECT_EQ(module_m_info.local_references_to_bind.size(), 1);
const auto ref_map(module_m_info.LocalReferencesMapViewForTesting());
ASSIGN_MUST_FIND(a_refs, ref_map, "a");
ASSERT_EQ(a_refs.size(), 1); // all references to "a" parameter
for (const auto &a_ref : a_refs) {
const ReferenceComponent &a_ref_comp(a_ref->components->Value());
EXPECT_EQ(a_ref_comp.ref_type, ReferenceType::kUnqualified);
EXPECT_EQ(a_ref_comp.required_metatype, SymbolMetaType::kUnspecified);
EXPECT_EQ(a_ref_comp.identifier, "a");
EXPECT_EQ(a_ref_comp.resolved_symbol, &a_variable); // pre-resolved
}
// Submodule "n"
MUST_ASSIGN_LOOKUP_SYMBOL(module_n, module_m, "n");
EXPECT_EQ(module_n_info.metatype, SymbolMetaType::kModule);
EXPECT_EQ(module_n_info.file_origin, &src);
EXPECT_EQ(module_n_info.declared_type.syntax_origin, nullptr);
EXPECT_EQ(module_n_info.local_references_to_bind.size(), 1);
const auto n_ref_map(module_n_info.LocalReferencesMapViewForTesting());
ASSIGN_MUST_FIND(n_a_refs, n_ref_map, "a");
ASSERT_EQ(n_a_refs.size(), 1); // references to "a" net in "n" module
for (const auto &n_a_ref : n_a_refs) {
const ReferenceComponent &n_a_ref_comp(n_a_ref->components->Value());
EXPECT_EQ(n_a_ref_comp.ref_type, ReferenceType::kUnqualified);
EXPECT_EQ(n_a_ref_comp.required_metatype, SymbolMetaType::kUnspecified);
EXPECT_EQ(n_a_ref_comp.identifier, "a");
EXPECT_EQ(n_a_ref_comp.resolved_symbol,
nullptr); // resolving only in same scope
}
{
std::vector<absl::Status> resolve_diagnostics;
// Resolve mustn't break anything
symbol_table.Resolve(&resolve_diagnostics);
EXPECT_EMPTY_STATUSES(resolve_diagnostics);
// All references to "a" resolved.
for (const auto &a_ref : a_refs) {
const ReferenceComponent &a_ref_comp(a_ref->components->Value());
EXPECT_EQ(a_ref_comp.resolved_symbol,
&a_variable); // resolved successfully
}
for (const auto &n_a_ref : n_a_refs) {
const ReferenceComponent &n_a_ref_comp(n_a_ref->components->Value());
EXPECT_EQ(n_a_ref_comp.resolved_symbol,
&a_variable); // resolved successfully
}
}
}
TEST_F(BuildSymbolTableTest, ModuleExplicitDeclarationInSubScope) {
TestVerilogSourceFile src("foo.sv",
"module m;"
" assign a = 1'b0;"
" module n;"
" wire a;"
" assign a = 1'b1;"
" endmodule;"
"endmodule\n");
const auto status = src.Parse();
ASSERT_TRUE(status.ok()) << status.message();
SymbolTable symbol_table(nullptr);
const SymbolTableNode &root_symbol(symbol_table.Root());
const auto build_diagnostics = BuildSymbolTable(src, &symbol_table);
EXPECT_EMPTY_STATUSES(build_diagnostics);
MUST_ASSIGN_LOOKUP_SYMBOL(module_m, root_symbol, "m");
EXPECT_EQ(module_m_info.metatype, SymbolMetaType::kModule);
EXPECT_EQ(module_m_info.file_origin, &src);
EXPECT_EQ(module_m_info.declared_type.syntax_origin,
nullptr); // there is no module meta-type
MUST_ASSIGN_LOOKUP_SYMBOL(a_variable, module_m, "a");
EXPECT_EQ(a_variable_info.metatype, SymbolMetaType::kDataNetVariableInstance);
const ReferenceComponentNode *a_type_ref =
a_variable_info.declared_type.user_defined_type;
EXPECT_EQ(a_type_ref, nullptr); // implicit type is primitive type
EXPECT_TRUE(a_variable_info.declared_type.implicit);
EXPECT_EQ(module_m_info.local_references_to_bind.size(), 1);
const auto ref_map(module_m_info.LocalReferencesMapViewForTesting());
ASSIGN_MUST_FIND(a_refs, ref_map, "a");
ASSERT_EQ(a_refs.size(), 1); // all references to "a" parameter
for (const auto &a_ref : a_refs) {
const ReferenceComponent &a_ref_comp(a_ref->components->Value());
EXPECT_EQ(a_ref_comp.ref_type, ReferenceType::kUnqualified);
EXPECT_EQ(a_ref_comp.required_metatype, SymbolMetaType::kUnspecified);
EXPECT_EQ(a_ref_comp.identifier, "a");
EXPECT_EQ(a_ref_comp.resolved_symbol, &a_variable); // pre-resolved
}
// Submodule "n"
MUST_ASSIGN_LOOKUP_SYMBOL(module_n, module_m, "n");
EXPECT_EQ(module_n_info.metatype, SymbolMetaType::kModule);
EXPECT_EQ(module_n_info.file_origin, &src);
EXPECT_EQ(module_n_info.declared_type.syntax_origin, nullptr);
MUST_ASSIGN_LOOKUP_SYMBOL(n_a_variable, module_n, "a");
EXPECT_EQ(n_a_variable_info.metatype,
SymbolMetaType::kDataNetVariableInstance);
const ReferenceComponentNode *n_a_type_ref =
n_a_variable_info.declared_type.user_defined_type;
EXPECT_EQ(n_a_type_ref, nullptr);
EXPECT_FALSE(n_a_variable_info.declared_type.implicit);
EXPECT_EQ(module_n_info.local_references_to_bind.size(), 1);
const auto n_ref_map(module_n_info.LocalReferencesMapViewForTesting());
ASSIGN_MUST_FIND(n_a_refs, n_ref_map, "a");
ASSERT_EQ(n_a_refs.size(), 1); // references to "a" net in "n" module
for (const auto &n_a_ref : n_a_refs) {
const ReferenceComponent &n_a_ref_comp(n_a_ref->components->Value());
EXPECT_EQ(n_a_ref_comp.ref_type, ReferenceType::kUnqualified);
EXPECT_EQ(n_a_ref_comp.required_metatype, SymbolMetaType::kUnspecified);
EXPECT_EQ(n_a_ref_comp.identifier, "a");
EXPECT_EQ(n_a_ref_comp.resolved_symbol,
nullptr); // resolving only in same scope
}
{
std::vector<absl::Status> resolve_diagnostics;
// Resolve mustn't break anything
symbol_table.Resolve(&resolve_diagnostics);
EXPECT_EMPTY_STATUSES(resolve_diagnostics);
// All references to "a" resolved.
for (const auto &a_ref : a_refs) {
const ReferenceComponent &a_ref_comp(a_ref->components->Value());
EXPECT_EQ(a_ref_comp.resolved_symbol,
&a_variable); // resolved successfully
}
for (const auto &n_a_ref : n_a_refs) {
const ReferenceComponent &n_a_ref_comp(n_a_ref->components->Value());
EXPECT_EQ(n_a_ref_comp.resolved_symbol,
&n_a_variable); // resolved successfully
}
}
}
TEST_F(BuildSymbolTableTest, ModuleExplicitAndImplicitDeclarations) {
TestVerilogSourceFile src("foo.sv",
"module m;"
"wire b;"
"assign a = 1'b0;"
"assign b = 1'b1;"
"endmodule\n");
const auto status = src.Parse();
ASSERT_TRUE(status.ok()) << status.message();
SymbolTable symbol_table(nullptr);
const SymbolTableNode &root_symbol(symbol_table.Root());
const auto build_diagnostics = BuildSymbolTable(src, &symbol_table);
EXPECT_EMPTY_STATUSES(build_diagnostics);
MUST_ASSIGN_LOOKUP_SYMBOL(module_m, root_symbol, "m");
EXPECT_EQ(module_m_info.metatype, SymbolMetaType::kModule);
EXPECT_EQ(module_m_info.file_origin, &src);
EXPECT_EQ(module_m_info.declared_type.syntax_origin,
nullptr); // there is no module meta-type
MUST_ASSIGN_LOOKUP_SYMBOL(a_variable, module_m, "a");
EXPECT_EQ(a_variable_info.metatype, SymbolMetaType::kDataNetVariableInstance);
const ReferenceComponentNode *a_type_ref =
a_variable_info.declared_type.user_defined_type;
EXPECT_EQ(a_type_ref, nullptr); // implicit type is primitive type
EXPECT_TRUE(a_variable_info.declared_type.implicit);
MUST_ASSIGN_LOOKUP_SYMBOL(b_variable, module_m, "b");
EXPECT_EQ(b_variable_info.metatype, SymbolMetaType::kDataNetVariableInstance);
const ReferenceComponentNode *b_type_ref =
b_variable_info.declared_type.user_defined_type;
EXPECT_EQ(b_type_ref, nullptr);
EXPECT_FALSE(b_variable_info.declared_type.implicit);
EXPECT_EQ(module_m_info.local_references_to_bind.size(), 2);
const auto ref_map(module_m_info.LocalReferencesMapViewForTesting());
ASSIGN_MUST_FIND(a_refs, ref_map, "a");
ASSERT_EQ(a_refs.size(), 1); // all references to "a" parameter
for (const auto &a_ref : a_refs) {
const ReferenceComponent &a_ref_comp(a_ref->components->Value());
EXPECT_EQ(a_ref_comp.ref_type, ReferenceType::kUnqualified);
EXPECT_EQ(a_ref_comp.required_metatype, SymbolMetaType::kUnspecified);
EXPECT_EQ(a_ref_comp.identifier, "a");
EXPECT_EQ(a_ref_comp.resolved_symbol, &a_variable); // pre-resolved
}
ASSIGN_MUST_FIND(b_refs, ref_map, "b");
ASSERT_EQ(b_refs.size(), 1);
for (const auto &b_ref : b_refs) {
const ReferenceComponent &b_ref_comp(b_ref->components->Value());
EXPECT_EQ(b_ref_comp.ref_type, ReferenceType::kUnqualified);
EXPECT_EQ(b_ref_comp.required_metatype, SymbolMetaType::kUnspecified);
EXPECT_EQ(b_ref_comp.identifier, "b");
EXPECT_EQ(b_ref_comp.resolved_symbol, nullptr);
}
{
std::vector<absl::Status> resolve_diagnostics;
// Resolve mustn't break anything
symbol_table.Resolve(&resolve_diagnostics);
EXPECT_EMPTY_STATUSES(resolve_diagnostics);
// All references to "a" resolved.
for (const auto &a_ref : a_refs) {
const ReferenceComponent &a_ref_comp(a_ref->components->Value());
EXPECT_EQ(a_ref_comp.resolved_symbol,
&a_variable); // resolved successfully
}
// All references to "b" resolved.
for (const auto &b_ref : b_refs) {
const ReferenceComponent &b_ref_comp(b_ref->components->Value());
EXPECT_EQ(b_ref_comp.resolved_symbol,
&b_variable); // resolved successfully
}
}
}
TEST_F(BuildSymbolTableTest, ModuleImplicitRedeclared) {
TestVerilogSourceFile src("foo.sv",
"module m;\n"
"assign a = 1'b0;\n"
"wire a;\n"
"endmodule\n");
const auto status = src.Parse();
ASSERT_TRUE(status.ok()) << status.message();
SymbolTable symbol_table(nullptr);
const auto build_diagnostics = BuildSymbolTable(src, &symbol_table);
EXPECT_EQ(build_diagnostics.size(), 1);
EXPECT_FALSE(build_diagnostics.front().ok());
EXPECT_EQ(build_diagnostics.front().message(),
"foo.sv:3:6: Symbol \"a\" is already defined in the $root::m scope "
"at 2:8:");
}
TEST_F(BuildSymbolTableTest, ClassDeclarationSingle) {
TestVerilogSourceFile src("foobar.sv", "class ccc;\nendclass\n");
const auto status = src.Parse();
ASSERT_TRUE(status.ok()) << status.message();
SymbolTable symbol_table(nullptr);
const SymbolTableNode &root_symbol(symbol_table.Root());
const auto build_diagnostics = BuildSymbolTable(src, &symbol_table);
EXPECT_EMPTY_STATUSES(build_diagnostics);
MUST_ASSIGN_LOOKUP_SYMBOL(ccc, root_symbol, "ccc");
EXPECT_EQ(ccc_info.metatype, SymbolMetaType::kClass);
EXPECT_EQ(ccc_info.file_origin, &src);
EXPECT_EQ(ccc_info.declared_type.syntax_origin,
nullptr); // there is no module meta-type
{
std::vector<absl::Status> resolve_diagnostics;
symbol_table.Resolve(&resolve_diagnostics); // nothing to resolve
EXPECT_EMPTY_STATUSES(resolve_diagnostics);
}
}
TEST_F(BuildSymbolTableTest, ClassDeclarationNested) {
TestVerilogSourceFile src("foobar.sv",
"package pp;\n"
" class c_outer;\n"
" class c_inner;\n"
" endclass\n"
" endclass\n"
"endpackage\n");
const auto status = src.Parse();
ASSERT_TRUE(status.ok()) << status.message();
SymbolTable symbol_table(nullptr);
const SymbolTableNode &root_symbol(symbol_table.Root());
const auto build_diagnostics = BuildSymbolTable(src, &symbol_table);
EXPECT_EMPTY_STATUSES(build_diagnostics);
MUST_ASSIGN_LOOKUP_SYMBOL(pp, root_symbol, "pp");
EXPECT_EQ(pp_info.metatype, SymbolMetaType::kPackage);
EXPECT_EQ(pp_info.file_origin, &src);
EXPECT_EQ(pp_info.declared_type.syntax_origin,
nullptr); // there is no package meta-type
{
MUST_ASSIGN_LOOKUP_SYMBOL(c_outer, pp, "c_outer");
EXPECT_EQ(c_outer_info.metatype, SymbolMetaType::kClass);
EXPECT_EQ(c_outer_info.file_origin, &src);
EXPECT_EQ(c_outer_info.declared_type.syntax_origin,
nullptr); // there is no class meta-type
{
MUST_ASSIGN_LOOKUP_SYMBOL(c_inner, c_outer, "c_inner");
EXPECT_EQ(c_inner_info.metatype, SymbolMetaType::kClass);
EXPECT_EQ(c_inner_info.file_origin, &src);
EXPECT_EQ(c_inner_info.declared_type.syntax_origin,
nullptr); // there is no class meta-type
}
}
{
std::vector<absl::Status> resolve_diagnostics;
symbol_table.Resolve(&resolve_diagnostics); // nothing to resolve
EXPECT_EMPTY_STATUSES(resolve_diagnostics);
}
}
TEST_F(BuildSymbolTableTest, ClassDeclarationWithParameter) {
TestVerilogSourceFile src("foobar.sv",
"class cc #(\n"
" int N = 2\n"
");\n"
"endclass\n");
const auto status = src.Parse();
ASSERT_TRUE(status.ok()) << status.message();
SymbolTable symbol_table(nullptr);
const SymbolTableNode &root_symbol(symbol_table.Root());
const auto build_diagnostics = BuildSymbolTable(src, &symbol_table);
EXPECT_EMPTY_STATUSES(build_diagnostics);
MUST_ASSIGN_LOOKUP_SYMBOL(class_cc, root_symbol, "cc");
EXPECT_EQ(class_cc_info.metatype, SymbolMetaType::kClass);
EXPECT_EQ(class_cc_info.file_origin, &src);
EXPECT_EQ(class_cc_info.declared_type.syntax_origin,
nullptr); // there is no class meta-type
MUST_ASSIGN_LOOKUP_SYMBOL(n_param, class_cc, "N");
EXPECT_EQ(n_param_info.metatype, SymbolMetaType::kParameter);
const ReferenceComponentNode *n_type_ref =
n_param_info.declared_type.user_defined_type;
EXPECT_EQ(n_type_ref, nullptr); // int is primitive type
EXPECT_TRUE(class_cc_info.local_references_to_bind.empty());
{
std::vector<absl::Status> resolve_diagnostics;
symbol_table.Resolve(&resolve_diagnostics);
EXPECT_EMPTY_STATUSES(resolve_diagnostics);
}
}
TEST_F(BuildSymbolTableTest, ClassDeclarationDataMember) {
TestVerilogSourceFile src("member_accessor.sv",
"class cc;\n"
" int size;\n"
" int count = 0;\n" // with initializer
"endclass\n");
const auto status = src.Parse();
ASSERT_TRUE(status.ok()) << status.message();
SymbolTable symbol_table(nullptr);
const SymbolTableNode &root_symbol(symbol_table.Root());
const auto build_diagnostics = BuildSymbolTable(src, &symbol_table);
EXPECT_EMPTY_STATUSES(build_diagnostics);
MUST_ASSIGN_LOOKUP_SYMBOL(class_cc, root_symbol, "cc");
EXPECT_EQ(class_cc_info.metatype, SymbolMetaType::kClass);
EXPECT_EQ(class_cc_info.file_origin, &src);
EXPECT_EQ(class_cc_info.declared_type.syntax_origin, nullptr);
MUST_ASSIGN_LOOKUP_SYMBOL(size_field, class_cc, "size");
EXPECT_EQ(size_field_info.metatype, SymbolMetaType::kDataNetVariableInstance);
const ReferenceComponentNode *size_type_ref =
size_field_info.declared_type.user_defined_type;
EXPECT_EQ(size_type_ref, nullptr); // int is primitive type
EXPECT_EQ(
verible::StringSpanOfSymbol(*size_field_info.declared_type.syntax_origin),
"int");
MUST_ASSIGN_LOOKUP_SYMBOL(count_field, class_cc, "count");
EXPECT_EQ(count_field_info.metatype,
SymbolMetaType::kDataNetVariableInstance);
const ReferenceComponentNode *count_type_ref =
count_field_info.declared_type.user_defined_type;
EXPECT_EQ(count_type_ref, nullptr); // int is primitive type
EXPECT_EQ(verible::StringSpanOfSymbol(
*count_field_info.declared_type.syntax_origin),
"int");
EXPECT_TRUE(class_cc_info.local_references_to_bind.empty());
{ // No references.
std::vector<absl::Status> resolve_diagnostics;
symbol_table.Resolve(&resolve_diagnostics);
EXPECT_EMPTY_STATUSES(resolve_diagnostics);
}
}
TEST_F(BuildSymbolTableTest, ClassDeclarationDataMemberMultiDeclaration) {
TestVerilogSourceFile src("member_accessor.sv",
"class cc;\n"
" real height, width;\n"
"endclass\n");
const auto status = src.Parse();
ASSERT_TRUE(status.ok()) << status.message();
SymbolTable symbol_table(nullptr);
const SymbolTableNode &root_symbol(symbol_table.Root());
const auto build_diagnostics = BuildSymbolTable(src, &symbol_table);
EXPECT_EMPTY_STATUSES(build_diagnostics);
MUST_ASSIGN_LOOKUP_SYMBOL(class_cc, root_symbol, "cc");
EXPECT_EQ(class_cc_info.metatype, SymbolMetaType::kClass);
EXPECT_EQ(class_cc_info.file_origin, &src);
EXPECT_EQ(class_cc_info.declared_type.syntax_origin, nullptr);
MUST_ASSIGN_LOOKUP_SYMBOL(height_field, class_cc, "height");
EXPECT_EQ(height_field_info.metatype,
SymbolMetaType::kDataNetVariableInstance);
const ReferenceComponentNode *height_type_ref =
height_field_info.declared_type.user_defined_type;
EXPECT_EQ(height_type_ref, nullptr); // int is primitive type
EXPECT_EQ(verible::StringSpanOfSymbol(
*height_field_info.declared_type.syntax_origin),
"real");
MUST_ASSIGN_LOOKUP_SYMBOL(width_field, class_cc, "width");
EXPECT_EQ(width_field_info.metatype,
SymbolMetaType::kDataNetVariableInstance);
const ReferenceComponentNode *width_type_ref =
width_field_info.declared_type.user_defined_type;
EXPECT_EQ(width_type_ref, nullptr); // int is primitive type
EXPECT_EQ(verible::StringSpanOfSymbol(
*width_field_info.declared_type.syntax_origin),
"real");
EXPECT_TRUE(class_cc_info.local_references_to_bind.empty());
{ // No references.
std::vector<absl::Status> resolve_diagnostics;
symbol_table.Resolve(&resolve_diagnostics);
EXPECT_EMPTY_STATUSES(resolve_diagnostics);
}
}
TEST_F(BuildSymbolTableTest, ClassDeclarationDataMemberAccessedFromMethod) {
TestVerilogSourceFile src("member_accessor.sv",
"class cc;\n"
" int size;\n"
" function int get_size();\n"
" return size;\n"
" endfunction\n"
"endclass\n");
const auto status = src.Parse();
ASSERT_TRUE(status.ok()) << status.message();
SymbolTable symbol_table(nullptr);
const SymbolTableNode &root_symbol(symbol_table.Root());
const auto build_diagnostics = BuildSymbolTable(src, &symbol_table);
EXPECT_EMPTY_STATUSES(build_diagnostics);
MUST_ASSIGN_LOOKUP_SYMBOL(class_cc, root_symbol, "cc");
EXPECT_EQ(class_cc_info.metatype, SymbolMetaType::kClass);
EXPECT_EQ(class_cc_info.file_origin, &src);
EXPECT_EQ(class_cc_info.declared_type.syntax_origin, nullptr);
MUST_ASSIGN_LOOKUP_SYMBOL(size_field, class_cc, "size");
EXPECT_EQ(size_field_info.metatype, SymbolMetaType::kDataNetVariableInstance);
const ReferenceComponentNode *size_type_ref =
size_field_info.declared_type.user_defined_type;
EXPECT_EQ(size_type_ref, nullptr); // int is primitive type
EXPECT_EQ(
verible::StringSpanOfSymbol(*size_field_info.declared_type.syntax_origin),
"int");
MUST_ASSIGN_LOOKUP_SYMBOL(get_size, class_cc, "get_size");
EXPECT_EQ(get_size_info.metatype, SymbolMetaType::kFunction);
EXPECT_EQ(get_size_info.file_origin, &src);
const auto ref_map(get_size_info.LocalReferencesMapViewForTesting());
ASSIGN_MUST_FIND_EXACTLY_ONE_REF(size_ref, ref_map, "size");
const ReferenceComponent &size_ref_comp(size_ref->components->Value());
EXPECT_EQ(size_ref_comp.ref_type, ReferenceType::kUnqualified);
EXPECT_EQ(size_ref_comp.required_metatype, SymbolMetaType::kUnspecified);
EXPECT_EQ(size_ref_comp.resolved_symbol, nullptr);
{
std::vector<absl::Status> resolve_diagnostics;
symbol_table.Resolve(&resolve_diagnostics);
EXPECT_EMPTY_STATUSES(resolve_diagnostics);
// "size" resolved to class data member
EXPECT_EQ(size_ref_comp.resolved_symbol, &size_field);
}
}
TEST_F(BuildSymbolTableTest, ClassDataMemberAccessedDirectly) {
TestVerilogSourceFile src("member_accessor.sv",
"class cc;\n"
" int size;\n"
"endclass\n"
"function int get_size();\n"
" cc cc_data;\n"
" return cc_data.size;\n"
"endfunction\n");
const auto status = src.Parse();
ASSERT_TRUE(status.ok()) << status.message();
SymbolTable symbol_table(nullptr);
const SymbolTableNode &root_symbol(symbol_table.Root());
const auto build_diagnostics = BuildSymbolTable(src, &symbol_table);
EXPECT_EMPTY_STATUSES(build_diagnostics);
MUST_ASSIGN_LOOKUP_SYMBOL(class_cc, root_symbol, "cc");
EXPECT_EQ(class_cc_info.metatype, SymbolMetaType::kClass);
EXPECT_EQ(class_cc_info.file_origin, &src);
EXPECT_EQ(class_cc_info.declared_type.syntax_origin, nullptr);
MUST_ASSIGN_LOOKUP_SYMBOL(size_field, class_cc, "size");
EXPECT_EQ(size_field_info.metatype, SymbolMetaType::kDataNetVariableInstance);
const ReferenceComponentNode *size_type_ref =
size_field_info.declared_type.user_defined_type;
EXPECT_EQ(size_type_ref, nullptr); // int is primitive type
EXPECT_EQ(
verible::StringSpanOfSymbol(*size_field_info.declared_type.syntax_origin),
"int");
MUST_ASSIGN_LOOKUP_SYMBOL(get_size, root_symbol, "get_size");
EXPECT_EQ(get_size_info.metatype, SymbolMetaType::kFunction);
EXPECT_EQ(get_size_info.file_origin, &src);
MUST_ASSIGN_LOOKUP_SYMBOL(cc_data, get_size, "cc_data");
EXPECT_EQ(cc_data_info.metatype, SymbolMetaType::kDataNetVariableInstance);
const auto ref_map(get_size_info.LocalReferencesMapViewForTesting());
ASSIGN_MUST_FIND_EXACTLY_ONE_REF(cc_data_ref, ref_map, "cc_data");
const ReferenceComponent &cc_data_ref_comp(cc_data_ref->components->Value());
EXPECT_EQ(cc_data_ref_comp.ref_type, ReferenceType::kUnqualified);
EXPECT_EQ(cc_data_ref_comp.required_metatype, SymbolMetaType::kUnspecified);
EXPECT_EQ(cc_data_ref_comp.resolved_symbol, nullptr);
ASSERT_EQ(cc_data_ref->components->Children().size(), 1);
const ReferenceComponentNode &size_ref(
cc_data_ref->components->Children().front());
const ReferenceComponent &size_ref_comp(size_ref.Value());
EXPECT_EQ(size_ref_comp.ref_type, ReferenceType::kMemberOfTypeOfParent);
EXPECT_EQ(size_ref_comp.required_metatype, SymbolMetaType::kUnspecified);
EXPECT_EQ(size_ref_comp.resolved_symbol, nullptr);
{
std::vector<absl::Status> resolve_diagnostics;
symbol_table.Resolve(&resolve_diagnostics);
EXPECT_EMPTY_STATUSES(resolve_diagnostics);
// "size" resolved to class data member
EXPECT_EQ(size_ref_comp.resolved_symbol, &size_field);
}
}
TEST_F(BuildSymbolTableTest, ClassDeclarationSingleInheritance) {
TestVerilogSourceFile src("member_accessor.sv",
"class base;\n"
"endclass\n"
"class derived extends base;\n"
"endclass\n");
const auto status = src.Parse();
ASSERT_TRUE(status.ok()) << status.message();
SymbolTable symbol_table(nullptr);
const SymbolTableNode &root_symbol(symbol_table.Root());
const auto build_diagnostics = BuildSymbolTable(src, &symbol_table);
EXPECT_EMPTY_STATUSES(build_diagnostics);
MUST_ASSIGN_LOOKUP_SYMBOL(base_class, root_symbol, "base");
EXPECT_EQ(base_class_info.metatype, SymbolMetaType::kClass);
EXPECT_EQ(base_class_info.file_origin, &src);
EXPECT_EQ(base_class_info.declared_type.syntax_origin, nullptr);
EXPECT_TRUE(base_class_info.local_references_to_bind.empty());
MUST_ASSIGN_LOOKUP_SYMBOL(derived_class, root_symbol, "derived");
EXPECT_EQ(derived_class_info.metatype, SymbolMetaType::kClass);
EXPECT_EQ(derived_class_info.file_origin, &src);
EXPECT_EQ(derived_class_info.declared_type.syntax_origin, nullptr);
EXPECT_TRUE(derived_class_info.local_references_to_bind.empty());
// "base" is referenced from the scope that contains "derived"
EXPECT_EQ(root_symbol.Value().local_references_to_bind.size(), 1);
const auto ref_map(root_symbol.Value().LocalReferencesMapViewForTesting());
ASSIGN_MUST_FIND_EXACTLY_ONE_REF(base_ref, ref_map, "base");
const ReferenceComponent &base_ref_comp(base_ref->components->Value());
EXPECT_EQ(base_ref_comp.identifier, "base");
EXPECT_EQ(base_ref_comp.ref_type, ReferenceType::kUnqualified);
EXPECT_EQ(base_ref_comp.required_metatype, SymbolMetaType::kClass);
EXPECT_EQ(base_ref_comp.resolved_symbol, nullptr);
// Make sure the "base" reference is linked from the "derived" class.
ASSERT_EQ(
derived_class_info.parent_type.user_defined_type,
root_symbol.Value().local_references_to_bind.front().LastTypeComponent());
{
std::vector<absl::Status> resolve_diagnostics;
symbol_table.Resolve(&resolve_diagnostics);
EXPECT_EMPTY_STATUSES(resolve_diagnostics);
// Resolve the "base" type reference to the "base" class.
EXPECT_EQ(derived_class_info.parent_type.user_defined_type->Value()
.resolved_symbol,
&base_class);
}
}
TEST_F(BuildSymbolTableTest, ClassDeclarationSingleInheritanceAcrossPackage) {
TestVerilogSourceFile src("member_accessor.sv",
"package pp;\n"
" class base;\n"
" endclass\n"
"endpackage\n"
"class derived extends pp::base;\n"
"endclass\n");
const auto status = src.Parse();
ASSERT_TRUE(status.ok()) << status.message();
SymbolTable symbol_table(nullptr);
const SymbolTableNode &root_symbol(symbol_table.Root());
const auto build_diagnostics = BuildSymbolTable(src, &symbol_table);
EXPECT_EMPTY_STATUSES(build_diagnostics);
MUST_ASSIGN_LOOKUP_SYMBOL(package_pp, root_symbol, "pp");
EXPECT_EQ(package_pp_info.metatype, SymbolMetaType::kPackage);
MUST_ASSIGN_LOOKUP_SYMBOL(base_class, package_pp, "base");
EXPECT_EQ(base_class_info.metatype, SymbolMetaType::kClass);
EXPECT_EQ(base_class_info.file_origin, &src);
EXPECT_EQ(base_class_info.declared_type.syntax_origin, nullptr);
EXPECT_TRUE(base_class_info.local_references_to_bind.empty());
MUST_ASSIGN_LOOKUP_SYMBOL(derived_class, root_symbol, "derived");
EXPECT_EQ(derived_class_info.metatype, SymbolMetaType::kClass);
EXPECT_EQ(derived_class_info.file_origin, &src);
EXPECT_EQ(derived_class_info.declared_type.syntax_origin, nullptr);
EXPECT_TRUE(derived_class_info.local_references_to_bind.empty());
// "pp::base" is referenced from the scope that contains "derived"
EXPECT_EQ(root_symbol.Value().local_references_to_bind.size(), 1);
const auto ref_map(root_symbol.Value().LocalReferencesMapViewForTesting());
ASSIGN_MUST_FIND_EXACTLY_ONE_REF(pp_ref, ref_map, "pp");
const ReferenceComponent &pp_ref_comp(pp_ref->components->Value());
EXPECT_EQ(pp_ref_comp.identifier, "pp");
EXPECT_EQ(pp_ref_comp.ref_type, ReferenceType::kUnqualified);
EXPECT_EQ(pp_ref_comp.required_metatype, SymbolMetaType::kUnspecified);
EXPECT_EQ(pp_ref_comp.resolved_symbol, nullptr);
ASSERT_EQ(pp_ref->components->Children().size(), 1);
const ReferenceComponentNode &base_ref(
pp_ref->components->Children().front());
const ReferenceComponent &base_ref_comp(base_ref.Value());
EXPECT_EQ(base_ref_comp.identifier, "base");
EXPECT_EQ(base_ref_comp.ref_type, ReferenceType::kDirectMember);
EXPECT_EQ(base_ref_comp.required_metatype, SymbolMetaType::kClass);
EXPECT_EQ(base_ref_comp.resolved_symbol, nullptr);
// Make sure the "pp::base" reference is linked from the "derived" class.
ASSERT_EQ(
derived_class_info.parent_type.user_defined_type,
root_symbol.Value().local_references_to_bind.front().LastTypeComponent());
{
std::vector<absl::Status> resolve_diagnostics;
symbol_table.Resolve(&resolve_diagnostics);
EXPECT_EMPTY_STATUSES(resolve_diagnostics);
// Resolve the "pp::base" type reference to the "pp::base" class.
EXPECT_EQ(pp_ref_comp.resolved_symbol, &package_pp);
EXPECT_EQ(base_ref_comp.resolved_symbol, &base_class);
EXPECT_EQ(derived_class_info.parent_type.user_defined_type->Value()
.resolved_symbol,
&base_class);
}
}
TEST_F(BuildSymbolTableTest,
ClassDeclarationSingleInheritancePackageToPackage) {
TestVerilogSourceFile src("member_accessor.sv",
"package pp;\n"
" class base;\n"
" endclass\n"
"endpackage\n"
"package qq;\n"
" class derived extends pp::base;\n"
" endclass\n"
"endpackage\n");
const auto status = src.Parse();
ASSERT_TRUE(status.ok()) << status.message();
SymbolTable symbol_table(nullptr);
const SymbolTableNode &root_symbol(symbol_table.Root());
const auto build_diagnostics = BuildSymbolTable(src, &symbol_table);
EXPECT_EMPTY_STATUSES(build_diagnostics);
MUST_ASSIGN_LOOKUP_SYMBOL(package_pp, root_symbol, "pp");
EXPECT_EQ(package_pp_info.metatype, SymbolMetaType::kPackage);
MUST_ASSIGN_LOOKUP_SYMBOL(base_class, package_pp, "base");
EXPECT_EQ(base_class_info.metatype, SymbolMetaType::kClass);
EXPECT_EQ(base_class_info.file_origin, &src);
EXPECT_EQ(base_class_info.declared_type.syntax_origin, nullptr);
EXPECT_TRUE(base_class_info.local_references_to_bind.empty());
MUST_ASSIGN_LOOKUP_SYMBOL(package_qq, root_symbol, "qq");
EXPECT_EQ(package_qq_info.metatype, SymbolMetaType::kPackage);
MUST_ASSIGN_LOOKUP_SYMBOL(derived_class, package_qq, "derived");
EXPECT_EQ(derived_class_info.metatype, SymbolMetaType::kClass);
EXPECT_EQ(derived_class_info.file_origin, &src);
EXPECT_EQ(derived_class_info.declared_type.syntax_origin, nullptr);
EXPECT_TRUE(derived_class_info.local_references_to_bind.empty());
// "pp::base" is referenced from the scope that contains "derived",
// which is package "qq".
EXPECT_EQ(package_qq_info.local_references_to_bind.size(), 1);
const auto ref_map(package_qq_info.LocalReferencesMapViewForTesting());
ASSIGN_MUST_FIND_EXACTLY_ONE_REF(pp_ref, ref_map, "pp");
const ReferenceComponent &pp_ref_comp(pp_ref->components->Value());
EXPECT_EQ(pp_ref_comp.identifier, "pp");
EXPECT_EQ(pp_ref_comp.ref_type, ReferenceType::kUnqualified);
EXPECT_EQ(pp_ref_comp.required_metatype, SymbolMetaType::kUnspecified);
EXPECT_EQ(pp_ref_comp.resolved_symbol, nullptr);
ASSERT_EQ(pp_ref->components->Children().size(), 1);
const ReferenceComponentNode &base_ref(
pp_ref->components->Children().front());
const ReferenceComponent &base_ref_comp(base_ref.Value());
EXPECT_EQ(base_ref_comp.identifier, "base");
EXPECT_EQ(base_ref_comp.ref_type, ReferenceType::kDirectMember);
EXPECT_EQ(base_ref_comp.required_metatype, SymbolMetaType::kClass);
EXPECT_EQ(base_ref_comp.resolved_symbol, nullptr);
// Make sure the "pp::base" reference is linked from the "qq::derived" class.
ASSERT_EQ(
derived_class_info.parent_type.user_defined_type,
package_qq_info.local_references_to_bind.front().LastTypeComponent());
{
std::vector<absl::Status> resolve_diagnostics;
symbol_table.Resolve(&resolve_diagnostics);
EXPECT_EMPTY_STATUSES(resolve_diagnostics);
// Resolve the "pp::base" type reference to the "pp::base" class.
EXPECT_EQ(pp_ref_comp.resolved_symbol, &package_pp);
EXPECT_EQ(base_ref_comp.resolved_symbol, &base_class);
EXPECT_EQ(derived_class_info.parent_type.user_defined_type->Value()
.resolved_symbol,
&base_class);
}
}
TEST_F(BuildSymbolTableTest, ClassDeclarationInheritanceFromNestedClass) {
TestVerilogSourceFile src("classilicious.sv",
"class pp;\n"
" class base;\n"
" endclass\n"
"endclass\n"
"class qq;\n"
" class derived extends pp::base;\n"
" endclass\n"
"endclass\n");
const auto status = src.Parse();
ASSERT_TRUE(status.ok()) << status.message();
SymbolTable symbol_table(nullptr);
const SymbolTableNode &root_symbol(symbol_table.Root());
const auto build_diagnostics = BuildSymbolTable(src, &symbol_table);
EXPECT_EMPTY_STATUSES(build_diagnostics);
MUST_ASSIGN_LOOKUP_SYMBOL(class_pp, root_symbol, "pp");
EXPECT_EQ(class_pp_info.metatype, SymbolMetaType::kClass);
MUST_ASSIGN_LOOKUP_SYMBOL(base_class, class_pp, "base");
EXPECT_EQ(base_class_info.metatype, SymbolMetaType::kClass);
EXPECT_EQ(base_class_info.file_origin, &src);
EXPECT_EQ(base_class_info.declared_type.syntax_origin, nullptr);
EXPECT_TRUE(base_class_info.local_references_to_bind.empty());
MUST_ASSIGN_LOOKUP_SYMBOL(class_qq, root_symbol, "qq");
EXPECT_EQ(class_qq_info.metatype, SymbolMetaType::kClass);
MUST_ASSIGN_LOOKUP_SYMBOL(derived_class, class_qq, "derived");
EXPECT_EQ(derived_class_info.metatype, SymbolMetaType::kClass);
EXPECT_EQ(derived_class_info.file_origin, &src);
EXPECT_EQ(derived_class_info.declared_type.syntax_origin, nullptr);
EXPECT_TRUE(derived_class_info.local_references_to_bind.empty());
// "pp::base" is referenced from the scope that contains "derived",
// which is package "qq".
EXPECT_EQ(class_qq_info.local_references_to_bind.size(), 1);
const auto ref_map(class_qq_info.LocalReferencesMapViewForTesting());
ASSIGN_MUST_FIND_EXACTLY_ONE_REF(pp_ref, ref_map, "pp");
const ReferenceComponent &pp_ref_comp(pp_ref->components->Value());
EXPECT_EQ(pp_ref_comp.identifier, "pp");
EXPECT_EQ(pp_ref_comp.ref_type, ReferenceType::kUnqualified);
EXPECT_EQ(pp_ref_comp.required_metatype, SymbolMetaType::kUnspecified);
EXPECT_EQ(pp_ref_comp.resolved_symbol, nullptr);
ASSERT_EQ(pp_ref->components->Children().size(), 1);
const ReferenceComponentNode &base_ref(
pp_ref->components->Children().front());
const ReferenceComponent &base_ref_comp(base_ref.Value());
EXPECT_EQ(base_ref_comp.identifier, "base");
EXPECT_EQ(base_ref_comp.ref_type, ReferenceType::kDirectMember);
EXPECT_EQ(base_ref_comp.required_metatype, SymbolMetaType::kClass);
EXPECT_EQ(base_ref_comp.resolved_symbol, nullptr);
// Make sure the "pp::base" reference is linked from the "qq::derived" class.
ASSERT_EQ(derived_class_info.parent_type.user_defined_type,
class_qq_info.local_references_to_bind.front().LastTypeComponent());
{
std::vector<absl::Status> resolve_diagnostics;
symbol_table.Resolve(&resolve_diagnostics);
EXPECT_EMPTY_STATUSES(resolve_diagnostics);
// Resolve the "pp::base" type reference to the "pp::base" class.
EXPECT_EQ(pp_ref_comp.resolved_symbol, &class_pp);
EXPECT_EQ(base_ref_comp.resolved_symbol, &base_class);
EXPECT_EQ(derived_class_info.parent_type.user_defined_type->Value()
.resolved_symbol,
&base_class);
}
}
TEST_F(BuildSymbolTableTest, ClassDeclarationInLineConstructorDefinition) {
TestVerilogSourceFile src("ctor.sv",
"class C;\n"
" function new();\n"
" endfunction\n"
"endclass\n");
const auto status = src.Parse();
ASSERT_TRUE(status.ok()) << status.message();
SymbolTable symbol_table(nullptr);
const SymbolTableNode &root_symbol(symbol_table.Root());
const auto build_diagnostics = BuildSymbolTable(src, &symbol_table);
EXPECT_EMPTY_STATUSES(build_diagnostics);
MUST_ASSIGN_LOOKUP_SYMBOL(class_c, root_symbol, "C");
EXPECT_EQ(class_c_info.metatype, SymbolMetaType::kClass);
EXPECT_EQ(class_c_info.file_origin, &src);
EXPECT_EQ(class_c_info.declared_type.syntax_origin, nullptr);
MUST_ASSIGN_LOOKUP_SYMBOL(ctor, class_c, "new");
EXPECT_EQ(ctor_info.metatype, SymbolMetaType::kFunction);
EXPECT_EQ(ctor_info.file_origin, &src);
EXPECT_NE(ctor_info.syntax_origin, nullptr);
EXPECT_NE(ctor_info.declared_type.syntax_origin, nullptr); // points to "new"
// constructor is already known to "return" its class type
EXPECT_EQ(ctor_info.declared_type.user_defined_type->Value().resolved_symbol,
&class_c);
{
std::vector<absl::Status> resolve_diagnostics;
symbol_table.Resolve(&resolve_diagnostics);
EXPECT_EMPTY_STATUSES(resolve_diagnostics);
}
}
TEST_F(BuildSymbolTableTest, ClassDeclarationOutOfLineConstructorDefinition) {
TestVerilogSourceFile src("ctor.sv",
"class C;\n"
" extern function new;\n"
"endclass\n"
"function C::new ();\n"
"endfunction\n");
const auto status = src.Parse();
ASSERT_TRUE(status.ok()) << status.message();
SymbolTable symbol_table(nullptr);
const SymbolTableNode &root_symbol(symbol_table.Root());
const auto build_diagnostics = BuildSymbolTable(src, &symbol_table);
EXPECT_EMPTY_STATUSES(build_diagnostics);
MUST_ASSIGN_LOOKUP_SYMBOL(class_c, root_symbol, "C");
EXPECT_EQ(class_c_info.metatype, SymbolMetaType::kClass);
EXPECT_EQ(class_c_info.file_origin, &src);
EXPECT_EQ(class_c_info.declared_type.syntax_origin, nullptr);
MUST_ASSIGN_LOOKUP_SYMBOL(ctor, class_c, "new");
EXPECT_EQ(ctor_info.metatype, SymbolMetaType::kFunction);
EXPECT_EQ(ctor_info.file_origin, &src);
EXPECT_NE(ctor_info.syntax_origin, nullptr);
EXPECT_NE(ctor_info.declared_type.syntax_origin, nullptr); // points to "new"
// constructor is already known to "return" its class type
EXPECT_EQ(ctor_info.declared_type.user_defined_type->Value().resolved_symbol,
&class_c);
// Expect a "C::new" reference from the out-of-line definition.
const auto ref_map(root_symbol.Value().LocalReferencesMapViewForTesting());
ASSIGN_MUST_FIND_EXACTLY_ONE_REF(class_c_ref, ref_map, "C");
const ReferenceComponent &c_ref_comp(class_c_ref->components->Value());
EXPECT_EQ(c_ref_comp.identifier, "C");
EXPECT_EQ(c_ref_comp.ref_type, ReferenceType::kImmediate);
EXPECT_EQ(c_ref_comp.required_metatype, SymbolMetaType::kClass);
// out-of-line class and method reference must be resolved at build-time
EXPECT_NE(c_ref_comp.resolved_symbol, nullptr);
const ReferenceComponent &ctor_ref_comp(class_c_ref->LastLeaf()->Value());
EXPECT_EQ(ctor_ref_comp.identifier, "new");
EXPECT_EQ(ctor_ref_comp.ref_type, ReferenceType::kDirectMember);
EXPECT_EQ(ctor_ref_comp.required_metatype, SymbolMetaType::kUnspecified);
EXPECT_NE(ctor_ref_comp.resolved_symbol, nullptr);
{
std::vector<absl::Status> resolve_diagnostics;
symbol_table.Resolve(&resolve_diagnostics);
EXPECT_EMPTY_STATUSES(resolve_diagnostics);
EXPECT_EQ(c_ref_comp.resolved_symbol, &class_c); // class C
EXPECT_EQ(ctor_ref_comp.resolved_symbol, &ctor); // function C::new
}
}
TEST_F(BuildSymbolTableTest,
ClassDeclarationReferenceInheritedMemberFromMethod) {
TestVerilogSourceFile src("member_from_parent.sv",
"class base;\n"
" int count;\n"
"endclass\n"
"class derived extends base;\n"
" function int get_count();\n"
" return count;\n"
" endfunction\n"
"endclass\n");
const auto status = src.Parse();
ASSERT_TRUE(status.ok()) << status.message();
SymbolTable symbol_table(nullptr);
const SymbolTableNode &root_symbol(symbol_table.Root());
const auto build_diagnostics = BuildSymbolTable(src, &symbol_table);
EXPECT_EMPTY_STATUSES(build_diagnostics);
MUST_ASSIGN_LOOKUP_SYMBOL(base_class, root_symbol, "base");
EXPECT_EQ(base_class_info.metatype, SymbolMetaType::kClass);
EXPECT_EQ(base_class_info.file_origin, &src);
EXPECT_EQ(base_class_info.declared_type.syntax_origin, nullptr);
EXPECT_TRUE(base_class_info.local_references_to_bind.empty());
MUST_ASSIGN_LOOKUP_SYMBOL(int_count, base_class, "count");
EXPECT_EQ(int_count_info.metatype, SymbolMetaType::kDataNetVariableInstance);
EXPECT_EQ(int_count_info.file_origin, &src);
MUST_ASSIGN_LOOKUP_SYMBOL(derived_class, root_symbol, "derived");
EXPECT_EQ(derived_class_info.metatype, SymbolMetaType::kClass);
EXPECT_EQ(derived_class_info.file_origin, &src);
EXPECT_EQ(derived_class_info.declared_type.syntax_origin, nullptr);
EXPECT_TRUE(derived_class_info.local_references_to_bind.empty());
MUST_ASSIGN_LOOKUP_SYMBOL(get_count, derived_class, "get_count");
EXPECT_EQ(get_count_info.metatype, SymbolMetaType::kFunction);
ASSERT_EQ(get_count_info.local_references_to_bind.size(), 1);
// "base::count" is referenced from the "derived::get_count" method
EXPECT_EQ(get_count_info.local_references_to_bind.size(), 1);
const auto ref_map(get_count_info.LocalReferencesMapViewForTesting());
ASSIGN_MUST_FIND_EXACTLY_ONE_REF(count_ref, ref_map, "count");
const ReferenceComponent &count_ref_comp(count_ref->components->Value());
EXPECT_EQ(count_ref_comp.identifier, "count");
EXPECT_EQ(count_ref_comp.ref_type, ReferenceType::kUnqualified);
EXPECT_EQ(count_ref_comp.required_metatype, SymbolMetaType::kUnspecified);
EXPECT_EQ(count_ref_comp.resolved_symbol, nullptr);
// Make sure the "base" reference is linked from the "derived" class.
ASSERT_EQ(
derived_class_info.parent_type.user_defined_type,
root_symbol.Value().local_references_to_bind.front().LastTypeComponent());
{
std::vector<absl::Status> resolve_diagnostics;
symbol_table.Resolve(&resolve_diagnostics);
EXPECT_EMPTY_STATUSES(resolve_diagnostics);
// Resolve the "base" type reference to the "base" class.
EXPECT_EQ(derived_class_info.parent_type.user_defined_type->Value()
.resolved_symbol,
&base_class);
// "count" in "get_count" resolved to "base::count"
EXPECT_EQ(count_ref_comp.resolved_symbol, &int_count);
}
}
TEST_F(BuildSymbolTableTest, ClassDeclarationReferenceGrandparentMember) {
TestVerilogSourceFile src("member_from_parent.sv",
"class base;\n"
" int count;\n"
"endclass\n"
"class derived extends base;\n"
"endclass\n"
"class more_derived extends derived;\n"
" function int get_count();\n"
" return count;\n"
" endfunction\n"
"endclass\n");
const auto status = src.Parse();
ASSERT_TRUE(status.ok()) << status.message();
SymbolTable symbol_table(nullptr);
const SymbolTableNode &root_symbol(symbol_table.Root());
const auto build_diagnostics = BuildSymbolTable(src, &symbol_table);
EXPECT_EMPTY_STATUSES(build_diagnostics);
MUST_ASSIGN_LOOKUP_SYMBOL(base_class, root_symbol, "base");
EXPECT_EQ(base_class_info.metatype, SymbolMetaType::kClass);
EXPECT_EQ(base_class_info.file_origin, &src);
EXPECT_EQ(base_class_info.declared_type.syntax_origin, nullptr);
EXPECT_TRUE(base_class_info.local_references_to_bind.empty());
MUST_ASSIGN_LOOKUP_SYMBOL(int_count, base_class, "count");
EXPECT_EQ(int_count_info.metatype, SymbolMetaType::kDataNetVariableInstance);
EXPECT_EQ(int_count_info.file_origin, &src);
MUST_ASSIGN_LOOKUP_SYMBOL(derived_class, root_symbol, "derived");
EXPECT_EQ(derived_class_info.metatype, SymbolMetaType::kClass);
EXPECT_EQ(derived_class_info.file_origin, &src);
EXPECT_EQ(derived_class_info.declared_type.syntax_origin, nullptr);
EXPECT_TRUE(derived_class_info.local_references_to_bind.empty());
MUST_ASSIGN_LOOKUP_SYMBOL(more_derived_class, root_symbol, "more_derived");
EXPECT_EQ(more_derived_class_info.metatype, SymbolMetaType::kClass);
EXPECT_EQ(more_derived_class_info.file_origin, &src);
EXPECT_EQ(more_derived_class_info.declared_type.syntax_origin, nullptr);
EXPECT_TRUE(more_derived_class_info.local_references_to_bind.empty());
MUST_ASSIGN_LOOKUP_SYMBOL(get_count, more_derived_class, "get_count");
EXPECT_EQ(get_count_info.metatype, SymbolMetaType::kFunction);
ASSERT_EQ(get_count_info.local_references_to_bind.size(), 1);
// "base::count" is referenced from the "more_derived::get_count" method
EXPECT_EQ(get_count_info.local_references_to_bind.size(), 1);
const auto ref_map(get_count_info.LocalReferencesMapViewForTesting());
ASSIGN_MUST_FIND_EXACTLY_ONE_REF(count_ref, ref_map, "count");
const ReferenceComponent &count_ref_comp(count_ref->components->Value());
EXPECT_EQ(count_ref_comp.identifier, "count");
EXPECT_EQ(count_ref_comp.ref_type, ReferenceType::kUnqualified);
EXPECT_EQ(count_ref_comp.required_metatype, SymbolMetaType::kUnspecified);
EXPECT_EQ(count_ref_comp.resolved_symbol, nullptr);
// Make sure the "base" reference is linked from the "derived" class.
// Make sure the "derived" reference is linked from the "more_derived" class.
const auto root_refs = root_symbol.Value().LocalReferencesMapViewForTesting();
ASSIGN_MUST_FIND_EXACTLY_ONE_REF(base_ref, root_refs, "base");
const ReferenceComponent &base_ref_comp(base_ref->components->Value());
EXPECT_EQ(base_ref_comp.identifier, "base");
EXPECT_EQ(base_ref_comp.ref_type, ReferenceType::kUnqualified);
EXPECT_EQ(base_ref_comp.required_metatype, SymbolMetaType::kClass);
EXPECT_EQ(base_ref_comp.resolved_symbol, nullptr);
ASSIGN_MUST_FIND_EXACTLY_ONE_REF(derived_ref, root_refs, "derived");
const ReferenceComponent &derived_ref_comp(derived_ref->components->Value());
EXPECT_EQ(derived_ref_comp.identifier, "derived");
EXPECT_EQ(derived_ref_comp.ref_type, ReferenceType::kUnqualified);
EXPECT_EQ(derived_ref_comp.required_metatype, SymbolMetaType::kClass);
EXPECT_EQ(derived_ref_comp.resolved_symbol, nullptr);
EXPECT_EQ(derived_class_info.parent_type.user_defined_type,
base_ref->LastTypeComponent());
EXPECT_EQ(more_derived_class_info.parent_type.user_defined_type,
derived_ref->LastTypeComponent());
{
std::vector<absl::Status> resolve_diagnostics;
symbol_table.Resolve(&resolve_diagnostics);
EXPECT_EMPTY_STATUSES(resolve_diagnostics);
// Resolve the "base" and "derived" type references
EXPECT_EQ(base_ref_comp.resolved_symbol, &base_class);
EXPECT_EQ(derived_class_info.parent_type.user_defined_type->Value()
.resolved_symbol,
&base_class);
EXPECT_EQ(derived_ref_comp.resolved_symbol, &derived_class);
EXPECT_EQ(more_derived_class_info.parent_type.user_defined_type->Value()
.resolved_symbol,
&derived_class);
// "count" in "more_derived::get_count" resolved to "base::count"
EXPECT_EQ(count_ref_comp.resolved_symbol, &int_count);
}
}
TEST_F(BuildSymbolTableTest,
ClassDeclarationReferenceInheritedMemberDirectAccess) {
TestVerilogSourceFile src("member_from_parent.sv",
"class base;\n"
" int count;\n"
"endclass\n"
"class derived extends base;\n"
"endclass\n"
"function int get_count(derived dd);\n"
" return dd.count;\n" // direct member access
"endfunction\n");
const auto status = src.Parse();
ASSERT_TRUE(status.ok()) << status.message();
SymbolTable symbol_table(nullptr);
const SymbolTableNode &root_symbol(symbol_table.Root());
const auto build_diagnostics = BuildSymbolTable(src, &symbol_table);
EXPECT_EMPTY_STATUSES(build_diagnostics);
MUST_ASSIGN_LOOKUP_SYMBOL(base_class, root_symbol, "base");
EXPECT_EQ(base_class_info.metatype, SymbolMetaType::kClass);
EXPECT_EQ(base_class_info.file_origin, &src);
EXPECT_EQ(base_class_info.declared_type.syntax_origin, nullptr);
EXPECT_TRUE(base_class_info.local_references_to_bind.empty());
MUST_ASSIGN_LOOKUP_SYMBOL(int_count, base_class, "count");
EXPECT_EQ(int_count_info.metatype, SymbolMetaType::kDataNetVariableInstance);
EXPECT_EQ(int_count_info.file_origin, &src);
MUST_ASSIGN_LOOKUP_SYMBOL(derived_class, root_symbol, "derived");
EXPECT_EQ(derived_class_info.metatype, SymbolMetaType::kClass);
EXPECT_EQ(derived_class_info.file_origin, &src);
EXPECT_EQ(derived_class_info.declared_type.syntax_origin, nullptr);
EXPECT_TRUE(derived_class_info.local_references_to_bind.empty());
MUST_ASSIGN_LOOKUP_SYMBOL(get_count, root_symbol, "get_count");
EXPECT_EQ(get_count_info.metatype, SymbolMetaType::kFunction);
// references "derived" as a type and "dd" as an argument
ASSERT_EQ(get_count_info.local_references_to_bind.size(), 2);
MUST_ASSIGN_LOOKUP_SYMBOL(dd_arg, get_count, "dd");
EXPECT_EQ(dd_arg_info.metatype, SymbolMetaType::kDataNetVariableInstance);
ASSERT_NE(dd_arg_info.declared_type.user_defined_type, nullptr);
EXPECT_EQ(dd_arg_info.declared_type.user_defined_type->Value().identifier,
"derived");
EXPECT_EQ(
dd_arg_info.declared_type.user_defined_type->Value().resolved_symbol,
nullptr);
// "base::count" is referenced from the "dd.count"
const auto ref_map(get_count_info.LocalReferencesMapViewForTesting());
ASSIGN_MUST_FIND_EXACTLY_ONE_REF(derived_type_ref, ref_map, "derived");
const ReferenceComponent &derived_type_ref_comp(
derived_type_ref->components->Value());
EXPECT_EQ(derived_type_ref_comp.ref_type, ReferenceType::kUnqualified);
EXPECT_EQ(derived_type_ref_comp.required_metatype,
SymbolMetaType::kUnspecified);
EXPECT_EQ(derived_type_ref_comp.identifier, "derived");
EXPECT_EQ(derived_type_ref_comp.resolved_symbol, nullptr);
// Make sure "derived dd"'s type uses this type reference.
EXPECT_EQ(dd_arg_info.declared_type.user_defined_type,
derived_type_ref->components.get());
ASSIGN_MUST_FIND_EXACTLY_ONE_REF(dd_ref, ref_map, "dd");
const ReferenceComponent &dd_ref_comp(dd_ref->components->Value());
EXPECT_EQ(dd_ref_comp.identifier, "dd");
EXPECT_EQ(dd_ref_comp.ref_type, ReferenceType::kUnqualified);
EXPECT_EQ(dd_ref_comp.required_metatype, SymbolMetaType::kUnspecified);
EXPECT_EQ(dd_ref_comp.resolved_symbol, nullptr);
ASSERT_EQ(dd_ref->components->Children().size(), 1);
const ReferenceComponentNode &dd_count_ref(
dd_ref->components->Children().front());
const ReferenceComponent &dd_count_ref_comp(dd_count_ref.Value());
EXPECT_EQ(dd_count_ref_comp.identifier, "count");
EXPECT_EQ(dd_count_ref_comp.ref_type, ReferenceType::kMemberOfTypeOfParent);
EXPECT_EQ(dd_count_ref_comp.required_metatype, SymbolMetaType::kUnspecified);
EXPECT_EQ(dd_count_ref_comp.resolved_symbol, nullptr);
// Make sure the "base" reference is linked from the "derived" class.
ASSERT_EQ(
derived_class_info.parent_type.user_defined_type,
root_symbol.Value().local_references_to_bind.front().LastTypeComponent());
{
std::vector<absl::Status> resolve_diagnostics;
symbol_table.Resolve(&resolve_diagnostics);
EXPECT_EMPTY_STATUSES(resolve_diagnostics);
// Resolve the "base" type reference to the "base" class.
EXPECT_EQ(derived_class_info.parent_type.user_defined_type->Value()
.resolved_symbol,
&base_class);
// "dd"'s type resolved to "derived"
EXPECT_EQ(
dd_arg_info.declared_type.user_defined_type->Value().resolved_symbol,
&derived_class);
// "dd" references function parameter
EXPECT_EQ(dd_ref_comp.resolved_symbol, &dd_arg);
// "count" in "dd.count" resolved to "base::count"
EXPECT_EQ(dd_count_ref_comp.resolved_symbol, &int_count);
}
}
TEST_F(BuildSymbolTableTest,
ClassDeclarationReferenceInheritedBaseClassMethod) {
TestVerilogSourceFile src("member_from_parent.sv",
"class base;\n"
" function int count();\n"
" endfunction\n"
"endclass\n"
"class derived extends base;\n"
" function int get_count();\n"
" return count();\n"
" endfunction\n"
"endclass\n");
const auto status = src.Parse();
ASSERT_TRUE(status.ok()) << status.message();
SymbolTable symbol_table(nullptr);
const SymbolTableNode &root_symbol(symbol_table.Root());
const auto build_diagnostics = BuildSymbolTable(src, &symbol_table);
EXPECT_EMPTY_STATUSES(build_diagnostics);
MUST_ASSIGN_LOOKUP_SYMBOL(base_class, root_symbol, "base");
EXPECT_EQ(base_class_info.metatype, SymbolMetaType::kClass);
EXPECT_EQ(base_class_info.file_origin, &src);
EXPECT_EQ(base_class_info.declared_type.syntax_origin, nullptr);
EXPECT_TRUE(base_class_info.local_references_to_bind.empty());
MUST_ASSIGN_LOOKUP_SYMBOL(int_count, base_class, "count");
EXPECT_EQ(int_count_info.metatype, SymbolMetaType::kFunction);
EXPECT_EQ(int_count_info.file_origin, &src);
MUST_ASSIGN_LOOKUP_SYMBOL(derived_class, root_symbol, "derived");
EXPECT_EQ(derived_class_info.metatype, SymbolMetaType::kClass);
EXPECT_EQ(derived_class_info.file_origin, &src);
EXPECT_EQ(derived_class_info.declared_type.syntax_origin, nullptr);
EXPECT_TRUE(derived_class_info.local_references_to_bind.empty());
MUST_ASSIGN_LOOKUP_SYMBOL(get_count, derived_class, "get_count");
EXPECT_EQ(get_count_info.metatype, SymbolMetaType::kFunction);
ASSERT_EQ(get_count_info.local_references_to_bind.size(), 1);
// "base::count" is referenced from the "derived::get_count" method
EXPECT_EQ(get_count_info.local_references_to_bind.size(), 1);
const auto ref_map(get_count_info.LocalReferencesMapViewForTesting());
ASSIGN_MUST_FIND_EXACTLY_ONE_REF(count_ref, ref_map, "count");
const ReferenceComponent &count_ref_comp(count_ref->components->Value());
EXPECT_EQ(count_ref_comp.identifier, "count");
EXPECT_EQ(count_ref_comp.ref_type, ReferenceType::kUnqualified);
EXPECT_EQ(count_ref_comp.required_metatype, SymbolMetaType::kCallable);
EXPECT_EQ(count_ref_comp.resolved_symbol, nullptr);
// Make sure the "base" reference is linked from the "derived" class.
ASSERT_EQ(
derived_class_info.parent_type.user_defined_type,
root_symbol.Value().local_references_to_bind.front().LastTypeComponent());
{
std::vector<absl::Status> resolve_diagnostics;
symbol_table.Resolve(&resolve_diagnostics);
EXPECT_EMPTY_STATUSES(resolve_diagnostics);
// Resolve the "base" type reference to the "base" class.
EXPECT_EQ(derived_class_info.parent_type.user_defined_type->Value()
.resolved_symbol,
&base_class);
// "count" in "get_count" resolved to "base::count"
EXPECT_EQ(count_ref_comp.resolved_symbol, &int_count);
}
}
TEST_F(BuildSymbolTableTest,
ClassDeclarationReferenceInheritedBaseMethodFromObject) {
TestVerilogSourceFile src("member_from_parent.sv",
"class base;\n"
" function int count();\n"
" endfunction\n"
"endclass\n"
"class derived extends base;\n"
"endclass\n"
"function int get_count(derived dd);\n"
" return dd.count();\n" // method call
"endfunction\n");
const auto status = src.Parse();
ASSERT_TRUE(status.ok()) << status.message();
SymbolTable symbol_table(nullptr);
const SymbolTableNode &root_symbol(symbol_table.Root());
const auto build_diagnostics = BuildSymbolTable(src, &symbol_table);
EXPECT_EMPTY_STATUSES(build_diagnostics);
MUST_ASSIGN_LOOKUP_SYMBOL(base_class, root_symbol, "base");
EXPECT_EQ(base_class_info.metatype, SymbolMetaType::kClass);
EXPECT_EQ(base_class_info.file_origin, &src);
EXPECT_EQ(base_class_info.declared_type.syntax_origin, nullptr);
EXPECT_TRUE(base_class_info.local_references_to_bind.empty());
MUST_ASSIGN_LOOKUP_SYMBOL(int_count, base_class, "count");
EXPECT_EQ(int_count_info.metatype, SymbolMetaType::kFunction);
EXPECT_EQ(int_count_info.file_origin, &src);
MUST_ASSIGN_LOOKUP_SYMBOL(derived_class, root_symbol, "derived");
EXPECT_EQ(derived_class_info.metatype, SymbolMetaType::kClass);
EXPECT_EQ(derived_class_info.file_origin, &src);
EXPECT_EQ(derived_class_info.declared_type.syntax_origin, nullptr);
EXPECT_TRUE(derived_class_info.local_references_to_bind.empty());
MUST_ASSIGN_LOOKUP_SYMBOL(get_count, root_symbol, "get_count");
EXPECT_EQ(get_count_info.metatype, SymbolMetaType::kFunction);
// references "derived" as a type and "dd" as an argument
ASSERT_EQ(get_count_info.local_references_to_bind.size(), 2);
MUST_ASSIGN_LOOKUP_SYMBOL(dd_arg, get_count, "dd");
EXPECT_EQ(dd_arg_info.metatype, SymbolMetaType::kDataNetVariableInstance);
ASSERT_NE(dd_arg_info.declared_type.user_defined_type, nullptr);
EXPECT_EQ(dd_arg_info.declared_type.user_defined_type->Value().identifier,
"derived");
EXPECT_EQ(
dd_arg_info.declared_type.user_defined_type->Value().resolved_symbol,
nullptr);
// "base::count" is referenced from the "dd.count"
const auto ref_map(get_count_info.LocalReferencesMapViewForTesting());
ASSIGN_MUST_FIND_EXACTLY_ONE_REF(derived_type_ref, ref_map, "derived");
const ReferenceComponent &derived_type_ref_comp(
derived_type_ref->components->Value());
EXPECT_EQ(derived_type_ref_comp.ref_type, ReferenceType::kUnqualified);
EXPECT_EQ(derived_type_ref_comp.required_metatype,
SymbolMetaType::kUnspecified);
EXPECT_EQ(derived_type_ref_comp.identifier, "derived");
EXPECT_EQ(derived_type_ref_comp.resolved_symbol, nullptr);
// Make sure "derived dd"'s type uses this type reference.
EXPECT_EQ(dd_arg_info.declared_type.user_defined_type,
derived_type_ref->components.get());
ASSIGN_MUST_FIND_EXACTLY_ONE_REF(dd_ref, ref_map, "dd");
const ReferenceComponent &dd_ref_comp(dd_ref->components->Value());
EXPECT_EQ(dd_ref_comp.identifier, "dd");
EXPECT_EQ(dd_ref_comp.ref_type, ReferenceType::kUnqualified);
EXPECT_EQ(dd_ref_comp.required_metatype, SymbolMetaType::kUnspecified);
EXPECT_EQ(dd_ref_comp.resolved_symbol, nullptr);
ASSERT_EQ(dd_ref->components->Children().size(), 1);
const ReferenceComponentNode &dd_count_ref(
dd_ref->components->Children().front());
const ReferenceComponent &dd_count_ref_comp(dd_count_ref.Value());
EXPECT_EQ(dd_count_ref_comp.identifier, "count");
EXPECT_EQ(dd_count_ref_comp.ref_type, ReferenceType::kMemberOfTypeOfParent);
EXPECT_EQ(dd_count_ref_comp.required_metatype, SymbolMetaType::kCallable);
EXPECT_EQ(dd_count_ref_comp.resolved_symbol, nullptr);
// Make sure the "base" reference is linked from the "derived" class.
ASSERT_EQ(
derived_class_info.parent_type.user_defined_type,
root_symbol.Value().local_references_to_bind.front().LastTypeComponent());
{
std::vector<absl::Status> resolve_diagnostics;
symbol_table.Resolve(&resolve_diagnostics);
EXPECT_EMPTY_STATUSES(resolve_diagnostics);
// Resolve the "base" type reference to the "base" class.
EXPECT_EQ(derived_class_info.parent_type.user_defined_type->Value()
.resolved_symbol,
&base_class);
// "dd"'s type resolved to "derived"
EXPECT_EQ(
dd_arg_info.declared_type.user_defined_type->Value().resolved_symbol,
&derived_class);
// "dd" references function parameter
EXPECT_EQ(dd_ref_comp.resolved_symbol, &dd_arg);
// "count()" in "dd.count()" resolved to "base::count()"
EXPECT_EQ(dd_count_ref_comp.resolved_symbol, &int_count);
}
}
TEST_F(BuildSymbolTableTest, TypeParameterizedModuleDeclaration) {
TestVerilogSourceFile src("camelot_param_alot.sv",
"module mm #(parameter type T = bit);\n"
"endmodule\n");
const auto status = src.Parse();
ASSERT_TRUE(status.ok()) << status.message();
SymbolTable symbol_table(nullptr);
const SymbolTableNode &root_symbol(symbol_table.Root());
const auto build_diagnostics = BuildSymbolTable(src, &symbol_table);
EXPECT_EMPTY_STATUSES(build_diagnostics);
MUST_ASSIGN_LOOKUP_SYMBOL(mm_module, root_symbol, "mm");
EXPECT_EQ(mm_module_info.metatype, SymbolMetaType::kModule);
EXPECT_EQ(mm_module_info.file_origin, &src);
EXPECT_EQ(mm_module_info.declared_type.syntax_origin, nullptr);
EXPECT_TRUE(mm_module_info.local_references_to_bind.empty());
MUST_ASSIGN_LOOKUP_SYMBOL(t_type_param, mm_module, "T");
EXPECT_EQ(t_type_param_info.metatype, SymbolMetaType::kParameter);
EXPECT_EQ(t_type_param_info.file_origin, &src);
EXPECT_TRUE(root_symbol.Value().local_references_to_bind.empty());
{ // No references.
std::vector<absl::Status> resolve_diagnostics;
symbol_table.Resolve(&resolve_diagnostics);
EXPECT_EMPTY_STATUSES(resolve_diagnostics);
}
}
TEST_F(BuildSymbolTableTest, TypeParameterizedClassDataDeclarations) {
TestVerilogSourceFile src("i_push_the_param_alot.sv",
"class cc #(parameter type T = bit);\n"
"endclass\n"
"cc#(cc#(int)) data;\n");
const auto status = src.Parse();
ASSERT_TRUE(status.ok()) << status.message();
SymbolTable symbol_table(nullptr);
const SymbolTableNode &root_symbol(symbol_table.Root());
const auto build_diagnostics = BuildSymbolTable(src, &symbol_table);
EXPECT_EMPTY_STATUSES(build_diagnostics);
MUST_ASSIGN_LOOKUP_SYMBOL(cc_class, root_symbol, "cc");
EXPECT_EQ(cc_class_info.metatype, SymbolMetaType::kClass);
EXPECT_EQ(cc_class_info.file_origin, &src);
EXPECT_EQ(cc_class_info.declared_type.syntax_origin, nullptr);
EXPECT_TRUE(cc_class_info.local_references_to_bind.empty());
MUST_ASSIGN_LOOKUP_SYMBOL(t_type_param, cc_class, "T");
EXPECT_EQ(t_type_param_info.metatype, SymbolMetaType::kParameter);
EXPECT_EQ(t_type_param_info.file_origin, &src);
MUST_ASSIGN_LOOKUP_SYMBOL(data, root_symbol, "data");
EXPECT_EQ(data_info.metatype, SymbolMetaType::kDataNetVariableInstance);
EXPECT_EQ(data_info.file_origin, &src);
ASSERT_EQ(root_symbol.Value().local_references_to_bind.size(), 2);
const auto ref_map(root_symbol.Value().LocalReferencesMapViewForTesting());
ASSIGN_MUST_FIND(cc_refs, ref_map, "cc");
EXPECT_EQ(cc_refs.size(), 2);
for (const auto &cc_ref : cc_refs) {
const ReferenceComponent &cc_ref_comp(cc_ref->components->Value());
EXPECT_EQ(cc_ref_comp.ref_type, ReferenceType::kUnqualified);
EXPECT_EQ(cc_ref_comp.required_metatype, SymbolMetaType::kUnspecified);
EXPECT_EQ(cc_ref_comp.identifier, "cc");
EXPECT_EQ(cc_ref_comp.resolved_symbol, nullptr);
}
// Of the two "cc" type refs, the outer one is the first one, by ordering of
// textual position among references that start with the same identifier.
const DependentReferences &data_cc_type(**cc_refs.begin());
EXPECT_EQ(data_info.declared_type.user_defined_type,
data_cc_type.LastTypeComponent());
{
std::vector<absl::Status> resolve_diagnostics;
symbol_table.Resolve(&resolve_diagnostics);
EXPECT_EMPTY_STATUSES(resolve_diagnostics);
for (const auto &cc_ref : cc_refs) {
const ReferenceComponent &cc_ref_comp(cc_ref->components->Value());
EXPECT_EQ(cc_ref_comp.resolved_symbol, &cc_class);
}
// type of "data" is resolved
EXPECT_EQ(
data_info.declared_type.user_defined_type->Value().resolved_symbol,
&cc_class);
}
}
TEST_F(BuildSymbolTableTest,
TypeParameterizedClassDataDeclarationsPackageQualifiedTwoParams) {
TestVerilogSourceFile src(
"i_eat_ham_and_jam_and_spam_alot.sv",
"package pp;\n"
" class cc #(\n"
" parameter type T1 = bit,\n"
" parameter type T2 = bit\n"
" );\n"
" endclass\n"
"endpackage\n"
"pp::cc#(pp::cc#(int, bit), pp::cc#(bit, int)) data;\n");
const auto status = src.Parse();
ASSERT_TRUE(status.ok()) << status.message();
SymbolTable symbol_table(nullptr);
const SymbolTableNode &root_symbol(symbol_table.Root());
const auto build_diagnostics = BuildSymbolTable(src, &symbol_table);
EXPECT_EMPTY_STATUSES(build_diagnostics);
MUST_ASSIGN_LOOKUP_SYMBOL(pp_package, root_symbol, "pp");
EXPECT_EQ(pp_package_info.metatype, SymbolMetaType::kPackage);
MUST_ASSIGN_LOOKUP_SYMBOL(cc_class, pp_package, "cc");
EXPECT_EQ(cc_class_info.metatype, SymbolMetaType::kClass);
EXPECT_EQ(cc_class_info.file_origin, &src);
EXPECT_EQ(cc_class_info.declared_type.syntax_origin, nullptr);
EXPECT_TRUE(cc_class_info.local_references_to_bind.empty());
MUST_ASSIGN_LOOKUP_SYMBOL(t1_type_param, cc_class, "T1");
EXPECT_EQ(t1_type_param_info.metatype, SymbolMetaType::kParameter);
EXPECT_EQ(t1_type_param_info.file_origin, &src);
MUST_ASSIGN_LOOKUP_SYMBOL(t2_type_param, cc_class, "T2");
EXPECT_EQ(t2_type_param_info.metatype, SymbolMetaType::kParameter);
EXPECT_EQ(t2_type_param_info.file_origin, &src);
MUST_ASSIGN_LOOKUP_SYMBOL(data, root_symbol, "data");
EXPECT_EQ(data_info.metatype, SymbolMetaType::kDataNetVariableInstance);
EXPECT_EQ(data_info.file_origin, &src);
ASSERT_EQ(root_symbol.Value().local_references_to_bind.size(), 3);
const auto ref_map(root_symbol.Value().LocalReferencesMapViewForTesting());
ASSIGN_MUST_FIND(pp_refs, ref_map, "pp");
EXPECT_EQ(pp_refs.size(), 3);
// all "pp::cc" references have the same structure
for (const auto &pp_ref : pp_refs) {
const ReferenceComponent &pp_ref_comp(pp_ref->components->Value());
EXPECT_EQ(pp_ref_comp.identifier, "pp");
EXPECT_EQ(pp_ref_comp.ref_type, ReferenceType::kUnqualified);
EXPECT_EQ(pp_ref_comp.required_metatype, SymbolMetaType::kUnspecified);
EXPECT_EQ(pp_ref_comp.resolved_symbol, nullptr);
ASSERT_EQ(pp_ref->components->Children().size(), 1);
const ReferenceComponentNode &cc_ref(
pp_ref->components->Children().front());
const ReferenceComponent &cc_ref_comp(cc_ref.Value());
EXPECT_EQ(cc_ref_comp.ref_type, ReferenceType::kDirectMember);
EXPECT_EQ(cc_ref_comp.required_metatype, SymbolMetaType::kUnspecified);
EXPECT_EQ(cc_ref_comp.identifier, "cc");
EXPECT_EQ(cc_ref_comp.resolved_symbol, nullptr);
}
// Of all the "pp::cc" type refs, the outer one is the first one, by ordering
// of textual position among references that start with the same identifier.
const DependentReferences &data_cc_type(**pp_refs.begin());
EXPECT_EQ(data_info.declared_type.user_defined_type,
data_cc_type.LastTypeComponent());
{
std::vector<absl::Status> resolve_diagnostics;
symbol_table.Resolve(&resolve_diagnostics);
EXPECT_EMPTY_STATUSES(resolve_diagnostics);
for (const auto &pp_ref : pp_refs) {
const ReferenceComponent &pp_ref_comp(pp_ref->components->Value());
EXPECT_EQ(pp_ref_comp.resolved_symbol, &pp_package);
const ReferenceComponentNode &cc_ref(
pp_ref->components->Children().front());
const ReferenceComponent &cc_ref_comp(cc_ref.Value());
EXPECT_EQ(cc_ref_comp.resolved_symbol, &cc_class);
}
// type of "data" is resolved
EXPECT_EQ(
data_info.declared_type.user_defined_type->Value().resolved_symbol,
&cc_class);
}
}
TEST_F(BuildSymbolTableTest, NestedTypeParameterizedClassDataDeclaration) {
TestVerilogSourceFile src(
"its_fun_down_here_in_Camelot.sv",
"class outer #(parameter type S = int);\n"
" class cc #(parameter type T = bit);\n"
" endclass\n"
"endclass\n"
"outer#(outer#(int)::cc#(int))::cc#(outer#(bit)::cc#(bit)) data;\n");
const auto status = src.Parse();
ASSERT_TRUE(status.ok()) << status.message();
SymbolTable symbol_table(nullptr);
const SymbolTableNode &root_symbol(symbol_table.Root());
const auto build_diagnostics = BuildSymbolTable(src, &symbol_table);
EXPECT_EMPTY_STATUSES(build_diagnostics);
MUST_ASSIGN_LOOKUP_SYMBOL(outer_class, root_symbol, "outer");
EXPECT_EQ(outer_class_info.metatype, SymbolMetaType::kClass);
EXPECT_EQ(outer_class_info.file_origin, &src);
EXPECT_EQ(outer_class_info.declared_type.syntax_origin, nullptr);
EXPECT_TRUE(outer_class_info.local_references_to_bind.empty());
MUST_ASSIGN_LOOKUP_SYMBOL(s_type_param, outer_class, "S");
EXPECT_EQ(s_type_param_info.metatype, SymbolMetaType::kParameter);
EXPECT_EQ(s_type_param_info.file_origin, &src);
MUST_ASSIGN_LOOKUP_SYMBOL(cc_class, outer_class, "cc");
EXPECT_EQ(cc_class_info.metatype, SymbolMetaType::kClass);
EXPECT_EQ(cc_class_info.file_origin, &src);
EXPECT_EQ(cc_class_info.declared_type.syntax_origin, nullptr);
EXPECT_TRUE(cc_class_info.local_references_to_bind.empty());
MUST_ASSIGN_LOOKUP_SYMBOL(t_type_param, cc_class, "T");
EXPECT_EQ(t_type_param_info.metatype, SymbolMetaType::kParameter);
EXPECT_EQ(t_type_param_info.file_origin, &src);
MUST_ASSIGN_LOOKUP_SYMBOL(data, root_symbol, "data");
EXPECT_EQ(data_info.metatype, SymbolMetaType::kDataNetVariableInstance);
EXPECT_EQ(data_info.file_origin, &src);
ASSERT_EQ(root_symbol.Value().local_references_to_bind.size(), 3);
const auto ref_map(root_symbol.Value().LocalReferencesMapViewForTesting());
ASSIGN_MUST_FIND(outer_refs, ref_map, "outer");
EXPECT_EQ(outer_refs.size(), 3);
// all "pp::cc" references have the same structure
for (const auto &outer_ref : outer_refs) {
const ReferenceComponent &outer_ref_comp(outer_ref->components->Value());
EXPECT_EQ(outer_ref_comp.identifier, "outer");
EXPECT_EQ(outer_ref_comp.ref_type, ReferenceType::kUnqualified);
EXPECT_EQ(outer_ref_comp.required_metatype, SymbolMetaType::kUnspecified);
EXPECT_EQ(outer_ref_comp.resolved_symbol, nullptr);
ASSERT_EQ(outer_ref->components->Children().size(), 1);
const ReferenceComponentNode &cc_ref(
outer_ref->components->Children().front());
const ReferenceComponent &cc_ref_comp(cc_ref.Value());
EXPECT_EQ(cc_ref_comp.ref_type, ReferenceType::kDirectMember);
EXPECT_EQ(cc_ref_comp.required_metatype, SymbolMetaType::kUnspecified);
EXPECT_EQ(cc_ref_comp.identifier, "cc");
EXPECT_EQ(cc_ref_comp.resolved_symbol, nullptr);
}
// Of all the "outer::cc" type refs, the outer one is the first one, by
// ordering of textual position among references that start with the same
// identifier.
const DependentReferences &data_cc_type(**outer_refs.begin());
EXPECT_EQ(data_info.declared_type.user_defined_type,
data_cc_type.LastTypeComponent());
{
std::vector<absl::Status> resolve_diagnostics;
symbol_table.Resolve(&resolve_diagnostics);
EXPECT_EMPTY_STATUSES(resolve_diagnostics);
for (const auto &outer_ref : outer_refs) {
const ReferenceComponent &outer_ref_comp(outer_ref->components->Value());
EXPECT_EQ(outer_ref_comp.resolved_symbol, &outer_class);
const ReferenceComponentNode &cc_ref(
outer_ref->components->Children().front());
const ReferenceComponent &cc_ref_comp(cc_ref.Value());
EXPECT_EQ(cc_ref_comp.resolved_symbol, &cc_class);
}
// type of "data" is resolved
EXPECT_EQ(
data_info.declared_type.user_defined_type->Value().resolved_symbol,
&cc_class);
}
}
TEST_F(BuildSymbolTableTest,
TypeParameterizedClassDataDeclarationNamedParameters) {
TestVerilogSourceFile src("its_fun_down_here_in_Camelot.sv",
"class cc #(\n"
" parameter type S = int,\n"
" parameter type T = bit\n"
");\n"
"endclass\n"
"cc#(.S(int), .T(int)) data;\n");
const auto status = src.Parse();
ASSERT_TRUE(status.ok()) << status.message();
SymbolTable symbol_table(nullptr);
const SymbolTableNode &root_symbol(symbol_table.Root());
const auto build_diagnostics = BuildSymbolTable(src, &symbol_table);
EXPECT_EMPTY_STATUSES(build_diagnostics);
MUST_ASSIGN_LOOKUP_SYMBOL(cc_class, root_symbol, "cc");
EXPECT_EQ(cc_class_info.metatype, SymbolMetaType::kClass);
EXPECT_EQ(cc_class_info.file_origin, &src);
EXPECT_EQ(cc_class_info.declared_type.syntax_origin, nullptr);
EXPECT_TRUE(cc_class_info.local_references_to_bind.empty());
MUST_ASSIGN_LOOKUP_SYMBOL(s_type_param, cc_class, "S");
EXPECT_EQ(s_type_param_info.metatype, SymbolMetaType::kParameter);
EXPECT_EQ(s_type_param_info.file_origin, &src);
MUST_ASSIGN_LOOKUP_SYMBOL(t_type_param, cc_class, "T");
EXPECT_EQ(t_type_param_info.metatype, SymbolMetaType::kParameter);
EXPECT_EQ(t_type_param_info.file_origin, &src);
MUST_ASSIGN_LOOKUP_SYMBOL(data, root_symbol, "data");
EXPECT_EQ(data_info.metatype, SymbolMetaType::kDataNetVariableInstance);
EXPECT_EQ(data_info.file_origin, &src);
ASSERT_EQ(root_symbol.Value().local_references_to_bind.size(), 1);
const auto ref_map(root_symbol.Value().LocalReferencesMapViewForTesting());
ASSIGN_MUST_FIND(cc_refs, ref_map, "cc");
ASSIGN_MUST_HAVE_UNIQUE(cc_ref, cc_refs);
const ReferenceComponent &cc_ref_comp(cc_ref->components->Value());
EXPECT_EQ(cc_ref_comp.identifier, "cc");
EXPECT_EQ(cc_ref_comp.ref_type, ReferenceType::kUnqualified);
EXPECT_EQ(cc_ref_comp.required_metatype, SymbolMetaType::kUnspecified);
EXPECT_EQ(cc_ref_comp.resolved_symbol, nullptr);
const ReferenceComponentMap param_ref_map(
ReferenceComponentNodeMapView(*cc_ref->components));
ASSIGN_MUST_FIND(s_ref, param_ref_map, "S");
const ReferenceComponent &s_ref_comp(s_ref->Value());
EXPECT_EQ(s_ref_comp.identifier, "S");
EXPECT_EQ(s_ref_comp.ref_type, ReferenceType::kDirectMember);
EXPECT_EQ(s_ref_comp.required_metatype, SymbolMetaType::kParameter);
EXPECT_EQ(s_ref_comp.resolved_symbol, nullptr);
ASSIGN_MUST_FIND(t_ref, param_ref_map, "T");
const ReferenceComponent &t_ref_comp(t_ref->Value());
EXPECT_EQ(t_ref_comp.identifier, "T");
EXPECT_EQ(t_ref_comp.ref_type, ReferenceType::kDirectMember);
EXPECT_EQ(t_ref_comp.required_metatype, SymbolMetaType::kParameter);
EXPECT_EQ(t_ref_comp.resolved_symbol, nullptr);
const DependentReferences &data_cc_type(*cc_ref);
EXPECT_EQ(data_info.declared_type.user_defined_type,
data_cc_type.components.get());
{
std::vector<absl::Status> resolve_diagnostics;
symbol_table.Resolve(&resolve_diagnostics);
EXPECT_EMPTY_STATUSES(resolve_diagnostics);
EXPECT_EQ(cc_ref_comp.resolved_symbol, &cc_class);
EXPECT_EQ(s_ref_comp.resolved_symbol, &s_type_param);
EXPECT_EQ(t_ref_comp.resolved_symbol, &t_type_param);
// type of "data" is resolved
EXPECT_EQ(
data_info.declared_type.user_defined_type->Value().resolved_symbol,
&cc_class);
}
}
TEST_F(BuildSymbolTableTest,
NestedTypeParameterizedClassDataDeclarationNamedParameters) {
TestVerilogSourceFile src(
"i_need_to_upgrade_my_RAM_alot.sv",
"class outer #(parameter type S = int);\n"
" class cc #(parameter type T = bit);\n"
" endclass\n"
"endclass\n"
"outer#(.S(outer#(.S(int))::cc#(.T(int))))\n"
" ::cc#(.T(outer#(.S(bit))::cc#(.T(bit)))) data;\n");
const auto status = src.Parse();
ASSERT_TRUE(status.ok()) << status.message();
SymbolTable symbol_table(nullptr);
const SymbolTableNode &root_symbol(symbol_table.Root());
const auto build_diagnostics = BuildSymbolTable(src, &symbol_table);
EXPECT_EMPTY_STATUSES(build_diagnostics);
MUST_ASSIGN_LOOKUP_SYMBOL(outer_class, root_symbol, "outer");
EXPECT_EQ(outer_class_info.metatype, SymbolMetaType::kClass);
EXPECT_EQ(outer_class_info.file_origin, &src);
EXPECT_EQ(outer_class_info.declared_type.syntax_origin, nullptr);
EXPECT_TRUE(outer_class_info.local_references_to_bind.empty());
MUST_ASSIGN_LOOKUP_SYMBOL(s_type_param, outer_class, "S");
EXPECT_EQ(s_type_param_info.metatype, SymbolMetaType::kParameter);
EXPECT_EQ(s_type_param_info.file_origin, &src);
MUST_ASSIGN_LOOKUP_SYMBOL(cc_class, outer_class, "cc");
EXPECT_EQ(cc_class_info.metatype, SymbolMetaType::kClass);
EXPECT_EQ(cc_class_info.file_origin, &src);
EXPECT_EQ(cc_class_info.declared_type.syntax_origin, nullptr);
EXPECT_TRUE(cc_class_info.local_references_to_bind.empty());
MUST_ASSIGN_LOOKUP_SYMBOL(t_type_param, cc_class, "T");
EXPECT_EQ(t_type_param_info.metatype, SymbolMetaType::kParameter);
EXPECT_EQ(t_type_param_info.file_origin, &src);
MUST_ASSIGN_LOOKUP_SYMBOL(data, root_symbol, "data");
EXPECT_EQ(data_info.metatype, SymbolMetaType::kDataNetVariableInstance);
EXPECT_EQ(data_info.file_origin, &src);
ASSERT_EQ(root_symbol.Value().local_references_to_bind.size(), 3);
const auto ref_map(root_symbol.Value().LocalReferencesMapViewForTesting());
ASSIGN_MUST_FIND(outer_refs, ref_map, "outer");
EXPECT_EQ(outer_refs.size(), 3);
// all "outer::cc" references have the same structure
for (const auto &outer_ref : outer_refs) {
const ReferenceComponent &outer_ref_comp(outer_ref->components->Value());
EXPECT_EQ(outer_ref_comp.identifier, "outer");
EXPECT_EQ(outer_ref_comp.ref_type, ReferenceType::kUnqualified);
EXPECT_EQ(outer_ref_comp.required_metatype, SymbolMetaType::kUnspecified);
EXPECT_EQ(outer_ref_comp.resolved_symbol, nullptr);
ASSERT_EQ(outer_ref->components->Children().size(), 2);
const ReferenceComponentNode &s_param_ref(
outer_ref->components->Children().front());
const ReferenceComponent &s_param_ref_comp(s_param_ref.Value());
EXPECT_EQ(s_param_ref_comp.ref_type, ReferenceType::kDirectMember);
EXPECT_EQ(s_param_ref_comp.required_metatype, SymbolMetaType::kParameter);
EXPECT_EQ(s_param_ref_comp.identifier, "S");
EXPECT_EQ(s_param_ref_comp.resolved_symbol, nullptr);
const ReferenceComponentNode &cc_ref(
outer_ref->components->Children().back());
const ReferenceComponent &cc_ref_comp(cc_ref.Value());
EXPECT_EQ(cc_ref_comp.ref_type, ReferenceType::kDirectMember);
EXPECT_EQ(cc_ref_comp.required_metatype, SymbolMetaType::kUnspecified);
EXPECT_EQ(cc_ref_comp.identifier, "cc");
EXPECT_EQ(cc_ref_comp.resolved_symbol, nullptr);
ASSERT_EQ(cc_ref.Children().size(), 1);
const ReferenceComponentNode &t_param_ref(cc_ref.Children().front());
const ReferenceComponent &t_param_ref_comp(t_param_ref.Value());
EXPECT_EQ(t_param_ref_comp.ref_type, ReferenceType::kDirectMember);
EXPECT_EQ(t_param_ref_comp.required_metatype, SymbolMetaType::kParameter);
EXPECT_EQ(t_param_ref_comp.identifier, "T");
EXPECT_EQ(t_param_ref_comp.resolved_symbol, nullptr);
}
// Of all the "outer::cc" type refs, the outer one is the first one, by
// ordering of textual position among references that start with the same
// identifier.
const DependentReferences &data_cc_type(**outer_refs.begin());
EXPECT_EQ(data_info.declared_type.user_defined_type,
data_cc_type.LastTypeComponent());
{
std::vector<absl::Status> resolve_diagnostics;
symbol_table.Resolve(&resolve_diagnostics);
EXPECT_EMPTY_STATUSES(resolve_diagnostics);
for (const auto &outer_ref : outer_refs) {
const ReferenceComponent &outer_ref_comp(outer_ref->components->Value());
EXPECT_EQ(outer_ref_comp.resolved_symbol, &outer_class);
const ReferenceComponentNode &s_param_ref(
outer_ref->components->Children().front());
const ReferenceComponent &s_param_ref_comp(s_param_ref.Value());
EXPECT_EQ(s_param_ref_comp.resolved_symbol, &s_type_param);
const ReferenceComponentNode &cc_ref(
outer_ref->components->Children().back());
const ReferenceComponent &cc_ref_comp(cc_ref.Value());
EXPECT_EQ(cc_ref_comp.resolved_symbol, &cc_class);
const ReferenceComponentNode &t_param_ref(cc_ref.Children().front());
const ReferenceComponent &t_param_ref_comp(t_param_ref.Value());
EXPECT_EQ(t_param_ref_comp.resolved_symbol, &t_type_param);
}
// type of "data" is resolved
EXPECT_EQ(
data_info.declared_type.user_defined_type->Value().resolved_symbol,
&cc_class);
}
}
TEST_F(BuildSymbolTableTest, FunctionDeclarationNoReturnType) {
TestVerilogSourceFile src("funkytown.sv",
"function ff;\n"
"endfunction\n");
const auto status = src.Parse();
ASSERT_TRUE(status.ok()) << status.message();
SymbolTable symbol_table(nullptr);
const SymbolTableNode &root_symbol(symbol_table.Root());
const auto build_diagnostics = BuildSymbolTable(src, &symbol_table);
EXPECT_EMPTY_STATUSES(build_diagnostics);
MUST_ASSIGN_LOOKUP_SYMBOL(function_ff, root_symbol, "ff");
EXPECT_EQ(function_ff_info.metatype, SymbolMetaType::kFunction);
EXPECT_EQ(function_ff_info.file_origin, &src);
// no return type
EXPECT_EQ(function_ff_info.declared_type.syntax_origin, nullptr);
EXPECT_TRUE(function_ff_info.local_references_to_bind.empty());
{
std::vector<absl::Status> resolve_diagnostics;
symbol_table.Resolve(&resolve_diagnostics);
EXPECT_EMPTY_STATUSES(resolve_diagnostics);
}
}
TEST_F(BuildSymbolTableTest, FunctionDeclarationWithPort) {
TestVerilogSourceFile src("funkytown.sv",
"function ff(int g);\n"
"endfunction\n");
// TODO: propagate type for ports like "int g, h"
const auto status = src.Parse();
ASSERT_TRUE(status.ok()) << status.message();
SymbolTable symbol_table(nullptr);
const SymbolTableNode &root_symbol(symbol_table.Root());
const auto build_diagnostics = BuildSymbolTable(src, &symbol_table);
EXPECT_EMPTY_STATUSES(build_diagnostics);
MUST_ASSIGN_LOOKUP_SYMBOL(function_ff, root_symbol, "ff");
EXPECT_EQ(function_ff_info.metatype, SymbolMetaType::kFunction);
EXPECT_EQ(function_ff_info.file_origin, &src);
EXPECT_EQ(function_ff_info.declared_type.syntax_origin,
nullptr); // there is no function return type
MUST_ASSIGN_LOOKUP_SYMBOL(param_g, function_ff, "g");
EXPECT_EQ(param_g_info.metatype, SymbolMetaType::kDataNetVariableInstance);
EXPECT_EQ(param_g_info.file_origin, &src);
ASSERT_NE(param_g_info.declared_type.syntax_origin, nullptr);
EXPECT_EQ(
verible::StringSpanOfSymbol(*param_g_info.declared_type.syntax_origin),
"int");
EXPECT_TRUE(function_ff_info.local_references_to_bind.empty());
EXPECT_TRUE(param_g_info.local_references_to_bind.empty());
{
std::vector<absl::Status> resolve_diagnostics;
symbol_table.Resolve(&resolve_diagnostics);
EXPECT_EMPTY_STATUSES(resolve_diagnostics);
}
}
TEST_F(BuildSymbolTableTest, FunctionDeclarationWithLocalVariable) {
TestVerilogSourceFile src("funkytown.sv",
"function ff();\n"
" logic g;\n"
"endfunction\n");
// TODO: propagate type for ports like "int g, h"
const auto status = src.Parse();
ASSERT_TRUE(status.ok()) << status.message();
SymbolTable symbol_table(nullptr);
const SymbolTableNode &root_symbol(symbol_table.Root());
const auto build_diagnostics = BuildSymbolTable(src, &symbol_table);
EXPECT_EMPTY_STATUSES(build_diagnostics);
MUST_ASSIGN_LOOKUP_SYMBOL(function_ff, root_symbol, "ff");
EXPECT_EQ(function_ff_info.metatype, SymbolMetaType::kFunction);
EXPECT_EQ(function_ff_info.file_origin, &src);
EXPECT_EQ(function_ff_info.declared_type.syntax_origin,
nullptr); // there is no function return type
MUST_ASSIGN_LOOKUP_SYMBOL(local_g, function_ff, "g");
EXPECT_EQ(local_g_info.metatype, SymbolMetaType::kDataNetVariableInstance);
EXPECT_EQ(local_g_info.file_origin, &src);
ASSERT_NE(local_g_info.declared_type.syntax_origin, nullptr);
EXPECT_EQ(
verible::StringSpanOfSymbol(*local_g_info.declared_type.syntax_origin),
"logic");
EXPECT_TRUE(function_ff_info.local_references_to_bind.empty());
EXPECT_TRUE(local_g_info.local_references_to_bind.empty());
{
std::vector<absl::Status> resolve_diagnostics;
symbol_table.Resolve(&resolve_diagnostics);
EXPECT_EMPTY_STATUSES(resolve_diagnostics);
}
}
TEST_F(BuildSymbolTableTest, FunctionDeclarationVoidReturnType) {
TestVerilogSourceFile src("funkytown.sv",
"function void ff;\n"
"endfunction\n");
const auto status = src.Parse();
ASSERT_TRUE(status.ok()) << status.message();
SymbolTable symbol_table(nullptr);
const SymbolTableNode &root_symbol(symbol_table.Root());
const auto build_diagnostics = BuildSymbolTable(src, &symbol_table);
EXPECT_EMPTY_STATUSES(build_diagnostics);
MUST_ASSIGN_LOOKUP_SYMBOL(function_ff, root_symbol, "ff");
EXPECT_EQ(function_ff_info.metatype, SymbolMetaType::kFunction);
EXPECT_EQ(function_ff_info.file_origin, &src);
ASSERT_NE(function_ff_info.declared_type.syntax_origin, nullptr);
EXPECT_EQ(verible::StringSpanOfSymbol(
*function_ff_info.declared_type.syntax_origin),
"void");
EXPECT_TRUE(function_ff_info.local_references_to_bind.empty());
{
std::vector<absl::Status> resolve_diagnostics;
symbol_table.Resolve(&resolve_diagnostics);
EXPECT_EMPTY_STATUSES(resolve_diagnostics);
}
}
TEST_F(BuildSymbolTableTest, FunctionDeclarationClassReturnType) {
TestVerilogSourceFile src("funkytown.sv",
"class cc;\n"
"endclass\n"
"function cc ff;\n" // user-defined return type
"endfunction\n");
const auto status = src.Parse();
ASSERT_TRUE(status.ok()) << status.message();
SymbolTable symbol_table(nullptr);
const SymbolTableNode &root_symbol(symbol_table.Root());
const auto build_diagnostics = BuildSymbolTable(src, &symbol_table);
EXPECT_EMPTY_STATUSES(build_diagnostics);
MUST_ASSIGN_LOOKUP_SYMBOL(class_cc, root_symbol, "cc");
MUST_ASSIGN_LOOKUP_SYMBOL(function_ff, root_symbol, "ff");
EXPECT_EQ(function_ff_info.metatype, SymbolMetaType::kFunction);
EXPECT_EQ(function_ff_info.file_origin, &src);
ASSERT_NE(function_ff_info.declared_type.syntax_origin, nullptr);
EXPECT_EQ(verible::StringSpanOfSymbol(
*function_ff_info.declared_type.syntax_origin),
"cc");
const ReferenceComponentNode *cc_ref =
function_ff_info.declared_type.user_defined_type;
ASSERT_NE(cc_ref, nullptr);
const ReferenceComponent &cc_ref_comp(cc_ref->Value());
EXPECT_EQ(cc_ref_comp.ref_type, ReferenceType::kUnqualified);
EXPECT_EQ(cc_ref_comp.required_metatype, SymbolMetaType::kUnspecified);
EXPECT_EQ(cc_ref_comp.identifier, "cc");
EXPECT_EQ(cc_ref_comp.resolved_symbol, nullptr);
// There should be one reference to return type "cc" of function "ff".
EXPECT_EQ(root_symbol.Value().local_references_to_bind.size(), 1);
{
std::vector<absl::Status> resolve_diagnostics;
symbol_table.Resolve(&resolve_diagnostics);
EXPECT_EMPTY_STATUSES(resolve_diagnostics);
// Expect "cc" return type to resolve to class declaration.
EXPECT_EQ(cc_ref_comp.resolved_symbol, &class_cc);
}
}
TEST_F(BuildSymbolTableTest, FunctionDeclarationInModule) {
TestVerilogSourceFile src("funkytown.sv",
"module mm;\n"
"function void ff();\n"
"endfunction\n"
"endmodule\n");
const auto status = src.Parse();
ASSERT_TRUE(status.ok()) << status.message();
SymbolTable symbol_table(nullptr);
const SymbolTableNode &root_symbol(symbol_table.Root());
const auto build_diagnostics = BuildSymbolTable(src, &symbol_table);
EXPECT_EMPTY_STATUSES(build_diagnostics);
MUST_ASSIGN_LOOKUP_SYMBOL(module_mm, root_symbol, "mm");
MUST_ASSIGN_LOOKUP_SYMBOL(function_ff, module_mm, "ff");
EXPECT_EQ(function_ff_info.metatype, SymbolMetaType::kFunction);
EXPECT_EQ(function_ff_info.file_origin, &src);
ASSERT_NE(function_ff_info.declared_type.syntax_origin, nullptr);
EXPECT_EQ(verible::StringSpanOfSymbol(
*function_ff_info.declared_type.syntax_origin),
"void");
const ReferenceComponentNode *ff_type =
function_ff_info.declared_type.user_defined_type;
EXPECT_EQ(ff_type, nullptr);
// There are no references to resolve.
EXPECT_TRUE(root_symbol.Value().local_references_to_bind.empty());
EXPECT_TRUE(module_mm.Value().local_references_to_bind.empty());
EXPECT_TRUE(function_ff.Value().local_references_to_bind.empty());
{
std::vector<absl::Status> resolve_diagnostics;
symbol_table.Resolve(&resolve_diagnostics);
EXPECT_EMPTY_STATUSES(resolve_diagnostics);
}
}
TEST_F(BuildSymbolTableTest, ClassMethodFunctionDeclaration) {
TestVerilogSourceFile src("funkytown.sv",
"class cc;\n"
"function int ff;\n"
"endfunction\n"
"endclass\n");
const auto status = src.Parse();
ASSERT_TRUE(status.ok()) << status.message();
SymbolTable symbol_table(nullptr);
const SymbolTableNode &root_symbol(symbol_table.Root());
const auto build_diagnostics = BuildSymbolTable(src, &symbol_table);
EXPECT_EMPTY_STATUSES(build_diagnostics);
MUST_ASSIGN_LOOKUP_SYMBOL(class_cc, root_symbol, "cc");
MUST_ASSIGN_LOOKUP_SYMBOL(function_ff, class_cc, "ff");
EXPECT_EQ(function_ff_info.metatype, SymbolMetaType::kFunction);
EXPECT_EQ(function_ff_info.file_origin, &src);
ASSERT_NE(function_ff_info.declared_type.syntax_origin, nullptr);
EXPECT_EQ(verible::StringSpanOfSymbol(
*function_ff_info.declared_type.syntax_origin),
"int");
const ReferenceComponentNode *ff_type =
function_ff_info.declared_type.user_defined_type;
EXPECT_EQ(ff_type, nullptr);
// There are no references to resolve.
EXPECT_TRUE(root_symbol.Value().local_references_to_bind.empty());
EXPECT_TRUE(class_cc.Value().local_references_to_bind.empty());
EXPECT_TRUE(function_ff.Value().local_references_to_bind.empty());
{
std::vector<absl::Status> resolve_diagnostics;
symbol_table.Resolve(&resolve_diagnostics);
EXPECT_EMPTY_STATUSES(resolve_diagnostics);
}
}
TEST_F(BuildSymbolTableTest,
ClassMethodFunctionDeclarationPackageTypeReturnType) {
TestVerilogSourceFile src("funkytown.sv",
"package aa;\n"
"class vv;\n"
"endclass\n"
"endpackage\n"
"package bb;\n"
"class cc;\n"
"function aa::vv ff();\n"
"endfunction\n"
"endclass\n"
"endpackage\n");
const auto status = src.Parse();
ASSERT_TRUE(status.ok()) << status.message();
SymbolTable symbol_table(nullptr);
const SymbolTableNode &root_symbol(symbol_table.Root());
const auto build_diagnostics = BuildSymbolTable(src, &symbol_table);
EXPECT_EMPTY_STATUSES(build_diagnostics);
MUST_ASSIGN_LOOKUP_SYMBOL(package_aa, root_symbol, "aa");
MUST_ASSIGN_LOOKUP_SYMBOL(package_bb, root_symbol, "bb");
MUST_ASSIGN_LOOKUP_SYMBOL(class_vv, package_aa, "vv");
MUST_ASSIGN_LOOKUP_SYMBOL(class_cc, package_bb, "cc");
MUST_ASSIGN_LOOKUP_SYMBOL(function_ff, class_cc, "ff");
EXPECT_EQ(function_ff_info.metatype, SymbolMetaType::kFunction);
EXPECT_EQ(function_ff_info.file_origin, &src);
ASSERT_NE(function_ff_info.declared_type.syntax_origin, nullptr);
EXPECT_EQ(verible::StringSpanOfSymbol(
*function_ff_info.declared_type.syntax_origin),
"aa::vv");
// return type points to the last component of the chain, "vv"
const ReferenceComponentNode *vv_ref =
function_ff_info.declared_type.user_defined_type;
ASSERT_NE(vv_ref, nullptr);
const ReferenceComponent &vv_ref_comp(vv_ref->Value());
EXPECT_EQ(vv_ref_comp.ref_type, ReferenceType::kDirectMember);
EXPECT_EQ(vv_ref_comp.required_metatype, SymbolMetaType::kUnspecified);
EXPECT_EQ(vv_ref_comp.identifier, "vv");
EXPECT_EQ(vv_ref_comp.resolved_symbol, nullptr);
// dependent reference parent is "aa" in "aa::vv"
const ReferenceComponentNode *aa_ref = vv_ref->Parent();
ASSERT_NE(aa_ref, nullptr);
const ReferenceComponent &aa_ref_comp(aa_ref->Value());
EXPECT_EQ(aa_ref_comp.ref_type, ReferenceType::kUnqualified);
EXPECT_EQ(aa_ref_comp.required_metatype, SymbolMetaType::kUnspecified);
EXPECT_EQ(aa_ref_comp.identifier, "aa");
EXPECT_EQ(aa_ref_comp.resolved_symbol, nullptr);
// There is only one (type) reference chain to resolve: "aa::vv".
EXPECT_TRUE(root_symbol.Value().local_references_to_bind.empty());
EXPECT_TRUE(package_aa.Value().local_references_to_bind.empty());
EXPECT_TRUE(package_bb.Value().local_references_to_bind.empty());
EXPECT_EQ(class_cc.Value().local_references_to_bind.size(), 1);
EXPECT_TRUE(function_ff.Value().local_references_to_bind.empty());
{
std::vector<absl::Status> resolve_diagnostics;
symbol_table.Resolve(&resolve_diagnostics);
EXPECT_EMPTY_STATUSES(resolve_diagnostics);
// Expect to resolve type reference chain "aa:vv"
EXPECT_EQ(aa_ref_comp.resolved_symbol, &package_aa);
EXPECT_EQ(vv_ref_comp.resolved_symbol, &class_vv);
}
}
TEST_F(BuildSymbolTableTest, FunctionDeclarationOutOfLineMissingOuterClass) {
TestVerilogSourceFile src("outofline_func.sv",
"function cc::ff;\n" // "cc" undeclared
"endfunction\n");
const auto status = src.Parse();
ASSERT_TRUE(status.ok()) << status.message();
SymbolTable symbol_table(nullptr);
const SymbolTableNode &root_symbol(symbol_table.Root());
const auto build_diagnostics = BuildSymbolTable(src, &symbol_table);
{
ASSIGN_MUST_HAVE_UNIQUE(err, build_diagnostics);
EXPECT_EQ(err.code(), absl::StatusCode::kNotFound);
EXPECT_THAT(
err.message(),
HasSubstr("No member symbol \"cc\" in parent scope (<root>) $root"));
}
// out-of-line declaration creates a self-reference.
EXPECT_EQ(root_symbol.Value().local_references_to_bind.size(), 1);
{
std::vector<absl::Status> resolve_diagnostics;
symbol_table.Resolve(&resolve_diagnostics);
// Same diagnostic as before.
ASSIGN_MUST_HAVE_UNIQUE(err, resolve_diagnostics);
EXPECT_EQ(err.code(), absl::StatusCode::kNotFound);
EXPECT_THAT(
err.message(),
HasSubstr("No member symbol \"cc\" in parent scope (<root>) $root"));
}
}
TEST_F(BuildSymbolTableTest,
FunctionDeclarationOutOfLineInvalidModuleInjection) {
TestVerilogSourceFile src("outofline_func.sv",
"module mm;\n"
"endmodule\n"
"function mm::ff;\n"
"endfunction\n");
const auto status = src.Parse();
ASSERT_TRUE(status.ok()) << status.message();
SymbolTable symbol_table(nullptr);
const SymbolTableNode &root_symbol(symbol_table.Root());
const auto build_diagnostics = BuildSymbolTable(src, &symbol_table);
{
// Expect that "tt" will not injected into "mm" because it is a module,
// not a class.
ASSIGN_MUST_HAVE_UNIQUE(err, build_diagnostics);
EXPECT_EQ(err.code(), absl::StatusCode::kInvalidArgument);
EXPECT_THAT(err.message(),
HasSubstr("Expecting reference \"mm\" to resolve to a class, "
"but found a module"));
}
MUST_ASSIGN_LOOKUP_SYMBOL(module_mm, root_symbol, "mm");
EXPECT_EQ(module_mm_info.metatype, SymbolMetaType::kModule);
EXPECT_EQ(module_mm.Find("ff"), module_mm.end());
// Reference must be resolved at Build-time.
EXPECT_EQ(root_symbol.Value().local_references_to_bind.size(), 1);
const auto ref_map(root_symbol.Value().LocalReferencesMapViewForTesting());
ASSIGN_MUST_FIND_EXACTLY_ONE_REF(mm_ref, ref_map, "mm");
EXPECT_EQ(mm_ref->components->Value().resolved_symbol, nullptr);
// Method injection will not happen for modules.
const ReferenceComponentNode *ff_ref = mm_ref->LastLeaf();
const ReferenceComponent &ref(ff_ref->Value());
EXPECT_EQ(ref.identifier, "ff");
EXPECT_EQ(ref.resolved_symbol, nullptr);
{
std::vector<absl::Status> resolve_diagnostics;
symbol_table.Resolve(&resolve_diagnostics);
ASSERT_EQ(resolve_diagnostics.size(), 1);
// Still remain unresolved.
EXPECT_EQ(mm_ref->components->Value().resolved_symbol, nullptr);
EXPECT_EQ(ref.resolved_symbol, nullptr);
}
}
TEST_F(BuildSymbolTableTest, FunctionDeclarationOutOfLineMissingPrototype) {
TestVerilogSourceFile src("outofline_func.sv",
"class cc;\n"
// no "ff" prototype
"endclass\n"
"function cc::ff;\n"
"endfunction\n");
const auto status = src.Parse();
ASSERT_TRUE(status.ok()) << status.message();
SymbolTable symbol_table(nullptr);
const SymbolTableNode &root_symbol(symbol_table.Root());
const auto build_diagnostics = BuildSymbolTable(src, &symbol_table);
{
// This diagnostic is non-fatal.
// Expect that "ff" will be injected into "cc" when its method prototype is
// missing.
ASSIGN_MUST_HAVE_UNIQUE(err, build_diagnostics);
EXPECT_EQ(err.code(), absl::StatusCode::kNotFound);
EXPECT_THAT(
err.message(),
HasSubstr("No member symbol \"ff\" in parent scope (class) cc"));
}
MUST_ASSIGN_LOOKUP_SYMBOL(class_cc, root_symbol, "cc");
MUST_ASSIGN_LOOKUP_SYMBOL(method_ff, class_cc, "ff");
EXPECT_EQ(method_ff_info.metatype, SymbolMetaType::kFunction);
// out-of-line declaration creates a self-reference.
// Reference must be resolved at Build-time.
EXPECT_EQ(root_symbol.Value().local_references_to_bind.size(), 1);
const auto ref_map(root_symbol.Value().LocalReferencesMapViewForTesting());
ASSIGN_MUST_FIND_EXACTLY_ONE_REF(cc_ref, ref_map, "cc");
EXPECT_EQ(cc_ref->components->Value().resolved_symbol, &class_cc);
// Method reference is resolved to the injected symbol.
const ReferenceComponentNode *ff_ref = cc_ref->LastLeaf();
const ReferenceComponent &ref(ff_ref->Value());
EXPECT_EQ(ref.identifier, "ff");
EXPECT_EQ(ref.resolved_symbol, &method_ff);
{
std::vector<absl::Status> resolve_diagnostics;
symbol_table.Resolve(&resolve_diagnostics);
EXPECT_EMPTY_STATUSES(resolve_diagnostics);
// Already resolved before, still remains resolved.
EXPECT_EQ(cc_ref->components->Value().resolved_symbol, &class_cc);
EXPECT_EQ(ref.resolved_symbol, &method_ff);
}
}
TEST_F(BuildSymbolTableTest, FunctionDeclarationMethodPrototypeOnly) {
TestVerilogSourceFile src("outofline_func.sv",
"class cc;\n"
" extern function int ff(logic ll);\n"
"endclass\n");
const auto status = src.Parse();
ASSERT_TRUE(status.ok()) << status.message();
SymbolTable symbol_table(nullptr);
const SymbolTableNode &root_symbol(symbol_table.Root());
const auto build_diagnostics = BuildSymbolTable(src, &symbol_table);
EXPECT_EMPTY_STATUSES(build_diagnostics);
MUST_ASSIGN_LOOKUP_SYMBOL(class_cc, root_symbol, "cc");
MUST_ASSIGN_LOOKUP_SYMBOL(method_ff, class_cc, "ff");
EXPECT_EQ(method_ff_info.metatype, SymbolMetaType::kFunction);
ASSERT_NE(method_ff_info.declared_type.syntax_origin, nullptr);
EXPECT_EQ(
verible::StringSpanOfSymbol(*method_ff_info.declared_type.syntax_origin),
"int");
MUST_ASSIGN_LOOKUP_SYMBOL(port_ll, method_ff, "ll");
EXPECT_EQ(port_ll_info.metatype, SymbolMetaType::kDataNetVariableInstance);
ASSERT_NE(port_ll_info.declared_type.syntax_origin, nullptr);
EXPECT_EQ(
verible::StringSpanOfSymbol(*port_ll_info.declared_type.syntax_origin),
"logic");
// No references to resolve.
EXPECT_TRUE(root_symbol.Value().local_references_to_bind.empty());
EXPECT_TRUE(class_cc_info.local_references_to_bind.empty());
EXPECT_TRUE(method_ff_info.local_references_to_bind.empty());
{
std::vector<absl::Status> resolve_diagnostics;
symbol_table.Resolve(&resolve_diagnostics);
EXPECT_EMPTY_STATUSES(resolve_diagnostics);
}
}
TEST_F(BuildSymbolTableTest, FunctionDeclarationOutOfLineWithMethodPrototype) {
TestVerilogSourceFile src(
"outofline_func.sv",
"class cc;\n"
" extern function int ff(logic ll);\n" // prototype
"endclass\n"
"function int cc::ff(logic ll);\n" // definition
" bit bb;\n" // local variable
"endfunction\n");
const auto status = src.Parse();
ASSERT_TRUE(status.ok()) << status.message();
SymbolTable symbol_table(nullptr);
const SymbolTableNode &root_symbol(symbol_table.Root());
const auto build_diagnostics = BuildSymbolTable(src, &symbol_table);
EXPECT_EMPTY_STATUSES(build_diagnostics);
MUST_ASSIGN_LOOKUP_SYMBOL(class_cc, root_symbol, "cc");
MUST_ASSIGN_LOOKUP_SYMBOL(method_ff, class_cc, "ff");
EXPECT_EQ(method_ff_info.metatype, SymbolMetaType::kFunction);
ASSERT_NE(method_ff_info.declared_type.syntax_origin, nullptr);
EXPECT_EQ(
verible::StringSpanOfSymbol(*method_ff_info.declared_type.syntax_origin),
"int");
MUST_ASSIGN_LOOKUP_SYMBOL(port_ll, method_ff, "ll");
EXPECT_EQ(port_ll_info.metatype, SymbolMetaType::kDataNetVariableInstance);
ASSERT_NE(port_ll_info.declared_type.syntax_origin, nullptr);
EXPECT_EQ(
verible::StringSpanOfSymbol(*port_ll_info.declared_type.syntax_origin),
"logic");
MUST_ASSIGN_LOOKUP_SYMBOL(local_bb, method_ff, "bb");
EXPECT_EQ(local_bb_info.metatype, SymbolMetaType::kDataNetVariableInstance);
ASSERT_NE(local_bb_info.declared_type.syntax_origin, nullptr);
EXPECT_EQ(
verible::StringSpanOfSymbol(*local_bb_info.declared_type.syntax_origin),
"bit");
// out-of-line declaration creates a self-reference.
// Reference must be resolved at Build-time.
EXPECT_EQ(root_symbol.Value().local_references_to_bind.size(), 1);
const auto ref_map(root_symbol.Value().LocalReferencesMapViewForTesting());
ASSIGN_MUST_FIND_EXACTLY_ONE_REF(cc_ref, ref_map, "cc");
EXPECT_EQ(cc_ref->components->Value().resolved_symbol, &class_cc);
// Method reference is resolved to the injected symbol.
const ReferenceComponentNode *ff_ref = cc_ref->LastLeaf();
const ReferenceComponent &ref(ff_ref->Value());
EXPECT_EQ(ref.identifier, "ff");
EXPECT_EQ(ref.resolved_symbol, &method_ff);
EXPECT_TRUE(class_cc_info.local_references_to_bind.empty());
EXPECT_TRUE(method_ff_info.local_references_to_bind.empty());
{
std::vector<absl::Status> resolve_diagnostics;
symbol_table.Resolve(&resolve_diagnostics);
EXPECT_EMPTY_STATUSES(resolve_diagnostics);
// Already resolved.
EXPECT_EQ(cc_ref->components->Value().resolved_symbol, &class_cc);
EXPECT_EQ(ref.resolved_symbol, &method_ff);
}
}
TEST_F(BuildSymbolTableTest, TaskDeclaration) {
TestVerilogSourceFile src("taskrabbit.sv",
"task tt;\n"
"endtask\n");
const auto status = src.Parse();
ASSERT_TRUE(status.ok()) << status.message();
SymbolTable symbol_table(nullptr);
const SymbolTableNode &root_symbol(symbol_table.Root());
const auto build_diagnostics = BuildSymbolTable(src, &symbol_table);
EXPECT_EMPTY_STATUSES(build_diagnostics);
MUST_ASSIGN_LOOKUP_SYMBOL(task_tt, root_symbol, "tt");
EXPECT_EQ(task_tt_info.metatype, SymbolMetaType::kTask);
EXPECT_EQ(task_tt_info.file_origin, &src);
// no return type
EXPECT_EQ(task_tt_info.declared_type.syntax_origin, nullptr);
EXPECT_TRUE(task_tt_info.local_references_to_bind.empty());
{
std::vector<absl::Status> resolve_diagnostics;
symbol_table.Resolve(&resolve_diagnostics);
EXPECT_EMPTY_STATUSES(resolve_diagnostics);
}
}
TEST_F(BuildSymbolTableTest, TaskDeclarationInPackage) {
TestVerilogSourceFile src("taskrabbit.sv",
"package pp;\n"
"task tt();\n"
"endtask\n"
"endpackage\n");
const auto status = src.Parse();
ASSERT_TRUE(status.ok()) << status.message();
SymbolTable symbol_table(nullptr);
const SymbolTableNode &root_symbol(symbol_table.Root());
const auto build_diagnostics = BuildSymbolTable(src, &symbol_table);
EXPECT_EMPTY_STATUSES(build_diagnostics);
MUST_ASSIGN_LOOKUP_SYMBOL(package_pp, root_symbol, "pp");
MUST_ASSIGN_LOOKUP_SYMBOL(task_tt, package_pp, "tt");
EXPECT_EQ(task_tt_info.metatype, SymbolMetaType::kTask);
EXPECT_EQ(task_tt_info.file_origin, &src);
// no return type
EXPECT_EQ(task_tt_info.declared_type.syntax_origin, nullptr);
EXPECT_TRUE(task_tt_info.local_references_to_bind.empty());
{
std::vector<absl::Status> resolve_diagnostics;
symbol_table.Resolve(&resolve_diagnostics);
EXPECT_EMPTY_STATUSES(resolve_diagnostics);
}
}
TEST_F(BuildSymbolTableTest, TaskDeclarationInModule) {
TestVerilogSourceFile src("taskrabbit.sv",
"module mm;\n"
"task tt();\n"
"endtask\n"
"endmodule\n");
const auto status = src.Parse();
ASSERT_TRUE(status.ok()) << status.message();
SymbolTable symbol_table(nullptr);
const SymbolTableNode &root_symbol(symbol_table.Root());
const auto build_diagnostics = BuildSymbolTable(src, &symbol_table);
EXPECT_EMPTY_STATUSES(build_diagnostics);
MUST_ASSIGN_LOOKUP_SYMBOL(module_mm, root_symbol, "mm");
MUST_ASSIGN_LOOKUP_SYMBOL(task_tt, module_mm, "tt");
EXPECT_EQ(task_tt_info.metatype, SymbolMetaType::kTask);
EXPECT_EQ(task_tt_info.file_origin, &src);
// no return type
EXPECT_EQ(task_tt_info.declared_type.syntax_origin, nullptr);
EXPECT_TRUE(task_tt_info.local_references_to_bind.empty());
{
std::vector<absl::Status> resolve_diagnostics;
symbol_table.Resolve(&resolve_diagnostics);
EXPECT_EMPTY_STATUSES(resolve_diagnostics);
}
}
TEST_F(BuildSymbolTableTest, TaskDeclarationInClass) {
TestVerilogSourceFile src("taskrabbit.sv",
"class cc;\n"
"task tt();\n"
"endtask\n"
"endclass\n");
const auto status = src.Parse();
ASSERT_TRUE(status.ok()) << status.message();
SymbolTable symbol_table(nullptr);
const SymbolTableNode &root_symbol(symbol_table.Root());
const auto build_diagnostics = BuildSymbolTable(src, &symbol_table);
EXPECT_EMPTY_STATUSES(build_diagnostics);
MUST_ASSIGN_LOOKUP_SYMBOL(class_cc, root_symbol, "cc");
MUST_ASSIGN_LOOKUP_SYMBOL(task_tt, class_cc, "tt");
EXPECT_EQ(task_tt_info.metatype, SymbolMetaType::kTask);
EXPECT_EQ(task_tt_info.file_origin, &src);
// no return type
EXPECT_EQ(task_tt_info.declared_type.syntax_origin, nullptr);
EXPECT_TRUE(task_tt_info.local_references_to_bind.empty());
{
std::vector<absl::Status> resolve_diagnostics;
symbol_table.Resolve(&resolve_diagnostics);
EXPECT_EMPTY_STATUSES(resolve_diagnostics);
}
}
TEST_F(BuildSymbolTableTest, TaskDeclarationWithPorts) {
TestVerilogSourceFile src("taskrabbit.sv",
"task tt(logic ll);\n"
"endtask\n");
const auto status = src.Parse();
ASSERT_TRUE(status.ok()) << status.message();
SymbolTable symbol_table(nullptr);
const SymbolTableNode &root_symbol(symbol_table.Root());
const auto build_diagnostics = BuildSymbolTable(src, &symbol_table);
EXPECT_EMPTY_STATUSES(build_diagnostics);
MUST_ASSIGN_LOOKUP_SYMBOL(task_tt, root_symbol, "tt");
EXPECT_EQ(task_tt_info.metatype, SymbolMetaType::kTask);
EXPECT_EQ(task_tt_info.file_origin, &src);
// no return type
EXPECT_EQ(task_tt_info.declared_type.syntax_origin, nullptr);
MUST_ASSIGN_LOOKUP_SYMBOL(logic_ll, task_tt, "ll");
EXPECT_EQ(logic_ll_info.metatype, SymbolMetaType::kDataNetVariableInstance);
EXPECT_EQ(logic_ll_info.file_origin, &src);
// primitive type
ASSERT_NE(logic_ll_info.declared_type.syntax_origin, nullptr);
EXPECT_EQ(
verible::StringSpanOfSymbol(*logic_ll_info.declared_type.syntax_origin),
"logic");
EXPECT_TRUE(task_tt_info.local_references_to_bind.empty());
{
std::vector<absl::Status> resolve_diagnostics;
symbol_table.Resolve(&resolve_diagnostics);
EXPECT_EMPTY_STATUSES(resolve_diagnostics);
}
}
TEST_F(BuildSymbolTableTest, TaskDeclarationOutOfLineMissingOuterClass) {
TestVerilogSourceFile src("outofline_task.sv",
"task cc::tt;\n" // "cc" undeclared
"endtask\n");
const auto status = src.Parse();
ASSERT_TRUE(status.ok()) << status.message();
SymbolTable symbol_table(nullptr);
const SymbolTableNode &root_symbol(symbol_table.Root());
const auto build_diagnostics = BuildSymbolTable(src, &symbol_table);
{
ASSIGN_MUST_HAVE_UNIQUE(err, build_diagnostics);
EXPECT_EQ(err.code(), absl::StatusCode::kNotFound);
EXPECT_THAT(
err.message(),
HasSubstr("No member symbol \"cc\" in parent scope (<root>) $root"));
}
// out-of-line declaration creates a self-reference.
EXPECT_EQ(root_symbol.Value().local_references_to_bind.size(), 1);
{
std::vector<absl::Status> resolve_diagnostics;
symbol_table.Resolve(&resolve_diagnostics);
// Same diagnostic as before.
ASSIGN_MUST_HAVE_UNIQUE(err, resolve_diagnostics);
EXPECT_EQ(err.code(), absl::StatusCode::kNotFound);
EXPECT_THAT(
err.message(),
HasSubstr("No member symbol \"cc\" in parent scope (<root>) $root"));
}
}
TEST_F(BuildSymbolTableTest, TaskDeclarationOutOfLineMissingPrototype) {
TestVerilogSourceFile src("outofline_task.sv",
"class cc;\n"
// no "tt" prototype
"endclass\n"
"task cc::tt;\n"
"endtask\n");
const auto status = src.Parse();
ASSERT_TRUE(status.ok()) << status.message();
SymbolTable symbol_table(nullptr);
const SymbolTableNode &root_symbol(symbol_table.Root());
const auto build_diagnostics = BuildSymbolTable(src, &symbol_table);
{
// This diagnostic is non-fatal.
// Expect that "tt" will be injected into "cc" when its method prototype is
// missing.
ASSIGN_MUST_HAVE_UNIQUE(err, build_diagnostics);
EXPECT_EQ(err.code(), absl::StatusCode::kNotFound);
EXPECT_THAT(
err.message(),
HasSubstr("No member symbol \"tt\" in parent scope (class) cc"));
}
MUST_ASSIGN_LOOKUP_SYMBOL(class_cc, root_symbol, "cc");
MUST_ASSIGN_LOOKUP_SYMBOL(method_tt, class_cc, "tt");
EXPECT_EQ(method_tt_info.metatype, SymbolMetaType::kTask);
// out-of-line declaration creates a self-reference.
// Reference must be resolved at Build-time.
EXPECT_EQ(root_symbol.Value().local_references_to_bind.size(), 1);
const auto ref_map(root_symbol.Value().LocalReferencesMapViewForTesting());
ASSIGN_MUST_FIND_EXACTLY_ONE_REF(cc_ref, ref_map, "cc");
EXPECT_EQ(cc_ref->components->Value().resolved_symbol, &class_cc);
// Method reference is resolved to the injected symbol.
const ReferenceComponentNode *tt_ref = cc_ref->LastLeaf();
const ReferenceComponent &ref(tt_ref->Value());
EXPECT_EQ(ref.identifier, "tt");
EXPECT_EQ(ref.resolved_symbol, &method_tt);
{
std::vector<absl::Status> resolve_diagnostics;
symbol_table.Resolve(&resolve_diagnostics);
EXPECT_EMPTY_STATUSES(resolve_diagnostics);
// Already resolved before, still remains resolved.
EXPECT_EQ(cc_ref->components->Value().resolved_symbol, &class_cc);
EXPECT_EQ(ref.resolved_symbol, &method_tt);
}
}
TEST_F(BuildSymbolTableTest, TaskDeclarationOutOfLineInvalidPackageInjection) {
TestVerilogSourceFile src("outofline_task.sv",
"package pp;\n"
"endpackage\n"
"task pp::tt;\n"
"endtask\n");
const auto status = src.Parse();
ASSERT_TRUE(status.ok()) << status.message();
SymbolTable symbol_table(nullptr);
const SymbolTableNode &root_symbol(symbol_table.Root());
const auto build_diagnostics = BuildSymbolTable(src, &symbol_table);
{
// Expect that "tt" will not injected into "pp" because it is a package,
// not a class.
ASSIGN_MUST_HAVE_UNIQUE(err, build_diagnostics);
EXPECT_EQ(err.code(), absl::StatusCode::kInvalidArgument);
EXPECT_THAT(err.message(),
HasSubstr("Expecting reference \"pp\" to resolve to a class, "
"but found a package"));
}
MUST_ASSIGN_LOOKUP_SYMBOL(package_pp, root_symbol, "pp");
EXPECT_EQ(package_pp_info.metatype, SymbolMetaType::kPackage);
EXPECT_EQ(package_pp.Find("tt"), package_pp.end());
// Reference must be resolved at Build-time.
EXPECT_EQ(root_symbol.Value().local_references_to_bind.size(), 1);
const auto ref_map(root_symbol.Value().LocalReferencesMapViewForTesting());
ASSIGN_MUST_FIND_EXACTLY_ONE_REF(pp_ref, ref_map, "pp");
EXPECT_EQ(pp_ref->components->Value().resolved_symbol, nullptr);
// Method injection will not happen for packages.
const ReferenceComponentNode *tt_ref = pp_ref->LastLeaf();
const ReferenceComponent &ref(tt_ref->Value());
EXPECT_EQ(ref.identifier, "tt");
EXPECT_EQ(ref.resolved_symbol, nullptr);
{
std::vector<absl::Status> resolve_diagnostics;
symbol_table.Resolve(&resolve_diagnostics);
ASSERT_EQ(resolve_diagnostics.size(), 1);
// Still remain unresolved.
EXPECT_EQ(pp_ref->components->Value().resolved_symbol, nullptr);
EXPECT_EQ(ref.resolved_symbol, nullptr);
}
}
TEST_F(BuildSymbolTableTest, TaskDeclarationMethodPrototypeOnly) {
TestVerilogSourceFile src("outofline_task.sv",
"class cc;\n"
" extern task tt(logic ll);\n"
"endclass\n");
const auto status = src.Parse();
ASSERT_TRUE(status.ok()) << status.message();
SymbolTable symbol_table(nullptr);
const SymbolTableNode &root_symbol(symbol_table.Root());
const auto build_diagnostics = BuildSymbolTable(src, &symbol_table);
EXPECT_EMPTY_STATUSES(build_diagnostics);
MUST_ASSIGN_LOOKUP_SYMBOL(class_cc, root_symbol, "cc");
MUST_ASSIGN_LOOKUP_SYMBOL(method_tt, class_cc, "tt");
EXPECT_EQ(method_tt_info.metatype, SymbolMetaType::kTask);
EXPECT_EQ(method_tt_info.declared_type.syntax_origin, nullptr);
MUST_ASSIGN_LOOKUP_SYMBOL(port_ll, method_tt, "ll");
EXPECT_EQ(port_ll_info.metatype, SymbolMetaType::kDataNetVariableInstance);
ASSERT_NE(port_ll_info.declared_type.syntax_origin, nullptr);
EXPECT_EQ(
verible::StringSpanOfSymbol(*port_ll_info.declared_type.syntax_origin),
"logic");
// No references to resolve.
EXPECT_TRUE(root_symbol.Value().local_references_to_bind.empty());
EXPECT_TRUE(class_cc_info.local_references_to_bind.empty());
EXPECT_TRUE(method_tt_info.local_references_to_bind.empty());
{
std::vector<absl::Status> resolve_diagnostics;
symbol_table.Resolve(&resolve_diagnostics);
EXPECT_EMPTY_STATUSES(resolve_diagnostics);
}
}
TEST_F(BuildSymbolTableTest, TaskDeclarationOutOfLineWithMethodPrototype) {
TestVerilogSourceFile src("outofline_task.sv",
"class cc;\n"
" extern task tt(logic ll);\n" // prototype
"endclass\n"
"task cc::tt(logic ll);\n" // definition
" bit bb;\n" // local variable
"endtask\n");
const auto status = src.Parse();
ASSERT_TRUE(status.ok()) << status.message();
SymbolTable symbol_table(nullptr);
const SymbolTableNode &root_symbol(symbol_table.Root());
const auto build_diagnostics = BuildSymbolTable(src, &symbol_table);
EXPECT_EMPTY_STATUSES(build_diagnostics);
MUST_ASSIGN_LOOKUP_SYMBOL(class_cc, root_symbol, "cc");
MUST_ASSIGN_LOOKUP_SYMBOL(method_tt, class_cc, "tt");
EXPECT_EQ(method_tt_info.metatype, SymbolMetaType::kTask);
EXPECT_EQ(method_tt_info.declared_type.syntax_origin, nullptr);
MUST_ASSIGN_LOOKUP_SYMBOL(port_ll, method_tt, "ll");
EXPECT_EQ(port_ll_info.metatype, SymbolMetaType::kDataNetVariableInstance);
ASSERT_NE(port_ll_info.declared_type.syntax_origin, nullptr);
EXPECT_EQ(
verible::StringSpanOfSymbol(*port_ll_info.declared_type.syntax_origin),
"logic");
MUST_ASSIGN_LOOKUP_SYMBOL(local_bb, method_tt, "bb");
EXPECT_EQ(local_bb_info.metatype, SymbolMetaType::kDataNetVariableInstance);
ASSERT_NE(local_bb_info.declared_type.syntax_origin, nullptr);
EXPECT_EQ(
verible::StringSpanOfSymbol(*local_bb_info.declared_type.syntax_origin),
"bit");
// out-of-line declaration creates a self-reference.
// Reference must be resolved at Build-time.
EXPECT_EQ(root_symbol.Value().local_references_to_bind.size(), 1);
const auto ref_map(root_symbol.Value().LocalReferencesMapViewForTesting());
ASSIGN_MUST_FIND_EXACTLY_ONE_REF(cc_ref, ref_map, "cc");
EXPECT_EQ(cc_ref->components->Value().resolved_symbol, &class_cc);
// Method reference is resolved to the injected symbol.
const ReferenceComponentNode *tt_ref = cc_ref->LastLeaf();
const ReferenceComponent &ref(tt_ref->Value());
EXPECT_EQ(ref.identifier, "tt");
EXPECT_EQ(ref.resolved_symbol, &method_tt);
EXPECT_TRUE(class_cc_info.local_references_to_bind.empty());
EXPECT_TRUE(method_tt_info.local_references_to_bind.empty());
{
std::vector<absl::Status> resolve_diagnostics;
symbol_table.Resolve(&resolve_diagnostics);
EXPECT_EMPTY_STATUSES(resolve_diagnostics);
// Already resolved.
EXPECT_EQ(cc_ref->components->Value().resolved_symbol, &class_cc);
EXPECT_EQ(ref.resolved_symbol, &method_tt);
}
}
TEST_F(BuildSymbolTableTest, OutOfLineDefinitionMismatchesPrototype) {
TestVerilogSourceFile src(
"outofline_task_or_func.sv",
"class cc;\n"
" extern task tt(logic ll);\n" // task prototype
"endclass\n"
"function int cc::tt(logic ll);\n" // function definition (wrong type)
"endfunction\n");
const auto status = src.Parse();
ASSERT_TRUE(status.ok()) << status.message();
SymbolTable symbol_table(nullptr);
const SymbolTableNode &root_symbol(symbol_table.Root());
const auto build_diagnostics = BuildSymbolTable(src, &symbol_table);
ASSIGN_MUST_HAVE_UNIQUE(err, build_diagnostics);
EXPECT_EQ(err.code(), absl::StatusCode::kAlreadyExists);
EXPECT_THAT(
err.message(),
HasSubstr(
"task $root::cc::tt cannot be redefined out-of-line as a function"));
MUST_ASSIGN_LOOKUP_SYMBOL(class_cc, root_symbol, "cc");
MUST_ASSIGN_LOOKUP_SYMBOL(method_tt, class_cc, "tt");
EXPECT_EQ(method_tt_info.metatype, SymbolMetaType::kTask);
EXPECT_EQ(method_tt_info.declared_type.syntax_origin, nullptr);
MUST_ASSIGN_LOOKUP_SYMBOL(port_ll, method_tt, "ll");
EXPECT_EQ(port_ll_info.metatype, SymbolMetaType::kDataNetVariableInstance);
ASSERT_NE(port_ll_info.declared_type.syntax_origin, nullptr);
EXPECT_EQ(
verible::StringSpanOfSymbol(*port_ll_info.declared_type.syntax_origin),
"logic");
// Reference must be resolved at Build-time.
EXPECT_EQ(root_symbol.Value().local_references_to_bind.size(), 1);
const auto ref_map(root_symbol.Value().LocalReferencesMapViewForTesting());
ASSIGN_MUST_FIND_EXACTLY_ONE_REF(cc_ref, ref_map, "cc");
EXPECT_EQ(cc_ref->components->Value().resolved_symbol, &class_cc);
// Method reference "tt" fails to resolve due to metatype mismatch.
const ReferenceComponentNode *tt_ref = cc_ref->LastLeaf();
const ReferenceComponent &ref(tt_ref->Value());
EXPECT_EQ(ref.identifier, "tt");
EXPECT_EQ(ref.resolved_symbol, nullptr);
EXPECT_TRUE(class_cc_info.local_references_to_bind.empty());
EXPECT_TRUE(method_tt_info.local_references_to_bind.empty());
{
std::vector<absl::Status> resolve_diagnostics;
symbol_table.Resolve(&resolve_diagnostics);
ASSIGN_MUST_HAVE_UNIQUE(err, resolve_diagnostics);
EXPECT_EQ(err.code(), absl::StatusCode::kInvalidArgument);
EXPECT_THAT(err.message(),
HasSubstr("Expecting reference \"tt\" to resolve to a "
"function, but found a task"));
EXPECT_EQ(cc_ref->components->Value().resolved_symbol, &class_cc);
EXPECT_EQ(ref.resolved_symbol, nullptr); // Still fails to resolve.
}
}
TEST_F(BuildSymbolTableTest, FunctionCallResolvedSameScope) {
TestVerilogSourceFile src("call_me.sv",
"function int tt();\n"
" return 1;\n"
"endfunction\n"
"function int vv();\n"
" return tt();\n"
"endfunction\n");
const auto status = src.Parse();
ASSERT_TRUE(status.ok()) << status.message();
SymbolTable symbol_table(nullptr);
const SymbolTableNode &root_symbol(symbol_table.Root());
const auto build_diagnostics = BuildSymbolTable(src, &symbol_table);
EXPECT_EMPTY_STATUSES(build_diagnostics);
MUST_ASSIGN_LOOKUP_SYMBOL(function_tt, root_symbol, "tt");
MUST_ASSIGN_LOOKUP_SYMBOL(function_vv, root_symbol, "vv");
EXPECT_EQ(function_tt_info.metatype, SymbolMetaType::kFunction);
ASSERT_NE(function_tt_info.declared_type.syntax_origin, nullptr);
EXPECT_EQ(function_vv_info.metatype, SymbolMetaType::kFunction);
ASSERT_NE(function_vv_info.declared_type.syntax_origin, nullptr);
EXPECT_EQ(function_vv_info.local_references_to_bind.size(), 1);
const auto ref_map(function_vv_info.LocalReferencesMapViewForTesting());
ASSIGN_MUST_FIND_EXACTLY_ONE_REF(tt_ref, ref_map, "tt");
const ReferenceComponent &tt_ref_comp(tt_ref->components->Value());
EXPECT_EQ(tt_ref_comp.required_metatype, SymbolMetaType::kCallable);
EXPECT_EQ(tt_ref_comp.resolved_symbol, nullptr);
{
std::vector<absl::Status> resolve_diagnostics;
symbol_table.Resolve(&resolve_diagnostics);
EXPECT_EMPTY_STATUSES(resolve_diagnostics);
// Call to "tt" is resolved.
EXPECT_EQ(tt_ref_comp.resolved_symbol, &function_tt);
}
}
TEST_F(BuildSymbolTableTest, FunctionCallUnresolved) {
TestVerilogSourceFile src("call_me_not.sv",
"function int vv();\n"
" return tt();\n" // undefined
"endfunction\n");
const auto status = src.Parse();
ASSERT_TRUE(status.ok()) << status.message();
SymbolTable symbol_table(nullptr);
const SymbolTableNode &root_symbol(symbol_table.Root());
const auto build_diagnostics = BuildSymbolTable(src, &symbol_table);
EXPECT_EMPTY_STATUSES(build_diagnostics);
MUST_ASSIGN_LOOKUP_SYMBOL(function_vv, root_symbol, "vv");
EXPECT_EQ(function_vv_info.metatype, SymbolMetaType::kFunction);
ASSERT_NE(function_vv_info.declared_type.syntax_origin, nullptr);
EXPECT_EQ(function_vv_info.local_references_to_bind.size(), 1);
const auto ref_map(function_vv_info.LocalReferencesMapViewForTesting());
ASSIGN_MUST_FIND_EXACTLY_ONE_REF(tt_ref, ref_map, "tt");
const ReferenceComponent &tt_ref_comp(tt_ref->components->Value());
EXPECT_EQ(tt_ref_comp.required_metatype, SymbolMetaType::kCallable);
EXPECT_EQ(tt_ref_comp.resolved_symbol, nullptr);
{
std::vector<absl::Status> resolve_diagnostics;
symbol_table.Resolve(&resolve_diagnostics);
ASSIGN_MUST_HAVE_UNIQUE(err, resolve_diagnostics);
EXPECT_EQ(err.code(), absl::StatusCode::kNotFound);
EXPECT_THAT(
err.message(),
HasSubstr("Unable to resolve symbol \"tt\" from context $root::vv"));
// Call to "tt" is unresolved.
EXPECT_EQ(tt_ref_comp.resolved_symbol, nullptr);
}
}
TEST_F(BuildSymbolTableTest, FunctionCallUnresolvedNamedParameters) {
TestVerilogSourceFile src("call_me_not.sv",
"function int vv();\n"
" return tt(.a(1), .b(2));\n" // undefined
"endfunction\n");
const auto status = src.Parse();
ASSERT_TRUE(status.ok()) << status.message();
SymbolTable symbol_table(nullptr);
const SymbolTableNode &root_symbol(symbol_table.Root());
const auto build_diagnostics = BuildSymbolTable(src, &symbol_table);
EXPECT_EMPTY_STATUSES(build_diagnostics);
MUST_ASSIGN_LOOKUP_SYMBOL(function_vv, root_symbol, "vv");
EXPECT_EQ(function_vv_info.metatype, SymbolMetaType::kFunction);
ASSERT_NE(function_vv_info.declared_type.syntax_origin, nullptr);
EXPECT_EQ(function_vv_info.local_references_to_bind.size(), 1);
const auto ref_map(function_vv_info.LocalReferencesMapViewForTesting());
ASSIGN_MUST_FIND_EXACTLY_ONE_REF(tt_ref, ref_map, "tt");
const ReferenceComponent &tt_ref_comp(tt_ref->components->Value());
EXPECT_EQ(tt_ref_comp.required_metatype, SymbolMetaType::kCallable);
EXPECT_EQ(tt_ref_comp.resolved_symbol, nullptr);
const ReferenceComponentMap param_refs(
ReferenceComponentNodeMapView(*tt_ref->components));
ASSIGN_MUST_FIND(a_ref, param_refs, "a");
const ReferenceComponent &a_ref_comp(a_ref->Value());
EXPECT_EQ(a_ref_comp.identifier, "a");
EXPECT_EQ(a_ref_comp.ref_type, ReferenceType::kDirectMember);
EXPECT_EQ(a_ref_comp.required_metatype,
SymbolMetaType::kDataNetVariableInstance);
EXPECT_EQ(a_ref_comp.resolved_symbol, nullptr); // not yet resolved
ASSIGN_MUST_FIND(b_ref, param_refs, "b");
const ReferenceComponent &b_ref_comp(b_ref->Value());
EXPECT_EQ(b_ref_comp.identifier, "b");
EXPECT_EQ(b_ref_comp.ref_type, ReferenceType::kDirectMember);
EXPECT_EQ(b_ref_comp.required_metatype,
SymbolMetaType::kDataNetVariableInstance);
EXPECT_EQ(b_ref_comp.resolved_symbol, nullptr); // not yet resolved
{
std::vector<absl::Status> resolve_diagnostics;
symbol_table.Resolve(&resolve_diagnostics);
ASSIGN_MUST_HAVE_UNIQUE(err, resolve_diagnostics);
EXPECT_EQ(err.code(), absl::StatusCode::kNotFound);
EXPECT_THAT(
err.message(),
HasSubstr("Unable to resolve symbol \"tt\" from context $root::vv"));
// Call to "tt" is unresolved, as are its named parameters.
EXPECT_EQ(tt_ref_comp.resolved_symbol, nullptr);
EXPECT_EQ(a_ref_comp.resolved_symbol, nullptr); // not yet resolved
EXPECT_EQ(b_ref_comp.resolved_symbol, nullptr); // not yet resolved
}
}
TEST_F(BuildSymbolTableTest, FunctionCallResolvedNamedParameters) {
TestVerilogSourceFile src("call_me_not.sv",
"function int tt(int a, int b);\n"
" return 0;\n"
"endfunction\n"
"function int vv();\n"
" return tt(.a(1), .b(2));\n" // valid
"endfunction\n");
const auto status = src.Parse();
ASSERT_TRUE(status.ok()) << status.message();
SymbolTable symbol_table(nullptr);
const SymbolTableNode &root_symbol(symbol_table.Root());
const auto build_diagnostics = BuildSymbolTable(src, &symbol_table);
EXPECT_EMPTY_STATUSES(build_diagnostics);
MUST_ASSIGN_LOOKUP_SYMBOL(function_tt, root_symbol, "tt");
EXPECT_EQ(function_tt_info.metatype, SymbolMetaType::kFunction);
ASSERT_NE(function_tt_info.declared_type.syntax_origin,
nullptr); // returns int
MUST_ASSIGN_LOOKUP_SYMBOL(param_a, function_tt, "a");
EXPECT_EQ(param_a_info.metatype, SymbolMetaType::kDataNetVariableInstance);
ASSERT_NE(param_a_info.declared_type.syntax_origin, nullptr); // int a
MUST_ASSIGN_LOOKUP_SYMBOL(param_b, function_tt, "b");
EXPECT_EQ(param_b_info.metatype, SymbolMetaType::kDataNetVariableInstance);
ASSERT_NE(param_b_info.declared_type.syntax_origin, nullptr); // int b
MUST_ASSIGN_LOOKUP_SYMBOL(function_vv, root_symbol, "vv");
EXPECT_EQ(function_vv_info.metatype, SymbolMetaType::kFunction);
ASSERT_NE(function_vv_info.declared_type.syntax_origin, nullptr);
EXPECT_EQ(function_vv_info.local_references_to_bind.size(), 1);
const auto ref_map(function_vv_info.LocalReferencesMapViewForTesting());
ASSIGN_MUST_FIND_EXACTLY_ONE_REF(tt_ref, ref_map, "tt");
const ReferenceComponent &tt_ref_comp(tt_ref->components->Value());
EXPECT_EQ(tt_ref_comp.required_metatype, SymbolMetaType::kCallable);
EXPECT_EQ(tt_ref_comp.resolved_symbol, nullptr);
const ReferenceComponentMap param_refs(
ReferenceComponentNodeMapView(*tt_ref->components));
ASSIGN_MUST_FIND(a_ref, param_refs, "a");
const ReferenceComponent &a_ref_comp(a_ref->Value());
EXPECT_EQ(a_ref_comp.identifier, "a");
EXPECT_EQ(a_ref_comp.ref_type, ReferenceType::kDirectMember);
EXPECT_EQ(a_ref_comp.required_metatype,
SymbolMetaType::kDataNetVariableInstance);
EXPECT_EQ(a_ref_comp.resolved_symbol, nullptr); // not yet resolved
ASSIGN_MUST_FIND(b_ref, param_refs, "b");
const ReferenceComponent &b_ref_comp(b_ref->Value());
EXPECT_EQ(b_ref_comp.identifier, "b");
EXPECT_EQ(b_ref_comp.ref_type, ReferenceType::kDirectMember);
EXPECT_EQ(b_ref_comp.required_metatype,
SymbolMetaType::kDataNetVariableInstance);
EXPECT_EQ(b_ref_comp.resolved_symbol, nullptr); // not yet resolved
{
std::vector<absl::Status> resolve_diagnostics;
symbol_table.Resolve(&resolve_diagnostics);
EXPECT_EMPTY_STATUSES(resolve_diagnostics);
// Call to "tt" is resolved, along with its named parameters.
EXPECT_EQ(tt_ref_comp.resolved_symbol, &function_tt);
EXPECT_EQ(a_ref_comp.resolved_symbol, &param_a);
EXPECT_EQ(b_ref_comp.resolved_symbol, &param_b);
}
}
TEST_F(BuildSymbolTableTest, CallNonFunction) {
TestVerilogSourceFile src("call_me.sv",
"module tt();\n"
"endmodule\n"
"function int vv();\n"
" return tt();\n" // not a function
"endfunction\n");
const auto status = src.Parse();
ASSERT_TRUE(status.ok()) << status.message();
SymbolTable symbol_table(nullptr);
const SymbolTableNode &root_symbol(symbol_table.Root());
const auto build_diagnostics = BuildSymbolTable(src, &symbol_table);
EXPECT_EMPTY_STATUSES(build_diagnostics);
MUST_ASSIGN_LOOKUP_SYMBOL(module_tt, root_symbol, "tt");
MUST_ASSIGN_LOOKUP_SYMBOL(function_vv, root_symbol, "vv");
EXPECT_EQ(module_tt_info.metatype, SymbolMetaType::kModule);
EXPECT_EQ(module_tt_info.declared_type.syntax_origin, nullptr);
EXPECT_EQ(function_vv_info.metatype, SymbolMetaType::kFunction);
EXPECT_NE(function_vv_info.declared_type.syntax_origin, nullptr);
EXPECT_EQ(function_vv_info.local_references_to_bind.size(), 1);
const auto ref_map(function_vv_info.LocalReferencesMapViewForTesting());
ASSIGN_MUST_FIND_EXACTLY_ONE_REF(tt_ref, ref_map, "tt");
const ReferenceComponent &tt_ref_comp(tt_ref->components->Value());
EXPECT_EQ(tt_ref_comp.required_metatype, SymbolMetaType::kCallable);
EXPECT_EQ(tt_ref_comp.resolved_symbol, nullptr);
{
std::vector<absl::Status> resolve_diagnostics;
symbol_table.Resolve(&resolve_diagnostics);
ASSIGN_MUST_HAVE_UNIQUE(err, resolve_diagnostics);
EXPECT_EQ(err.code(), absl::StatusCode::kInvalidArgument);
EXPECT_THAT(err.message(),
HasSubstr("Expecting reference \"tt\" to resolve to a "
"<callable>, but found a module"));
EXPECT_EQ(tt_ref_comp.resolved_symbol, nullptr);
}
}
TEST_F(BuildSymbolTableTest, NestedCallsArguments) {
TestVerilogSourceFile src("call_me.sv",
"function int tt(int aa);\n"
" return aa + 1;\n"
"endfunction\n"
"function int vv();\n"
" return tt(tt(tt(2)));\n"
"endfunction\n");
const auto status = src.Parse();
ASSERT_TRUE(status.ok()) << status.message();
SymbolTable symbol_table(nullptr);
const SymbolTableNode &root_symbol(symbol_table.Root());
const auto build_diagnostics = BuildSymbolTable(src, &symbol_table);
EXPECT_EMPTY_STATUSES(build_diagnostics);
MUST_ASSIGN_LOOKUP_SYMBOL(function_tt, root_symbol, "tt");
MUST_ASSIGN_LOOKUP_SYMBOL(function_vv, root_symbol, "vv");
EXPECT_EQ(function_tt_info.metatype, SymbolMetaType::kFunction);
ASSERT_NE(function_tt_info.declared_type.syntax_origin, nullptr);
EXPECT_EQ(function_vv_info.metatype, SymbolMetaType::kFunction);
ASSERT_NE(function_vv_info.declared_type.syntax_origin, nullptr);
MUST_ASSIGN_LOOKUP_SYMBOL(arg_aa, function_tt, "aa");
EXPECT_EQ(arg_aa_info.metatype, SymbolMetaType::kDataNetVariableInstance);
EXPECT_EQ(function_tt_info.local_references_to_bind.size(), 1);
const auto tt_ref_map(function_tt_info.LocalReferencesMapViewForTesting());
ASSIGN_MUST_FIND_EXACTLY_ONE_REF(aa_ref, tt_ref_map, "aa");
const ReferenceComponent &aa_ref_comp(aa_ref->components->Value());
EXPECT_EQ(aa_ref_comp.required_metatype, SymbolMetaType::kUnspecified);
EXPECT_EQ(aa_ref_comp.resolved_symbol, nullptr);
// Expect 3 calls to "tt" from the same scope.
EXPECT_EQ(function_vv_info.local_references_to_bind.size(), 3);
const auto vv_ref_map(function_vv_info.LocalReferencesMapViewForTesting());
ASSIGN_MUST_FIND(tt_refs, vv_ref_map, "tt");
for (const auto &tt_ref : tt_refs) {
const ReferenceComponent &tt_ref_comp(tt_ref->components->Value());
EXPECT_EQ(tt_ref_comp.required_metatype, SymbolMetaType::kCallable);
EXPECT_EQ(tt_ref_comp.resolved_symbol, nullptr);
}
{
std::vector<absl::Status> resolve_diagnostics;
symbol_table.Resolve(&resolve_diagnostics);
EXPECT_EMPTY_STATUSES(resolve_diagnostics);
EXPECT_EQ(aa_ref_comp.resolved_symbol, &arg_aa);
// Calls to "tt" are all resolved.
for (const auto &tt_ref : tt_refs) {
const ReferenceComponent &tt_ref_comp(tt_ref->components->Value());
EXPECT_EQ(tt_ref_comp.resolved_symbol, &function_tt);
}
}
}
TEST_F(BuildSymbolTableTest, SelfRecursion) {
TestVerilogSourceFile src("call_me_from_me.sv",
"function int tt();\n"
" return 1 - tt();\n"
"endfunction\n");
const auto status = src.Parse();
ASSERT_TRUE(status.ok()) << status.message();
SymbolTable symbol_table(nullptr);
const SymbolTableNode &root_symbol(symbol_table.Root());
const auto build_diagnostics = BuildSymbolTable(src, &symbol_table);
EXPECT_EMPTY_STATUSES(build_diagnostics);
MUST_ASSIGN_LOOKUP_SYMBOL(function_tt, root_symbol, "tt");
EXPECT_EQ(function_tt_info.metatype, SymbolMetaType::kFunction);
ASSERT_NE(function_tt_info.declared_type.syntax_origin, nullptr);
EXPECT_EQ(function_tt_info.local_references_to_bind.size(), 1);
const auto tt_ref_map(function_tt_info.LocalReferencesMapViewForTesting());
ASSIGN_MUST_FIND_EXACTLY_ONE_REF(tt_ref, tt_ref_map, "tt");
const ReferenceComponent &tt_ref_comp(tt_ref->components->Value());
EXPECT_EQ(tt_ref_comp.required_metatype, SymbolMetaType::kCallable);
EXPECT_EQ(tt_ref_comp.resolved_symbol, nullptr);
{
std::vector<absl::Status> resolve_diagnostics;
symbol_table.Resolve(&resolve_diagnostics);
EXPECT_EMPTY_STATUSES(resolve_diagnostics);
// Call to "tt" (recursive) is resolved.
EXPECT_EQ(tt_ref_comp.resolved_symbol, &function_tt);
}
}
TEST_F(BuildSymbolTableTest, MutualRecursion) {
TestVerilogSourceFile src("call_me_back.sv",
"function int tt();\n"
" return vv();\n"
"endfunction\n"
"function int vv();\n"
" return tt();\n"
"endfunction\n");
const auto status = src.Parse();
ASSERT_TRUE(status.ok()) << status.message();
SymbolTable symbol_table(nullptr);
const SymbolTableNode &root_symbol(symbol_table.Root());
const auto build_diagnostics = BuildSymbolTable(src, &symbol_table);
EXPECT_EMPTY_STATUSES(build_diagnostics);
MUST_ASSIGN_LOOKUP_SYMBOL(function_tt, root_symbol, "tt");
MUST_ASSIGN_LOOKUP_SYMBOL(function_vv, root_symbol, "vv");
EXPECT_EQ(function_tt_info.metatype, SymbolMetaType::kFunction);
ASSERT_NE(function_tt_info.declared_type.syntax_origin, nullptr);
EXPECT_EQ(function_vv_info.metatype, SymbolMetaType::kFunction);
ASSERT_NE(function_vv_info.declared_type.syntax_origin, nullptr);
EXPECT_EQ(function_tt_info.local_references_to_bind.size(), 1);
const auto tt_ref_map(function_tt_info.LocalReferencesMapViewForTesting());
ASSIGN_MUST_FIND_EXACTLY_ONE_REF(vv_ref, tt_ref_map, "vv");
const ReferenceComponent &vv_ref_comp(vv_ref->components->Value());
EXPECT_EQ(vv_ref_comp.required_metatype, SymbolMetaType::kCallable);
EXPECT_EQ(vv_ref_comp.resolved_symbol, nullptr);
EXPECT_EQ(function_vv_info.local_references_to_bind.size(), 1);
const auto vv_ref_map(function_vv_info.LocalReferencesMapViewForTesting());
ASSIGN_MUST_FIND_EXACTLY_ONE_REF(tt_ref, vv_ref_map, "tt");
const ReferenceComponent &tt_ref_comp(tt_ref->components->Value());
EXPECT_EQ(tt_ref_comp.required_metatype, SymbolMetaType::kCallable);
EXPECT_EQ(tt_ref_comp.resolved_symbol, nullptr);
{
std::vector<absl::Status> resolve_diagnostics;
symbol_table.Resolve(&resolve_diagnostics);
EXPECT_EMPTY_STATUSES(resolve_diagnostics);
// Calls to "tt" and "vv" are all resolved.
EXPECT_EQ(vv_ref_comp.resolved_symbol, &function_vv);
EXPECT_EQ(tt_ref_comp.resolved_symbol, &function_tt);
}
}
TEST_F(BuildSymbolTableTest, PackageQualifiedFunctionCall) {
TestVerilogSourceFile src("call_me.sv",
"package pp;\n"
" function int tt();\n"
" return 1;\n"
" endfunction\n"
"endpackage\n"
"function int vv();\n"
" return pp::tt();\n"
"endfunction\n");
const auto status = src.Parse();
ASSERT_TRUE(status.ok()) << status.message();
SymbolTable symbol_table(nullptr);
const SymbolTableNode &root_symbol(symbol_table.Root());
const auto build_diagnostics = BuildSymbolTable(src, &symbol_table);
EXPECT_EMPTY_STATUSES(build_diagnostics);
MUST_ASSIGN_LOOKUP_SYMBOL(package_pp, root_symbol, "pp");
MUST_ASSIGN_LOOKUP_SYMBOL(function_tt, package_pp, "tt");
EXPECT_EQ(package_pp_info.metatype, SymbolMetaType::kPackage);
MUST_ASSIGN_LOOKUP_SYMBOL(function_vv, root_symbol, "vv");
EXPECT_EQ(function_tt_info.metatype, SymbolMetaType::kFunction);
ASSERT_NE(function_tt_info.declared_type.syntax_origin, nullptr);
EXPECT_EQ(function_vv_info.metatype, SymbolMetaType::kFunction);
ASSERT_NE(function_vv_info.declared_type.syntax_origin, nullptr);
EXPECT_EQ(function_vv_info.local_references_to_bind.size(), 1);
const auto ref_map(function_vv_info.LocalReferencesMapViewForTesting());
ASSIGN_MUST_FIND_EXACTLY_ONE_REF(pp_ref, ref_map, "pp");
ASSERT_EQ(pp_ref->components->Children().size(), 1);
const ReferenceComponent &pp_ref_comp(pp_ref->components->Value());
EXPECT_EQ(pp_ref_comp.ref_type, ReferenceType::kUnqualified);
EXPECT_EQ(pp_ref_comp.required_metatype, SymbolMetaType::kUnspecified);
EXPECT_EQ(pp_ref_comp.resolved_symbol, nullptr);
const ReferenceComponent &tt_ref_comp(
pp_ref->components->Children().front().Value());
EXPECT_EQ(tt_ref_comp.identifier, "tt");
EXPECT_EQ(tt_ref_comp.ref_type, ReferenceType::kDirectMember);
EXPECT_EQ(tt_ref_comp.required_metatype, SymbolMetaType::kCallable);
EXPECT_EQ(tt_ref_comp.resolved_symbol, nullptr);
{
std::vector<absl::Status> resolve_diagnostics;
symbol_table.Resolve(&resolve_diagnostics);
EXPECT_EMPTY_STATUSES(resolve_diagnostics);
// Call to "tt" is resolved.
EXPECT_EQ(pp_ref_comp.resolved_symbol, &package_pp);
EXPECT_EQ(tt_ref_comp.resolved_symbol, &function_tt);
}
}
TEST_F(BuildSymbolTableTest, ClassQualifiedFunctionCall) {
TestVerilogSourceFile src("call_me.sv",
"class cc;\n"
" function static int tt();\n"
" return 1;\n"
" endfunction\n"
"endclass\n"
"function int vv();\n"
" return cc::tt();\n"
"endfunction\n");
const auto status = src.Parse();
ASSERT_TRUE(status.ok()) << status.message();
SymbolTable symbol_table(nullptr);
const SymbolTableNode &root_symbol(symbol_table.Root());
const auto build_diagnostics = BuildSymbolTable(src, &symbol_table);
EXPECT_EMPTY_STATUSES(build_diagnostics);
MUST_ASSIGN_LOOKUP_SYMBOL(class_cc, root_symbol, "cc");
MUST_ASSIGN_LOOKUP_SYMBOL(function_tt, class_cc, "tt");
EXPECT_EQ(class_cc_info.metatype, SymbolMetaType::kClass);
MUST_ASSIGN_LOOKUP_SYMBOL(function_vv, root_symbol, "vv");
EXPECT_EQ(function_tt_info.metatype, SymbolMetaType::kFunction);
ASSERT_NE(function_tt_info.declared_type.syntax_origin, nullptr);
EXPECT_EQ(function_vv_info.metatype, SymbolMetaType::kFunction);
ASSERT_NE(function_vv_info.declared_type.syntax_origin, nullptr);
EXPECT_EQ(function_vv_info.local_references_to_bind.size(), 1);
const auto ref_map(function_vv_info.LocalReferencesMapViewForTesting());
ASSIGN_MUST_FIND_EXACTLY_ONE_REF(cc_ref, ref_map, "cc");
ASSERT_EQ(cc_ref->components->Children().size(), 1);
const ReferenceComponent &cc_ref_comp(cc_ref->components->Value());
EXPECT_EQ(cc_ref_comp.ref_type, ReferenceType::kUnqualified);
EXPECT_EQ(cc_ref_comp.required_metatype, SymbolMetaType::kUnspecified);
EXPECT_EQ(cc_ref_comp.resolved_symbol, nullptr);
const ReferenceComponent &tt_ref_comp(
cc_ref->components->Children().front().Value());
EXPECT_EQ(tt_ref_comp.identifier, "tt");
EXPECT_EQ(tt_ref_comp.ref_type, ReferenceType::kDirectMember);
EXPECT_EQ(tt_ref_comp.required_metatype, SymbolMetaType::kCallable);
EXPECT_EQ(tt_ref_comp.resolved_symbol, nullptr);
{
std::vector<absl::Status> resolve_diagnostics;
symbol_table.Resolve(&resolve_diagnostics);
EXPECT_EMPTY_STATUSES(resolve_diagnostics);
// Call to "tt" is resolved.
EXPECT_EQ(cc_ref_comp.resolved_symbol, &class_cc);
EXPECT_EQ(tt_ref_comp.resolved_symbol, &function_tt);
}
}
TEST_F(BuildSymbolTableTest, ClassQualifiedFunctionCallUnresolved) {
TestVerilogSourceFile src("call_me.sv",
"class cc;\n"
"endclass\n"
"function int vv();\n"
" return cc::tt();\n"
"endfunction\n");
const auto status = src.Parse();
ASSERT_TRUE(status.ok()) << status.message();
SymbolTable symbol_table(nullptr);
const SymbolTableNode &root_symbol(symbol_table.Root());
const auto build_diagnostics = BuildSymbolTable(src, &symbol_table);
EXPECT_EMPTY_STATUSES(build_diagnostics);
MUST_ASSIGN_LOOKUP_SYMBOL(class_cc, root_symbol, "cc");
EXPECT_EQ(class_cc_info.metatype, SymbolMetaType::kClass);
MUST_ASSIGN_LOOKUP_SYMBOL(function_vv, root_symbol, "vv");
EXPECT_EQ(function_vv_info.metatype, SymbolMetaType::kFunction);
ASSERT_NE(function_vv_info.declared_type.syntax_origin, nullptr);
EXPECT_EQ(function_vv_info.local_references_to_bind.size(), 1);
const auto ref_map(function_vv_info.LocalReferencesMapViewForTesting());
ASSIGN_MUST_FIND_EXACTLY_ONE_REF(cc_ref, ref_map, "cc");
ASSERT_EQ(cc_ref->components->Children().size(), 1);
const ReferenceComponent &cc_ref_comp(cc_ref->components->Value());
EXPECT_EQ(cc_ref_comp.ref_type, ReferenceType::kUnqualified);
EXPECT_EQ(cc_ref_comp.required_metatype, SymbolMetaType::kUnspecified);
EXPECT_EQ(cc_ref_comp.resolved_symbol, nullptr);
const ReferenceComponent &tt_ref_comp(
cc_ref->components->Children().front().Value());
EXPECT_EQ(tt_ref_comp.identifier, "tt");
EXPECT_EQ(tt_ref_comp.ref_type, ReferenceType::kDirectMember);
EXPECT_EQ(tt_ref_comp.required_metatype, SymbolMetaType::kCallable);
EXPECT_EQ(tt_ref_comp.resolved_symbol, nullptr);
{
std::vector<absl::Status> resolve_diagnostics;
symbol_table.Resolve(&resolve_diagnostics);
ASSIGN_MUST_HAVE_UNIQUE(err, resolve_diagnostics);
EXPECT_EQ(err.code(), absl::StatusCode::kNotFound);
EXPECT_EQ(cc_ref_comp.resolved_symbol, &class_cc);
EXPECT_EQ(tt_ref_comp.resolved_symbol, nullptr); // error
}
}
TEST_F(BuildSymbolTableTest, ClassMethodCall) {
TestVerilogSourceFile src("call_me.sv",
"class cc;\n"
" function int tt();\n"
" return 1;\n"
" endfunction\n"
"endclass\n"
"function int vv();\n"
" cc cc_obj;\n"
" return cc_obj.tt();\n"
"endfunction\n");
const auto status = src.Parse();
ASSERT_TRUE(status.ok()) << status.message();
SymbolTable symbol_table(nullptr);
const SymbolTableNode &root_symbol(symbol_table.Root());
const auto build_diagnostics = BuildSymbolTable(src, &symbol_table);
EXPECT_EMPTY_STATUSES(build_diagnostics);
MUST_ASSIGN_LOOKUP_SYMBOL(class_cc, root_symbol, "cc");
MUST_ASSIGN_LOOKUP_SYMBOL(function_tt, class_cc, "tt");
EXPECT_EQ(class_cc_info.metatype, SymbolMetaType::kClass);
MUST_ASSIGN_LOOKUP_SYMBOL(function_vv, root_symbol, "vv");
EXPECT_EQ(function_tt_info.metatype, SymbolMetaType::kFunction);
ASSERT_NE(function_tt_info.declared_type.syntax_origin, nullptr);
EXPECT_EQ(function_vv_info.metatype, SymbolMetaType::kFunction);
ASSERT_NE(function_vv_info.declared_type.syntax_origin, nullptr);
MUST_ASSIGN_LOOKUP_SYMBOL(cc_obj, function_vv, "cc_obj");
EXPECT_EQ(cc_obj_info.metatype, SymbolMetaType::kDataNetVariableInstance);
EXPECT_EQ(function_vv_info.local_references_to_bind.size(), 2);
const auto ref_map(function_vv_info.LocalReferencesMapViewForTesting());
ASSIGN_MUST_FIND_EXACTLY_ONE_REF(cc_type_ref, ref_map,
"cc"); // "cc" is a type
const ReferenceComponent &cc_type_ref_comp(cc_type_ref->components->Value());
ASSIGN_MUST_FIND_EXACTLY_ONE_REF(cc_obj_ref, ref_map,
"cc_obj"); // "cc_obj" is data
ASSERT_EQ(cc_obj_ref->components->Children().size(), 1);
const ReferenceComponent &cc_obj_ref_comp(cc_obj_ref->components->Value());
EXPECT_EQ(cc_obj_ref_comp.ref_type, ReferenceType::kUnqualified);
EXPECT_EQ(cc_obj_ref_comp.required_metatype, SymbolMetaType::kUnspecified);
EXPECT_EQ(cc_obj_ref_comp.resolved_symbol, nullptr);
const ReferenceComponent &tt_ref_comp(
cc_obj_ref->components->Children().front().Value());
EXPECT_EQ(tt_ref_comp.identifier, "tt");
EXPECT_EQ(tt_ref_comp.ref_type, ReferenceType::kMemberOfTypeOfParent);
EXPECT_EQ(tt_ref_comp.required_metatype, SymbolMetaType::kCallable);
EXPECT_EQ(tt_ref_comp.resolved_symbol, nullptr);
{
std::vector<absl::Status> resolve_diagnostics;
symbol_table.Resolve(&resolve_diagnostics);
EXPECT_EMPTY_STATUSES(resolve_diagnostics);
// Call to ".tt" is resolved.
EXPECT_EQ(cc_type_ref_comp.resolved_symbol, &class_cc);
EXPECT_EQ(cc_obj_ref_comp.resolved_symbol, &cc_obj);
EXPECT_EQ(tt_ref_comp.resolved_symbol, &function_tt);
}
}
TEST_F(BuildSymbolTableTest, ClassMethodCallUnresolved) {
TestVerilogSourceFile src("call_me.sv",
"class cc;\n"
"endclass\n"
"function int vv();\n"
" cc cc_obj;\n"
" return cc_obj.tt();\n"
"endfunction\n");
const auto status = src.Parse();
ASSERT_TRUE(status.ok()) << status.message();
SymbolTable symbol_table(nullptr);
const SymbolTableNode &root_symbol(symbol_table.Root());
const auto build_diagnostics = BuildSymbolTable(src, &symbol_table);
EXPECT_EMPTY_STATUSES(build_diagnostics);
MUST_ASSIGN_LOOKUP_SYMBOL(class_cc, root_symbol, "cc");
EXPECT_EQ(class_cc_info.metatype, SymbolMetaType::kClass);
MUST_ASSIGN_LOOKUP_SYMBOL(function_vv, root_symbol, "vv");
EXPECT_EQ(function_vv_info.metatype, SymbolMetaType::kFunction);
ASSERT_NE(function_vv_info.declared_type.syntax_origin, nullptr);
MUST_ASSIGN_LOOKUP_SYMBOL(cc_obj, function_vv, "cc_obj");
EXPECT_EQ(cc_obj_info.metatype, SymbolMetaType::kDataNetVariableInstance);
EXPECT_EQ(function_vv_info.local_references_to_bind.size(), 2);
const auto ref_map(function_vv_info.LocalReferencesMapViewForTesting());
ASSIGN_MUST_FIND_EXACTLY_ONE_REF(cc_type_ref, ref_map,
"cc"); // "cc" is a type
const ReferenceComponent &cc_type_ref_comp(cc_type_ref->components->Value());
ASSIGN_MUST_FIND_EXACTLY_ONE_REF(cc_obj_ref, ref_map,
"cc_obj"); // "cc_obj" is data
ASSERT_EQ(cc_obj_ref->components->Children().size(), 1);
const ReferenceComponent &cc_obj_ref_comp(cc_obj_ref->components->Value());
EXPECT_EQ(cc_obj_ref_comp.ref_type, ReferenceType::kUnqualified);
EXPECT_EQ(cc_obj_ref_comp.required_metatype, SymbolMetaType::kUnspecified);
EXPECT_EQ(cc_obj_ref_comp.resolved_symbol, nullptr);
const ReferenceComponent &tt_ref_comp(
cc_obj_ref->components->Children().front().Value());
EXPECT_EQ(tt_ref_comp.identifier, "tt");
EXPECT_EQ(tt_ref_comp.ref_type, ReferenceType::kMemberOfTypeOfParent);
EXPECT_EQ(tt_ref_comp.required_metatype, SymbolMetaType::kCallable);
EXPECT_EQ(tt_ref_comp.resolved_symbol, nullptr);
{
std::vector<absl::Status> resolve_diagnostics;
symbol_table.Resolve(&resolve_diagnostics);
ASSIGN_MUST_HAVE_UNIQUE(err, resolve_diagnostics);
EXPECT_EQ(err.code(), absl::StatusCode::kNotFound);
EXPECT_EQ(cc_type_ref_comp.resolved_symbol, &class_cc);
EXPECT_EQ(cc_obj_ref_comp.resolved_symbol, &cc_obj);
EXPECT_EQ(tt_ref_comp.resolved_symbol, nullptr); // unresolved
}
}
TEST_F(BuildSymbolTableTest, ChainedMethodCall) {
TestVerilogSourceFile src("call_me.sv",
"class cc;\n"
" function dd tt();\n"
" endfunction\n"
"endclass\n"
"class dd;\n"
" function cc gg();\n"
" endfunction\n"
"endclass\n"
"function dd vv();\n"
" dd dd_obj;\n"
" return dd_obj.gg().tt();\n"
// .gg() -> cc
// .tt() -> dd
"endfunction\n");
const auto status = src.Parse();
ASSERT_TRUE(status.ok()) << status.message();
SymbolTable symbol_table(nullptr);
const SymbolTableNode &root_symbol(symbol_table.Root());
const auto build_diagnostics = BuildSymbolTable(src, &symbol_table);
EXPECT_EMPTY_STATUSES(build_diagnostics);
MUST_ASSIGN_LOOKUP_SYMBOL(class_cc, root_symbol, "cc");
EXPECT_EQ(class_cc_info.metatype, SymbolMetaType::kClass);
MUST_ASSIGN_LOOKUP_SYMBOL(class_dd, root_symbol, "dd");
EXPECT_EQ(class_dd_info.metatype, SymbolMetaType::kClass);
MUST_ASSIGN_LOOKUP_SYMBOL(function_tt, class_cc, "tt");
EXPECT_EQ(function_tt_info.metatype, SymbolMetaType::kFunction);
ASSERT_NE(function_tt_info.declared_type.syntax_origin, nullptr);
ASSERT_NE(function_tt_info.declared_type.user_defined_type, nullptr);
MUST_ASSIGN_LOOKUP_SYMBOL(function_gg, class_dd, "gg");
EXPECT_EQ(function_gg_info.metatype, SymbolMetaType::kFunction);
ASSERT_NE(function_gg_info.declared_type.syntax_origin, nullptr);
ASSERT_NE(function_gg_info.declared_type.user_defined_type, nullptr);
MUST_ASSIGN_LOOKUP_SYMBOL(function_vv, root_symbol, "vv");
EXPECT_EQ(function_vv_info.metatype, SymbolMetaType::kFunction);
ASSERT_NE(function_vv_info.declared_type.syntax_origin, nullptr);
MUST_ASSIGN_LOOKUP_SYMBOL(dd_obj, function_vv, "dd_obj");
EXPECT_EQ(dd_obj_info.metatype, SymbolMetaType::kDataNetVariableInstance);
EXPECT_EQ(function_vv_info.local_references_to_bind.size(), 2);
const auto ref_map(function_vv_info.LocalReferencesMapViewForTesting());
ASSIGN_MUST_FIND_EXACTLY_ONE_REF(dd_type_ref, ref_map,
"dd"); // "dd" is a type
const ReferenceComponent &dd_type_ref_comp(dd_type_ref->components->Value());
// Examine the dd_obj.gg().tt() reference chain.
ASSIGN_MUST_FIND_EXACTLY_ONE_REF(dd_obj_ref, ref_map,
"dd_obj"); // "dd_obj" is data
ASSERT_EQ(dd_obj_ref->components->Children().size(), 1);
const ReferenceComponent &dd_obj_ref_comp(dd_obj_ref->components->Value());
EXPECT_EQ(dd_obj_ref_comp.ref_type, ReferenceType::kUnqualified);
EXPECT_EQ(dd_obj_ref_comp.required_metatype, SymbolMetaType::kUnspecified);
EXPECT_EQ(dd_obj_ref_comp.resolved_symbol, nullptr);
const ReferenceComponentNode &dd_gg_ref(
dd_obj_ref->components->Children().front());
const ReferenceComponent &dd_gg_ref_comp(dd_gg_ref.Value());
EXPECT_EQ(dd_gg_ref_comp.identifier, "gg");
EXPECT_EQ(dd_gg_ref_comp.ref_type, ReferenceType::kMemberOfTypeOfParent);
EXPECT_EQ(dd_gg_ref_comp.required_metatype, SymbolMetaType::kCallable);
EXPECT_EQ(dd_gg_ref_comp.resolved_symbol, nullptr);
const ReferenceComponentNode &dd_gg_tt_ref(dd_gg_ref.Children().front());
const ReferenceComponent &dd_gg_tt_ref_comp(dd_gg_tt_ref.Value());
EXPECT_EQ(dd_gg_tt_ref_comp.identifier, "tt");
EXPECT_EQ(dd_gg_tt_ref_comp.ref_type, ReferenceType::kMemberOfTypeOfParent);
EXPECT_EQ(dd_gg_tt_ref_comp.required_metatype, SymbolMetaType::kCallable);
EXPECT_EQ(dd_gg_tt_ref_comp.resolved_symbol, nullptr);
{
std::vector<absl::Status> resolve_diagnostics;
symbol_table.Resolve(&resolve_diagnostics);
EXPECT_EMPTY_STATUSES(resolve_diagnostics);
// Return types of methods are resolved.
EXPECT_EQ(function_tt_info.declared_type.user_defined_type->Value()
.resolved_symbol,
&class_dd);
EXPECT_EQ(function_gg_info.declared_type.user_defined_type->Value()
.resolved_symbol,
&class_cc);
// Chained call is resolved.
EXPECT_EQ(dd_type_ref_comp.resolved_symbol, &class_dd);
EXPECT_EQ(dd_obj_ref_comp.resolved_symbol, &dd_obj);
EXPECT_EQ(dd_gg_ref_comp.resolved_symbol, &function_gg);
EXPECT_EQ(dd_gg_tt_ref_comp.resolved_symbol, &function_tt);
}
}
TEST_F(BuildSymbolTableTest, ChainedMethodCallReturnTypeNotAClass) {
TestVerilogSourceFile src(
"call_me.sv",
"class cc;\n"
" function dd tt();\n"
" endfunction\n"
"endclass\n"
"class dd;\n"
" function int gg();\n" // return type is a primitive
" endfunction\n"
"endclass\n"
"function dd vv();\n"
" dd dd_obj;\n"
" return dd_obj.gg().tt();\n"
// .gg() -> cc
// .tt() -> <error>
"endfunction\n");
const auto status = src.Parse();
ASSERT_TRUE(status.ok()) << status.message();
SymbolTable symbol_table(nullptr);
const SymbolTableNode &root_symbol(symbol_table.Root());
const auto build_diagnostics = BuildSymbolTable(src, &symbol_table);
EXPECT_EMPTY_STATUSES(build_diagnostics);
MUST_ASSIGN_LOOKUP_SYMBOL(class_cc, root_symbol, "cc");
EXPECT_EQ(class_cc_info.metatype, SymbolMetaType::kClass);
MUST_ASSIGN_LOOKUP_SYMBOL(class_dd, root_symbol, "dd");
EXPECT_EQ(class_dd_info.metatype, SymbolMetaType::kClass);
MUST_ASSIGN_LOOKUP_SYMBOL(function_tt, class_cc, "tt");
EXPECT_EQ(function_tt_info.metatype, SymbolMetaType::kFunction);
ASSERT_NE(function_tt_info.declared_type.syntax_origin, nullptr);
ASSERT_NE(function_tt_info.declared_type.user_defined_type, nullptr);
MUST_ASSIGN_LOOKUP_SYMBOL(function_gg, class_dd, "gg");
EXPECT_EQ(function_gg_info.metatype, SymbolMetaType::kFunction);
ASSERT_NE(function_gg_info.declared_type.syntax_origin, nullptr);
EXPECT_EQ(function_gg_info.declared_type.user_defined_type, nullptr);
MUST_ASSIGN_LOOKUP_SYMBOL(function_vv, root_symbol, "vv");
EXPECT_EQ(function_vv_info.metatype, SymbolMetaType::kFunction);
ASSERT_NE(function_vv_info.declared_type.syntax_origin, nullptr);
MUST_ASSIGN_LOOKUP_SYMBOL(dd_obj, function_vv, "dd_obj");
EXPECT_EQ(dd_obj_info.metatype, SymbolMetaType::kDataNetVariableInstance);
EXPECT_EQ(function_vv_info.local_references_to_bind.size(), 2);
const auto ref_map(function_vv_info.LocalReferencesMapViewForTesting());
ASSIGN_MUST_FIND_EXACTLY_ONE_REF(dd_type_ref, ref_map,
"dd"); // "dd" is a type
const ReferenceComponent &dd_type_ref_comp(dd_type_ref->components->Value());
// Examine the dd_obj.gg().tt() reference chain.
ASSIGN_MUST_FIND_EXACTLY_ONE_REF(dd_obj_ref, ref_map,
"dd_obj"); // "dd_obj" is data
ASSERT_EQ(dd_obj_ref->components->Children().size(), 1);
const ReferenceComponent &dd_obj_ref_comp(dd_obj_ref->components->Value());
EXPECT_EQ(dd_obj_ref_comp.ref_type, ReferenceType::kUnqualified);
EXPECT_EQ(dd_obj_ref_comp.required_metatype, SymbolMetaType::kUnspecified);
EXPECT_EQ(dd_obj_ref_comp.resolved_symbol, nullptr);
const ReferenceComponentNode &dd_gg_ref(
dd_obj_ref->components->Children().front());
const ReferenceComponent &dd_gg_ref_comp(dd_gg_ref.Value());
EXPECT_EQ(dd_gg_ref_comp.identifier, "gg");
EXPECT_EQ(dd_gg_ref_comp.ref_type, ReferenceType::kMemberOfTypeOfParent);
EXPECT_EQ(dd_gg_ref_comp.required_metatype, SymbolMetaType::kCallable);
EXPECT_EQ(dd_gg_ref_comp.resolved_symbol, nullptr);
const ReferenceComponentNode &dd_gg_tt_ref(dd_gg_ref.Children().front());
const ReferenceComponent &dd_gg_tt_ref_comp(dd_gg_tt_ref.Value());
EXPECT_EQ(dd_gg_tt_ref_comp.identifier, "tt");
EXPECT_EQ(dd_gg_tt_ref_comp.ref_type, ReferenceType::kMemberOfTypeOfParent);
EXPECT_EQ(dd_gg_tt_ref_comp.required_metatype, SymbolMetaType::kCallable);
EXPECT_EQ(dd_gg_tt_ref_comp.resolved_symbol, nullptr);
{
std::vector<absl::Status> resolve_diagnostics;
symbol_table.Resolve(&resolve_diagnostics);
ASSIGN_MUST_HAVE_UNIQUE(err, resolve_diagnostics);
EXPECT_EQ(err.code(), absl::StatusCode::kInvalidArgument);
EXPECT_THAT(err.message(), HasSubstr("Type of parent reference"));
// reference text in diagnostic looks like: "@dd_obj.gg[<callable>]"
EXPECT_THAT(err.message(), HasSubstr("(int) does not have any members"));
// Return types of methods are resolved (where non-primitive).
EXPECT_EQ(function_tt_info.declared_type.user_defined_type->Value()
.resolved_symbol,
&class_dd);
// Chained call is partially resolved.
EXPECT_EQ(dd_type_ref_comp.resolved_symbol, &class_dd);
EXPECT_EQ(dd_obj_ref_comp.resolved_symbol, &dd_obj);
EXPECT_EQ(dd_gg_ref_comp.resolved_symbol, &function_gg);
EXPECT_EQ(dd_gg_tt_ref_comp.resolved_symbol, nullptr); // failed to resolve
}
}
TEST_F(BuildSymbolTableTest, AnonymousStructTypeData) {
TestVerilogSourceFile src("structy.sv",
"struct {\n"
" int size;\n"
"} data;\n");
const auto status = src.Parse();
ASSERT_TRUE(status.ok()) << status.message();
SymbolTable symbol_table(nullptr);
const SymbolTableNode &root_symbol(symbol_table.Root());
const auto build_diagnostics = BuildSymbolTable(src, &symbol_table);
EXPECT_EMPTY_STATUSES(build_diagnostics);
// Expect one anonymous struct definition and reference.
EXPECT_EQ(root_symbol.Value().anonymous_scope_names.size(), 1);
ASSERT_EQ(root_symbol.Children().size(), 2);
// Find the symbol that is a struct (anon), which is not "data".
const auto found =
std::find_if(root_symbol.Children().begin(), root_symbol.Children().end(),
[](const SymbolTableNode::key_value_type &p) {
return p.first != "data";
});
ASSERT_NE(found, root_symbol.Children().end());
const SymbolTableNode &anon_struct(found->second);
const SymbolInfo &anon_struct_info(anon_struct.Value());
EXPECT_EQ(anon_struct_info.metatype, SymbolMetaType::kStruct);
EXPECT_TRUE(anon_struct_info.local_references_to_bind.empty());
// Struct has one member.
MUST_ASSIGN_LOOKUP_SYMBOL(int_size, anon_struct, "size");
EXPECT_EQ(int_size_info.metatype, SymbolMetaType::kDataNetVariableInstance);
EXPECT_EQ(int_size_info.file_origin, &src);
EXPECT_EQ(
verible::StringSpanOfSymbol(*int_size_info.declared_type.syntax_origin),
"int");
EXPECT_EQ(int_size_info.declared_type.user_defined_type, nullptr);
// Expect to bind anonymous struct immediately.
ASSERT_EQ(root_symbol.Value().local_references_to_bind.size(), 1);
const DependentReferences &anon_struct_ref(
root_symbol.Value().local_references_to_bind.front());
const ReferenceComponent &anon_struct_ref_comp(
anon_struct_ref.components->Value());
EXPECT_EQ(anon_struct_ref_comp.ref_type, ReferenceType::kImmediate);
EXPECT_EQ(anon_struct_ref_comp.required_metatype, SymbolMetaType::kStruct);
EXPECT_EQ(anon_struct_ref_comp.resolved_symbol, &anon_struct);
// "data"'s type is the (internal) anonymous struct type reference.
MUST_ASSIGN_LOOKUP_SYMBOL(data, root_symbol, "data");
EXPECT_EQ(data_info.metatype, SymbolMetaType::kDataNetVariableInstance);
EXPECT_EQ(data_info.file_origin, &src);
EXPECT_EQ(data_info.declared_type.user_defined_type,
anon_struct_ref.LastLeaf());
{
std::vector<absl::Status> resolve_diagnostics;
symbol_table.Resolve(&resolve_diagnostics);
EXPECT_EMPTY_STATUSES(resolve_diagnostics);
EXPECT_EQ(anon_struct_ref_comp.resolved_symbol, &anon_struct); // unchanged
}
}
TEST_F(BuildSymbolTableTest, AnonymousStructTypeDataMultiFields) {
TestVerilogSourceFile src("structy.sv",
"struct {\n"
" int size;\n"
" real weight;\n"
"} data;\n");
const auto status = src.Parse();
ASSERT_TRUE(status.ok()) << status.message();
SymbolTable symbol_table(nullptr);
const SymbolTableNode &root_symbol(symbol_table.Root());
const auto build_diagnostics = BuildSymbolTable(src, &symbol_table);
EXPECT_EMPTY_STATUSES(build_diagnostics);
// Expect one anonymous struct definition and reference.
EXPECT_EQ(root_symbol.Value().anonymous_scope_names.size(), 1);
ASSERT_EQ(root_symbol.Children().size(), 2);
// Find the symbol that is a struct (anon), which is not "data".
const auto found =
std::find_if(root_symbol.Children().begin(), root_symbol.Children().end(),
[](const SymbolTableNode::key_value_type &p) {
return p.first != "data";
});
ASSERT_NE(found, root_symbol.Children().end());
const SymbolTableNode &anon_struct(found->second);
const SymbolInfo &anon_struct_info(anon_struct.Value());
EXPECT_EQ(anon_struct_info.metatype, SymbolMetaType::kStruct);
EXPECT_TRUE(anon_struct_info.local_references_to_bind.empty());
// Struct has two members.
MUST_ASSIGN_LOOKUP_SYMBOL(int_size, anon_struct, "size");
EXPECT_EQ(int_size_info.metatype, SymbolMetaType::kDataNetVariableInstance);
EXPECT_EQ(int_size_info.file_origin, &src);
EXPECT_EQ(
verible::StringSpanOfSymbol(*int_size_info.declared_type.syntax_origin),
"int");
EXPECT_EQ(int_size_info.declared_type.user_defined_type, nullptr);
MUST_ASSIGN_LOOKUP_SYMBOL(int_weight, anon_struct, "weight");
EXPECT_EQ(int_weight_info.metatype, SymbolMetaType::kDataNetVariableInstance);
EXPECT_EQ(int_weight_info.file_origin, &src);
EXPECT_EQ(
verible::StringSpanOfSymbol(*int_weight_info.declared_type.syntax_origin),
"real");
EXPECT_EQ(int_weight_info.declared_type.user_defined_type, nullptr);
// Expect to bind anonymous struct immediately.
ASSERT_EQ(root_symbol.Value().local_references_to_bind.size(), 1);
const DependentReferences &anon_struct_ref(
root_symbol.Value().local_references_to_bind.front());
const ReferenceComponent &anon_struct_ref_comp(
anon_struct_ref.components->Value());
EXPECT_EQ(anon_struct_ref_comp.ref_type, ReferenceType::kImmediate);
EXPECT_EQ(anon_struct_ref_comp.required_metatype, SymbolMetaType::kStruct);
EXPECT_EQ(anon_struct_ref_comp.resolved_symbol, &anon_struct);
// "data"'s type is the (internal) anonymous struct type reference.
MUST_ASSIGN_LOOKUP_SYMBOL(data, root_symbol, "data");
EXPECT_EQ(data_info.metatype, SymbolMetaType::kDataNetVariableInstance);
EXPECT_EQ(data_info.file_origin, &src);
EXPECT_EQ(data_info.declared_type.user_defined_type,
anon_struct_ref.LastLeaf());
{
std::vector<absl::Status> resolve_diagnostics;
symbol_table.Resolve(&resolve_diagnostics);
EXPECT_EMPTY_STATUSES(resolve_diagnostics);
EXPECT_EQ(anon_struct_ref_comp.resolved_symbol, &anon_struct); // unchanged
}
}
TEST_F(BuildSymbolTableTest, AnonymousStructTypeDataMultiDeclaration) {
TestVerilogSourceFile src("structy.sv",
"struct {\n"
" int size, weight;\n"
// Note: the syntax tree structure for "weight"
// looks different than that of the the first
// variable "size". Make sure this test continues to
// work after CST restructuring.
"} data;\n");
const auto status = src.Parse();
ASSERT_TRUE(status.ok()) << status.message();
SymbolTable symbol_table(nullptr);
const SymbolTableNode &root_symbol(symbol_table.Root());
const auto build_diagnostics = BuildSymbolTable(src, &symbol_table);
EXPECT_EMPTY_STATUSES(build_diagnostics);
// Expect one anonymous struct definition and reference.
EXPECT_EQ(root_symbol.Value().anonymous_scope_names.size(), 1);
ASSERT_EQ(root_symbol.Children().size(), 2);
// Find the symbol that is a struct (anon), which is not "data".
const auto found =
std::find_if(root_symbol.Children().begin(), root_symbol.Children().end(),
[](const SymbolTableNode::key_value_type &p) {
return p.first != "data";
});
ASSERT_NE(found, root_symbol.Children().end());
const SymbolTableNode &anon_struct(found->second);
const SymbolInfo &anon_struct_info(anon_struct.Value());
EXPECT_EQ(anon_struct_info.metatype, SymbolMetaType::kStruct);
EXPECT_TRUE(anon_struct_info.local_references_to_bind.empty());
// Struct has two members.
MUST_ASSIGN_LOOKUP_SYMBOL(int_size, anon_struct, "size");
EXPECT_EQ(int_size_info.metatype, SymbolMetaType::kDataNetVariableInstance);
EXPECT_EQ(int_size_info.file_origin, &src);
EXPECT_EQ(
verible::StringSpanOfSymbol(*int_size_info.declared_type.syntax_origin),
"int");
EXPECT_EQ(int_size_info.declared_type.user_defined_type, nullptr);
MUST_ASSIGN_LOOKUP_SYMBOL(int_weight, anon_struct, "weight");
EXPECT_EQ(int_weight_info.metatype, SymbolMetaType::kDataNetVariableInstance);
EXPECT_EQ(int_weight_info.file_origin, &src);
EXPECT_EQ(
verible::StringSpanOfSymbol(*int_weight_info.declared_type.syntax_origin),
"int");
EXPECT_EQ(int_weight_info.declared_type.user_defined_type, nullptr);
// Expect to bind anonymous struct immediately.
ASSERT_EQ(root_symbol.Value().local_references_to_bind.size(), 1);
const DependentReferences &anon_struct_ref(
root_symbol.Value().local_references_to_bind.front());
const ReferenceComponent &anon_struct_ref_comp(
anon_struct_ref.components->Value());
EXPECT_EQ(anon_struct_ref_comp.ref_type, ReferenceType::kImmediate);
EXPECT_EQ(anon_struct_ref_comp.required_metatype, SymbolMetaType::kStruct);
EXPECT_EQ(anon_struct_ref_comp.resolved_symbol, &anon_struct);
// "data"'s type is the (internal) anonymous struct type reference.
MUST_ASSIGN_LOOKUP_SYMBOL(data, root_symbol, "data");
EXPECT_EQ(data_info.metatype, SymbolMetaType::kDataNetVariableInstance);
EXPECT_EQ(data_info.file_origin, &src);
EXPECT_EQ(data_info.declared_type.user_defined_type,
anon_struct_ref.LastLeaf());
{
std::vector<absl::Status> resolve_diagnostics;
symbol_table.Resolve(&resolve_diagnostics);
EXPECT_EMPTY_STATUSES(resolve_diagnostics);
EXPECT_EQ(anon_struct_ref_comp.resolved_symbol, &anon_struct); // unchanged
}
}
TEST_F(BuildSymbolTableTest, AnonymousStructTypeDataMultiVariables) {
TestVerilogSourceFile src("structy.sv",
"struct {\n"
" int size;\n"
"} data, foobar;\n");
const auto status = src.Parse();
ASSERT_TRUE(status.ok()) << status.message();
SymbolTable symbol_table(nullptr);
const SymbolTableNode &root_symbol(symbol_table.Root());
const auto build_diagnostics = BuildSymbolTable(src, &symbol_table);
EXPECT_EMPTY_STATUSES(build_diagnostics);
// Expect one anonymous struct definition and two type references.
EXPECT_EQ(root_symbol.Value().anonymous_scope_names.size(), 1);
ASSERT_EQ(root_symbol.Children().size(), 3);
// Find the first symbol that is a struct (anon).
const auto found = std::find_if(
root_symbol.Children().begin(), root_symbol.Children().end(),
[](const SymbolTableNode::key_value_type &p) {
return p.second.Value().metatype == SymbolMetaType::kStruct;
});
ASSERT_NE(found, root_symbol.Children().end());
const SymbolTableNode &anon_struct(found->second);
const SymbolInfo &anon_struct_info(anon_struct.Value());
EXPECT_EQ(anon_struct_info.metatype, SymbolMetaType::kStruct);
EXPECT_TRUE(anon_struct_info.local_references_to_bind.empty());
// Struct has one member.
MUST_ASSIGN_LOOKUP_SYMBOL(int_size, anon_struct, "size");
EXPECT_EQ(int_size_info.metatype, SymbolMetaType::kDataNetVariableInstance);
EXPECT_EQ(int_size_info.file_origin, &src);
EXPECT_EQ(
verible::StringSpanOfSymbol(*int_size_info.declared_type.syntax_origin),
"int");
EXPECT_EQ(int_size_info.declared_type.user_defined_type, nullptr);
// Expect to bind anonymous struct immediately.
ASSERT_EQ(root_symbol.Value().local_references_to_bind.size(), 1);
const DependentReferences &anon_struct_ref(
root_symbol.Value().local_references_to_bind.front());
const ReferenceComponent &anon_struct_ref_comp(
anon_struct_ref.components->Value());
EXPECT_EQ(anon_struct_ref_comp.ref_type, ReferenceType::kImmediate);
EXPECT_EQ(anon_struct_ref_comp.required_metatype, SymbolMetaType::kStruct);
EXPECT_EQ(anon_struct_ref_comp.resolved_symbol, &anon_struct);
// "data"'s type is the (internal) anonymous struct type reference.
MUST_ASSIGN_LOOKUP_SYMBOL(data, root_symbol, "data");
EXPECT_EQ(data_info.metatype, SymbolMetaType::kDataNetVariableInstance);
EXPECT_EQ(data_info.file_origin, &src);
EXPECT_EQ(data_info.declared_type.user_defined_type,
anon_struct_ref.LastLeaf());
// "foobar" has same anonymous struct type
MUST_ASSIGN_LOOKUP_SYMBOL(foobar, root_symbol, "foobar");
EXPECT_EQ(foobar_info.metatype, SymbolMetaType::kDataNetVariableInstance);
EXPECT_EQ(foobar_info.file_origin, &src);
EXPECT_EQ(foobar_info.declared_type.user_defined_type,
anon_struct_ref.LastLeaf());
{
std::vector<absl::Status> resolve_diagnostics;
symbol_table.Resolve(&resolve_diagnostics);
EXPECT_EMPTY_STATUSES(resolve_diagnostics);
// "data" and "foobar" share the same anonymous struct type.
EXPECT_EQ(anon_struct_ref_comp.resolved_symbol, &anon_struct); // unchanged
EXPECT_EQ(
data_info.declared_type.user_defined_type->Value().resolved_symbol,
&anon_struct);
EXPECT_EQ(
foobar_info.declared_type.user_defined_type->Value().resolved_symbol,
&anon_struct);
}
}
static bool IsStruct(const SymbolTableNode::key_value_type &p) {
return p.second.Value().metatype == SymbolMetaType::kStruct;
}
TEST_F(BuildSymbolTableTest,
AnonymousStructTypeDataMultiVariablesDistinctTypes) {
TestVerilogSourceFile src("structy.sv",
"struct {\n"
" int size;\n"
"} data;\n"
"struct {\n"
" int size;\n"
"} foobar;\n");
const auto status = src.Parse();
ASSERT_TRUE(status.ok()) << status.message();
SymbolTable symbol_table(nullptr);
const SymbolTableNode &root_symbol(symbol_table.Root());
const auto build_diagnostics = BuildSymbolTable(src, &symbol_table);
EXPECT_EMPTY_STATUSES(build_diagnostics);
// Expect two anonymous struct definitions and two type references.
EXPECT_EQ(root_symbol.Value().anonymous_scope_names.size(), 2);
ASSERT_EQ(root_symbol.Children().size(), 4);
// Find the symbol that is a struct (anon).
const auto found = std::find_if(root_symbol.Children().begin(),
root_symbol.Children().end(), IsStruct);
ASSERT_NE(found, root_symbol.Children().end());
const SymbolTableNode &anon_struct(found->second);
const SymbolInfo &anon_struct_info(anon_struct.Value());
EXPECT_EQ(anon_struct_info.metatype, SymbolMetaType::kStruct);
EXPECT_TRUE(anon_struct_info.local_references_to_bind.empty());
const auto found_2 =
std::find_if(std::next(found), root_symbol.Children().end(), IsStruct);
ASSERT_NE(found_2, root_symbol.Children().end());
const SymbolTableNode &anon_struct_2(found_2->second);
const SymbolInfo &anon_struct_2_info(anon_struct.Value());
EXPECT_EQ(anon_struct_2_info.metatype, SymbolMetaType::kStruct);
EXPECT_TRUE(anon_struct_2_info.local_references_to_bind.empty());
// Struct has one member. Both structs have the same elements and structure,
// but have distinct scopes in the symbol table.
for (const auto *anon_struct_iter : {&anon_struct, &anon_struct_2}) {
MUST_ASSIGN_LOOKUP_SYMBOL(int_size, *anon_struct_iter, "size");
EXPECT_EQ(int_size_info.metatype, SymbolMetaType::kDataNetVariableInstance);
EXPECT_EQ(int_size_info.file_origin, &src);
EXPECT_EQ(
verible::StringSpanOfSymbol(*int_size_info.declared_type.syntax_origin),
"int");
EXPECT_EQ(int_size_info.declared_type.user_defined_type, nullptr);
}
// Expect to bind both anonymous structs immediately.
ASSERT_EQ(root_symbol.Value().local_references_to_bind.size(), 2);
const DependentReferences &anon_struct_ref(
root_symbol.Value().local_references_to_bind.front());
const ReferenceComponent &anon_struct_ref_comp(
anon_struct_ref.components->Value());
EXPECT_EQ(anon_struct_ref_comp.ref_type, ReferenceType::kImmediate);
EXPECT_EQ(anon_struct_ref_comp.required_metatype, SymbolMetaType::kStruct);
EXPECT_EQ(anon_struct_ref_comp.resolved_symbol, &anon_struct);
const DependentReferences &anon_struct_2_ref(
root_symbol.Value().local_references_to_bind.back());
const ReferenceComponent &anon_struct_2_ref_comp(
anon_struct_2_ref.components->Value());
EXPECT_EQ(anon_struct_2_ref_comp.ref_type, ReferenceType::kImmediate);
EXPECT_EQ(anon_struct_2_ref_comp.required_metatype, SymbolMetaType::kStruct);
EXPECT_EQ(anon_struct_2_ref_comp.resolved_symbol, &anon_struct_2);
// "data"'s type is the (internal) anonymous struct type reference.
MUST_ASSIGN_LOOKUP_SYMBOL(data, root_symbol, "data");
EXPECT_EQ(data_info.metatype, SymbolMetaType::kDataNetVariableInstance);
EXPECT_EQ(data_info.file_origin, &src);
EXPECT_EQ(data_info.declared_type.user_defined_type,
anon_struct_ref.LastLeaf());
// "foobar" has a different anonymous struct type
MUST_ASSIGN_LOOKUP_SYMBOL(foobar, root_symbol, "foobar");
EXPECT_EQ(foobar_info.metatype, SymbolMetaType::kDataNetVariableInstance);
EXPECT_EQ(foobar_info.file_origin, &src);
EXPECT_EQ(foobar_info.declared_type.user_defined_type,
anon_struct_2_ref.LastLeaf());
{
std::vector<absl::Status> resolve_diagnostics;
symbol_table.Resolve(&resolve_diagnostics);
EXPECT_EMPTY_STATUSES(resolve_diagnostics);
// "data" and "foobar" have different anonymous struct types.
EXPECT_EQ(anon_struct_ref_comp.resolved_symbol, &anon_struct); // unchanged
EXPECT_EQ(anon_struct_2_ref_comp.resolved_symbol,
&anon_struct_2); // unchanged
EXPECT_EQ(
data_info.declared_type.user_defined_type->Value().resolved_symbol,
&anon_struct);
EXPECT_EQ(
foobar_info.declared_type.user_defined_type->Value().resolved_symbol,
&anon_struct_2);
}
}
TEST_F(BuildSymbolTableTest, AnonymousStructTypeFunctionParameter) {
TestVerilogSourceFile src("structy_funky.sv",
"function int ff(struct {\n"
" int weight;\n"
" } data);\n"
" return data.weight;\n"
"endfunction\n");
const auto status = src.Parse();
ASSERT_TRUE(status.ok()) << status.message();
SymbolTable symbol_table(nullptr);
const SymbolTableNode &root_symbol(symbol_table.Root());
const auto build_diagnostics = BuildSymbolTable(src, &symbol_table);
EXPECT_EMPTY_STATUSES(build_diagnostics);
MUST_ASSIGN_LOOKUP_SYMBOL(ff_function, root_symbol, "ff");
EXPECT_EQ(ff_function_info.metatype, SymbolMetaType::kFunction);
// Expect one anonymous struct definition and reference.
EXPECT_EQ(ff_function_info.anonymous_scope_names.size(), 1);
ASSERT_EQ(ff_function.Children().size(), 2);
// Find the symbol that is a struct (anon).
const auto found = std::find_if(
ff_function.Children().begin(), ff_function.Children().end(),
[](const SymbolTableNode::key_value_type &p) {
return p.second.Value().metatype == SymbolMetaType::kStruct;
});
ASSERT_NE(found, ff_function.Children().end());
const SymbolTableNode &anon_struct(found->second);
const SymbolInfo &anon_struct_info(anon_struct.Value());
EXPECT_EQ(anon_struct_info.metatype, SymbolMetaType::kStruct);
EXPECT_TRUE(anon_struct_info.local_references_to_bind.empty());
MUST_ASSIGN_LOOKUP_SYMBOL(int_weight, anon_struct, "weight");
EXPECT_EQ(int_weight_info.metatype, SymbolMetaType::kDataNetVariableInstance);
EXPECT_EQ(int_weight_info.file_origin, &src);
EXPECT_EQ(
verible::StringSpanOfSymbol(*int_weight_info.declared_type.syntax_origin),
"int");
EXPECT_EQ(int_weight_info.declared_type.user_defined_type, nullptr);
// Expect to bind anonymous struct immediately.
const auto ref_map(ff_function_info.LocalReferencesMapViewForTesting());
// Expect one type reference and one reference rooted at "data".
ASSIGN_MUST_FIND_EXACTLY_ONE_REF(data_ref, ref_map, "data");
const ReferenceComponent &data_ref_comp(data_ref->components->Value());
EXPECT_EQ(data_ref_comp.identifier, "data");
EXPECT_EQ(data_ref_comp.ref_type, ReferenceType::kUnqualified);
EXPECT_EQ(data_ref_comp.required_metatype, SymbolMetaType::kUnspecified);
EXPECT_EQ(data_ref_comp.resolved_symbol, nullptr);
// "data.weight"
ASSIGN_MUST_HAVE_UNIQUE(weight_ref, data_ref->components->Children());
const ReferenceComponent &weight_ref_comp(weight_ref.Value());
EXPECT_EQ(weight_ref_comp.identifier, "weight");
EXPECT_EQ(weight_ref_comp.ref_type, ReferenceType::kMemberOfTypeOfParent);
EXPECT_EQ(weight_ref_comp.required_metatype, SymbolMetaType::kUnspecified);
EXPECT_EQ(weight_ref_comp.resolved_symbol, nullptr);
const DependentReferences &anon_struct_ref(
**std::find_if(ref_map.begin(), ref_map.end(),
[](const decltype(ref_map)::value_type &r) {
return (*r.second.begin())
->components->Value()
.required_metatype == SymbolMetaType::kStruct;
})
->second.begin());
const ReferenceComponent &anon_struct_ref_comp(
anon_struct_ref.components->Value());
EXPECT_EQ(anon_struct_ref_comp.ref_type, ReferenceType::kImmediate);
EXPECT_EQ(anon_struct_ref_comp.required_metatype, SymbolMetaType::kStruct);
EXPECT_EQ(anon_struct_ref_comp.resolved_symbol, &anon_struct);
// "data"'s type is the (internal) anonymous struct type reference.
MUST_ASSIGN_LOOKUP_SYMBOL(data, ff_function, "data");
EXPECT_EQ(data_info.metatype, SymbolMetaType::kDataNetVariableInstance);
EXPECT_EQ(data_info.file_origin, &src);
EXPECT_EQ(data_info.declared_type.user_defined_type,
anon_struct_ref.LastLeaf());
{
std::vector<absl::Status> resolve_diagnostics;
symbol_table.Resolve(&resolve_diagnostics);
EXPECT_EMPTY_STATUSES(resolve_diagnostics);
EXPECT_EQ(data_ref_comp.resolved_symbol, &data); // "data"
EXPECT_EQ(weight_ref_comp.resolved_symbol, &int_weight); // ".weight"
EXPECT_EQ(data_info.declared_type.user_defined_type,
anon_struct_ref.LastLeaf());
EXPECT_EQ(anon_struct_ref_comp.resolved_symbol, &anon_struct); // unchanged
}
}
TEST_F(BuildSymbolTableTest, AnonymousStructTypeNested) {
TestVerilogSourceFile src("structy.sv",
"struct {\n"
" struct {\n"
" int size;\n"
" } foo;\n"
"} data;\n");
const auto status = src.Parse();
ASSERT_TRUE(status.ok()) << status.message();
SymbolTable symbol_table(nullptr);
const SymbolTableNode &root_symbol(symbol_table.Root());
const auto build_diagnostics = BuildSymbolTable(src, &symbol_table);
EXPECT_EMPTY_STATUSES(build_diagnostics);
// Expect one anonymous struct definition and reference at root level.
EXPECT_EQ(root_symbol.Value().anonymous_scope_names.size(), 1);
ASSERT_EQ(root_symbol.Children().size(), 2);
// Find the symbol that is a struct (anon), which is not "data".
const auto outer_found = std::find_if(root_symbol.Children().begin(),
root_symbol.Children().end(), IsStruct);
ASSERT_NE(outer_found, root_symbol.Children().end());
const SymbolTableNode &outer_anon_struct(outer_found->second);
const SymbolInfo &outer_anon_struct_info(outer_anon_struct.Value());
EXPECT_EQ(outer_anon_struct_info.metatype, SymbolMetaType::kStruct);
EXPECT_EQ(outer_anon_struct_info.local_references_to_bind.size(), 1);
// Expect one anonymous struct definition inside the outer struct.
EXPECT_EQ(outer_anon_struct_info.anonymous_scope_names.size(), 1);
// Outer struct has one member.
MUST_ASSIGN_LOOKUP_SYMBOL(struct_foo, outer_anon_struct, "foo");
EXPECT_TRUE(struct_foo.Children().empty());
EXPECT_EQ(struct_foo_info.metatype, SymbolMetaType::kDataNetVariableInstance);
EXPECT_EQ(struct_foo_info.file_origin, &src);
EXPECT_NE(struct_foo_info.declared_type.syntax_origin, nullptr);
// Inner struct lives in the scope of the outer struct.
const auto inner_found =
std::find_if(outer_anon_struct.Children().begin(),
outer_anon_struct.Children().end(), IsStruct);
ASSERT_NE(inner_found, outer_anon_struct.Children().end());
const SymbolTableNode &inner_anon_struct(inner_found->second);
const SymbolInfo &inner_anon_struct_info(inner_anon_struct.Value());
EXPECT_EQ(inner_anon_struct_info.metatype, SymbolMetaType::kStruct);
EXPECT_TRUE(inner_anon_struct_info.local_references_to_bind.empty());
// "foo"'s type is pre-bound to the inner anonymous struct.
const ReferenceComponentNode *foo_type =
struct_foo_info.declared_type.user_defined_type;
ASSERT_NE(foo_type, nullptr);
const ReferenceComponent &foo_type_comp(foo_type->Value());
EXPECT_EQ(foo_type_comp.ref_type, ReferenceType::kImmediate);
EXPECT_EQ(foo_type_comp.required_metatype, SymbolMetaType::kStruct);
EXPECT_EQ(foo_type_comp.resolved_symbol, &inner_anon_struct);
// Inner struct has one member.
MUST_ASSIGN_LOOKUP_SYMBOL(int_size, inner_anon_struct, "size");
EXPECT_EQ(int_size_info.metatype, SymbolMetaType::kDataNetVariableInstance);
EXPECT_EQ(int_size_info.file_origin, &src);
EXPECT_EQ(
verible::StringSpanOfSymbol(*int_size_info.declared_type.syntax_origin),
"int");
EXPECT_EQ(int_size_info.declared_type.user_defined_type, nullptr);
// Expect to bind (outer) anonymous struct immediately.
ASSERT_EQ(root_symbol.Value().local_references_to_bind.size(), 1);
const DependentReferences &anon_struct_ref(
root_symbol.Value().local_references_to_bind.front());
const ReferenceComponent &anon_struct_ref_comp(
anon_struct_ref.components->Value());
EXPECT_EQ(anon_struct_ref_comp.ref_type, ReferenceType::kImmediate);
EXPECT_EQ(anon_struct_ref_comp.required_metatype, SymbolMetaType::kStruct);
EXPECT_EQ(anon_struct_ref_comp.resolved_symbol, &outer_anon_struct);
// "data"'s type is the (internal) anonymous struct type reference.
MUST_ASSIGN_LOOKUP_SYMBOL(data, root_symbol, "data");
EXPECT_EQ(data_info.metatype, SymbolMetaType::kDataNetVariableInstance);
EXPECT_EQ(data_info.file_origin, &src);
EXPECT_EQ(data_info.declared_type.user_defined_type,
anon_struct_ref.LastLeaf());
{
std::vector<absl::Status> resolve_diagnostics;
symbol_table.Resolve(&resolve_diagnostics);
EXPECT_EMPTY_STATUSES(resolve_diagnostics);
// No change, anonymous types were already bound.
EXPECT_EQ(foo_type_comp.resolved_symbol, &inner_anon_struct);
EXPECT_EQ(anon_struct_ref_comp.resolved_symbol, &outer_anon_struct);
}
}
TEST_F(BuildSymbolTableTest, AnonymousStructTypeNestedMemberReference) {
TestVerilogSourceFile src("funky_structy.sv",
"function int ff();\n"
" struct {\n"
" struct {\n"
" int size;\n"
" } foo;\n"
" } data;\n"
" return data.foo.size;\n"
"endfunction\n");
const auto status = src.Parse();
ASSERT_TRUE(status.ok()) << status.message();
SymbolTable symbol_table(nullptr);
const SymbolTableNode &root_symbol(symbol_table.Root());
const auto build_diagnostics = BuildSymbolTable(src, &symbol_table);
EXPECT_EMPTY_STATUSES(build_diagnostics);
MUST_ASSIGN_LOOKUP_SYMBOL(function_ff, root_symbol, "ff");
EXPECT_EQ(function_ff_info.metatype, SymbolMetaType::kFunction);
EXPECT_EQ(function_ff_info.file_origin, &src);
// Expect one anonymous struct definition and reference in function.
EXPECT_EQ(function_ff_info.anonymous_scope_names.size(), 1);
ASSERT_EQ(function_ff.Children().size(), 2);
// Find the symbol that is a struct (anon), which is not "data".
const auto outer_found = std::find_if(function_ff.Children().begin(),
function_ff.Children().end(), IsStruct);
ASSERT_NE(outer_found, function_ff.Children().end());
const SymbolTableNode &outer_anon_struct(outer_found->second);
const SymbolInfo &outer_anon_struct_info(outer_anon_struct.Value());
EXPECT_EQ(outer_anon_struct_info.metatype, SymbolMetaType::kStruct);
EXPECT_EQ(outer_anon_struct_info.local_references_to_bind.size(), 1);
// Expect one anonymous struct definition inside the outer struct.
EXPECT_EQ(outer_anon_struct_info.anonymous_scope_names.size(), 1);
// Outer struct has one member.
MUST_ASSIGN_LOOKUP_SYMBOL(struct_foo, outer_anon_struct, "foo");
EXPECT_TRUE(struct_foo.Children().empty());
EXPECT_EQ(struct_foo_info.metatype, SymbolMetaType::kDataNetVariableInstance);
EXPECT_EQ(struct_foo_info.file_origin, &src);
EXPECT_NE(struct_foo_info.declared_type.syntax_origin, nullptr);
// Inner struct lives in the scope of the outer struct.
const auto inner_found =
std::find_if(outer_anon_struct.Children().begin(),
outer_anon_struct.Children().end(), IsStruct);
ASSERT_NE(inner_found, outer_anon_struct.Children().end());
const SymbolTableNode &inner_anon_struct(inner_found->second);
const SymbolInfo &inner_anon_struct_info(inner_anon_struct.Value());
EXPECT_EQ(inner_anon_struct_info.metatype, SymbolMetaType::kStruct);
EXPECT_TRUE(inner_anon_struct_info.local_references_to_bind.empty());
// "foo"'s type is pre-bound to the inner anonymous struct.
const ReferenceComponentNode *foo_type =
struct_foo_info.declared_type.user_defined_type;
ASSERT_NE(foo_type, nullptr);
const ReferenceComponent &foo_type_comp(foo_type->Value());
EXPECT_EQ(foo_type_comp.ref_type, ReferenceType::kImmediate);
EXPECT_EQ(foo_type_comp.required_metatype, SymbolMetaType::kStruct);
EXPECT_EQ(foo_type_comp.resolved_symbol, &inner_anon_struct);
// Inner struct has one member.
MUST_ASSIGN_LOOKUP_SYMBOL(int_size, inner_anon_struct, "size");
EXPECT_EQ(int_size_info.metatype, SymbolMetaType::kDataNetVariableInstance);
EXPECT_EQ(int_size_info.file_origin, &src);
EXPECT_EQ(
verible::StringSpanOfSymbol(*int_size_info.declared_type.syntax_origin),
"int");
EXPECT_EQ(int_size_info.declared_type.user_defined_type, nullptr);
// Expect to bind (outer) anonymous struct immediately.
// First reference to anonymous struct, second reference to "data".
ASSERT_EQ(function_ff_info.local_references_to_bind.size(), 2);
const DependentReferences &anon_struct_ref(
function_ff_info.local_references_to_bind.front());
const ReferenceComponent &anon_struct_ref_comp(
anon_struct_ref.components->Value());
EXPECT_EQ(anon_struct_ref_comp.ref_type, ReferenceType::kImmediate);
EXPECT_EQ(anon_struct_ref_comp.required_metatype, SymbolMetaType::kStruct);
EXPECT_EQ(anon_struct_ref_comp.resolved_symbol, &outer_anon_struct);
// "data"'s type is the (internal) anonymous struct type reference.
MUST_ASSIGN_LOOKUP_SYMBOL(data, function_ff, "data");
EXPECT_EQ(data_info.metatype, SymbolMetaType::kDataNetVariableInstance);
EXPECT_EQ(data_info.file_origin, &src);
EXPECT_EQ(data_info.declared_type.user_defined_type,
anon_struct_ref.LastLeaf());
// Find the "data.foo.size" reference
const DependentReferences &data_ref(
function_ff_info.local_references_to_bind.back());
const ReferenceComponent &data_ref_comp(data_ref.components->Value());
EXPECT_EQ(data_ref_comp.identifier, "data");
EXPECT_EQ(data_ref_comp.ref_type, ReferenceType::kUnqualified);
EXPECT_EQ(data_ref_comp.required_metatype, SymbolMetaType::kUnspecified);
EXPECT_EQ(data_ref_comp.resolved_symbol, nullptr);
ASSIGN_MUST_HAVE_UNIQUE(data_foo_ref, data_ref.components->Children());
const ReferenceComponent &data_foo_ref_comp(data_foo_ref.Value());
EXPECT_EQ(data_foo_ref_comp.identifier, "foo");
EXPECT_EQ(data_foo_ref_comp.ref_type, ReferenceType::kMemberOfTypeOfParent);
EXPECT_EQ(data_foo_ref_comp.required_metatype, SymbolMetaType::kUnspecified);
EXPECT_EQ(data_foo_ref_comp.resolved_symbol, nullptr);
ASSIGN_MUST_HAVE_UNIQUE(data_foo_size_ref, data_foo_ref.Children());
const ReferenceComponent &data_foo_size_ref_comp(data_foo_size_ref.Value());
EXPECT_EQ(data_foo_size_ref_comp.identifier, "size");
EXPECT_EQ(data_foo_size_ref_comp.ref_type,
ReferenceType::kMemberOfTypeOfParent);
EXPECT_EQ(data_foo_size_ref_comp.required_metatype,
SymbolMetaType::kUnspecified);
EXPECT_EQ(data_foo_size_ref_comp.resolved_symbol, nullptr);
{
std::vector<absl::Status> resolve_diagnostics;
symbol_table.Resolve(&resolve_diagnostics);
EXPECT_EMPTY_STATUSES(resolve_diagnostics);
EXPECT_EQ(foo_type_comp.resolved_symbol, &inner_anon_struct);
EXPECT_EQ(anon_struct_ref_comp.resolved_symbol, &outer_anon_struct);
// Resolve the reference chain "data.foo.size".
EXPECT_EQ(data_ref_comp.resolved_symbol, &data);
EXPECT_EQ(data_foo_ref_comp.resolved_symbol, &struct_foo);
EXPECT_EQ(data_foo_size_ref_comp.resolved_symbol, &int_size);
}
}
TEST_F(BuildSymbolTableTest, AnonymousEnumTypeData) {
TestVerilogSourceFile src("simple_enum.sv",
"enum {\n"
" idle, busy\n"
"} data;\n");
const auto status = src.Parse();
ASSERT_TRUE(status.ok()) << status.message();
SymbolTable symbol_table(nullptr);
const SymbolTableNode &root_symbol(symbol_table.Root());
const auto build_diagnostics = BuildSymbolTable(src, &symbol_table);
EXPECT_EMPTY_STATUSES(build_diagnostics);
// Expect one anonymous enum definition and reference.
EXPECT_EQ(root_symbol.Value().anonymous_scope_names.size(), 1);
// Expect four symbols (enum, data, idle, busy)
ASSERT_EQ(root_symbol.Children().size(), 4);
// Find the symbol that is a enum (anon)
const auto found = std::find_if(
root_symbol.Children().begin(), root_symbol.Children().end(),
[](const SymbolTableNode::key_value_type &p) {
return p.first != "data" && p.first != "idle" && p.first != "busy";
});
ASSERT_NE(found, root_symbol.Children().end());
const SymbolTableNode &anon_enum(found->second);
const SymbolInfo &anon_enum_info(anon_enum.Value());
EXPECT_EQ(anon_enum_info.metatype, SymbolMetaType::kEnumType);
EXPECT_TRUE(anon_enum_info.local_references_to_bind.empty());
// Enum has two members.
EXPECT_EQ(anon_enum.Children().size(), 2);
MUST_ASSIGN_LOOKUP_SYMBOL(idle, anon_enum, "idle");
EXPECT_EQ(idle_info.metatype, SymbolMetaType::kEnumConstant);
EXPECT_EQ(idle_info.file_origin, &src);
EXPECT_EQ(idle_info.declared_type.user_defined_type, nullptr);
MUST_ASSIGN_LOOKUP_SYMBOL(busy, anon_enum, "busy");
EXPECT_EQ(busy_info.metatype, SymbolMetaType::kEnumConstant);
EXPECT_EQ(busy_info.file_origin, &src);
EXPECT_EQ(busy_info.declared_type.user_defined_type, nullptr);
// Find idle symbol
const auto found_enum_idle =
std::find_if(root_symbol.Children().begin(), root_symbol.Children().end(),
[](const SymbolTableNode::key_value_type &p) {
return p.first == "idle";
});
ASSERT_NE(found_enum_idle, root_symbol.Children().end());
const SymbolTableNode &enum_idle(found_enum_idle->second);
const SymbolInfo &enum_idle_info(enum_idle.Value());
EXPECT_EQ(enum_idle_info.metatype, SymbolMetaType::kTypeAlias);
EXPECT_TRUE(enum_idle_info.local_references_to_bind.empty());
// Find busy symbol
const auto found_enum_busy =
std::find_if(root_symbol.Children().begin(), root_symbol.Children().end(),
[](const SymbolTableNode::key_value_type &p) {
return p.first == "busy";
});
ASSERT_NE(found_enum_busy, root_symbol.Children().end());
const SymbolTableNode &enum_busy(found_enum_busy->second);
const SymbolInfo &enum_busy_info(enum_busy.Value());
EXPECT_EQ(enum_busy_info.metatype, SymbolMetaType::kTypeAlias);
EXPECT_TRUE(enum_busy_info.local_references_to_bind.empty());
// Three references: data and two enum constants
ASSERT_EQ(root_symbol.Value().local_references_to_bind.size(), 3);
// Expect them to bind immediately.
const ReferenceComponent &anon_enum_ref_comp(
root_symbol.Value().local_references_to_bind[2].LastLeaf()->Value());
EXPECT_EQ(anon_enum_ref_comp.ref_type, ReferenceType::kImmediate);
EXPECT_EQ(anon_enum_ref_comp.required_metatype, SymbolMetaType::kEnumType);
EXPECT_EQ(anon_enum_ref_comp.resolved_symbol, &anon_enum);
const ReferenceComponent &enum_idle_ref_comp(
root_symbol.Value().local_references_to_bind[0].LastLeaf()->Value());
EXPECT_EQ(enum_idle_ref_comp.ref_type, ReferenceType::kImmediate);
EXPECT_EQ(enum_idle_ref_comp.required_metatype,
SymbolMetaType::kEnumConstant);
EXPECT_EQ(enum_idle_ref_comp.resolved_symbol, &busy);
const ReferenceComponent &enum_busy_ref_comp(
root_symbol.Value().local_references_to_bind[1].LastLeaf()->Value());
EXPECT_EQ(enum_busy_ref_comp.ref_type, ReferenceType::kImmediate);
EXPECT_EQ(enum_busy_ref_comp.required_metatype,
SymbolMetaType::kEnumConstant);
EXPECT_EQ(enum_busy_ref_comp.resolved_symbol, &idle);
// "data"'s type is the (internal) anonymous enum type reference.
MUST_ASSIGN_LOOKUP_SYMBOL(data, root_symbol, "data");
EXPECT_EQ(data_info.metatype, SymbolMetaType::kDataNetVariableInstance);
EXPECT_EQ(data_info.file_origin, &src);
const DependentReferences &anon_enum_ref(
root_symbol.Value().local_references_to_bind[2]);
EXPECT_EQ(data_info.declared_type.user_defined_type,
anon_enum_ref.LastLeaf());
{
std::vector<absl::Status> resolve_diagnostics;
symbol_table.Resolve(&resolve_diagnostics);
EXPECT_EMPTY_STATUSES(resolve_diagnostics);
// Make sure that resolve doesn't change/break anything
EXPECT_EQ(anon_enum_ref_comp.resolved_symbol, &anon_enum);
EXPECT_EQ(enum_idle_ref_comp.resolved_symbol, &busy);
EXPECT_EQ(enum_busy_ref_comp.resolved_symbol, &idle);
}
}
TEST_F(BuildSymbolTableTest, TypedefPrimitive) {
TestVerilogSourceFile src("typedef.sv",
"typedef int number;\n"
"number one = 1;\n");
const auto status = src.Parse();
ASSERT_TRUE(status.ok()) << status.message();
SymbolTable symbol_table(nullptr);
const SymbolTableNode &root_symbol(symbol_table.Root());
const auto build_diagnostics = BuildSymbolTable(src, &symbol_table);
EXPECT_EMPTY_STATUSES(build_diagnostics);
MUST_ASSIGN_LOOKUP_SYMBOL(number_typedef, root_symbol, "number");
EXPECT_EQ(number_typedef_info.metatype, SymbolMetaType::kTypeAlias);
EXPECT_EQ(number_typedef_info.file_origin, &src);
MUST_ASSIGN_LOOKUP_SYMBOL(one_var, root_symbol, "one");
EXPECT_EQ(one_var_info.metatype, SymbolMetaType::kDataNetVariableInstance);
EXPECT_EQ(one_var_info.file_origin, &src);
// Expect one type reference to "number".
ASSIGN_MUST_HAVE_UNIQUE(number_ref,
root_symbol.Value().local_references_to_bind);
const ReferenceComponent &number_ref_comp(number_ref.components->Value());
EXPECT_EQ(number_ref_comp.identifier, "number");
EXPECT_EQ(number_ref_comp.ref_type, ReferenceType::kUnqualified);
EXPECT_EQ(number_ref_comp.required_metatype, SymbolMetaType::kUnspecified);
EXPECT_EQ(number_ref_comp.resolved_symbol, nullptr);
EXPECT_EQ(one_var_info.declared_type.user_defined_type,
number_ref.components.get());
{
std::vector<absl::Status> resolve_diagnostics;
symbol_table.Resolve(&resolve_diagnostics);
EXPECT_EMPTY_STATUSES(resolve_diagnostics);
// Resolve type "number" to the typedef.
EXPECT_EQ(number_ref_comp.resolved_symbol, &number_typedef);
}
}
TEST_F(BuildSymbolTableTest, TypedefTransitive) {
TestVerilogSourceFile src("typedef.sv",
"typedef int num;\n"
"typedef num number;\n"
"number one = 1;\n");
const auto status = src.Parse();
ASSERT_TRUE(status.ok()) << status.message();
SymbolTable symbol_table(nullptr);
const SymbolTableNode &root_symbol(symbol_table.Root());
const auto build_diagnostics = BuildSymbolTable(src, &symbol_table);
EXPECT_EMPTY_STATUSES(build_diagnostics);
MUST_ASSIGN_LOOKUP_SYMBOL(num_typedef, root_symbol, "num");
EXPECT_EQ(num_typedef_info.metatype, SymbolMetaType::kTypeAlias);
EXPECT_EQ(num_typedef_info.file_origin, &src);
MUST_ASSIGN_LOOKUP_SYMBOL(number_typedef, root_symbol, "number");
EXPECT_EQ(number_typedef_info.metatype, SymbolMetaType::kTypeAlias);
EXPECT_EQ(number_typedef_info.file_origin, &src);
MUST_ASSIGN_LOOKUP_SYMBOL(one_var, root_symbol, "one");
EXPECT_EQ(one_var_info.metatype, SymbolMetaType::kDataNetVariableInstance);
EXPECT_EQ(one_var_info.file_origin, &src);
const auto ref_map(root_symbol.Value().LocalReferencesMapViewForTesting());
// Expect one type reference to "num".
ASSIGN_MUST_FIND_EXACTLY_ONE_REF(num_ref, ref_map, "num");
const ReferenceComponent &num_ref_comp(num_ref->components->Value());
EXPECT_EQ(num_ref_comp.identifier, "num");
EXPECT_EQ(num_ref_comp.ref_type, ReferenceType::kUnqualified);
EXPECT_EQ(num_ref_comp.required_metatype, SymbolMetaType::kUnspecified);
EXPECT_EQ(num_ref_comp.resolved_symbol, nullptr);
// Expect one type reference to "number".
ASSIGN_MUST_FIND_EXACTLY_ONE_REF(number_ref, ref_map, "number");
const ReferenceComponent &number_ref_comp(number_ref->components->Value());
EXPECT_EQ(number_ref_comp.identifier, "number");
EXPECT_EQ(number_ref_comp.ref_type, ReferenceType::kUnqualified);
EXPECT_EQ(number_ref_comp.required_metatype, SymbolMetaType::kUnspecified);
EXPECT_EQ(number_ref_comp.resolved_symbol, nullptr);
EXPECT_EQ(number_typedef_info.declared_type.user_defined_type,
num_ref->components.get());
EXPECT_EQ(one_var_info.declared_type.user_defined_type,
number_ref->components.get());
{
std::vector<absl::Status> resolve_diagnostics;
symbol_table.Resolve(&resolve_diagnostics);
EXPECT_EMPTY_STATUSES(resolve_diagnostics);
// Resolve type "num" to the typedef.
EXPECT_EQ(num_ref_comp.resolved_symbol, &num_typedef);
// Resolve type "number" to the typedef.
EXPECT_EQ(number_ref_comp.resolved_symbol, &number_typedef);
}
}
TEST_F(BuildSymbolTableTest, TypedefClass) {
TestVerilogSourceFile src("typedef.sv",
"class cc;\n"
"endclass\n"
"typedef cc number;\n"
"number foo;\n");
const auto status = src.Parse();
ASSERT_TRUE(status.ok()) << status.message();
SymbolTable symbol_table(nullptr);
const SymbolTableNode &root_symbol(symbol_table.Root());
const auto build_diagnostics = BuildSymbolTable(src, &symbol_table);
EXPECT_EMPTY_STATUSES(build_diagnostics);
MUST_ASSIGN_LOOKUP_SYMBOL(cc_class, root_symbol, "cc");
EXPECT_EQ(cc_class_info.metatype, SymbolMetaType::kClass);
EXPECT_EQ(cc_class_info.file_origin, &src);
MUST_ASSIGN_LOOKUP_SYMBOL(number_typedef, root_symbol, "number");
EXPECT_EQ(number_typedef_info.metatype, SymbolMetaType::kTypeAlias);
EXPECT_EQ(number_typedef_info.file_origin, &src);
MUST_ASSIGN_LOOKUP_SYMBOL(foo_var, root_symbol, "foo");
EXPECT_EQ(foo_var_info.metatype, SymbolMetaType::kDataNetVariableInstance);
EXPECT_EQ(foo_var_info.file_origin, &src);
// Expect one type reference to "number", and one to "cc".
const auto &ref_map(root_symbol.Value().LocalReferencesMapViewForTesting());
ASSIGN_MUST_FIND(cc_type_refs, ref_map, "cc");
ASSIGN_MUST_HAVE_UNIQUE(cc_type_ref, cc_type_refs);
const ReferenceComponent &cc_type_ref_comp(cc_type_ref->components->Value());
EXPECT_EQ(cc_type_ref_comp.identifier, "cc");
EXPECT_EQ(cc_type_ref_comp.ref_type, ReferenceType::kUnqualified);
EXPECT_EQ(cc_type_ref_comp.required_metatype, SymbolMetaType::kUnspecified);
EXPECT_EQ(cc_type_ref_comp.resolved_symbol, nullptr);
ASSIGN_MUST_FIND(number_refs, ref_map, "number");
ASSIGN_MUST_HAVE_UNIQUE(number_ref, number_refs);
const ReferenceComponent &number_ref_comp(number_ref->components->Value());
EXPECT_EQ(number_ref_comp.identifier, "number");
EXPECT_EQ(number_ref_comp.ref_type, ReferenceType::kUnqualified);
EXPECT_EQ(number_ref_comp.required_metatype, SymbolMetaType::kUnspecified);
EXPECT_EQ(number_ref_comp.resolved_symbol, nullptr);
EXPECT_EQ(foo_var_info.declared_type.user_defined_type,
number_ref->components.get());
{
std::vector<absl::Status> resolve_diagnostics;
symbol_table.Resolve(&resolve_diagnostics);
EXPECT_EMPTY_STATUSES(resolve_diagnostics);
EXPECT_EQ(cc_type_ref_comp.resolved_symbol, &cc_class);
EXPECT_EQ(number_ref_comp.resolved_symbol, &number_typedef);
}
}
TEST_F(BuildSymbolTableTest, TypedefClassPackageQualified) {
TestVerilogSourceFile src("typedef.sv",
"package pp;\n"
" class cc;\n"
" endclass\n"
"endpackage\n"
"typedef pp::cc number;\n"
"number foo;\n");
const auto status = src.Parse();
ASSERT_TRUE(status.ok()) << status.message();
SymbolTable symbol_table(nullptr);
const SymbolTableNode &root_symbol(symbol_table.Root());
const auto build_diagnostics = BuildSymbolTable(src, &symbol_table);
EXPECT_EMPTY_STATUSES(build_diagnostics);
MUST_ASSIGN_LOOKUP_SYMBOL(pp_package, root_symbol, "pp");
EXPECT_EQ(pp_package_info.metatype, SymbolMetaType::kPackage);
EXPECT_EQ(pp_package_info.file_origin, &src);
MUST_ASSIGN_LOOKUP_SYMBOL(cc_class, pp_package, "cc");
EXPECT_EQ(cc_class_info.metatype, SymbolMetaType::kClass);
EXPECT_EQ(cc_class_info.file_origin, &src);
MUST_ASSIGN_LOOKUP_SYMBOL(number_typedef, root_symbol, "number");
EXPECT_EQ(number_typedef_info.metatype, SymbolMetaType::kTypeAlias);
EXPECT_EQ(number_typedef_info.file_origin, &src);
MUST_ASSIGN_LOOKUP_SYMBOL(foo_var, root_symbol, "foo");
EXPECT_EQ(foo_var_info.metatype, SymbolMetaType::kDataNetVariableInstance);
EXPECT_EQ(foo_var_info.file_origin, &src);
const auto &ref_map(root_symbol.Value().LocalReferencesMapViewForTesting());
// Expect one type reference to "pp::cc".
ASSIGN_MUST_FIND(pp_type_refs, ref_map, "pp");
ASSIGN_MUST_HAVE_UNIQUE(pp_type_ref, pp_type_refs);
const ReferenceComponent &pp_type_ref_comp(pp_type_ref->components->Value());
EXPECT_EQ(pp_type_ref_comp.identifier, "pp");
EXPECT_EQ(pp_type_ref_comp.ref_type, ReferenceType::kUnqualified);
EXPECT_EQ(pp_type_ref_comp.required_metatype, SymbolMetaType::kUnspecified);
EXPECT_EQ(pp_type_ref_comp.resolved_symbol, nullptr);
ASSIGN_MUST_HAVE_UNIQUE(cc_type_ref, pp_type_ref->components->Children());
const ReferenceComponent &cc_type_ref_comp(cc_type_ref.Value());
EXPECT_EQ(cc_type_ref_comp.identifier, "cc");
EXPECT_EQ(cc_type_ref_comp.ref_type, ReferenceType::kDirectMember);
EXPECT_EQ(cc_type_ref_comp.required_metatype, SymbolMetaType::kUnspecified);
EXPECT_EQ(cc_type_ref_comp.resolved_symbol, nullptr);
// Expect one type reference to "number".
ASSIGN_MUST_FIND(number_refs, ref_map, "number");
ASSIGN_MUST_HAVE_UNIQUE(number_ref, number_refs);
const ReferenceComponent &number_ref_comp(number_ref->components->Value());
EXPECT_EQ(number_ref_comp.identifier, "number");
EXPECT_EQ(number_ref_comp.ref_type, ReferenceType::kUnqualified);
EXPECT_EQ(number_ref_comp.required_metatype, SymbolMetaType::kUnspecified);
EXPECT_EQ(number_ref_comp.resolved_symbol, nullptr);
EXPECT_EQ(foo_var_info.declared_type.user_defined_type,
number_ref->components.get());
{
std::vector<absl::Status> resolve_diagnostics;
symbol_table.Resolve(&resolve_diagnostics);
EXPECT_EMPTY_STATUSES(resolve_diagnostics);
EXPECT_EQ(pp_type_ref_comp.resolved_symbol, &pp_package);
EXPECT_EQ(cc_type_ref_comp.resolved_symbol, &cc_class);
EXPECT_EQ(number_ref_comp.resolved_symbol, &number_typedef);
}
}
TEST_F(BuildSymbolTableTest, TypedefClassUnresolvedQualifiedReferenceBase) {
TestVerilogSourceFile src("typedef.sv",
"typedef pp::cc number;\n" // unresolved
"number foo;\n");
const auto status = src.Parse();
ASSERT_TRUE(status.ok()) << status.message();
SymbolTable symbol_table(nullptr);
const SymbolTableNode &root_symbol(symbol_table.Root());
const auto build_diagnostics = BuildSymbolTable(src, &symbol_table);
EXPECT_EMPTY_STATUSES(build_diagnostics);
MUST_ASSIGN_LOOKUP_SYMBOL(number_typedef, root_symbol, "number");
EXPECT_EQ(number_typedef_info.metatype, SymbolMetaType::kTypeAlias);
EXPECT_EQ(number_typedef_info.file_origin, &src);
MUST_ASSIGN_LOOKUP_SYMBOL(foo_var, root_symbol, "foo");
EXPECT_EQ(foo_var_info.metatype, SymbolMetaType::kDataNetVariableInstance);
EXPECT_EQ(foo_var_info.file_origin, &src);
const auto &ref_map(root_symbol.Value().LocalReferencesMapViewForTesting());
// Expect one type reference to "pp::cc".
ASSIGN_MUST_FIND(pp_type_refs, ref_map, "pp");
ASSIGN_MUST_HAVE_UNIQUE(pp_type_ref, pp_type_refs);
const ReferenceComponent &pp_type_ref_comp(pp_type_ref->components->Value());
EXPECT_EQ(pp_type_ref_comp.identifier, "pp");
EXPECT_EQ(pp_type_ref_comp.ref_type, ReferenceType::kUnqualified);
EXPECT_EQ(pp_type_ref_comp.required_metatype, SymbolMetaType::kUnspecified);
EXPECT_EQ(pp_type_ref_comp.resolved_symbol, nullptr);
ASSIGN_MUST_HAVE_UNIQUE(cc_type_ref, pp_type_ref->components->Children());
const ReferenceComponent &cc_type_ref_comp(cc_type_ref.Value());
EXPECT_EQ(cc_type_ref_comp.identifier, "cc");
EXPECT_EQ(cc_type_ref_comp.ref_type, ReferenceType::kDirectMember);
EXPECT_EQ(cc_type_ref_comp.required_metatype, SymbolMetaType::kUnspecified);
EXPECT_EQ(cc_type_ref_comp.resolved_symbol, nullptr);
// Expect one type reference to "number".
ASSIGN_MUST_FIND(number_refs, ref_map, "number");
ASSIGN_MUST_HAVE_UNIQUE(number_ref, number_refs);
const ReferenceComponent &number_ref_comp(number_ref->components->Value());
EXPECT_EQ(number_ref_comp.identifier, "number");
EXPECT_EQ(number_ref_comp.ref_type, ReferenceType::kUnqualified);
EXPECT_EQ(number_ref_comp.required_metatype, SymbolMetaType::kUnspecified);
EXPECT_EQ(number_ref_comp.resolved_symbol, nullptr);
EXPECT_EQ(foo_var_info.declared_type.user_defined_type,
number_ref->components.get());
{
std::vector<absl::Status> resolve_diagnostics;
symbol_table.Resolve(&resolve_diagnostics);
ASSIGN_MUST_HAVE_UNIQUE(err, resolve_diagnostics);
EXPECT_EQ(err.code(), absl::StatusCode::kNotFound);
EXPECT_EQ(pp_type_ref_comp.resolved_symbol, nullptr);
EXPECT_EQ(cc_type_ref_comp.resolved_symbol, nullptr);
EXPECT_EQ(number_ref_comp.resolved_symbol, &number_typedef);
}
}
TEST_F(BuildSymbolTableTest, TypedefClassPartiallyResolvedQualifiedReference) {
TestVerilogSourceFile src(
"typedef.sv",
"package pp;\n" // empty
"endpackage\n"
"typedef pp::cc::dd number;\n" // unresolved at "cc"
"number foo;\n");
const auto status = src.Parse();
ASSERT_TRUE(status.ok()) << status.message();
SymbolTable symbol_table(nullptr);
const SymbolTableNode &root_symbol(symbol_table.Root());
const auto build_diagnostics = BuildSymbolTable(src, &symbol_table);
EXPECT_EMPTY_STATUSES(build_diagnostics);
MUST_ASSIGN_LOOKUP_SYMBOL(package_pp, root_symbol, "pp");
EXPECT_EQ(package_pp_info.metatype, SymbolMetaType::kPackage);
MUST_ASSIGN_LOOKUP_SYMBOL(number_typedef, root_symbol, "number");
EXPECT_EQ(number_typedef_info.metatype, SymbolMetaType::kTypeAlias);
EXPECT_EQ(number_typedef_info.file_origin, &src);
MUST_ASSIGN_LOOKUP_SYMBOL(foo_var, root_symbol, "foo");
EXPECT_EQ(foo_var_info.metatype, SymbolMetaType::kDataNetVariableInstance);
EXPECT_EQ(foo_var_info.file_origin, &src);
const auto &ref_map(root_symbol.Value().LocalReferencesMapViewForTesting());
// Expect one type reference to "pp::cc::dd".
ASSIGN_MUST_FIND(pp_type_refs, ref_map, "pp");
ASSIGN_MUST_HAVE_UNIQUE(pp_type_ref, pp_type_refs);
const ReferenceComponent &pp_type_ref_comp(pp_type_ref->components->Value());
EXPECT_EQ(pp_type_ref_comp.identifier, "pp");
EXPECT_EQ(pp_type_ref_comp.ref_type, ReferenceType::kUnqualified);
EXPECT_EQ(pp_type_ref_comp.required_metatype, SymbolMetaType::kUnspecified);
EXPECT_EQ(pp_type_ref_comp.resolved_symbol, nullptr);
ASSIGN_MUST_HAVE_UNIQUE(cc_type_ref, pp_type_ref->components->Children());
const ReferenceComponent &cc_type_ref_comp(cc_type_ref.Value());
EXPECT_EQ(cc_type_ref_comp.identifier, "cc");
EXPECT_EQ(cc_type_ref_comp.ref_type, ReferenceType::kDirectMember);
EXPECT_EQ(cc_type_ref_comp.required_metatype, SymbolMetaType::kUnspecified);
EXPECT_EQ(cc_type_ref_comp.resolved_symbol, nullptr);
ASSIGN_MUST_HAVE_UNIQUE(dd_type_ref, cc_type_ref.Children());
const ReferenceComponent &dd_type_ref_comp(dd_type_ref.Value());
EXPECT_EQ(dd_type_ref_comp.identifier, "dd");
EXPECT_EQ(dd_type_ref_comp.ref_type, ReferenceType::kDirectMember);
EXPECT_EQ(dd_type_ref_comp.required_metatype, SymbolMetaType::kUnspecified);
EXPECT_EQ(dd_type_ref_comp.resolved_symbol, nullptr);
// Expect one type reference to "number".
ASSIGN_MUST_FIND(number_refs, ref_map, "number");
ASSIGN_MUST_HAVE_UNIQUE(number_ref, number_refs);
const ReferenceComponent &number_ref_comp(number_ref->components->Value());
EXPECT_EQ(number_ref_comp.identifier, "number");
EXPECT_EQ(number_ref_comp.ref_type, ReferenceType::kUnqualified);
EXPECT_EQ(number_ref_comp.required_metatype, SymbolMetaType::kUnspecified);
EXPECT_EQ(number_ref_comp.resolved_symbol, nullptr);
EXPECT_EQ(foo_var_info.declared_type.user_defined_type,
number_ref->components.get());
{
std::vector<absl::Status> resolve_diagnostics;
symbol_table.Resolve(&resolve_diagnostics);
ASSIGN_MUST_HAVE_UNIQUE(err, resolve_diagnostics);
EXPECT_EQ(err.code(), absl::StatusCode::kNotFound);
EXPECT_EQ(pp_type_ref_comp.resolved_symbol, &package_pp);
EXPECT_EQ(cc_type_ref_comp.resolved_symbol, nullptr); // chain fails here
EXPECT_EQ(dd_type_ref_comp.resolved_symbol, nullptr);
EXPECT_EQ(number_ref_comp.resolved_symbol, &number_typedef);
}
}
TEST_F(BuildSymbolTableTest, TypedefOfClassTypeParameter) {
TestVerilogSourceFile src("typedef.sv",
"class cc #(parameter type T = int);\n"
" typedef T number;\n"
" number foo;\n"
"endclass\n");
const auto status = src.Parse();
ASSERT_TRUE(status.ok()) << status.message();
SymbolTable symbol_table(nullptr);
const SymbolTableNode &root_symbol(symbol_table.Root());
const auto build_diagnostics = BuildSymbolTable(src, &symbol_table);
EXPECT_EMPTY_STATUSES(build_diagnostics);
MUST_ASSIGN_LOOKUP_SYMBOL(cc_class, root_symbol, "cc");
EXPECT_EQ(cc_class_info.metatype, SymbolMetaType::kClass);
EXPECT_EQ(cc_class_info.file_origin, &src);
MUST_ASSIGN_LOOKUP_SYMBOL(t_type_param, cc_class, "T");
EXPECT_EQ(t_type_param_info.metatype, SymbolMetaType::kParameter);
EXPECT_EQ(t_type_param_info.file_origin, &src);
MUST_ASSIGN_LOOKUP_SYMBOL(number_typedef, cc_class, "number");
EXPECT_EQ(number_typedef_info.metatype, SymbolMetaType::kTypeAlias);
EXPECT_EQ(number_typedef_info.file_origin, &src);
MUST_ASSIGN_LOOKUP_SYMBOL(foo_var, cc_class, "foo");
EXPECT_EQ(foo_var_info.metatype, SymbolMetaType::kDataNetVariableInstance);
EXPECT_EQ(foo_var_info.file_origin, &src);
// Expect one type reference to "number", and one to "T".
const auto &ref_map(cc_class_info.LocalReferencesMapViewForTesting());
ASSIGN_MUST_FIND(t_type_refs, ref_map, "T");
ASSIGN_MUST_HAVE_UNIQUE(t_type_ref, t_type_refs);
const ReferenceComponent &t_type_ref_comp(t_type_ref->components->Value());
EXPECT_EQ(t_type_ref_comp.identifier, "T");
EXPECT_EQ(t_type_ref_comp.ref_type, ReferenceType::kUnqualified);
EXPECT_EQ(t_type_ref_comp.required_metatype, SymbolMetaType::kUnspecified);
EXPECT_EQ(t_type_ref_comp.resolved_symbol, nullptr);
ASSIGN_MUST_FIND(number_refs, ref_map, "number");
ASSIGN_MUST_HAVE_UNIQUE(number_ref, number_refs);
const ReferenceComponent &number_ref_comp(number_ref->components->Value());
EXPECT_EQ(number_ref_comp.identifier, "number");
EXPECT_EQ(number_ref_comp.ref_type, ReferenceType::kUnqualified);
EXPECT_EQ(number_ref_comp.required_metatype, SymbolMetaType::kUnspecified);
EXPECT_EQ(number_ref_comp.resolved_symbol, nullptr);
EXPECT_EQ(foo_var_info.declared_type.user_defined_type,
number_ref->components.get());
{
std::vector<absl::Status> resolve_diagnostics;
symbol_table.Resolve(&resolve_diagnostics);
EXPECT_EMPTY_STATUSES(resolve_diagnostics);
// "number" is type-aliased to "T"
EXPECT_EQ(t_type_ref_comp.resolved_symbol, &t_type_param);
EXPECT_EQ(number_ref_comp.resolved_symbol, &number_typedef);
}
}
TEST_F(BuildSymbolTableTest, TypedefOfParameterizedClassPositionalParams) {
TestVerilogSourceFile src("typedef.sv",
"package pp;\n"
" class cc #(parameter type T = int);\n"
" endclass\n"
"endpackage\n"
"typedef pp::cc#(pp::cc#(int)) number;\n"
"number foo;\n");
const auto status = src.Parse();
ASSERT_TRUE(status.ok()) << status.message();
SymbolTable symbol_table(nullptr);
const SymbolTableNode &root_symbol(symbol_table.Root());
const auto build_diagnostics = BuildSymbolTable(src, &symbol_table);
EXPECT_EMPTY_STATUSES(build_diagnostics);
MUST_ASSIGN_LOOKUP_SYMBOL(pp_package, root_symbol, "pp");
EXPECT_EQ(pp_package_info.metatype, SymbolMetaType::kPackage);
EXPECT_EQ(pp_package_info.file_origin, &src);
MUST_ASSIGN_LOOKUP_SYMBOL(cc_class, pp_package, "cc");
EXPECT_EQ(cc_class_info.metatype, SymbolMetaType::kClass);
EXPECT_EQ(cc_class_info.file_origin, &src);
MUST_ASSIGN_LOOKUP_SYMBOL(t_type_param, cc_class, "T");
EXPECT_EQ(t_type_param_info.metatype, SymbolMetaType::kParameter);
EXPECT_EQ(t_type_param_info.file_origin, &src);
MUST_ASSIGN_LOOKUP_SYMBOL(number_typedef, root_symbol, "number");
EXPECT_EQ(number_typedef_info.metatype, SymbolMetaType::kTypeAlias);
EXPECT_EQ(number_typedef_info.file_origin, &src);
MUST_ASSIGN_LOOKUP_SYMBOL(foo_var, root_symbol, "foo");
EXPECT_EQ(foo_var_info.metatype, SymbolMetaType::kDataNetVariableInstance);
EXPECT_EQ(foo_var_info.file_origin, &src);
const auto &ref_map(root_symbol.Value().LocalReferencesMapViewForTesting());
// Expect one type reference to "number".
ASSIGN_MUST_FIND(number_refs, ref_map, "number");
ASSIGN_MUST_HAVE_UNIQUE(number_ref, number_refs);
const ReferenceComponent &number_ref_comp(number_ref->components->Value());
EXPECT_EQ(number_ref_comp.identifier, "number");
EXPECT_EQ(number_ref_comp.ref_type, ReferenceType::kUnqualified);
EXPECT_EQ(number_ref_comp.required_metatype, SymbolMetaType::kUnspecified);
EXPECT_EQ(number_ref_comp.resolved_symbol, nullptr);
// Expect two type references to "pp::cc".
ASSIGN_MUST_FIND(pp_refs, ref_map, "pp");
EXPECT_EQ(pp_refs.size(), 2);
for (const auto &pp_ref_iter : pp_refs) {
const ReferenceComponent &pp_ref_comp(pp_ref_iter->components->Value());
EXPECT_EQ(pp_ref_comp.identifier, "pp");
EXPECT_EQ(pp_ref_comp.ref_type, ReferenceType::kUnqualified);
EXPECT_EQ(pp_ref_comp.required_metatype, SymbolMetaType::kUnspecified);
EXPECT_EQ(pp_ref_comp.resolved_symbol, nullptr);
ASSIGN_MUST_HAVE_UNIQUE(cc_ref, pp_ref_iter->components->Children());
const ReferenceComponent &cc_ref_comp(cc_ref.Value());
EXPECT_EQ(cc_ref_comp.identifier, "cc");
EXPECT_EQ(cc_ref_comp.ref_type, ReferenceType::kDirectMember);
EXPECT_EQ(cc_ref_comp.required_metatype, SymbolMetaType::kUnspecified);
EXPECT_EQ(cc_ref_comp.resolved_symbol, nullptr);
}
EXPECT_EQ(foo_var_info.declared_type.user_defined_type,
number_ref->components.get());
{
std::vector<absl::Status> resolve_diagnostics;
symbol_table.Resolve(&resolve_diagnostics);
EXPECT_EMPTY_STATUSES(resolve_diagnostics);
// Resolve "pp::cc" type references
for (const auto &pp_ref_iter : pp_refs) {
const ReferenceComponent &pp_ref_comp(pp_ref_iter->components->Value());
EXPECT_EQ(pp_ref_comp.resolved_symbol, &pp_package);
ASSIGN_MUST_HAVE_UNIQUE(cc_ref, pp_ref_iter->components->Children());
const ReferenceComponent &cc_ref_comp(cc_ref.Value());
EXPECT_EQ(cc_ref_comp.resolved_symbol, &cc_class);
}
// Resolve "number" type reference.
EXPECT_EQ(number_ref_comp.resolved_symbol, &number_typedef);
}
}
TEST_F(BuildSymbolTableTest, TypedefOfParameterizedClassNamedParams) {
TestVerilogSourceFile src("typedef.sv",
"package pp;\n"
" class cc #(parameter type T = int);\n"
" endclass\n"
"endpackage\n"
"typedef pp::cc#(.T(pp::cc#(.T(int)))) number;\n"
"number foo;\n");
const auto status = src.Parse();
ASSERT_TRUE(status.ok()) << status.message();
SymbolTable symbol_table(nullptr);
const SymbolTableNode &root_symbol(symbol_table.Root());
const auto build_diagnostics = BuildSymbolTable(src, &symbol_table);
EXPECT_EMPTY_STATUSES(build_diagnostics);
MUST_ASSIGN_LOOKUP_SYMBOL(pp_package, root_symbol, "pp");
EXPECT_EQ(pp_package_info.metatype, SymbolMetaType::kPackage);
EXPECT_EQ(pp_package_info.file_origin, &src);
MUST_ASSIGN_LOOKUP_SYMBOL(cc_class, pp_package, "cc");
EXPECT_EQ(cc_class_info.metatype, SymbolMetaType::kClass);
EXPECT_EQ(cc_class_info.file_origin, &src);
MUST_ASSIGN_LOOKUP_SYMBOL(t_type_param, cc_class, "T");
EXPECT_EQ(t_type_param_info.metatype, SymbolMetaType::kParameter);
EXPECT_EQ(t_type_param_info.file_origin, &src);
MUST_ASSIGN_LOOKUP_SYMBOL(number_typedef, root_symbol, "number");
EXPECT_EQ(number_typedef_info.metatype, SymbolMetaType::kTypeAlias);
EXPECT_EQ(number_typedef_info.file_origin, &src);
MUST_ASSIGN_LOOKUP_SYMBOL(foo_var, root_symbol, "foo");
EXPECT_EQ(foo_var_info.metatype, SymbolMetaType::kDataNetVariableInstance);
EXPECT_EQ(foo_var_info.file_origin, &src);
const auto &ref_map(root_symbol.Value().LocalReferencesMapViewForTesting());
// Expect one type reference to "number".
ASSIGN_MUST_FIND(number_refs, ref_map, "number");
ASSIGN_MUST_HAVE_UNIQUE(number_ref, number_refs);
const ReferenceComponent &number_ref_comp(number_ref->components->Value());
EXPECT_EQ(number_ref_comp.identifier, "number");
EXPECT_EQ(number_ref_comp.ref_type, ReferenceType::kUnqualified);
EXPECT_EQ(number_ref_comp.required_metatype, SymbolMetaType::kUnspecified);
EXPECT_EQ(number_ref_comp.resolved_symbol, nullptr);
// Expect two type references to "pp::cc#(.T(...))".
ASSIGN_MUST_FIND(pp_refs, ref_map, "pp");
EXPECT_EQ(pp_refs.size(), 2);
for (const auto &pp_ref_iter : pp_refs) {
const ReferenceComponent &pp_ref_comp(pp_ref_iter->components->Value());
EXPECT_EQ(pp_ref_comp.identifier, "pp");
EXPECT_EQ(pp_ref_comp.ref_type, ReferenceType::kUnqualified);
EXPECT_EQ(pp_ref_comp.required_metatype, SymbolMetaType::kUnspecified);
EXPECT_EQ(pp_ref_comp.resolved_symbol, nullptr);
ASSIGN_MUST_HAVE_UNIQUE(cc_ref, pp_ref_iter->components->Children());
const ReferenceComponent &cc_ref_comp(cc_ref.Value());
EXPECT_EQ(cc_ref_comp.identifier, "cc");
EXPECT_EQ(cc_ref_comp.ref_type, ReferenceType::kDirectMember);
EXPECT_EQ(cc_ref_comp.required_metatype, SymbolMetaType::kUnspecified);
EXPECT_EQ(cc_ref_comp.resolved_symbol, nullptr);
ASSIGN_MUST_HAVE_UNIQUE(t_param_ref, cc_ref.Children());
const ReferenceComponent &t_param_ref_comp(t_param_ref.Value());
EXPECT_EQ(t_param_ref_comp.identifier, "T");
EXPECT_EQ(t_param_ref_comp.ref_type, ReferenceType::kDirectMember);
EXPECT_EQ(t_param_ref_comp.required_metatype, SymbolMetaType::kParameter);
EXPECT_EQ(t_param_ref_comp.resolved_symbol, nullptr);
}
EXPECT_EQ(foo_var_info.declared_type.user_defined_type,
number_ref->components.get());
{
std::vector<absl::Status> resolve_diagnostics;
symbol_table.Resolve(&resolve_diagnostics);
EXPECT_EMPTY_STATUSES(resolve_diagnostics);
// Resolve "pp::cc#(.T(...))" type references
for (const auto &pp_ref_iter : pp_refs) {
const ReferenceComponent &pp_ref_comp(pp_ref_iter->components->Value());
EXPECT_EQ(pp_ref_comp.resolved_symbol, &pp_package);
ASSIGN_MUST_HAVE_UNIQUE(cc_ref, pp_ref_iter->components->Children());
const ReferenceComponent &cc_ref_comp(cc_ref.Value());
EXPECT_EQ(cc_ref_comp.resolved_symbol, &cc_class);
ASSIGN_MUST_HAVE_UNIQUE(t_param_ref, cc_ref.Children());
const ReferenceComponent &t_param_ref_comp(t_param_ref.Value());
EXPECT_EQ(t_param_ref_comp.resolved_symbol, &t_type_param);
}
// Resolve "number" type reference.
EXPECT_EQ(number_ref_comp.resolved_symbol, &number_typedef);
}
}
TEST_F(BuildSymbolTableTest, InvalidMemberLookupOfAliasedType) {
TestVerilogSourceFile src("typedef.sv",
"typedef int number;\n"
"typedef number::count bar;\n");
const auto status = src.Parse();
ASSERT_TRUE(status.ok()) << status.message();
SymbolTable symbol_table(nullptr);
const SymbolTableNode &root_symbol(symbol_table.Root());
const auto build_diagnostics = BuildSymbolTable(src, &symbol_table);
EXPECT_EMPTY_STATUSES(build_diagnostics);
MUST_ASSIGN_LOOKUP_SYMBOL(number_typedef, root_symbol, "number");
EXPECT_EQ(number_typedef_info.metatype, SymbolMetaType::kTypeAlias);
EXPECT_EQ(number_typedef_info.file_origin, &src);
EXPECT_EQ(number_typedef_info.declared_type.user_defined_type, nullptr);
EXPECT_EQ(verible::StringSpanOfSymbol(
*number_typedef_info.declared_type.syntax_origin),
"int");
// Expect one type reference to "number".
const auto &get_count_ref_map(
root_symbol.Value().LocalReferencesMapViewForTesting());
ASSIGN_MUST_FIND(number_refs, get_count_ref_map, "number");
ASSIGN_MUST_HAVE_UNIQUE(number_ref, number_refs);
const ReferenceComponent &number_ref_comp(number_ref->components->Value());
EXPECT_EQ(number_ref_comp.identifier, "number");
EXPECT_EQ(number_ref_comp.ref_type, ReferenceType::kUnqualified);
EXPECT_EQ(number_ref_comp.required_metatype, SymbolMetaType::kUnspecified);
EXPECT_EQ(number_ref_comp.resolved_symbol, nullptr);
ASSIGN_MUST_HAVE_UNIQUE(number_count_ref, number_ref->components->Children());
const ReferenceComponent &number_count_ref_comp(number_count_ref.Value());
EXPECT_EQ(number_count_ref_comp.identifier, "count");
EXPECT_EQ(number_count_ref_comp.ref_type, ReferenceType::kDirectMember);
EXPECT_EQ(number_count_ref_comp.required_metatype,
SymbolMetaType::kUnspecified);
EXPECT_EQ(number_count_ref_comp.resolved_symbol, nullptr);
// Expect one type reference to "number".
MUST_ASSIGN_LOOKUP_SYMBOL(bar, root_symbol, "bar");
EXPECT_EQ(bar_info.metatype, SymbolMetaType::kTypeAlias);
EXPECT_EQ(bar_info.file_origin, &src);
// type of "bar" is "number::count"
EXPECT_EQ(bar_info.declared_type.user_defined_type, &number_count_ref);
{
std::vector<absl::Status> resolve_diagnostics;
symbol_table.Resolve(&resolve_diagnostics);
ASSIGN_MUST_HAVE_UNIQUE(err, resolve_diagnostics);
EXPECT_EQ(err.code(), absl::StatusCode::kInvalidArgument);
EXPECT_THAT(err.message(), HasSubstr("Canonical type of "));
EXPECT_THAT(err.message(), HasSubstr("does not have any members"));
// Resolving "number::count" should fail.
EXPECT_EQ(number_ref_comp.resolved_symbol, &number_typedef);
EXPECT_EQ(number_count_ref_comp.resolved_symbol, nullptr); // failed
}
}
TEST_F(BuildSymbolTableTest, InvalidMemberLookupOfTypedefPrimitive) {
TestVerilogSourceFile src("typedef.sv",
"typedef int number;\n"
"function int get_count(number foo);\n"
" return foo.count;\n" // invalid member
"endfunction\n");
const auto status = src.Parse();
ASSERT_TRUE(status.ok()) << status.message();
SymbolTable symbol_table(nullptr);
const SymbolTableNode &root_symbol(symbol_table.Root());
const auto build_diagnostics = BuildSymbolTable(src, &symbol_table);
EXPECT_EMPTY_STATUSES(build_diagnostics);
MUST_ASSIGN_LOOKUP_SYMBOL(number_typedef, root_symbol, "number");
EXPECT_EQ(number_typedef_info.metatype, SymbolMetaType::kTypeAlias);
EXPECT_EQ(number_typedef_info.file_origin, &src);
EXPECT_EQ(number_typedef_info.declared_type.user_defined_type, nullptr);
EXPECT_EQ(verible::StringSpanOfSymbol(
*number_typedef_info.declared_type.syntax_origin),
"int");
MUST_ASSIGN_LOOKUP_SYMBOL(get_count, root_symbol, "get_count");
EXPECT_EQ(get_count_info.metatype, SymbolMetaType::kFunction);
EXPECT_EQ(get_count_info.file_origin, &src);
EXPECT_EQ(get_count_info.declared_type.user_defined_type, nullptr); // int
MUST_ASSIGN_LOOKUP_SYMBOL(foo_var, get_count, "foo");
EXPECT_EQ(foo_var_info.metatype, SymbolMetaType::kDataNetVariableInstance);
EXPECT_EQ(foo_var_info.file_origin, &src);
// Expect one type reference to "number".
const auto &get_count_ref_map(
get_count_info.LocalReferencesMapViewForTesting());
ASSIGN_MUST_FIND(number_refs, get_count_ref_map, "number");
ASSIGN_MUST_HAVE_UNIQUE(number_ref, number_refs);
const ReferenceComponent &number_ref_comp(number_ref->components->Value());
EXPECT_EQ(number_ref_comp.identifier, "number");
EXPECT_EQ(number_ref_comp.ref_type, ReferenceType::kUnqualified);
EXPECT_EQ(number_ref_comp.required_metatype, SymbolMetaType::kUnspecified);
EXPECT_EQ(number_ref_comp.resolved_symbol, nullptr);
ASSIGN_MUST_FIND(foo_refs, get_count_ref_map, "foo");
ASSIGN_MUST_HAVE_UNIQUE(foo_ref, foo_refs);
const ReferenceComponent &foo_ref_comp(foo_ref->components->Value());
EXPECT_EQ(foo_ref_comp.identifier, "foo");
EXPECT_EQ(foo_ref_comp.ref_type, ReferenceType::kUnqualified);
EXPECT_EQ(foo_ref_comp.required_metatype, SymbolMetaType::kUnspecified);
EXPECT_EQ(foo_ref_comp.resolved_symbol, nullptr);
ASSIGN_MUST_HAVE_UNIQUE(foo_count_ref, foo_ref->components->Children());
const ReferenceComponent &foo_count_ref_comp(foo_count_ref.Value());
EXPECT_EQ(foo_count_ref_comp.identifier, "count");
EXPECT_EQ(foo_count_ref_comp.ref_type, ReferenceType::kMemberOfTypeOfParent);
EXPECT_EQ(foo_count_ref_comp.required_metatype, SymbolMetaType::kUnspecified);
EXPECT_EQ(foo_count_ref_comp.resolved_symbol, nullptr);
// type of "foo" is "number"
EXPECT_EQ(foo_var_info.declared_type.user_defined_type,
number_ref->components.get());
{
std::vector<absl::Status> resolve_diagnostics;
symbol_table.Resolve(&resolve_diagnostics);
ASSIGN_MUST_HAVE_UNIQUE(err, resolve_diagnostics);
EXPECT_EQ(err.code(), absl::StatusCode::kInvalidArgument);
EXPECT_THAT(err.message(), HasSubstr("Canonical type of "));
EXPECT_THAT(err.message(), HasSubstr("does not have any members"));
// Resolve "foo.count" to "cc::count" through typedef "number"
EXPECT_EQ(foo_ref_comp.resolved_symbol, &foo_var);
EXPECT_EQ(foo_count_ref_comp.resolved_symbol, nullptr); // failed
EXPECT_EQ(number_ref_comp.resolved_symbol, &number_typedef);
}
}
TEST_F(BuildSymbolTableTest, AccessClassMemberThroughTypedef) {
TestVerilogSourceFile src("typedef.sv",
"class cc;\n"
" int count;\n"
"endclass\n"
"typedef cc number;\n"
"function int get_count(number foo);\n"
" return foo.count;\n"
"endfunction\n");
const auto status = src.Parse();
ASSERT_TRUE(status.ok()) << status.message();
SymbolTable symbol_table(nullptr);
const SymbolTableNode &root_symbol(symbol_table.Root());
const auto build_diagnostics = BuildSymbolTable(src, &symbol_table);
EXPECT_EMPTY_STATUSES(build_diagnostics);
MUST_ASSIGN_LOOKUP_SYMBOL(cc_class, root_symbol, "cc");
EXPECT_EQ(cc_class_info.metatype, SymbolMetaType::kClass);
EXPECT_EQ(cc_class_info.file_origin, &src);
MUST_ASSIGN_LOOKUP_SYMBOL(int_count, cc_class, "count");
EXPECT_EQ(int_count_info.metatype, SymbolMetaType::kDataNetVariableInstance);
EXPECT_EQ(int_count_info.file_origin, &src);
EXPECT_EQ(int_count_info.declared_type.user_defined_type, nullptr); // int
MUST_ASSIGN_LOOKUP_SYMBOL(number_typedef, root_symbol, "number");
EXPECT_EQ(number_typedef_info.metatype, SymbolMetaType::kTypeAlias);
EXPECT_EQ(number_typedef_info.file_origin, &src);
MUST_ASSIGN_LOOKUP_SYMBOL(get_count, root_symbol, "get_count");
EXPECT_EQ(get_count_info.metatype, SymbolMetaType::kFunction);
EXPECT_EQ(get_count_info.file_origin, &src);
EXPECT_EQ(get_count_info.declared_type.user_defined_type, nullptr); // int
MUST_ASSIGN_LOOKUP_SYMBOL(foo_var, get_count, "foo");
EXPECT_EQ(foo_var_info.metatype, SymbolMetaType::kDataNetVariableInstance);
EXPECT_EQ(foo_var_info.file_origin, &src);
// Expect one type reference to "cc".
const auto &root_ref_map(
root_symbol.Value().LocalReferencesMapViewForTesting());
ASSIGN_MUST_FIND(cc_type_refs, root_ref_map, "cc");
ASSIGN_MUST_HAVE_UNIQUE(cc_type_ref, cc_type_refs);
const ReferenceComponent &cc_type_ref_comp(cc_type_ref->components->Value());
EXPECT_EQ(cc_type_ref_comp.identifier, "cc");
EXPECT_EQ(cc_type_ref_comp.ref_type, ReferenceType::kUnqualified);
EXPECT_EQ(cc_type_ref_comp.required_metatype, SymbolMetaType::kUnspecified);
EXPECT_EQ(cc_type_ref_comp.resolved_symbol, nullptr);
EXPECT_EQ(number_typedef_info.declared_type.user_defined_type,
cc_type_ref->LastTypeComponent());
// Expect one type reference to "number".
const auto &get_count_ref_map(
get_count_info.LocalReferencesMapViewForTesting());
ASSIGN_MUST_FIND(number_refs, get_count_ref_map, "number");
ASSIGN_MUST_HAVE_UNIQUE(number_ref, number_refs);
const ReferenceComponent &number_ref_comp(number_ref->components->Value());
EXPECT_EQ(number_ref_comp.identifier, "number");
EXPECT_EQ(number_ref_comp.ref_type, ReferenceType::kUnqualified);
EXPECT_EQ(number_ref_comp.required_metatype, SymbolMetaType::kUnspecified);
EXPECT_EQ(number_ref_comp.resolved_symbol, nullptr);
ASSIGN_MUST_FIND(foo_refs, get_count_ref_map, "foo");
ASSIGN_MUST_HAVE_UNIQUE(foo_ref, foo_refs);
const ReferenceComponent &foo_ref_comp(foo_ref->components->Value());
EXPECT_EQ(foo_ref_comp.identifier, "foo");
EXPECT_EQ(foo_ref_comp.ref_type, ReferenceType::kUnqualified);
EXPECT_EQ(foo_ref_comp.required_metatype, SymbolMetaType::kUnspecified);
EXPECT_EQ(foo_ref_comp.resolved_symbol, nullptr);
ASSIGN_MUST_HAVE_UNIQUE(foo_count_ref, foo_ref->components->Children());
const ReferenceComponent &foo_count_ref_comp(foo_count_ref.Value());
EXPECT_EQ(foo_count_ref_comp.identifier, "count");
EXPECT_EQ(foo_count_ref_comp.ref_type, ReferenceType::kMemberOfTypeOfParent);
EXPECT_EQ(foo_count_ref_comp.required_metatype, SymbolMetaType::kUnspecified);
EXPECT_EQ(foo_count_ref_comp.resolved_symbol, nullptr);
// type of "foo" is "number"
EXPECT_EQ(foo_var_info.declared_type.user_defined_type,
number_ref->components.get());
{
std::vector<absl::Status> resolve_diagnostics;
symbol_table.Resolve(&resolve_diagnostics);
EXPECT_EMPTY_STATUSES(resolve_diagnostics);
// Resolve "foo.count" to "cc::count" through typedef "number"
EXPECT_EQ(cc_type_ref_comp.resolved_symbol, &cc_class);
EXPECT_EQ(foo_ref_comp.resolved_symbol, &foo_var);
EXPECT_EQ(foo_count_ref_comp.resolved_symbol, &int_count);
EXPECT_EQ(number_ref_comp.resolved_symbol, &number_typedef);
}
}
TEST_F(BuildSymbolTableTest, AccessStructMemberThroughTypedef) {
TestVerilogSourceFile src("typedef.sv",
"typedef struct {\n"
" int count;\n"
"} number;\n"
"function int get_count(number foo);\n"
" return foo.count;\n"
"endfunction\n");
const auto status = src.Parse();
ASSERT_TRUE(status.ok()) << status.message();
SymbolTable symbol_table(nullptr);
const SymbolTableNode &root_symbol(symbol_table.Root());
const auto build_diagnostics = BuildSymbolTable(src, &symbol_table);
EXPECT_EMPTY_STATUSES(build_diagnostics);
// Find the symbol that is a struct (anon).
const auto found = std::find_if(root_symbol.Children().begin(),
root_symbol.Children().end(), IsStruct);
ASSERT_NE(found, root_symbol.Children().end());
const SymbolTableNode &anon_struct(found->second);
const SymbolInfo &anon_struct_info(anon_struct.Value());
EXPECT_EQ(anon_struct_info.metatype, SymbolMetaType::kStruct);
EXPECT_TRUE(anon_struct_info.local_references_to_bind.empty());
MUST_ASSIGN_LOOKUP_SYMBOL(int_count, anon_struct, "count");
EXPECT_EQ(int_count_info.metatype, SymbolMetaType::kDataNetVariableInstance);
EXPECT_EQ(int_count_info.file_origin, &src);
EXPECT_EQ(int_count_info.declared_type.user_defined_type, nullptr); // int
MUST_ASSIGN_LOOKUP_SYMBOL(number_typedef, root_symbol, "number");
EXPECT_EQ(number_typedef_info.metatype, SymbolMetaType::kTypeAlias);
EXPECT_EQ(number_typedef_info.file_origin, &src);
MUST_ASSIGN_LOOKUP_SYMBOL(get_count, root_symbol, "get_count");
EXPECT_EQ(get_count_info.metatype, SymbolMetaType::kFunction);
EXPECT_EQ(get_count_info.file_origin, &src);
EXPECT_EQ(get_count_info.declared_type.user_defined_type, nullptr); // int
MUST_ASSIGN_LOOKUP_SYMBOL(foo_var, get_count, "foo");
EXPECT_EQ(foo_var_info.metatype, SymbolMetaType::kDataNetVariableInstance);
EXPECT_EQ(foo_var_info.file_origin, &src);
// typedef struct is already resolved.
ASSERT_NE(number_typedef_info.declared_type.user_defined_type, nullptr);
EXPECT_EQ(number_typedef_info.declared_type.user_defined_type->Value()
.resolved_symbol,
&anon_struct);
// Expect one type reference to "number".
const auto &get_count_ref_map(
get_count_info.LocalReferencesMapViewForTesting());
ASSIGN_MUST_FIND(number_refs, get_count_ref_map, "number");
ASSIGN_MUST_HAVE_UNIQUE(number_ref, number_refs);
const ReferenceComponent &number_ref_comp(number_ref->components->Value());
EXPECT_EQ(number_ref_comp.identifier, "number");
EXPECT_EQ(number_ref_comp.ref_type, ReferenceType::kUnqualified);
EXPECT_EQ(number_ref_comp.required_metatype, SymbolMetaType::kUnspecified);
EXPECT_EQ(number_ref_comp.resolved_symbol, nullptr);
ASSIGN_MUST_FIND(foo_refs, get_count_ref_map, "foo");
ASSIGN_MUST_HAVE_UNIQUE(foo_ref, foo_refs);
const ReferenceComponent &foo_ref_comp(foo_ref->components->Value());
EXPECT_EQ(foo_ref_comp.identifier, "foo");
EXPECT_EQ(foo_ref_comp.ref_type, ReferenceType::kUnqualified);
EXPECT_EQ(foo_ref_comp.required_metatype, SymbolMetaType::kUnspecified);
EXPECT_EQ(foo_ref_comp.resolved_symbol, nullptr);
ASSIGN_MUST_HAVE_UNIQUE(foo_count_ref, foo_ref->components->Children());
const ReferenceComponent &foo_count_ref_comp(foo_count_ref.Value());
EXPECT_EQ(foo_count_ref_comp.identifier, "count");
EXPECT_EQ(foo_count_ref_comp.ref_type, ReferenceType::kMemberOfTypeOfParent);
EXPECT_EQ(foo_count_ref_comp.required_metatype, SymbolMetaType::kUnspecified);
EXPECT_EQ(foo_count_ref_comp.resolved_symbol, nullptr);
// type of "foo" is "number"
EXPECT_EQ(foo_var_info.declared_type.user_defined_type,
number_ref->components.get());
{
std::vector<absl::Status> resolve_diagnostics;
symbol_table.Resolve(&resolve_diagnostics);
EXPECT_EMPTY_STATUSES(resolve_diagnostics);
// Resolve "foo.count" to "cc::count" through typedef "number"
EXPECT_EQ(foo_ref_comp.resolved_symbol, &foo_var);
EXPECT_EQ(foo_count_ref_comp.resolved_symbol, &int_count);
EXPECT_EQ(number_ref_comp.resolved_symbol, &number_typedef);
}
}
TEST_F(BuildSymbolTableTest, InheritBaseClassThroughTypedef) {
TestVerilogSourceFile src("typedef.sv",
"class base;\n"
" typedef int number;\n"
"endclass\n"
"typedef base base_alias;\n"
"class derived extends base_alias;\n"
" number count;\n"
"endclass\n");
const auto status = src.Parse();
ASSERT_TRUE(status.ok()) << status.message();
SymbolTable symbol_table(nullptr);
const SymbolTableNode &root_symbol(symbol_table.Root());
const auto build_diagnostics = BuildSymbolTable(src, &symbol_table);
EXPECT_EMPTY_STATUSES(build_diagnostics);
MUST_ASSIGN_LOOKUP_SYMBOL(base_class, root_symbol, "base");
EXPECT_EQ(base_class_info.metatype, SymbolMetaType::kClass);
EXPECT_EQ(base_class_info.file_origin, &src);
MUST_ASSIGN_LOOKUP_SYMBOL(number_typedef, base_class, "number");
EXPECT_EQ(number_typedef_info.metatype, SymbolMetaType::kTypeAlias);
EXPECT_EQ(number_typedef_info.file_origin, &src);
EXPECT_EQ(number_typedef_info.declared_type.user_defined_type,
nullptr); // int
MUST_ASSIGN_LOOKUP_SYMBOL(base_alias, root_symbol, "base_alias");
EXPECT_EQ(base_alias_info.metatype, SymbolMetaType::kTypeAlias);
EXPECT_EQ(base_alias_info.file_origin, &src);
MUST_ASSIGN_LOOKUP_SYMBOL(derived_class, root_symbol, "derived");
EXPECT_EQ(derived_class_info.metatype, SymbolMetaType::kClass);
EXPECT_EQ(derived_class_info.file_origin, &src);
MUST_ASSIGN_LOOKUP_SYMBOL(count, derived_class, "count");
EXPECT_EQ(count_info.metatype, SymbolMetaType::kDataNetVariableInstance);
EXPECT_EQ(count_info.file_origin, &src);
const auto &root_ref_map(
root_symbol.Value().LocalReferencesMapViewForTesting());
// Expect one reference to "base"
ASSIGN_MUST_FIND(base_type_refs, root_ref_map, "base");
ASSIGN_MUST_HAVE_UNIQUE(base_type_ref, base_type_refs);
const ReferenceComponent &base_type_ref_comp(
base_type_ref->components->Value());
EXPECT_EQ(base_type_ref_comp.identifier, "base");
EXPECT_EQ(base_type_ref_comp.ref_type, ReferenceType::kUnqualified);
EXPECT_EQ(base_type_ref_comp.required_metatype, SymbolMetaType::kUnspecified);
EXPECT_EQ(base_type_ref_comp.resolved_symbol, nullptr);
EXPECT_EQ(base_alias_info.declared_type.user_defined_type,
base_type_ref->components.get());
// Expect one reference to "base_alias"
ASSIGN_MUST_FIND(base_alias_refs, root_ref_map, "base_alias");
ASSIGN_MUST_HAVE_UNIQUE(base_alias_ref, base_alias_refs);
const ReferenceComponent &base_alias_ref_comp(
base_alias_ref->components->Value());
EXPECT_EQ(base_alias_ref_comp.identifier, "base_alias");
EXPECT_EQ(base_alias_ref_comp.ref_type, ReferenceType::kUnqualified);
EXPECT_EQ(base_alias_ref_comp.required_metatype, SymbolMetaType::kClass);
EXPECT_EQ(base_alias_ref_comp.resolved_symbol, nullptr);
EXPECT_EQ(derived_class_info.parent_type.user_defined_type,
base_alias_ref->components.get());
const auto &derived_ref_map(
derived_class_info.LocalReferencesMapViewForTesting());
// Expect one type reference to "number".
ASSIGN_MUST_FIND(number_refs, derived_ref_map, "number");
ASSIGN_MUST_HAVE_UNIQUE(number_ref, number_refs);
const ReferenceComponent &number_ref_comp(number_ref->components->Value());
EXPECT_EQ(number_ref_comp.identifier, "number");
EXPECT_EQ(number_ref_comp.ref_type, ReferenceType::kUnqualified);
EXPECT_EQ(number_ref_comp.required_metatype, SymbolMetaType::kUnspecified);
EXPECT_EQ(number_ref_comp.resolved_symbol, nullptr);
EXPECT_EQ(count_info.declared_type.user_defined_type,
number_ref->components.get());
{
std::vector<absl::Status> resolve_diagnostics;
symbol_table.Resolve(&resolve_diagnostics);
EXPECT_EMPTY_STATUSES(resolve_diagnostics);
EXPECT_EQ(base_type_ref_comp.resolved_symbol, &base_class);
EXPECT_EQ(base_alias_ref_comp.resolved_symbol, &base_alias);
// "number" is resolved to "base::number"
EXPECT_EQ(number_ref_comp.resolved_symbol, &number_typedef);
}
}
static bool SourceFileLess(const TestVerilogSourceFile *left,
const TestVerilogSourceFile *right) {
return left->ReferencedPath() < right->ReferencedPath();
}
static void SortSourceFiles(
std::vector<const TestVerilogSourceFile *> *sources) {
std::sort(sources->begin(), sources->end(), SourceFileLess);
}
static bool PermuteSourceFiles(
std::vector<const TestVerilogSourceFile *> *sources) {
return std::next_permutation(sources->begin(), sources->end(),
SourceFileLess);
}
TEST_F(BuildSymbolTableTest, MultiFileModuleInstance) {
// Linear dependency chain between 3 files.
TestVerilogSourceFile pp_src("pp.sv",
"module pp;\n"
"endmodule\n");
TestVerilogSourceFile qq_src("qq.sv",
"module qq;\n"
" pp pp_inst();\n" // instance
"endmodule\n");
TestVerilogSourceFile ss_src("ss.sv",
"module ss;\n"
" qq qq_inst();\n" // instance
"endmodule\n");
{
const auto status = pp_src.Parse();
ASSERT_TRUE(status.ok()) << status.message();
}
{
const auto status = qq_src.Parse();
ASSERT_TRUE(status.ok()) << status.message();
}
{
const auto status = ss_src.Parse();
ASSERT_TRUE(status.ok()) << status.message();
}
// All permutations of the following file ordering should end up with the
// same results.
std::vector<const TestVerilogSourceFile *> ordering(
{&pp_src, &qq_src, &ss_src});
// start with the lexicographically "lowest" permutation
SortSourceFiles(&ordering);
int count = 0;
do {
SymbolTable symbol_table(nullptr);
const SymbolTableNode &root_symbol(symbol_table.Root());
for (const auto *src : ordering) {
const auto build_diagnostics = BuildSymbolTable(*src, &symbol_table);
EXPECT_EMPTY_STATUSES(build_diagnostics);
}
// Goal: resolve the reference of "pp" to this definition node.
MUST_ASSIGN_LOOKUP_SYMBOL(pp, root_symbol, "pp");
// Inspect inside the "qq" module definition.
MUST_ASSIGN_LOOKUP_SYMBOL(qq, root_symbol, "qq");
MUST_ASSIGN_LOOKUP_SYMBOL(ss, root_symbol, "ss");
// "pp_inst" is an instance of type "pp"
MUST_ASSIGN_LOOKUP_SYMBOL(pp_inst, qq, "pp_inst");
// "qq_inst" is an instance of type "qq"
MUST_ASSIGN_LOOKUP_SYMBOL(qq_inst, ss, "qq_inst");
EXPECT_EQ(pp_info.file_origin, &pp_src);
EXPECT_EQ(qq_info.file_origin, &qq_src);
EXPECT_EQ(ss_info.file_origin, &ss_src);
{
ASSERT_EQ(qq_info.local_references_to_bind.size(), 2);
const auto ref_map(qq_info.LocalReferencesMapViewForTesting());
{
ASSIGN_MUST_FIND_EXACTLY_ONE_REF(pp_type, ref_map, "pp");
const ReferenceComponentNode *ref_node = pp_type->LastTypeComponent();
ASSERT_NE(ref_node, nullptr);
const ReferenceComponent &ref(ref_node->Value());
EXPECT_EQ(ref.identifier, "pp");
EXPECT_TRUE(verible::IsSubRange(ref.identifier,
qq_src.GetTextStructure()->Contents()));
EXPECT_EQ(ref.ref_type, ReferenceType::kUnqualified);
EXPECT_EQ(ref.required_metatype, SymbolMetaType::kUnspecified);
EXPECT_EQ(ref.resolved_symbol, nullptr);
}
{ // self-reference to "pp_inst" instance
ASSIGN_MUST_FIND_EXACTLY_ONE_REF(pp_inst_self_ref, ref_map, "pp_inst");
EXPECT_TRUE(is_leaf(*pp_inst_self_ref->components)); // no named ports
// self-reference is already bound.
EXPECT_EQ(pp_inst_self_ref->components->Value().resolved_symbol,
&pp_inst);
}
}
{
ASSERT_EQ(ss_info.local_references_to_bind.size(), 2);
const auto ref_map(ss_info.LocalReferencesMapViewForTesting());
{
ASSIGN_MUST_FIND_EXACTLY_ONE_REF(qq_type, ref_map, "qq");
const ReferenceComponentNode *ref_node = qq_type->LastTypeComponent();
ASSERT_NE(ref_node, nullptr);
const ReferenceComponent &ref(ref_node->Value());
EXPECT_EQ(ref.identifier, "qq");
EXPECT_TRUE(verible::IsSubRange(ref.identifier,
ss_src.GetTextStructure()->Contents()));
EXPECT_EQ(ref.ref_type, ReferenceType::kUnqualified);
EXPECT_EQ(ref.required_metatype, SymbolMetaType::kUnspecified);
EXPECT_EQ(ref.resolved_symbol, nullptr);
}
{ // self-reference to "qq_inst" instance
ASSIGN_MUST_FIND_EXACTLY_ONE_REF(qq_inst_self_ref, ref_map, "qq_inst");
EXPECT_TRUE(is_leaf(*qq_inst_self_ref->components)); // no named ports
// self-reference is already bound.
EXPECT_EQ(qq_inst_self_ref->components->Value().resolved_symbol,
&qq_inst);
}
}
{ // Verify pp_inst's type info
EXPECT_TRUE(pp_inst_info.local_references_to_bind.empty());
EXPECT_NE(pp_inst_info.declared_type.user_defined_type, nullptr);
const ReferenceComponent &pp_type(
pp_inst_info.declared_type.user_defined_type->Value());
EXPECT_EQ(pp_type.identifier, "pp");
EXPECT_EQ(pp_type.resolved_symbol, nullptr); // nothing resolved yet
EXPECT_EQ(pp_type.ref_type, ReferenceType::kUnqualified);
EXPECT_EQ(pp_type.required_metatype, SymbolMetaType::kUnspecified);
EXPECT_EQ(pp_inst_info.file_origin, &qq_src);
}
{ // Verify qq_inst's type info
EXPECT_TRUE(qq_inst_info.local_references_to_bind.empty());
EXPECT_NE(qq_inst_info.declared_type.user_defined_type, nullptr);
const ReferenceComponent &qq_type(
qq_inst_info.declared_type.user_defined_type->Value());
EXPECT_EQ(qq_type.identifier, "qq");
EXPECT_EQ(qq_type.resolved_symbol, nullptr); // nothing resolved yet
EXPECT_EQ(qq_type.ref_type, ReferenceType::kUnqualified);
EXPECT_EQ(qq_type.required_metatype, SymbolMetaType::kUnspecified);
EXPECT_EQ(qq_inst_info.file_origin, &ss_src);
}
// Resolve symbols.
std::vector<absl::Status> resolve_diagnostics;
symbol_table.Resolve(&resolve_diagnostics);
EXPECT_EMPTY_STATUSES(resolve_diagnostics);
// Verify that typeof(pp_inst) successfully resolved to module pp.
EXPECT_EQ(
pp_inst_info.declared_type.user_defined_type->Value().resolved_symbol,
&pp);
// Verify that typeof(qq_inst) successfully resolved to module qq.
EXPECT_EQ(
qq_inst_info.declared_type.user_defined_type->Value().resolved_symbol,
&qq);
++count;
} while (PermuteSourceFiles(&ordering));
EXPECT_EQ(count, 6); // make sure we covered all permutations
}
TEST_F(BuildSymbolTableTest, ModuleInstancesFromProjectOneFileAtATime) {
VerilogProject project(sources_dir, {/* no include path */});
// Linear dependency chain between 3 files. Order arbitrarily chosen.
constexpr std::string_view //
text1(
"module ss;\n"
" qq qq_inst();\n" // instance
"endmodule\n"),
text2(
"module pp;\n"
"endmodule\n"),
text3(
"module qq;\n"
" pp pp_inst();\n" // instance
"endmodule\n");
// Write to temporary files.
const ScopedTestFile file1(sources_dir, text1);
const ScopedTestFile file2(sources_dir, text2);
const ScopedTestFile file3(sources_dir, text3);
// Register files as part of project.
for (const auto *file : {&file1, &file2, &file3}) {
const auto status_or_file =
project.OpenTranslationUnit(Basename(file->filename()));
ASSERT_TRUE(status_or_file.ok());
}
SymbolTable symbol_table(&project);
EXPECT_EQ(symbol_table.Project(), &project);
// Caller decides order of processing files, which doesn't matter for this
// example.
std::vector<absl::Status> build_diagnostics;
for (const auto *file : {&file3, &file2, &file1}) {
symbol_table.BuildSingleTranslationUnit(Basename(file->filename()),
&build_diagnostics);
EXPECT_EMPTY_STATUSES(build_diagnostics);
}
const SymbolTableNode &root_symbol(symbol_table.Root());
// Goal: resolve the reference of "pp" to this definition node.
MUST_ASSIGN_LOOKUP_SYMBOL(pp, root_symbol, "pp");
// Inspect inside the "qq" module definition.
MUST_ASSIGN_LOOKUP_SYMBOL(qq, root_symbol, "qq");
MUST_ASSIGN_LOOKUP_SYMBOL(ss, root_symbol, "ss");
// "pp_inst" is an instance of type "pp"
MUST_ASSIGN_LOOKUP_SYMBOL(pp_inst, qq, "pp_inst");
// "qq_inst" is an instance of type "qq"
MUST_ASSIGN_LOOKUP_SYMBOL(qq_inst, ss, "qq_inst");
{
ASSERT_EQ(qq_info.local_references_to_bind.size(), 2);
const auto ref_map(qq_info.LocalReferencesMapViewForTesting());
{
ASSIGN_MUST_FIND_EXACTLY_ONE_REF(pp_type, ref_map, "pp");
const ReferenceComponentNode *ref_node = pp_type->LastTypeComponent();
ASSERT_NE(ref_node, nullptr);
const ReferenceComponent &ref(ref_node->Value());
EXPECT_EQ(ref.identifier, "pp");
EXPECT_EQ(ref.ref_type, ReferenceType::kUnqualified);
EXPECT_EQ(ref.required_metatype, SymbolMetaType::kUnspecified);
EXPECT_EQ(ref.resolved_symbol, nullptr);
}
{ // self-reference to "pp_inst" instance
ASSIGN_MUST_FIND_EXACTLY_ONE_REF(pp_inst_self_ref, ref_map, "pp_inst");
EXPECT_TRUE(is_leaf(*pp_inst_self_ref->components)); // no named ports
// self-reference is already bound.
EXPECT_EQ(pp_inst_self_ref->components->Value().resolved_symbol,
&pp_inst);
}
}
{
ASSERT_EQ(ss_info.local_references_to_bind.size(), 2);
const auto ref_map(ss_info.LocalReferencesMapViewForTesting());
{
ASSIGN_MUST_FIND_EXACTLY_ONE_REF(qq_type, ref_map, "qq");
const ReferenceComponentNode *ref_node = qq_type->LastTypeComponent();
ASSERT_NE(ref_node, nullptr);
const ReferenceComponent &ref(ref_node->Value());
EXPECT_EQ(ref.identifier, "qq");
EXPECT_EQ(ref.ref_type, ReferenceType::kUnqualified);
EXPECT_EQ(ref.required_metatype, SymbolMetaType::kUnspecified);
EXPECT_EQ(ref.resolved_symbol, nullptr);
}
{ // self-reference to "qq_inst" instance
ASSIGN_MUST_FIND_EXACTLY_ONE_REF(qq_inst_self_ref, ref_map, "qq_inst");
EXPECT_TRUE(is_leaf(*qq_inst_self_ref->components)); // no named ports
// self-reference is already bound.
EXPECT_EQ(qq_inst_self_ref->components->Value().resolved_symbol,
&qq_inst);
}
}
{ // Verify pp_inst's type info
EXPECT_TRUE(pp_inst_info.local_references_to_bind.empty());
EXPECT_NE(pp_inst_info.declared_type.user_defined_type, nullptr);
const ReferenceComponent &pp_type(
pp_inst_info.declared_type.user_defined_type->Value());
EXPECT_EQ(pp_type.identifier, "pp");
EXPECT_EQ(pp_type.resolved_symbol, nullptr); // nothing resolved yet
EXPECT_EQ(pp_type.ref_type, ReferenceType::kUnqualified);
EXPECT_EQ(pp_type.required_metatype, SymbolMetaType::kUnspecified);
}
{ // Verify qq_inst's type info
EXPECT_TRUE(qq_inst_info.local_references_to_bind.empty());
EXPECT_NE(qq_inst_info.declared_type.user_defined_type, nullptr);
const ReferenceComponent &qq_type(
qq_inst_info.declared_type.user_defined_type->Value());
EXPECT_EQ(qq_type.identifier, "qq");
EXPECT_EQ(qq_type.resolved_symbol, nullptr); // nothing resolved yet
EXPECT_EQ(qq_type.ref_type, ReferenceType::kUnqualified);
EXPECT_EQ(qq_type.required_metatype, SymbolMetaType::kUnspecified);
}
// Resolve symbols.
std::vector<absl::Status> resolve_diagnostics;
symbol_table.Resolve(&resolve_diagnostics);
EXPECT_EMPTY_STATUSES(resolve_diagnostics);
// Verify that typeof(pp_inst) successfully resolved to module pp.
EXPECT_EQ(
pp_inst_info.declared_type.user_defined_type->Value().resolved_symbol,
&pp);
// Verify that typeof(qq_inst) successfully resolved to module qq.
EXPECT_EQ(
qq_inst_info.declared_type.user_defined_type->Value().resolved_symbol,
&qq);
}
TEST_F(BuildSymbolTableTest, ModuleInstancesFromProjectMissingFile) {
VerilogProject project(sources_dir, {/* no include path */});
SymbolTable symbol_table(&project);
EXPECT_EQ(symbol_table.Project(), &project);
std::vector<absl::Status> build_diagnostics;
symbol_table.BuildSingleTranslationUnit("file/not/found.txt",
&build_diagnostics);
ASSERT_FALSE(build_diagnostics.empty());
EXPECT_THAT(build_diagnostics.front().code(), absl::StatusCode::kNotFound)
<< build_diagnostics.front();
}
TEST_F(BuildSymbolTableTest, ModuleInstancesFromProjectFilesGood) {
VerilogProject project(sources_dir, {/* no include path */});
// Linear dependency chain between 3 files. Order arbitrarily chosen.
constexpr std::string_view //
text1(
"module ss;\n"
" qq qq_inst();\n" // instance
"endmodule\n"),
text2(
"module pp;\n"
"endmodule\n"),
text3(
"module qq;\n"
" pp pp_inst();\n" // instance
"endmodule\n");
// Write to temporary files.
const ScopedTestFile file1(sources_dir, text1);
const ScopedTestFile file2(sources_dir, text2);
const ScopedTestFile file3(sources_dir, text3);
// Register files as part of project.
for (const auto *file : {&file1, &file2, &file3}) {
const auto status_or_file =
project.OpenTranslationUnit(Basename(file->filename()));
ASSERT_TRUE(status_or_file.ok());
}
SymbolTable symbol_table(&project);
EXPECT_EQ(symbol_table.Project(), &project);
std::vector<absl::Status> build_diagnostics;
symbol_table.Build(&build_diagnostics);
EXPECT_EMPTY_STATUSES(build_diagnostics);
const SymbolTableNode &root_symbol(symbol_table.Root());
// Goal: resolve the reference of "pp" to this definition node.
MUST_ASSIGN_LOOKUP_SYMBOL(pp, root_symbol, "pp");
// Inspect inside the "qq" module definition.
MUST_ASSIGN_LOOKUP_SYMBOL(qq, root_symbol, "qq");
MUST_ASSIGN_LOOKUP_SYMBOL(ss, root_symbol, "ss");
// "pp_inst" is an instance of type "pp"
MUST_ASSIGN_LOOKUP_SYMBOL(pp_inst, qq, "pp_inst");
// "qq_inst" is an instance of type "qq"
MUST_ASSIGN_LOOKUP_SYMBOL(qq_inst, ss, "qq_inst");
{
ASSERT_EQ(qq_info.local_references_to_bind.size(), 2);
const auto ref_map(qq_info.LocalReferencesMapViewForTesting());
{
ASSIGN_MUST_FIND_EXACTLY_ONE_REF(pp_type, ref_map, "pp");
const ReferenceComponentNode *ref_node = pp_type->LastTypeComponent();
ASSERT_NE(ref_node, nullptr);
const ReferenceComponent &ref(ref_node->Value());
EXPECT_EQ(ref.identifier, "pp");
EXPECT_EQ(ref.ref_type, ReferenceType::kUnqualified);
EXPECT_EQ(ref.required_metatype, SymbolMetaType::kUnspecified);
EXPECT_EQ(ref.resolved_symbol, nullptr);
}
{ // self-reference to "pp_inst" instance
ASSIGN_MUST_FIND_EXACTLY_ONE_REF(pp_inst_self_ref, ref_map, "pp_inst");
EXPECT_TRUE(is_leaf(*pp_inst_self_ref->components)); // no named ports
// self-reference is already bound.
EXPECT_EQ(pp_inst_self_ref->components->Value().resolved_symbol,
&pp_inst);
}
}
{
ASSERT_EQ(ss_info.local_references_to_bind.size(), 2);
const auto ref_map(ss_info.LocalReferencesMapViewForTesting());
{
ASSIGN_MUST_FIND_EXACTLY_ONE_REF(qq_type, ref_map, "qq");
const ReferenceComponentNode *ref_node = qq_type->LastTypeComponent();
ASSERT_NE(ref_node, nullptr);
const ReferenceComponent &ref(ref_node->Value());
EXPECT_EQ(ref.identifier, "qq");
EXPECT_EQ(ref.ref_type, ReferenceType::kUnqualified);
EXPECT_EQ(ref.required_metatype, SymbolMetaType::kUnspecified);
EXPECT_EQ(ref.resolved_symbol, nullptr);
}
{ // self-reference to "qq_inst" instance
ASSIGN_MUST_FIND_EXACTLY_ONE_REF(qq_inst_self_ref, ref_map, "qq_inst");
EXPECT_TRUE(is_leaf(*qq_inst_self_ref->components)); // no named ports
// self-reference is already bound.
EXPECT_EQ(qq_inst_self_ref->components->Value().resolved_symbol,
&qq_inst);
}
}
{ // Verify pp_inst's type info
EXPECT_TRUE(pp_inst_info.local_references_to_bind.empty());
EXPECT_NE(pp_inst_info.declared_type.user_defined_type, nullptr);
const ReferenceComponent &pp_type(
pp_inst_info.declared_type.user_defined_type->Value());
EXPECT_EQ(pp_type.identifier, "pp");
EXPECT_EQ(pp_type.resolved_symbol, nullptr); // nothing resolved yet
EXPECT_EQ(pp_type.ref_type, ReferenceType::kUnqualified);
EXPECT_EQ(pp_type.required_metatype, SymbolMetaType::kUnspecified);
}
{ // Verify qq_inst's type info
EXPECT_TRUE(qq_inst_info.local_references_to_bind.empty());
EXPECT_NE(qq_inst_info.declared_type.user_defined_type, nullptr);
const ReferenceComponent &qq_type(
qq_inst_info.declared_type.user_defined_type->Value());
EXPECT_EQ(qq_type.identifier, "qq");
EXPECT_EQ(qq_type.resolved_symbol, nullptr); // nothing resolved yet
EXPECT_EQ(qq_type.ref_type, ReferenceType::kUnqualified);
EXPECT_EQ(qq_type.required_metatype, SymbolMetaType::kUnspecified);
}
// Resolve symbols.
std::vector<absl::Status> resolve_diagnostics;
symbol_table.Resolve(&resolve_diagnostics);
EXPECT_EMPTY_STATUSES(resolve_diagnostics);
// Verify that typeof(pp_inst) successfully resolved to module pp.
EXPECT_EQ(
pp_inst_info.declared_type.user_defined_type->Value().resolved_symbol,
&pp);
// Verify that typeof(qq_inst) successfully resolved to module qq.
EXPECT_EQ(
qq_inst_info.declared_type.user_defined_type->Value().resolved_symbol,
&qq);
}
TEST_F(BuildSymbolTableTest, SingleFileModuleInstanceCyclicDependencies) {
// Cyclic dependencies among three modules in one file.
// Make sure this can still build and resolve without hanging,
// even if this is semantically illegal.
TestVerilogSourceFile src("cycle.sv",
"module pp;\n"
" ss ss_inst();\n" // instance
"endmodule\n"
"module qq;\n"
" pp pp_inst();\n" // instance
"endmodule\n"
"module ss;\n"
" qq qq_inst();\n" // instance
"endmodule\n");
{
const auto status = src.Parse();
ASSERT_TRUE(status.ok()) << status.message();
}
SymbolTable symbol_table(nullptr);
const SymbolTableNode &root_symbol(symbol_table.Root());
const auto build_diagnostics = BuildSymbolTable(src, &symbol_table);
EXPECT_EMPTY_STATUSES(build_diagnostics);
// Goal: resolve the reference of "pp" to this definition node.
MUST_ASSIGN_LOOKUP_SYMBOL(pp, root_symbol, "pp");
// Inspect inside the "qq" module definition.
MUST_ASSIGN_LOOKUP_SYMBOL(qq, root_symbol, "qq");
MUST_ASSIGN_LOOKUP_SYMBOL(ss, root_symbol, "ss");
// "ss_inst" is an instance of type "ss"
MUST_ASSIGN_LOOKUP_SYMBOL(ss_inst, pp, "ss_inst");
// "pp_inst" is an instance of type "pp"
MUST_ASSIGN_LOOKUP_SYMBOL(pp_inst, qq, "pp_inst");
// "qq_inst" is an instance of type "qq"
MUST_ASSIGN_LOOKUP_SYMBOL(qq_inst, ss, "qq_inst");
EXPECT_EQ(pp_info.file_origin, &src);
EXPECT_EQ(qq_info.file_origin, &src);
EXPECT_EQ(ss_info.file_origin, &src);
{
ASSERT_EQ(pp_info.local_references_to_bind.size(), 2);
const auto ref_map(pp_info.LocalReferencesMapViewForTesting());
{
ASSIGN_MUST_FIND_EXACTLY_ONE_REF(ss_type, ref_map, "ss");
const ReferenceComponentNode *ref_node = ss_type->LastTypeComponent();
ASSERT_NE(ref_node, nullptr);
const ReferenceComponent &ref(ref_node->Value());
EXPECT_EQ(ref.identifier, "ss");
EXPECT_TRUE(verible::IsSubRange(ref.identifier,
src.GetTextStructure()->Contents()));
EXPECT_EQ(ref.ref_type, ReferenceType::kUnqualified);
EXPECT_EQ(ref.required_metatype, SymbolMetaType::kUnspecified);
EXPECT_EQ(ref.resolved_symbol, nullptr);
}
{ // self-reference to "ss_inst" instance
ASSIGN_MUST_FIND_EXACTLY_ONE_REF(ss_inst_self_ref, ref_map, "ss_inst");
EXPECT_TRUE(is_leaf(*ss_inst_self_ref->components)); // no named ports
// self-reference is already bound.
EXPECT_EQ(ss_inst_self_ref->components->Value().resolved_symbol,
&ss_inst);
}
}
{
ASSERT_EQ(qq_info.local_references_to_bind.size(), 2);
const auto ref_map(qq_info.LocalReferencesMapViewForTesting());
{
ASSIGN_MUST_FIND_EXACTLY_ONE_REF(pp_type, ref_map, "pp");
const ReferenceComponentNode *ref_node = pp_type->LastTypeComponent();
ASSERT_NE(ref_node, nullptr);
const ReferenceComponent &ref(ref_node->Value());
EXPECT_EQ(ref.identifier, "pp");
EXPECT_TRUE(verible::IsSubRange(ref.identifier,
src.GetTextStructure()->Contents()));
EXPECT_EQ(ref.ref_type, ReferenceType::kUnqualified);
EXPECT_EQ(ref.required_metatype, SymbolMetaType::kUnspecified);
EXPECT_EQ(ref.resolved_symbol, nullptr);
}
{ // self-reference to "pp_inst" instance
ASSIGN_MUST_FIND_EXACTLY_ONE_REF(pp_inst_self_ref, ref_map, "pp_inst");
EXPECT_TRUE(is_leaf(*pp_inst_self_ref->components)); // no named ports
// self-reference is already bound.
EXPECT_EQ(pp_inst_self_ref->components->Value().resolved_symbol,
&pp_inst);
}
}
{
ASSERT_EQ(ss_info.local_references_to_bind.size(), 2);
const auto ref_map(ss_info.LocalReferencesMapViewForTesting());
{
ASSIGN_MUST_FIND_EXACTLY_ONE_REF(qq_type, ref_map, "qq");
const ReferenceComponentNode *ref_node = qq_type->LastTypeComponent();
ASSERT_NE(ref_node, nullptr);
const ReferenceComponent &ref(ref_node->Value());
EXPECT_EQ(ref.identifier, "qq");
EXPECT_TRUE(verible::IsSubRange(ref.identifier,
src.GetTextStructure()->Contents()));
EXPECT_EQ(ref.ref_type, ReferenceType::kUnqualified);
EXPECT_EQ(ref.required_metatype, SymbolMetaType::kUnspecified);
EXPECT_EQ(ref.resolved_symbol, nullptr);
}
{ // self-reference to "qq_inst" instance
ASSIGN_MUST_FIND_EXACTLY_ONE_REF(qq_inst_self_ref, ref_map, "qq_inst");
EXPECT_TRUE(is_leaf(*qq_inst_self_ref->components)); // no named ports
// self-reference is already bound.
EXPECT_EQ(qq_inst_self_ref->components->Value().resolved_symbol,
&qq_inst);
}
}
{ // Verify ss_inst's type info
EXPECT_TRUE(ss_inst_info.local_references_to_bind.empty());
EXPECT_NE(ss_inst_info.declared_type.user_defined_type, nullptr);
const ReferenceComponent &ss_type(
ss_inst_info.declared_type.user_defined_type->Value());
EXPECT_EQ(ss_type.identifier, "ss");
EXPECT_EQ(ss_type.resolved_symbol, nullptr); // nothing resolved yet
EXPECT_EQ(ss_type.ref_type, ReferenceType::kUnqualified);
EXPECT_EQ(ss_type.required_metatype, SymbolMetaType::kUnspecified);
EXPECT_EQ(ss_inst_info.file_origin, &src);
}
{ // Verify pp_inst's type info
EXPECT_TRUE(pp_inst_info.local_references_to_bind.empty());
EXPECT_NE(pp_inst_info.declared_type.user_defined_type, nullptr);
const ReferenceComponent &pp_type(
pp_inst_info.declared_type.user_defined_type->Value());
EXPECT_EQ(pp_type.identifier, "pp");
EXPECT_EQ(pp_type.resolved_symbol, nullptr); // nothing resolved yet
EXPECT_EQ(pp_type.ref_type, ReferenceType::kUnqualified);
EXPECT_EQ(pp_type.required_metatype, SymbolMetaType::kUnspecified);
EXPECT_EQ(pp_inst_info.file_origin, &src);
}
{ // Verify qq_inst's type info
EXPECT_TRUE(qq_inst_info.local_references_to_bind.empty());
EXPECT_NE(qq_inst_info.declared_type.user_defined_type, nullptr);
const ReferenceComponent &qq_type(
qq_inst_info.declared_type.user_defined_type->Value());
EXPECT_EQ(qq_type.identifier, "qq");
EXPECT_EQ(qq_type.resolved_symbol, nullptr); // nothing resolved yet
EXPECT_EQ(qq_type.ref_type, ReferenceType::kUnqualified);
EXPECT_EQ(qq_type.required_metatype, SymbolMetaType::kUnspecified);
EXPECT_EQ(qq_inst_info.file_origin, &src);
}
// Resolve symbols.
std::vector<absl::Status> resolve_diagnostics;
symbol_table.Resolve(&resolve_diagnostics);
EXPECT_EMPTY_STATUSES(resolve_diagnostics);
// Verify that typeof(ss_inst) successfully resolved to module ss.
EXPECT_EQ(
ss_inst_info.declared_type.user_defined_type->Value().resolved_symbol,
&ss);
// Verify that typeof(pp_inst) successfully resolved to module pp.
EXPECT_EQ(
pp_inst_info.declared_type.user_defined_type->Value().resolved_symbol,
&pp);
// Verify that typeof(qq_inst) successfully resolved to module qq.
EXPECT_EQ(
qq_inst_info.declared_type.user_defined_type->Value().resolved_symbol,
&qq);
}
TEST_F(BuildSymbolTableTest, MultiFileModuleInstanceCyclicDependencies) {
// Cyclic dependencies among three files.
// Make sure this can still build and resolve without hanging,
// even if this is semantically illegal.
TestVerilogSourceFile pp_src("pp.sv",
"module pp;\n"
" ss ss_inst();\n" // instance
"endmodule\n");
TestVerilogSourceFile qq_src("qq.sv",
"module qq;\n"
" pp pp_inst();\n" // instance
"endmodule\n");
TestVerilogSourceFile ss_src("ss.sv",
"module ss;\n"
" qq qq_inst();\n" // instance
"endmodule\n");
{
const auto status = pp_src.Parse();
ASSERT_TRUE(status.ok()) << status.message();
}
{
const auto status = qq_src.Parse();
ASSERT_TRUE(status.ok()) << status.message();
}
{
const auto status = ss_src.Parse();
ASSERT_TRUE(status.ok()) << status.message();
}
// All permutations of the following file ordering should end up with the
// same results.
std::vector<const TestVerilogSourceFile *> ordering(
{&pp_src, &qq_src, &ss_src});
// start with the lexicographically "lowest" permutation
SortSourceFiles(&ordering);
int count = 0;
do {
SymbolTable symbol_table(nullptr);
const SymbolTableNode &root_symbol(symbol_table.Root());
for (const auto *src : ordering) {
const auto build_diagnostics = BuildSymbolTable(*src, &symbol_table);
EXPECT_EMPTY_STATUSES(build_diagnostics);
}
// Goal: resolve the reference of "pp" to this definition node.
MUST_ASSIGN_LOOKUP_SYMBOL(pp, root_symbol, "pp");
// Inspect inside the "qq" module definition.
MUST_ASSIGN_LOOKUP_SYMBOL(qq, root_symbol, "qq");
MUST_ASSIGN_LOOKUP_SYMBOL(ss, root_symbol, "ss");
// "ss_inst" is an instance of type "ss"
MUST_ASSIGN_LOOKUP_SYMBOL(ss_inst, pp, "ss_inst");
// "pp_inst" is an instance of type "pp"
MUST_ASSIGN_LOOKUP_SYMBOL(pp_inst, qq, "pp_inst");
// "qq_inst" is an instance of type "qq"
MUST_ASSIGN_LOOKUP_SYMBOL(qq_inst, ss, "qq_inst");
EXPECT_EQ(pp_info.file_origin, &pp_src);
EXPECT_EQ(qq_info.file_origin, &qq_src);
EXPECT_EQ(ss_info.file_origin, &ss_src);
{
ASSERT_EQ(pp_info.local_references_to_bind.size(), 2);
const auto ref_map(pp_info.LocalReferencesMapViewForTesting());
{
ASSIGN_MUST_FIND_EXACTLY_ONE_REF(ss_type, ref_map, "ss");
const ReferenceComponentNode *ref_node = ss_type->LastTypeComponent();
ASSERT_NE(ref_node, nullptr);
const ReferenceComponent &ref(ref_node->Value());
EXPECT_EQ(ref.identifier, "ss");
EXPECT_TRUE(verible::IsSubRange(ref.identifier,
pp_src.GetTextStructure()->Contents()));
EXPECT_EQ(ref.ref_type, ReferenceType::kUnqualified);
EXPECT_EQ(ref.required_metatype, SymbolMetaType::kUnspecified);
EXPECT_EQ(ref.resolved_symbol, nullptr);
}
{ // self-reference to "ss_inst" instance
ASSIGN_MUST_FIND_EXACTLY_ONE_REF(ss_inst_self_ref, ref_map, "ss_inst");
EXPECT_TRUE(is_leaf(*ss_inst_self_ref->components)); // no named ports
// self-reference is already bound.
EXPECT_EQ(ss_inst_self_ref->components->Value().resolved_symbol,
&ss_inst);
}
}
{
ASSERT_EQ(qq_info.local_references_to_bind.size(), 2);
const auto ref_map(qq_info.LocalReferencesMapViewForTesting());
{
ASSIGN_MUST_FIND_EXACTLY_ONE_REF(pp_type, ref_map, "pp");
const ReferenceComponentNode *ref_node = pp_type->LastTypeComponent();
ASSERT_NE(ref_node, nullptr);
const ReferenceComponent &ref(ref_node->Value());
EXPECT_EQ(ref.identifier, "pp");
EXPECT_TRUE(verible::IsSubRange(ref.identifier,
qq_src.GetTextStructure()->Contents()));
EXPECT_EQ(ref.ref_type, ReferenceType::kUnqualified);
EXPECT_EQ(ref.required_metatype, SymbolMetaType::kUnspecified);
EXPECT_EQ(ref.resolved_symbol, nullptr);
}
{ // self-reference to "pp_inst" instance
ASSIGN_MUST_FIND_EXACTLY_ONE_REF(pp_inst_self_ref, ref_map, "pp_inst");
EXPECT_TRUE(is_leaf(*pp_inst_self_ref->components)); // no named ports
// self-reference is already bound.
EXPECT_EQ(pp_inst_self_ref->components->Value().resolved_symbol,
&pp_inst);
}
}
{
ASSERT_EQ(ss_info.local_references_to_bind.size(), 2);
const auto ref_map(ss_info.LocalReferencesMapViewForTesting());
{
ASSIGN_MUST_FIND_EXACTLY_ONE_REF(qq_type, ref_map, "qq");
const ReferenceComponentNode *ref_node = qq_type->LastTypeComponent();
ASSERT_NE(ref_node, nullptr);
const ReferenceComponent &ref(ref_node->Value());
EXPECT_EQ(ref.identifier, "qq");
EXPECT_TRUE(verible::IsSubRange(ref.identifier,
ss_src.GetTextStructure()->Contents()));
EXPECT_EQ(ref.ref_type, ReferenceType::kUnqualified);
EXPECT_EQ(ref.required_metatype, SymbolMetaType::kUnspecified);
EXPECT_EQ(ref.resolved_symbol, nullptr);
}
{ // self-reference to "qq_inst" instance
ASSIGN_MUST_FIND_EXACTLY_ONE_REF(qq_inst_self_ref, ref_map, "qq_inst");
EXPECT_TRUE(is_leaf(*qq_inst_self_ref->components)); // no named ports
// self-reference is already bound.
EXPECT_EQ(qq_inst_self_ref->components->Value().resolved_symbol,
&qq_inst);
}
}
{ // Verify ss_inst's type info
EXPECT_TRUE(ss_inst_info.local_references_to_bind.empty());
EXPECT_NE(ss_inst_info.declared_type.user_defined_type, nullptr);
const ReferenceComponent &ss_type(
ss_inst_info.declared_type.user_defined_type->Value());
EXPECT_EQ(ss_type.identifier, "ss");
EXPECT_EQ(ss_type.resolved_symbol, nullptr); // nothing resolved yet
EXPECT_EQ(ss_type.ref_type, ReferenceType::kUnqualified);
EXPECT_EQ(ss_type.required_metatype, SymbolMetaType::kUnspecified);
EXPECT_EQ(ss_inst_info.file_origin, &pp_src);
}
{ // Verify pp_inst's type info
EXPECT_TRUE(pp_inst_info.local_references_to_bind.empty());
EXPECT_NE(pp_inst_info.declared_type.user_defined_type, nullptr);
const ReferenceComponent &pp_type(
pp_inst_info.declared_type.user_defined_type->Value());
EXPECT_EQ(pp_type.identifier, "pp");
EXPECT_EQ(pp_type.resolved_symbol, nullptr); // nothing resolved yet
EXPECT_EQ(pp_type.ref_type, ReferenceType::kUnqualified);
EXPECT_EQ(pp_type.required_metatype, SymbolMetaType::kUnspecified);
EXPECT_EQ(pp_inst_info.file_origin, &qq_src);
}
{ // Verify qq_inst's type info
EXPECT_TRUE(qq_inst_info.local_references_to_bind.empty());
EXPECT_NE(qq_inst_info.declared_type.user_defined_type, nullptr);
const ReferenceComponent &qq_type(
qq_inst_info.declared_type.user_defined_type->Value());
EXPECT_EQ(qq_type.identifier, "qq");
EXPECT_EQ(qq_type.resolved_symbol, nullptr); // nothing resolved yet
EXPECT_EQ(qq_type.ref_type, ReferenceType::kUnqualified);
EXPECT_EQ(qq_type.required_metatype, SymbolMetaType::kUnspecified);
EXPECT_EQ(qq_inst_info.file_origin, &ss_src);
}
// Resolve symbols.
std::vector<absl::Status> resolve_diagnostics;
symbol_table.Resolve(&resolve_diagnostics);
EXPECT_EMPTY_STATUSES(resolve_diagnostics);
// Verify that typeof(ss_inst) successfully resolved to module ss.
EXPECT_EQ(
ss_inst_info.declared_type.user_defined_type->Value().resolved_symbol,
&ss);
// Verify that typeof(pp_inst) successfully resolved to module pp.
EXPECT_EQ(
pp_inst_info.declared_type.user_defined_type->Value().resolved_symbol,
&pp);
// Verify that typeof(qq_inst) successfully resolved to module qq.
EXPECT_EQ(
qq_inst_info.declared_type.user_defined_type->Value().resolved_symbol,
&qq);
++count;
} while (PermuteSourceFiles(&ordering));
EXPECT_EQ(count, 6); // make sure we covered all permutations
}
TEST_F(BuildSymbolTableTest, IncludeModuleDefinition) {
// Create files.
ScopedTestFile IncludedFile(sources_dir,
"module pp;\n"
"endmodule\n",
"module.sv");
ScopedTestFile pp_src(sources_dir, "`include \"module.sv\"\n", "pp.sv");
VerilogProject project(sources_dir, {sources_dir});
const auto file_or_status =
project.OpenTranslationUnit(Basename(pp_src.filename()));
ASSERT_TRUE(file_or_status.ok()) << file_or_status.status().message();
SymbolTable symbol_table(&project);
const SymbolTableNode &root_symbol(symbol_table.Root());
std::vector<absl::Status> build_diagnostics;
symbol_table.Build(&build_diagnostics);
EXPECT_EMPTY_STATUSES(build_diagnostics);
MUST_ASSIGN_LOOKUP_SYMBOL(pp, root_symbol, "pp");
const VerilogSourceFile *included = project.LookupRegisteredFile("module.sv");
ASSERT_NE(included, nullptr);
EXPECT_EQ(pp_info.file_origin, included);
// Resolve symbols. Nothing to resolve.
std::vector<absl::Status> resolve_diagnostics;
symbol_table.Resolve(&resolve_diagnostics);
EXPECT_EMPTY_STATUSES(resolve_diagnostics);
}
TEST_F(BuildSymbolTableTest, IncludeWithoutProject) {
// Create files.
ScopedTestFile IncludedFile(sources_dir,
"module pp;\n"
"endmodule\n",
"module.sv");
TestVerilogSourceFile pp_src("pp.sv", "`include \"module.sv\"\n");
SymbolTable symbol_table(nullptr);
const auto build_diagnostics = BuildSymbolTable(pp_src, nullptr);
// include files are ignored.
EXPECT_EMPTY_STATUSES(build_diagnostics);
// Resolve symbols. Nothing to resolve.
std::vector<absl::Status> resolve_diagnostics;
symbol_table.Resolve(&resolve_diagnostics);
EXPECT_EMPTY_STATUSES(resolve_diagnostics);
}
TEST_F(BuildSymbolTableTest, IncludeFileNotFound) {
// Create files.
ScopedTestFile pp_src(sources_dir, "`include \"not-found.sv\"\n", "pp.sv");
VerilogProject project(sources_dir, {sources_dir});
const auto file_or_status =
project.OpenTranslationUnit(Basename(pp_src.filename()));
ASSERT_TRUE(file_or_status.ok()) << file_or_status.status().message();
SymbolTable symbol_table(&project);
const SymbolTableNode &root_symbol(symbol_table.Root());
std::vector<absl::Status> build_diagnostics;
symbol_table.Build(&build_diagnostics);
ASSERT_FALSE(build_diagnostics.empty());
EXPECT_EQ(build_diagnostics.front().code(), absl::StatusCode::kNotFound);
EXPECT_TRUE(root_symbol.Children().empty());
// Resolve symbols. Nothing to resolve.
std::vector<absl::Status> resolve_diagnostics;
symbol_table.Resolve(&resolve_diagnostics);
EXPECT_EMPTY_STATUSES(resolve_diagnostics);
}
TEST_F(BuildSymbolTableTest, IncludeFileParseError) {
// Create files.
ScopedTestFile IncludedFile(sources_dir,
"module 333;\n" // syntax error
"endmodule\n",
"module.sv");
ScopedTestFile pp_src(sources_dir, "`include \"module.sv\"\n", "pp.sv");
VerilogProject project(sources_dir, {sources_dir});
const auto file_or_status =
project.OpenTranslationUnit(Basename(pp_src.filename()));
ASSERT_TRUE(file_or_status.ok()) << file_or_status.status().message();
SymbolTable symbol_table(&project);
std::vector<absl::Status> build_diagnostics;
symbol_table.Build(&build_diagnostics);
ASSERT_FALSE(build_diagnostics.empty());
EXPECT_EQ(build_diagnostics.front().code(),
absl::StatusCode::kInvalidArgument);
// Resolve symbols. Nothing to resolve.
std::vector<absl::Status> resolve_diagnostics;
symbol_table.Resolve(&resolve_diagnostics);
EXPECT_EMPTY_STATUSES(resolve_diagnostics);
}
TEST_F(BuildSymbolTableTest, IncludeFileEmpty) {
// Create files.
ScopedTestFile IncludedFile(sources_dir,
"", // empty
"empty.sv");
ScopedTestFile pp_src(sources_dir, "`include \"empty.sv\"\n", "pp.sv");
VerilogProject project(sources_dir, {sources_dir});
const auto file_or_status =
project.OpenTranslationUnit(Basename(pp_src.filename()));
ASSERT_TRUE(file_or_status.ok()) << file_or_status.status().message();
SymbolTable symbol_table(&project);
std::vector<absl::Status> build_diagnostics;
symbol_table.Build(&build_diagnostics);
EXPECT_EMPTY_STATUSES(build_diagnostics);
// Resolve symbols. Nothing to resolve.
std::vector<absl::Status> resolve_diagnostics;
symbol_table.Resolve(&resolve_diagnostics);
EXPECT_EMPTY_STATUSES(resolve_diagnostics);
}
TEST_F(BuildSymbolTableTest, IncludedTwiceFromOneFile) {
// Create files.
ScopedTestFile IncludedFile(sources_dir,
"// verilog_syntax: parse-as-module-body\n"
"wire ww;\n",
"wires.sv");
ScopedTestFile pp_src(sources_dir,
"module pp;\n"
"`include \"wires.sv\"\n"
"endmodule\n"
"module qq;\n"
"`include \"wires.sv\"\n"
"endmodule\n",
"pp.sv");
VerilogProject project(sources_dir, {sources_dir});
const auto file_or_status =
project.OpenTranslationUnit(Basename(pp_src.filename()));
ASSERT_TRUE(file_or_status.ok()) << file_or_status.status().message();
const VerilogSourceFile *pp_file = *file_or_status;
ASSERT_NE(pp_file, nullptr);
SymbolTable symbol_table(&project);
const SymbolTableNode &root_symbol(symbol_table.Root());
std::vector<absl::Status> build_diagnostics;
symbol_table.Build(&build_diagnostics);
EXPECT_EMPTY_STATUSES(build_diagnostics);
MUST_ASSIGN_LOOKUP_SYMBOL(pp, root_symbol, "pp");
MUST_ASSIGN_LOOKUP_SYMBOL(qq, root_symbol, "qq");
MUST_ASSIGN_LOOKUP_SYMBOL(pp_ww, pp, "ww");
MUST_ASSIGN_LOOKUP_SYMBOL(qq_ww, qq, "ww");
const VerilogSourceFile *included = project.LookupRegisteredFile("wires.sv");
ASSERT_NE(included, nullptr);
EXPECT_EQ(pp_info.file_origin, pp_file);
EXPECT_EQ(qq_info.file_origin, pp_file);
EXPECT_EQ(pp_ww_info.file_origin, included);
EXPECT_EQ(qq_ww_info.file_origin, included);
// Resolve symbols. Nothing to resolve.
std::vector<absl::Status> resolve_diagnostics;
symbol_table.Resolve(&resolve_diagnostics);
EXPECT_EMPTY_STATUSES(resolve_diagnostics);
}
TEST_F(BuildSymbolTableTest, IncludedTwiceFromDifferentFiles) {
// Create files.
ScopedTestFile IncludedFile(sources_dir,
"// verilog_syntax: parse-as-module-body\n"
"wire ww;\n",
"wires.sv");
ScopedTestFile pp_src(sources_dir,
"module pp;\n"
"`include \"wires.sv\"\n"
"endmodule\n",
"pp.sv");
ScopedTestFile qq_src(sources_dir,
"module qq;\n"
"`include \"wires.sv\"\n"
"endmodule\n",
"qq.sv");
VerilogProject project(sources_dir, {sources_dir});
const auto pp_file_or_status =
project.OpenTranslationUnit(Basename(pp_src.filename()));
ASSERT_TRUE(pp_file_or_status.ok()) << pp_file_or_status.status().message();
const VerilogSourceFile *pp_file = *pp_file_or_status;
ASSERT_NE(pp_file, nullptr);
const auto qq_file_or_status =
project.OpenTranslationUnit(Basename(qq_src.filename()));
ASSERT_TRUE(qq_file_or_status.ok()) << qq_file_or_status.status().message();
const VerilogSourceFile *qq_file = *qq_file_or_status;
ASSERT_NE(qq_file, nullptr);
SymbolTable symbol_table(&project);
const SymbolTableNode &root_symbol(symbol_table.Root());
std::vector<absl::Status> build_diagnostics;
symbol_table.Build(&build_diagnostics);
EXPECT_EMPTY_STATUSES(build_diagnostics);
MUST_ASSIGN_LOOKUP_SYMBOL(pp, root_symbol, "pp");
MUST_ASSIGN_LOOKUP_SYMBOL(qq, root_symbol, "qq");
MUST_ASSIGN_LOOKUP_SYMBOL(pp_ww, pp, "ww");
MUST_ASSIGN_LOOKUP_SYMBOL(qq_ww, qq, "ww");
const VerilogSourceFile *included = project.LookupRegisteredFile("wires.sv");
ASSERT_NE(included, nullptr);
EXPECT_EQ(pp_info.file_origin, pp_file);
EXPECT_EQ(qq_info.file_origin, qq_file);
EXPECT_EQ(pp_ww_info.file_origin, included);
EXPECT_EQ(qq_ww_info.file_origin, included);
// Resolve symbols. Nothing to resolve.
std::vector<absl::Status> resolve_diagnostics;
symbol_table.Resolve(&resolve_diagnostics);
EXPECT_EMPTY_STATUSES(resolve_diagnostics);
}
TEST_F(BuildSymbolTableTest, ModulePortDeclarationMultiline) {
TestVerilogSourceFile src("foobar.sv",
"module a; endmodule\n"
"module m(mport);\n"
" input mport;\n"
" wire mport;\n"
"endmodule\n");
const auto status = src.Parse();
ASSERT_TRUE(status.ok()) << status.message();
SymbolTable symbol_table(nullptr);
const auto build_diagnostics = BuildSymbolTable(src, &symbol_table);
EXPECT_EMPTY_STATUSES(build_diagnostics);
}
TEST_F(BuildSymbolTableTest, ModulePortDeclarationDirectionRedefinition) {
TestVerilogSourceFile src(
"foobar.sv",
"module m(mport);\n"
" input mport;\n"
" output mport;\n" // direction is already declared
"endmodule\n");
const auto status = src.Parse();
ASSERT_TRUE(status.ok()) << status.message();
SymbolTable symbol_table(nullptr);
const SymbolTableNode &root_symbol(symbol_table.Root());
const auto build_diagnostics = BuildSymbolTable(src, &symbol_table);
MUST_ASSIGN_LOOKUP_SYMBOL(module_node, root_symbol, "m");
EXPECT_EQ(module_node_info.metatype, SymbolMetaType::kModule);
EXPECT_EQ(module_node_info.file_origin, &src);
EXPECT_EQ(module_node_info.declared_type.syntax_origin,
nullptr); // there is no module meta-type
ASSIGN_MUST_HAVE_UNIQUE(err_status, build_diagnostics);
EXPECT_EQ(err_status.code(), absl::StatusCode::kAlreadyExists);
EXPECT_THAT(err_status.message(),
HasSubstr("\"mport\" is already defined in the $root::m scope"));
{
std::vector<absl::Status> resolve_diagnostics;
symbol_table.Resolve(&resolve_diagnostics); // nothing to resolve
EXPECT_EMPTY_STATUSES(resolve_diagnostics);
}
}
TEST_F(BuildSymbolTableTest, ModulePortDeclarationTypeRedefinition) {
TestVerilogSourceFile src("foobar.sv",
"module a; endmodule\n"
"module m(mport);\n"
" input mport;\n"
" wire mport;\n"
" logic mport;\n"
"endmodule\n");
const auto status = src.Parse();
ASSERT_TRUE(status.ok()) << status.message();
SymbolTable symbol_table(nullptr);
const SymbolTableNode &root_symbol(symbol_table.Root());
const auto build_diagnostics = BuildSymbolTable(src, &symbol_table);
MUST_ASSIGN_LOOKUP_SYMBOL(module_node, root_symbol, "m");
EXPECT_EQ(module_node_info.metatype, SymbolMetaType::kModule);
EXPECT_EQ(module_node_info.file_origin, &src);
EXPECT_EQ(module_node_info.declared_type.syntax_origin,
nullptr); // there is no module meta-type
ASSIGN_MUST_HAVE_UNIQUE(err_status, build_diagnostics);
EXPECT_EQ(err_status.code(), absl::StatusCode::kAlreadyExists);
EXPECT_THAT(err_status.message(),
HasSubstr("\"mport\" is already defined in the $root::m scope"));
{
std::vector<absl::Status> resolve_diagnostics;
symbol_table.Resolve(&resolve_diagnostics); // nothing to resolve
EXPECT_EMPTY_STATUSES(resolve_diagnostics);
}
}
TEST_F(BuildSymbolTableTest, ModulePortDeclarationTypeMultilineWithDimensions) {
TestVerilogSourceFile src("foobar.sv",
"module m(mport);\n"
" input [10:0] mport;\n"
" reg [10:0] mport;\n"
"endmodule\n");
const auto status = src.Parse();
ASSERT_TRUE(status.ok()) << status.message();
SymbolTable symbol_table(nullptr);
const auto build_diagnostics = BuildSymbolTable(src, &symbol_table);
EXPECT_EMPTY_STATUSES(build_diagnostics);
}
TEST_F(BuildSymbolTableTest,
ModulePortDeclarationTypeMultilineWithMismatchingDimensions) {
TestVerilogSourceFile src("foobar.sv",
"module m(mport);\n"
" input [10:0] mport;\n"
" reg [8:0] mport;\n"
"endmodule\n");
const auto status = src.Parse();
ASSERT_TRUE(status.ok()) << status.message();
SymbolTable symbol_table(nullptr);
const SymbolTableNode &root_symbol(symbol_table.Root());
const auto build_diagnostics = BuildSymbolTable(src, &symbol_table);
MUST_ASSIGN_LOOKUP_SYMBOL(module_node, root_symbol, "m");
EXPECT_EQ(module_node_info.metatype, SymbolMetaType::kModule);
EXPECT_EQ(module_node_info.file_origin, &src);
EXPECT_EQ(module_node_info.declared_type.syntax_origin,
nullptr); // there is no module meta-type
ASSIGN_MUST_HAVE_UNIQUE(err_status, build_diagnostics);
EXPECT_EQ(err_status.code(), absl::StatusCode::kAlreadyExists);
EXPECT_THAT(err_status.message(),
HasSubstr("\"mport\" is already defined in the $root::m scope"));
{
std::vector<absl::Status> resolve_diagnostics;
symbol_table.Resolve(&resolve_diagnostics); // nothing to resolve
EXPECT_EMPTY_STATUSES(resolve_diagnostics);
}
}
TEST_F(BuildSymbolTableTest,
ModulePortDeclarationTypeMultilineCorrectSignPlacements) {
TestVerilogSourceFile src("foobar.sv",
"module m(a, b, c, d);\n"
" input signed [10:0] a;\n"
" output unsigned [10:0] b;\n"
" input [10:0] c;\n"
" output [10:0] d;\n"
" wire [10:0] a;\n"
" logic [10:0] b;\n"
" logic unsigned [10:0] c;\n"
" wire signed [10:0] d;\n"
"endmodule\n");
const auto status = src.Parse();
ASSERT_TRUE(status.ok()) << status.message();
SymbolTable symbol_table(nullptr);
const auto build_diagnostics = BuildSymbolTable(src, &symbol_table);
EXPECT_EMPTY_STATUSES(build_diagnostics);
}
TEST_F(BuildSymbolTableTest,
ModulePortDeclarationTypeMultilineWithMismatchingSigns) {
TestVerilogSourceFile src("foobar.sv",
"module m(mport);\n"
" input unsigned [10:0] mport;\n"
" reg signed [10:0] mport;\n"
"endmodule\n");
const auto status = src.Parse();
ASSERT_TRUE(status.ok()) << status.message();
SymbolTable symbol_table(nullptr);
const SymbolTableNode &root_symbol(symbol_table.Root());
const auto build_diagnostics = BuildSymbolTable(src, &symbol_table);
MUST_ASSIGN_LOOKUP_SYMBOL(module_node, root_symbol, "m");
EXPECT_EQ(module_node_info.metatype, SymbolMetaType::kModule);
EXPECT_EQ(module_node_info.file_origin, &src);
EXPECT_EQ(module_node_info.declared_type.syntax_origin,
nullptr); // there is no module meta-type
ASSIGN_MUST_HAVE_UNIQUE(err_status, build_diagnostics);
EXPECT_EQ(err_status.code(), absl::StatusCode::kAlreadyExists);
EXPECT_THAT(err_status.message(),
HasSubstr("\"mport\" is already defined in the $root::m scope"));
{
std::vector<absl::Status> resolve_diagnostics;
symbol_table.Resolve(&resolve_diagnostics); // nothing to resolve
EXPECT_EMPTY_STATUSES(resolve_diagnostics);
}
}
TEST_F(BuildSymbolTableTest, ModulePortDeclarationTypeMultilineWithPortList) {
TestVerilogSourceFile src("foobar.sv",
"module m(a, b, c);\n"
" input a, b;\n"
" output b, c;\n"
"endmodule\n");
const auto status = src.Parse();
ASSERT_TRUE(status.ok()) << status.message();
SymbolTable symbol_table(nullptr);
const SymbolTableNode &root_symbol(symbol_table.Root());
const auto build_diagnostics = BuildSymbolTable(src, &symbol_table);
MUST_ASSIGN_LOOKUP_SYMBOL(module_node, root_symbol, "m");
EXPECT_EQ(module_node_info.metatype, SymbolMetaType::kModule);
EXPECT_EQ(module_node_info.file_origin, &src);
EXPECT_EQ(module_node_info.declared_type.syntax_origin,
nullptr); // there is no module meta-type
ASSIGN_MUST_HAVE_UNIQUE(err_status, build_diagnostics);
EXPECT_EQ(err_status.code(), absl::StatusCode::kAlreadyExists);
EXPECT_THAT(err_status.message(),
HasSubstr("\"b\" is already defined in the $root::m scope"));
{
std::vector<absl::Status> resolve_diagnostics;
symbol_table.Resolve(&resolve_diagnostics); // nothing to resolve
EXPECT_EMPTY_STATUSES(resolve_diagnostics);
}
}
TEST_F(BuildSymbolTableTest, InterfaceDeclarationSingleEmpty) {
TestVerilogSourceFile src("foobar_if.sv",
"interface foobar_if;\n"
"endinterface\n");
const auto status = src.Parse();
ASSERT_TRUE(status.ok()) << status.message();
SymbolTable symbol_table(nullptr);
const SymbolTableNode &root_symbol(symbol_table.Root());
const auto build_diagnostics = BuildSymbolTable(src, &symbol_table);
MUST_ASSIGN_LOOKUP_SYMBOL(interface_node, root_symbol, "foobar_if");
EXPECT_EQ(interface_node_info.metatype, SymbolMetaType::kInterface);
EXPECT_EQ(interface_node_info.file_origin, &src);
EXPECT_EQ(interface_node_info.declared_type.syntax_origin,
nullptr); // there is no interface meta-type
EXPECT_EMPTY_STATUSES(build_diagnostics);
{
std::vector<absl::Status> resolve_diagnostics;
symbol_table.Resolve(&resolve_diagnostics); // nothing to resolve
EXPECT_EMPTY_STATUSES(resolve_diagnostics);
}
}
TEST_F(BuildSymbolTableTest, InterfaceDeclarationLocalNetsVariables) {
TestVerilogSourceFile src("foobar_if.sv",
"interface foobar_if;\n"
" logic l1;\n"
" logic l2;\n"
"endinterface\n");
const auto status = src.Parse();
ASSERT_TRUE(status.ok()) << status.message();
SymbolTable symbol_table(nullptr);
const SymbolTableNode &root_symbol(symbol_table.Root());
const auto build_diagnostics = BuildSymbolTable(src, &symbol_table);
MUST_ASSIGN_LOOKUP_SYMBOL(interface_node, root_symbol, "foobar_if");
EXPECT_EQ(interface_node_info.metatype, SymbolMetaType::kInterface);
EXPECT_EQ(interface_node_info.file_origin, &src);
EXPECT_EQ(interface_node_info.declared_type.syntax_origin,
nullptr); // there is no interface meta-type
EXPECT_EMPTY_STATUSES(build_diagnostics);
static constexpr std::string_view members[] = {"l1", "l2"};
for (const auto &member : members) {
MUST_ASSIGN_LOOKUP_SYMBOL(member_node, interface_node, member);
EXPECT_EQ(member_node_info.metatype,
SymbolMetaType::kDataNetVariableInstance);
EXPECT_EQ(member_node_info.declared_type.user_defined_type,
nullptr); // types are primitive
}
{
std::vector<absl::Status> resolve_diagnostics;
symbol_table.Resolve(&resolve_diagnostics); // nothing to resolve
EXPECT_EMPTY_STATUSES(resolve_diagnostics);
}
}
TEST_F(BuildSymbolTableTest, InterfaceDeclarationWithPorts) {
TestVerilogSourceFile src("foobar_if.sv",
"interface foobar_if (\n"
" input wire clk,\n"
" input logic reset\n"
");\n"
" logic d;"
" logic q;"
" modport dff ("
" input d,"
" output q);"
" modport dff_test ("
" output d,"
" input q);"
"endinterface\n");
const auto status = src.Parse();
ASSERT_TRUE(status.ok()) << status.message();
SymbolTable symbol_table(nullptr);
const SymbolTableNode &root_symbol(symbol_table.Root());
const auto build_diagnostics = BuildSymbolTable(src, &symbol_table);
EXPECT_EMPTY_STATUSES(build_diagnostics);
MUST_ASSIGN_LOOKUP_SYMBOL(interface_node, root_symbol, "foobar_if");
EXPECT_EQ(interface_node_info.metatype, SymbolMetaType::kInterface);
EXPECT_EQ(interface_node_info.file_origin, &src);
EXPECT_EQ(interface_node_info.declared_type.syntax_origin,
nullptr); // there is no interface meta-type
static constexpr std::string_view members[] = {"clk", "reset", "d", "q"};
for (const auto &member : members) {
MUST_ASSIGN_LOOKUP_SYMBOL(member_node, interface_node, member);
EXPECT_EQ(member_node_info.metatype,
SymbolMetaType::kDataNetVariableInstance);
EXPECT_EQ(member_node_info.declared_type.user_defined_type,
nullptr); // types are primitive
}
{
std::vector<absl::Status> resolve_diagnostics;
symbol_table.Resolve(&resolve_diagnostics); // nothing to resolve
EXPECT_EMPTY_STATUSES(resolve_diagnostics);
}
}
TEST_F(BuildSymbolTableTest, InterfaceDeclarationMultiple) {
TestVerilogSourceFile src("foobar_if.sv",
"interface foobar1_if;\nendinterface\n"
"interface foobar2_if;\nendinterface\n");
const auto status = src.Parse();
ASSERT_TRUE(status.ok()) << status.message();
SymbolTable symbol_table(nullptr);
const SymbolTableNode &root_symbol(symbol_table.Root());
const auto build_diagnostics = BuildSymbolTable(src, &symbol_table);
EXPECT_EMPTY_STATUSES(build_diagnostics);
const std::string_view expected_interfaces[] = {"foobar1_if", "foobar2_if"};
for (const auto &expected_interface : expected_interfaces) {
MUST_ASSIGN_LOOKUP_SYMBOL(interface_node, root_symbol, expected_interface);
EXPECT_EQ(interface_node_info.metatype, SymbolMetaType::kInterface);
EXPECT_EQ(interface_node_info.file_origin, &src);
EXPECT_EQ(interface_node_info.declared_type.syntax_origin,
nullptr); // there is no interface meta-type
}
{
std::vector<absl::Status> resolve_diagnostics;
symbol_table.Resolve(&resolve_diagnostics); // nothing to resolve
EXPECT_EMPTY_STATUSES(resolve_diagnostics);
}
}
TEST_F(BuildSymbolTableTest, InterfaceDeclarationDuplicate) {
TestVerilogSourceFile src("foobar_if.sv",
"interface foobar_if;\nendinterface\n"
"interface foobar_if;\nendinterface\n");
const auto status = src.Parse();
ASSERT_TRUE(status.ok()) << status.message();
SymbolTable symbol_table(nullptr);
const SymbolTableNode &root_symbol(symbol_table.Root());
const auto build_diagnostics = BuildSymbolTable(src, &symbol_table);
MUST_ASSIGN_LOOKUP_SYMBOL(interface_node, root_symbol, "foobar_if");
EXPECT_EQ(interface_node_info.metatype, SymbolMetaType::kInterface);
EXPECT_EQ(interface_node_info.file_origin, &src);
EXPECT_EQ(interface_node_info.declared_type.syntax_origin,
nullptr); // there is no interface meta-type
ASSIGN_MUST_HAVE_UNIQUE(err, build_diagnostics);
EXPECT_EQ(err.code(), absl::StatusCode::kAlreadyExists);
EXPECT_THAT(err.message(),
HasSubstr("\"foobar_if\" is already defined in the $root scope"));
{
std::vector<absl::Status> resolve_diagnostics;
symbol_table.Resolve(&resolve_diagnostics); // nothing to resolve
EXPECT_EMPTY_STATUSES(resolve_diagnostics);
}
}
TEST_F(BuildSymbolTableTest, InterfaceDeclarationDuplicateSeparateFiles) {
TestVerilogSourceFile src("foobar_if.sv",
"interface foobar_if;\nendinterface\n");
TestVerilogSourceFile src2("foobar_if-2.sv",
"interface foobar_if;\nendinterface\n");
const auto status = src.Parse();
ASSERT_TRUE(status.ok()) << status.message();
const auto status2 = src2.Parse();
ASSERT_TRUE(status2.ok()) << status2.message();
SymbolTable symbol_table(nullptr);
const SymbolTableNode &root_symbol(symbol_table.Root());
const auto build_diagnostics1 = BuildSymbolTable(src, &symbol_table);
const auto build_diagnostics = BuildSymbolTable(src2, &symbol_table);
MUST_ASSIGN_LOOKUP_SYMBOL(interface_node, root_symbol, "foobar_if");
EXPECT_EQ(interface_node_info.metatype, SymbolMetaType::kInterface);
EXPECT_EQ(interface_node_info.file_origin, &src);
EXPECT_EQ(interface_node_info.declared_type.syntax_origin,
nullptr); // there is no interface meta-type
ASSIGN_MUST_HAVE_UNIQUE(err, build_diagnostics);
EXPECT_EQ(err.code(), absl::StatusCode::kAlreadyExists);
EXPECT_THAT(err.message(),
HasSubstr("\"foobar_if\" is already defined in the $root scope"));
{
std::vector<absl::Status> resolve_diagnostics;
symbol_table.Resolve(&resolve_diagnostics); // nothing to resolve
EXPECT_EMPTY_STATUSES(resolve_diagnostics);
}
}
struct FileListTestCase {
std::string_view contents;
std::vector<std::string_view> expected_files;
};
TEST(ParseSourceFileListFromFileTest, FileNotFound) {
FileList file_list;
const auto status(AppendFileListFromFile("/no/such/file.txt", &file_list));
EXPECT_FALSE(status.ok());
}
TEST(ParseSourceFileListFromFileTest, VariousValidFiles) {
const FileListTestCase kTestCases[] = {
{"", {}}, // empty
{"\n\n", {}}, // blank lines
{"foo.sv", {"foo.sv"}}, // missing terminating newline, but still works
{"foo.sv\n", {"foo.sv"}},
{"file name contains space.sv\n", {"file name contains space.sv"}},
{"foo/bar.sv\n", {"foo/bar.sv"}}, // with path separator
{" foo.sv\n", {"foo.sv"}}, // remove leading whitespace
{"foo.sv \n", {"foo.sv"}}, // remove trailing whitespace
{"#foo.sv\n", {}}, // commented out
{"# foo.sv\n", {}}, // commented out
{"foo.sv\nbar/bar.sv\n", {"foo.sv", "bar/bar.sv"}},
{"/foo/bar.sv\n"
"### ignore this one\n"
"bar/baz.txt\n",
{"/foo/bar.sv", "bar/baz.txt"}},
};
for (const auto &test : kTestCases) {
const ScopedTestFile test_file(testing::TempDir(), test.contents);
FileList file_list;
const auto status(AppendFileListFromFile(test_file.filename(), &file_list));
ASSERT_TRUE(status.ok()) << status;
EXPECT_THAT(file_list.file_paths, ElementsAreArray(test.expected_files))
<< "input: " << test.contents;
}
}
} // namespace
} // namespace verilog