blob: 9e7d0c43f4fdc47f851c2ee0921f61ad009d5350 [file] [log] [blame]
// Copyright 2017-2020 The Verible Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "verilog/CST/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