| // Copyright 2017-2020 The Verible Authors. | |
| // | |
| // Licensed under the Apache License, Version 2.0 (the "License"); | |
| // you may not use this file except in compliance with the License. | |
| // You may obtain a copy of the License at | |
| // | |
| // http://www.apache.org/licenses/LICENSE-2.0 | |
| // | |
| // Unless required by applicable law or agreed to in writing, software | |
| // distributed under the License is distributed on an "AS IS" BASIS, | |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| // See the License for the specific language governing permissions and | |
| // limitations under the License. | |
| #include "verilog/analysis/symbol_table.h" | |
| #include <algorithm> | |
| #include <functional> | |
| #include <iostream> | |
| #include <memory> | |
| #include <sstream> | |
| #include <vector> | |
| #include "absl/base/attributes.h" | |
| #include "absl/status/status.h" | |
| #include "absl/strings/string_view.h" | |
| #include "common/text/symbol.h" | |
| #include "common/text/tree_utils.h" | |
| #include "common/util/file_util.h" | |
| #include "common/util/logging.h" | |
| #include "common/util/range.h" | |
| #include "common/util/tree_operations.h" | |
| #include "gmock/gmock.h" | |
| #include "gtest/gtest.h" | |
| #include "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::CreateDir; | |
| using verible::file::JoinPath; | |
| 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 (absl::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) { | |
| typedef SymbolTableNode::key_value_type KV; | |
| 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) { | |
| typedef ReferenceComponentNode Node; | |
| typedef ReferenceComponent Data; | |
| 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. | |
| typedef SymbolTableNode::key_value_type KV; | |
| 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: ) } | |
| } | |
| })"); | |
| } | |
| } | |
| TEST(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.push_back(DependentReferences{ | |
| 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(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.push_back(DependentReferences{ | |
| 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(BuildSymbolTableTest, InvalidSyntax) { | |
| constexpr absl::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(BuildSymbolTableTest, AvoidCrashFromFuzzer) { | |
| // All that matters is that these test cases do not trigger crashes. | |
| constexpr absl::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(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(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 absl::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(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(BuildSymbolTableTest, ModuleDeclarationConditionalGenerateAnonymous) { | |
| constexpr absl::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(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(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 absl::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(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 absl::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(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(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(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(BuildSymbolTableTest, ModuleInstance) { | |
| // The following code variants should yield the same symbol table results: | |
| static constexpr absl::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(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(BuildSymbolTableTest, ModuleInstanceTwoInSameDecl) { | |
| static constexpr absl::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 absl::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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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, ¶m_a); | |
| EXPECT_EQ(b_ref_comp.resolved_symbol, ¶m_b); | |
| } | |
| } | |
| TEST(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(BuildSymbolTableTest, ModuleInstancesFromProjectOneFileAtATime) { | |
| const auto tempdir = ::testing::TempDir(); | |
| const std::string sources_dir = JoinPath(tempdir, __FUNCTION__); | |
| ASSERT_TRUE(CreateDir(sources_dir).ok()); | |
| VerilogProject project(sources_dir, {/* no include path */}); | |
| // Linear dependency chain between 3 files. Order arbitrarily chosen. | |
| constexpr absl::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(BuildSymbolTableTest, ModuleInstancesFromProjectMissingFile) { | |
| const auto tempdir = ::testing::TempDir(); | |
| const std::string sources_dir = JoinPath(tempdir, __FUNCTION__); | |
| 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); | |
| } | |
| TEST(BuildSymbolTableTest, ModuleInstancesFromProjectFilesGood) { | |
| const auto tempdir = ::testing::TempDir(); | |
| const std::string sources_dir = JoinPath(tempdir, __FUNCTION__); | |
| ASSERT_TRUE(CreateDir(sources_dir).ok()); | |
| VerilogProject project(sources_dir, {/* no include path */}); | |
| // Linear dependency chain between 3 files. Order arbitrarily chosen. | |
| constexpr absl::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(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(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(BuildSymbolTableTest, IncludeModuleDefinition) { | |
| const auto tempdir = ::testing::TempDir(); | |
| const std::string sources_dir = JoinPath(tempdir, __FUNCTION__); | |
| ASSERT_TRUE(CreateDir(sources_dir).ok()); | |
| // 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(BuildSymbolTableTest, IncludeWithoutProject) { | |
| const auto tempdir = ::testing::TempDir(); | |
| const std::string sources_dir = JoinPath(tempdir, __FUNCTION__); | |
| ASSERT_TRUE(CreateDir(sources_dir).ok()); | |
| // 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(BuildSymbolTableTest, IncludeFileNotFound) { | |
| const auto tempdir = ::testing::TempDir(); | |
| const std::string sources_dir = JoinPath(tempdir, __FUNCTION__); | |
| ASSERT_TRUE(CreateDir(sources_dir).ok()); | |
| // 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(BuildSymbolTableTest, IncludeFileParseError) { | |
| const auto tempdir = ::testing::TempDir(); | |
| const std::string sources_dir = JoinPath(tempdir, __FUNCTION__); | |
| ASSERT_TRUE(CreateDir(sources_dir).ok()); | |
| // 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(BuildSymbolTableTest, IncludeFileEmpty) { | |
| const auto tempdir = ::testing::TempDir(); | |
| const std::string sources_dir = JoinPath(tempdir, __FUNCTION__); | |
| ASSERT_TRUE(CreateDir(sources_dir).ok()); | |
| // 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(BuildSymbolTableTest, IncludedTwiceFromOneFile) { | |
| const auto tempdir = ::testing::TempDir(); | |
| const std::string sources_dir = JoinPath(tempdir, __FUNCTION__); | |
| ASSERT_TRUE(CreateDir(sources_dir).ok()); | |
| // 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(BuildSymbolTableTest, IncludedTwiceFromDifferentFiles) { | |
| const auto tempdir = ::testing::TempDir(); | |
| const std::string sources_dir = JoinPath(tempdir, __FUNCTION__); | |
| ASSERT_TRUE(CreateDir(sources_dir).ok()); | |
| // 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); | |
| } | |
| struct FileListTestCase { | |
| absl::string_view contents; | |
| std::vector<absl::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 |