| // Copyright 2017-2020 The Verible Authors. | |
| // | |
| // Licensed under the Apache License, Version 2.0 (the "License"); | |
| // you may not use this file except in compliance with the License. | |
| // You may obtain a copy of the License at | |
| // | |
| // http://www.apache.org/licenses/LICENSE-2.0 | |
| // | |
| // Unless required by applicable law or agreed to in writing, software | |
| // distributed under the License is distributed on an "AS IS" BASIS, | |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| // See the License for the specific language governing permissions and | |
| // limitations under the License. | |
| #include "verilog/CST/functions.h" | |
| #include <memory> | |
| #include <string> | |
| #include <utility> | |
| #include <vector> | |
| #include "absl/strings/string_view.h" | |
| #include "common/analysis/matcher/matcher_builders.h" | |
| #include "common/analysis/syntax_tree_search.h" | |
| #include "common/analysis/syntax_tree_search_test_utils.h" | |
| #include "common/text/concrete_syntax_leaf.h" | |
| #include "common/text/concrete_syntax_tree.h" | |
| #include "common/text/symbol.h" | |
| #include "common/text/text_structure.h" | |
| #include "common/text/token_info.h" | |
| #include "common/text/token_info_test_util.h" | |
| #include "common/text/tree_utils.h" | |
| #include "common/util/casts.h" | |
| #include "common/util/logging.h" | |
| #include "common/util/range.h" | |
| #include "gmock/gmock.h" | |
| #include "gtest/gtest.h" | |
| #include "verilog/CST/identifier.h" | |
| #include "verilog/CST/match_test_utils.h" | |
| #include "verilog/analysis/verilog_analyzer.h" | |
| #include "verilog/parser/verilog_token_enum.h" | |
| #undef ASSERT_OK | |
| #define ASSERT_OK(value) ASSERT_TRUE((value).ok()) | |
| namespace verilog { | |
| namespace { | |
| using verible::CheckSymbolAsLeaf; | |
| using verible::SymbolCastToNode; | |
| using verible::SyntaxTreeSearchTestCase; | |
| using verible::TextStructureView; | |
| using verible::TreeSearchMatch; | |
| TEST(FindAllFunctionDeclarationsTest, Various) { | |
| constexpr int kTag = 1; // value doesn't matter | |
| const SyntaxTreeSearchTestCase kTestCases[] = { | |
| {""}, | |
| {"class foo; endclass"}, | |
| {"task foo; endtask"}, | |
| {"module foo; endmodule"}, | |
| {{kTag, "function foo(); endfunction"}}, | |
| {// multiple function declarations | |
| {kTag, "function foo(); endfunction"}, | |
| "\n", | |
| {kTag, "function foo2(); endfunction"}, | |
| "\n"}, | |
| {// package function method declaration | |
| "package bar;\n", | |
| {kTag, "function foo(); endfunction"}, | |
| "\nendpackage\n"}, | |
| {// class function method declaration | |
| "class bar;\n", | |
| {kTag, "function foo(); endfunction"}, | |
| "\nendclass\n"}, | |
| {// function declaration inside module | |
| "module bar;\n", | |
| {kTag, "function foo(); endfunction"}, | |
| "\nendmodule\n"}, | |
| {// forward declaration is not a full function declaration | |
| "class bar;\n", | |
| "extern function foo();\n" | |
| "endclass\n"}, | |
| {// pure virtual is not a full function declaration | |
| "class bar;\n", | |
| "pure virtual function foo();\n" | |
| "endclass\n"}, | |
| {// virtual is a full function declaration | |
| "class bar;\n", | |
| {kTag, | |
| "virtual function foo();\n" | |
| "endfunction"}, | |
| "\n" | |
| "endclass\n"}, | |
| {// function declaration inside cross_body_item | |
| "module cover_that;\n" | |
| "covergroup settings;\n" | |
| " _name : cross dbi, mask {\n" | |
| " ", | |
| {kTag, | |
| "function int foo(int bar);\n" // function declaration | |
| " return bar;\n" | |
| " endfunction"}, | |
| "\n" | |
| " }\n" | |
| "endgroup\n" | |
| "endmodule"}, | |
| }; | |
| for (const auto& test : kTestCases) { | |
| TestVerilogSyntaxRangeMatches( | |
| __FUNCTION__, test, [](const TextStructureView& text_structure) { | |
| const auto& root = text_structure.SyntaxTree(); | |
| return FindAllFunctionDeclarations(*ABSL_DIE_IF_NULL(root)); | |
| }); | |
| } | |
| } | |
| TEST(FindAllFunctionPrototypesTest, Various) { | |
| constexpr int kTag = 1; // value doesn't matter | |
| const SyntaxTreeSearchTestCase kTestCases[] = { | |
| {""}, | |
| {"class foo; endclass"}, | |
| {"task foo; endtask"}, | |
| {"module foo; endmodule"}, | |
| {"function foo(); endfunction"}, | |
| {// package function method declaration | |
| "package bar;\n", | |
| "function foo(); endfunction\n" | |
| "endpackage\n"}, | |
| {// class function method declaration (not a prototype) | |
| "class bar;\n", | |
| "function foo(); endfunction\n" | |
| "endclass\n"}, | |
| {// function declaration inside module | |
| "module bar;\n" | |
| "function foo(); endfunction\n" | |
| "endmodule\n"}, | |
| {// forward declaration is a prototype | |
| "class bar;\n", | |
| "extern ", | |
| {kTag, "function foo();"}, | |
| "\n" | |
| "endclass\n"}, | |
| {// pure virtual is a prototype | |
| "class bar;\n", | |
| "pure virtual ", | |
| {kTag, "function foo();"}, | |
| "\n" | |
| "endclass\n"}, | |
| {// virtual declaration is not a prototype | |
| "class bar;\n", | |
| "virtual function foo();\n" | |
| "endfunction\n" | |
| "endclass\n"}, | |
| }; | |
| for (const auto& test : kTestCases) { | |
| TestVerilogSyntaxRangeMatches( | |
| __FUNCTION__, test, [](const TextStructureView& text_structure) { | |
| const auto& root = text_structure.SyntaxTree(); | |
| return FindAllFunctionPrototypes(*ABSL_DIE_IF_NULL(root)); | |
| }); | |
| } | |
| // Protype headers map to the same range as the enclosing prototype. | |
| for (const auto& test : kTestCases) { | |
| TestVerilogSyntaxRangeMatches( | |
| __FUNCTION__, test, [](const TextStructureView& text_structure) { | |
| const auto& root = text_structure.SyntaxTree(); | |
| const auto protos(FindAllFunctionPrototypes(*ABSL_DIE_IF_NULL(root))); | |
| std::vector<TreeSearchMatch> headers; | |
| for (const auto& proto : protos) { | |
| headers.push_back(TreeSearchMatch{ | |
| GetFunctionPrototypeHeader(*proto.match), /* no context */}); | |
| } | |
| return headers; | |
| }); | |
| } | |
| } | |
| TEST(FunctionPrototypesReturnTypesTest, Various) { | |
| constexpr int kTag = 1; // value doesn't matter | |
| const SyntaxTreeSearchTestCase kTestCases[] = { | |
| {""}, | |
| {// forward declaration is a prototype, implicit return type | |
| "class bar;\n" | |
| "extern function foo();\n" | |
| "endclass\n"}, | |
| {// forward declaration is a prototype, explicit return type | |
| "class bar;\n" | |
| "extern function ", | |
| {kTag, "bar#(int)"}, | |
| " foo();\n" | |
| "endclass\n"}, | |
| {// pure virtual is a prototype, implicit return type | |
| "class bar;\n" | |
| "pure virtual function foo();\n" | |
| "endclass\n"}, | |
| {// pure virtual is a prototype, explicit return type | |
| "class bar;\n" | |
| "pure virtual function ", | |
| {kTag, "p_pkg::baz_t"}, | |
| " foo();\n" | |
| "endclass\n"}, | |
| }; | |
| // Protype headers map to the same range as the enclosing prototype. | |
| for (const auto& test : kTestCases) { | |
| TestVerilogSyntaxRangeMatches( | |
| __FUNCTION__, test, [](const TextStructureView& text_structure) { | |
| const auto& root = text_structure.SyntaxTree(); | |
| const auto protos(FindAllFunctionPrototypes(*ABSL_DIE_IF_NULL(root))); | |
| std::vector<TreeSearchMatch> returns; | |
| for (const auto& proto : protos) { | |
| const auto* return_type = GetFunctionHeaderReturnType( | |
| *GetFunctionPrototypeHeader(*proto.match)); | |
| if (return_type == nullptr) continue; | |
| if (verible::StringSpanOfSymbol(*return_type).empty()) continue; | |
| returns.push_back(TreeSearchMatch{return_type, /* no context */}); | |
| } | |
| return returns; | |
| }); | |
| } | |
| } | |
| TEST(FunctionPrototypesIdsTest, Various) { | |
| constexpr int kTag = 1; // value doesn't matter | |
| const SyntaxTreeSearchTestCase kTestCases[] = { | |
| {""}, | |
| {// forward declaration is a prototype, implicit return type | |
| "class bar;\n" | |
| "extern function ", | |
| {kTag, "moo"}, | |
| "();\n" | |
| "endclass\n"}, | |
| {// forward declaration is a prototype, explicit return type | |
| "class bar;\n" | |
| "extern function bar#(int) ", | |
| {kTag, "gooo"}, | |
| "();\n" | |
| "endclass\n"}, | |
| {// pure virtual is a prototype, implicit return type | |
| "class bar;\n" | |
| "pure virtual function ", | |
| {kTag, "weeee"}, | |
| "();\n" | |
| "endclass\n"}, | |
| {// pure virtual is a prototype, explicit return type | |
| "class bar;\n", | |
| "pure virtual function p_pkg::baz_t ", | |
| {kTag, "h456"}, | |
| "();\n" | |
| "endclass\n"}, | |
| }; | |
| // Protype headers map to the same range as the enclosing prototype. | |
| for (const auto& test : kTestCases) { | |
| TestVerilogSyntaxRangeMatches( | |
| __FUNCTION__, test, [](const TextStructureView& text_structure) { | |
| const auto& root = text_structure.SyntaxTree(); | |
| const auto protos(FindAllFunctionPrototypes(*ABSL_DIE_IF_NULL(root))); | |
| std::vector<TreeSearchMatch> ids; | |
| for (const auto& proto : protos) { | |
| const auto* id = | |
| GetFunctionHeaderId(*GetFunctionPrototypeHeader(*proto.match)); | |
| if (id == nullptr) continue; | |
| if (verible::StringSpanOfSymbol(*id).empty()) continue; | |
| ids.push_back(TreeSearchMatch{id, /* no context */}); | |
| } | |
| return ids; | |
| }); | |
| } | |
| } | |
| TEST(FindAllFunctionHeadersTest, Various) { | |
| constexpr int kTag = 1; // value doesn't matter | |
| const SyntaxTreeSearchTestCase kTestCases[] = { | |
| {""}, | |
| {"class foo; endclass"}, | |
| {"task foo; endtask"}, | |
| {"module foo; endmodule"}, | |
| {{kTag, "function foo();"}, " endfunction"}, | |
| {// multiple function declarations | |
| {kTag, "function foo();"}, | |
| " endfunction\n", | |
| {kTag, "function foo2();"}, | |
| " endfunction\n"}, | |
| {// package function method declaration | |
| "package bar;\n", | |
| {kTag, "function foo();"}, | |
| " endfunction" | |
| "\nendpackage\n"}, | |
| {// class function method declaration | |
| "class bar;\n", | |
| {kTag, "function foo();"}, | |
| " endfunction" | |
| "\nendclass\n"}, | |
| {// function declaration inside module | |
| "module bar;\n", | |
| {kTag, "function foo();"}, | |
| " endfunction" | |
| "\nendmodule\n"}, | |
| {// forward declaration | |
| "class bar;\n", | |
| "extern ", | |
| {kTag, "function foo();"}, | |
| "\n" | |
| "endclass\n"}, | |
| {// pure virtual | |
| "class bar;\n", | |
| "pure virtual ", | |
| {kTag, "function foo();"}, | |
| "\n" | |
| "endclass\n"}, | |
| {// virtual | |
| "class bar;\n", | |
| {kTag, "virtual function foo();"}, | |
| "\n" | |
| "endfunction\n" | |
| "endclass\n"}, | |
| {// function declaration inside cross_body_item | |
| "module cover_that;\n" | |
| "covergroup settings;\n" | |
| " _name : cross dbi, mask {\n" | |
| " ", | |
| {kTag, "function int foo(int bar);"}, | |
| "\n" | |
| " return bar;\n" | |
| " endfunction\n" | |
| " }\n" | |
| "endgroup\n" | |
| "endmodule"}, | |
| }; | |
| for (const auto& test : kTestCases) { | |
| TestVerilogSyntaxRangeMatches( | |
| __FUNCTION__, test, [](const TextStructureView& text_structure) { | |
| const auto& root = text_structure.SyntaxTree(); | |
| return FindAllFunctionHeaders(*ABSL_DIE_IF_NULL(root)); | |
| }); | |
| } | |
| } | |
| TEST(GetFunctionHeaderTest, DeclarationHeader) { | |
| constexpr int kTag = 1; // value doesn't matter | |
| const SyntaxTreeSearchTestCase kTestCases[] = { | |
| {{kTag, "function foo();"}, " endfunction"}, | |
| {"class c; ", {kTag, "function foo();"}, " endfunction endclass"}, | |
| {"module m; ", {kTag, "function foo();"}, " endfunction endmodule"}, | |
| {"package p; ", {kTag, "function foo();"}, " endfunction endpackage"}, | |
| }; | |
| for (const auto& test : kTestCases) { | |
| TestVerilogSyntaxRangeMatches( | |
| __FUNCTION__, test, [](const TextStructureView& text_structure) { | |
| const auto& root = text_structure.SyntaxTree(); | |
| // Root node is a description list, not a function. | |
| const auto function_declarations = FindAllFunctionDeclarations(*root); | |
| std::vector<TreeSearchMatch> headers; | |
| for (const auto& decl : function_declarations) { | |
| const auto& function_node = SymbolCastToNode(*decl.match); | |
| headers.push_back(TreeSearchMatch{GetFunctionHeader(function_node), | |
| /* no context */}); | |
| } | |
| return headers; | |
| }); | |
| } | |
| } | |
| TEST(GetFunctionLifetimeTest, NoLifetimeDeclared) { | |
| VerilogAnalyzer analyzer("function foo(); endfunction", ""); | |
| ASSERT_OK(analyzer.Analyze()); | |
| // Root node is a description list, not a function. | |
| const auto& root = analyzer.Data().SyntaxTree(); | |
| const auto function_declarations = FindAllFunctionDeclarations(*root); | |
| ASSERT_EQ(function_declarations.size(), 1); | |
| const auto& function_node = | |
| SymbolCastToNode(*function_declarations.front().match); | |
| const auto* lifetime = GetFunctionLifetime(function_node); | |
| EXPECT_EQ(lifetime, nullptr); | |
| } | |
| TEST(GetFunctionLifetimeTest, StaticLifetimeDeclared) { | |
| VerilogAnalyzer analyzer("function static foo(); endfunction", ""); | |
| ASSERT_OK(analyzer.Analyze()); | |
| // Root node is a description list, not a function. | |
| const auto& root = analyzer.Data().SyntaxTree(); | |
| const auto function_declarations = FindAllFunctionDeclarations(*root); | |
| ASSERT_EQ(function_declarations.size(), 1); | |
| const auto& function_node = | |
| SymbolCastToNode(*function_declarations.front().match); | |
| const auto* lifetime = GetFunctionLifetime(function_node); | |
| CheckSymbolAsLeaf(*ABSL_DIE_IF_NULL(lifetime), TK_static); | |
| // TODO(b/151371397): verify substring range | |
| } | |
| TEST(GetFunctionLifetimeTest, AutomaticLifetimeDeclared) { | |
| VerilogAnalyzer analyzer("function automatic foo(); endfunction", ""); | |
| ASSERT_OK(analyzer.Analyze()); | |
| // Root node is a description list, not a function.e | |
| const auto& root = analyzer.Data().SyntaxTree(); | |
| const auto function_declarations = FindAllFunctionDeclarations(*root); | |
| ASSERT_EQ(function_declarations.size(), 1); | |
| const auto& function_node = | |
| SymbolCastToNode(*function_declarations.front().match); | |
| const auto* lifetime = GetFunctionLifetime(function_node); | |
| CheckSymbolAsLeaf(*ABSL_DIE_IF_NULL(lifetime), TK_automatic); | |
| // TODO(b/151371397): verify substring range | |
| } | |
| TEST(GetFunctionIdTest, UnqualifiedIds) { | |
| constexpr int kTag = 1; // don't care | |
| const SyntaxTreeSearchTestCase kTestCases[] = { | |
| {"function ", {kTag, "foo"}, "(); endfunction"}, | |
| {"function automatic ", {kTag, "bar"}, "(); endfunction"}, | |
| {"function static ", {kTag, "baz"}, "(); endfunction"}, | |
| {"package p; function ", {kTag, "foo"}, "(); endfunction endpackage"}, | |
| {"class c; function ", {kTag, "zoo"}, "(); endfunction endclass"}, | |
| {"function ", {kTag, "myclass"}, "::", {kTag, "foo"}, "(); endfunction"}, | |
| }; | |
| for (const auto& test : kTestCases) { | |
| TestVerilogSyntaxRangeMatches( | |
| __FUNCTION__, test, [](const TextStructureView& text_structure) { | |
| const auto& root = text_structure.SyntaxTree(); | |
| const auto function_declarations = FindAllFunctionDeclarations(*root); | |
| std::vector<TreeSearchMatch> got_ids; | |
| for (const auto& decl : function_declarations) { | |
| const auto& function_node = SymbolCastToNode(*decl.match); | |
| const auto* function_id = GetFunctionId(function_node); | |
| for (const auto& id : FindAllUnqualifiedIds(*function_id)) { | |
| const verible::SyntaxTreeLeaf* base = GetIdentifier(*id.match); | |
| got_ids.push_back(TreeSearchMatch{base, /* empty context */}); | |
| } | |
| } | |
| return got_ids; | |
| }); | |
| } | |
| } | |
| TEST(GetFunctionIdTest, QualifiedIds) { | |
| constexpr int kTag = 1; // don't care | |
| const SyntaxTreeSearchTestCase kTestCases[] = { | |
| {"function foo(); endfunction"}, | |
| {"function ", {kTag, "myclass::foo"}, "(); endfunction"}, | |
| }; | |
| for (const auto& test : kTestCases) { | |
| TestVerilogSyntaxRangeMatches( | |
| __FUNCTION__, test, [](const TextStructureView& text_structure) { | |
| const auto& root = text_structure.SyntaxTree(); | |
| const auto function_declarations = FindAllFunctionDeclarations(*root); | |
| std::vector<TreeSearchMatch> got_ids; | |
| for (const auto& decl : function_declarations) { | |
| const auto& function_node = SymbolCastToNode(*decl.match); | |
| const auto* function_id = GetFunctionId(function_node); | |
| for (const auto& id : FindAllQualifiedIds(*function_id)) { | |
| got_ids.push_back(id); | |
| } | |
| } | |
| return got_ids; | |
| }); | |
| } | |
| } | |
| struct SubtreeTestData { | |
| NodeEnum expected_construct; | |
| verible::TokenInfoTestData token_data; | |
| }; | |
| TEST(GetFunctionReturnTypeTest, VariousReturnTypes) { | |
| constexpr int kTag = 1; // don't care | |
| const SyntaxTreeSearchTestCase kTestCases[] = { | |
| // implicit/missing return types | |
| {"function f;endfunction\n"}, | |
| {"package p;\nfunction f;\nendfunction\nendpackage\n"}, | |
| {"class c;\nfunction f;\nendfunction\nendclass\n"}, | |
| {"module m;\nfunction f;\nendfunction\nendmodule\n"}, | |
| // explicit return types | |
| {"function ", {kTag, "void"}, " f;endfunction\n"}, | |
| {"package p;\nfunction ", | |
| {kTag, "int"}, | |
| " f;\nendfunction\nendpackage\n"}, | |
| {"class c;\nfunction ", | |
| {kTag, "foo_pkg::bar_t"}, | |
| " f;\nendfunction\nendclass\n"}, | |
| {"module m;\nfunction ", | |
| {kTag, "foo#(bar)"}, | |
| " f;\nendfunction\nendmodule\n"}, | |
| }; | |
| for (const auto& test : kTestCases) { | |
| TestVerilogSyntaxRangeMatches( | |
| __FUNCTION__, test, [](const TextStructureView& text_structure) { | |
| const auto& root = text_structure.SyntaxTree(); | |
| const auto decls = FindAllFunctionDeclarations(*root); | |
| std::vector<TreeSearchMatch> returns; | |
| for (const auto& decl : decls) { | |
| const auto& statement = *decl.match; | |
| const auto* return_type = GetFunctionReturnType(statement); | |
| // Expect a type node, even when type is implicit or empty. | |
| if (return_type == nullptr) continue; | |
| const auto return_type_span = | |
| verible::StringSpanOfSymbol(*return_type); | |
| if (!return_type_span.empty()) { | |
| returns.push_back(TreeSearchMatch{return_type, /* no context */}); | |
| } | |
| } | |
| return returns; | |
| }); | |
| } | |
| } | |
| TEST(GetFunctionFormalPortsGroupTest, MixedFormalPorts) { | |
| constexpr int kTag = 1; // don't care | |
| const SyntaxTreeSearchTestCase kTestCases[] = { | |
| // no ports | |
| {"function f;endfunction\n"}, | |
| {"package p;\nfunction f;\nendfunction\nendpackage\n"}, | |
| {"class c;\nfunction f;\nendfunction\nendclass\n"}, | |
| {"module m;\nfunction f;\nendfunction\nendmodule\n"}, | |
| // with ports | |
| {"function f", {kTag, "()"}, ";endfunction\n"}, | |
| {"package p;\nfunction f", | |
| {kTag, "(string s)"}, | |
| ";\nendfunction\nendpackage\n"}, | |
| {"class c;\nfunction f", | |
| {kTag, "(int i, string s)"}, | |
| ";\nendfunction\nendclass\n"}, | |
| {"module m;\nfunction f", | |
| {kTag, "(input logic foo, bar)"}, | |
| ";\nendfunction\nendmodule\n"}, | |
| }; | |
| for (const auto& test : kTestCases) { | |
| TestVerilogSyntaxRangeMatches( | |
| __FUNCTION__, test, [](const TextStructureView& text_structure) { | |
| const auto& root = text_structure.SyntaxTree(); | |
| const auto decls = FindAllFunctionDeclarations(*root); | |
| std::vector<TreeSearchMatch> ports; | |
| for (const auto& decl : decls) { | |
| const auto& statement = *decl.match; | |
| const auto* port_formals = GetFunctionFormalPortsGroup(statement); | |
| if (port_formals == nullptr) continue; | |
| const auto port_formals_span = | |
| verible::StringSpanOfSymbol(*port_formals); | |
| if (port_formals_span.empty()) continue; | |
| ports.push_back(TreeSearchMatch{port_formals, /* no context */}); | |
| } | |
| return ports; | |
| }); | |
| } | |
| } | |
| TEST(GetFunctionHeaderTest, GetFunctionName) { | |
| constexpr int kTag = 1; // value doesn't matter | |
| const SyntaxTreeSearchTestCase kTestCases[] = { | |
| {""}, | |
| {"module m(); endmodule: m"}, | |
| {"function ", | |
| {kTag, "foo"}, | |
| "();\n endfunction\n function ", | |
| {kTag, "bar"}, | |
| "()\n; endfunction"}, | |
| {"function int ", | |
| {kTag, "foo"}, | |
| "();\n endfunction\n function ", | |
| {kTag, "bar"}, | |
| "()\n; endfunction"}, | |
| {"function int ", | |
| {kTag, "foo"}, | |
| "(int a, bit x);\nreturn a;\n endfunction\n function ", | |
| {kTag, "bar"}, | |
| "()\n; endfunction"}, | |
| {"module my_module;\nfunction int ", | |
| {kTag, "inner_function"}, | |
| "(int my_args);\nreturn my_args;\nendfunction\nendmodule"}, | |
| {"class function_class;\nfunction int ", | |
| {kTag, "my_function"}, | |
| "(input int a, b, output int c);\nc = a + b;\nreturn " | |
| "c;\nendfunction\nendclass"}, | |
| {"package my_pkg;\nfunction automatic int ", | |
| {kTag, "my_function"}, | |
| "(input int a, b, output int c);\nc = a + b;\nreturn " | |
| "c;\nendfunction\nendpackage"}, | |
| {"class m;\n virtual function int ", | |
| {kTag, "my_fun"}, | |
| "();\n return 10;\n endfunction\n endclass"}, | |
| {"class m;\n static function int ", | |
| {kTag, "my_fun"}, | |
| "();\n return 10;\n endfunction\n endclass"}, | |
| }; | |
| for (const auto& test : kTestCases) { | |
| TestVerilogSyntaxRangeMatches( | |
| __FUNCTION__, test, [](const TextStructureView& text_structure) { | |
| const auto& root = text_structure.SyntaxTree(); | |
| const auto decls = | |
| FindAllFunctionDeclarations(*ABSL_DIE_IF_NULL(root)); | |
| std::vector<TreeSearchMatch> types; | |
| for (const auto& decl : decls) { | |
| const auto* type = GetFunctionName(*decl.match); | |
| types.push_back(TreeSearchMatch{type, {/* ignored context */}}); | |
| } | |
| return types; | |
| }); | |
| } | |
| } | |
| TEST(GetFunctionHeaderTest, GetFunctionClassCallName) { | |
| constexpr int kTag = 1; // value doesn't matter | |
| const SyntaxTreeSearchTestCase kTestCases[] = { | |
| {""}, | |
| {"module m(); endmodule: m"}, | |
| {"module m();\n initial $display(my_class.", | |
| {kTag, "function_name"}, | |
| "());\nendmodule"}, | |
| {"module m();\n initial " | |
| "$display(pkg::my_class.", | |
| {kTag, "function_name"}, | |
| "());\nendmodule"}, | |
| }; | |
| for (const auto& test : kTestCases) { | |
| TestVerilogSyntaxRangeMatches( | |
| __FUNCTION__, test, [](const TextStructureView& text_structure) { | |
| const auto& root = text_structure.SyntaxTree(); | |
| const auto calls = | |
| FindAllFunctionOrTaskCallsExtension(*ABSL_DIE_IF_NULL(root)); | |
| std::vector<TreeSearchMatch> names; | |
| for (const auto& Call : calls) { | |
| const auto* name = | |
| GetFunctionCallNameFromCallExtension(*Call.match); | |
| names.emplace_back(TreeSearchMatch{name, {/* ignored context */}}); | |
| } | |
| return names; | |
| }); | |
| } | |
| } | |
| TEST(GetFunctionBlockStatement, GetFunctionBody) { | |
| constexpr int kTag = 1; // value doesn't matter | |
| const SyntaxTreeSearchTestCase kTestCases[] = { | |
| {""}, | |
| {"module m(); endmodule: m"}, | |
| {"function int foo(int a, bit x);\n", | |
| {kTag, "return a;"}, | |
| " endfunction\n"}, | |
| {"module my_module;\nfunction int inner_function(int my_args);\n", | |
| {kTag, "return my_args;"}, | |
| "\nendfunction\nendmodule"}, | |
| {"class function_class;\nfunction int my_function(input int a, b, output " | |
| "int c);\n", | |
| {kTag, "c = a + b;\nreturn c;"}, | |
| "\nendfunction\nendclass"}, | |
| {"package my_pkg;\nfunction automatic int my_function(input int a, b, " | |
| "output int c);\n", | |
| {kTag, "c = a + b;\nreturn c;"}, | |
| "\nendfunction\nendpackage"}, | |
| {"class m;\n virtual function int my_fun();\n ", | |
| {kTag, "return 10;"}, | |
| "\n endfunction\n endclass"}, | |
| {"class m;\n static function int my_fun();\n ", | |
| {kTag, "return 10;"}, | |
| "\n endfunction\n endclass"}, | |
| {"function int f;\n", {kTag, "return 1;"}, "\nendfunction"}, | |
| {"class s;\nfunction int f;", | |
| {kTag, "return 1;"}, | |
| "\nendfunction\nendclass"}, | |
| }; | |
| for (const auto& test : kTestCases) { | |
| TestVerilogSyntaxRangeMatches( | |
| __FUNCTION__, test, [](const TextStructureView& text_structure) { | |
| const auto& root = text_structure.SyntaxTree(); | |
| const auto decls = | |
| FindAllFunctionDeclarations(*ABSL_DIE_IF_NULL(root)); | |
| std::vector<TreeSearchMatch> functions_body; | |
| for (const auto& decl : decls) { | |
| const auto& body = GetFunctionBlockStatementList(*decl.match); | |
| functions_body.push_back( | |
| TreeSearchMatch{body, {/* ignored context */}}); | |
| } | |
| return functions_body; | |
| }); | |
| } | |
| } | |
| TEST(FunctionCallTest, GetFunctionCallName) { | |
| constexpr int kTag = 1; // value doesn't matter | |
| const SyntaxTreeSearchTestCase kTestCases[] = { | |
| {""}, | |
| {"module m;\nendmodule"}, | |
| {"module m;\ninitial begin\n", {kTag, "f1"}, "();\nend\nendmodule"}, | |
| {"module m;\ninitial begin\n", {kTag, "pkg::f1"}, "();\nend\nendmodule"}, | |
| {"module m;\ninitial begin\n", | |
| {kTag, "class_name#(1)::f1"}, | |
| "(a, b, c);\nend\nendmodule"}, | |
| {"r=this();"}, | |
| }; | |
| for (const auto& test : kTestCases) { | |
| TestVerilogSyntaxRangeMatches( | |
| __FUNCTION__, test, [](const TextStructureView& text_structure) { | |
| const auto& root = text_structure.SyntaxTree(); | |
| const auto& calls = | |
| FindAllFunctionOrTaskCalls(*ABSL_DIE_IF_NULL(root)); | |
| std::vector<TreeSearchMatch> identifiers; | |
| for (const auto& call : calls) { | |
| const auto* identifier = | |
| GetIdentifiersFromFunctionCall(*call.match); | |
| if (identifier == nullptr) { | |
| continue; | |
| } | |
| identifiers.emplace_back( | |
| TreeSearchMatch{identifier, {/* ignored context */}}); | |
| } | |
| return identifiers; | |
| }); | |
| } | |
| } // namespace | |
| TEST(FunctionCallTest, GetFunctionCallArguments) { | |
| constexpr int kTag = 1; // value doesn't matter | |
| const SyntaxTreeSearchTestCase kTestCases[] = { | |
| {"module m;\ninitial foo", {kTag, "()"}, ";\nendmodule"}, | |
| {"module m;\ninitial foo", {kTag, "(a, b, c)"}, ";\nendmodule"}, | |
| {"class c;\nfunction foo();\nendfunction\nfunction " | |
| "bar();\nfoo", | |
| {kTag, "()"}, | |
| ";\nendfunction\ntask tsk(x, y);\nendtask\ntask " | |
| "task_2();\ntsk", | |
| {kTag, "(a, b)"}, | |
| ";\nendtask\nendclass"}, | |
| }; | |
| for (const auto& test : kTestCases) { | |
| TestVerilogSyntaxRangeMatches( | |
| __FUNCTION__, test, [](const TextStructureView& text_structure) { | |
| const auto& root = text_structure.SyntaxTree(); | |
| const auto& instances = | |
| FindAllFunctionOrTaskCalls(*ABSL_DIE_IF_NULL(root)); | |
| std::vector<TreeSearchMatch> paren_groups; | |
| for (const auto& decl : instances) { | |
| const auto* paren_group = GetParenGroupFromCall(*decl.match); | |
| paren_groups.emplace_back( | |
| TreeSearchMatch{paren_group, {/* ignored context */}}); | |
| } | |
| return paren_groups; | |
| }); | |
| } | |
| } | |
| TEST(FunctionCallTest, GetFunctionCallExtensionArguments) { | |
| constexpr int kTag = 1; // value doesn't matter | |
| const SyntaxTreeSearchTestCase kTestCases[] = { | |
| {""}, | |
| {"module m(); endmodule: m"}, | |
| {"module m();\n initial $display(my_class.function_name", | |
| {kTag, "(x, y)"}, | |
| ");\nendmodule"}, | |
| {"module m();\n initial " | |
| "$display(pkg::my_class.function_name", | |
| {kTag, "()"}, | |
| ");\nendmodule"}, | |
| }; | |
| for (const auto& test : kTestCases) { | |
| TestVerilogSyntaxRangeMatches( | |
| __FUNCTION__, test, [](const TextStructureView& text_structure) { | |
| const auto& root = text_structure.SyntaxTree(); | |
| const auto& instances = | |
| FindAllFunctionOrTaskCallsExtension(*ABSL_DIE_IF_NULL(root)); | |
| std::vector<TreeSearchMatch> paren_groups; | |
| for (const auto& decl : instances) { | |
| const auto* paren_group = | |
| GetParenGroupFromCallExtension(*decl.match); | |
| paren_groups.emplace_back( | |
| TreeSearchMatch{paren_group, {/* ignored context */}}); | |
| } | |
| return paren_groups; | |
| }); | |
| } | |
| } | |
| TEST(FunctionCallTest, GetConstructorNewKeyword) { | |
| constexpr int kTag = 1; // value doesn't matter | |
| const SyntaxTreeSearchTestCase kTestCases[] = { | |
| {""}, | |
| {"class c; endclass: c"}, | |
| {"class c;\n" | |
| "function int foo();\n" | |
| "endfunction\n" | |
| "endclass\n"}, | |
| {"class c;\n" | |
| "extern function int foo();\n" | |
| "endclass\n"}, | |
| {"class c;\n" | |
| "extern function ", | |
| {kTag, "new"}, | |
| ";\n" | |
| "endclass\n"}, | |
| {"class c;\n" | |
| "extern function ", | |
| {kTag, "new"}, | |
| "();\n" | |
| "endclass\n"}, | |
| {"class c;\n" | |
| "extern function ", | |
| {kTag, "new"}, | |
| "(string name);\n" | |
| "endclass\n"}, | |
| }; | |
| for (const auto& test : kTestCases) { | |
| TestVerilogSyntaxRangeMatches( | |
| __FUNCTION__, test, [](const TextStructureView& text_structure) { | |
| const auto& root = text_structure.SyntaxTree(); | |
| const auto& ctors = | |
| FindAllConstructorPrototypes(*ABSL_DIE_IF_NULL(root)); | |
| std::vector<TreeSearchMatch> new_tokens; | |
| for (const auto& decl : ctors) { | |
| const auto* paren_group = | |
| GetConstructorPrototypeNewKeyword(*decl.match); | |
| new_tokens.emplace_back( | |
| TreeSearchMatch{paren_group, {/* ignored context */}}); | |
| } | |
| return new_tokens; | |
| }); | |
| } | |
| } | |
| } // namespace | |
| } // namespace verilog |