blob: 3d27f259d78eef8f9fce9244e8a3640f74cb1196 [file] [log] [blame]
// Copyright 2017-2023 The Verible Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "verible/verilog/CST/statement.h"
#include <memory>
#include <string_view>
#include <vector>
#include "gtest/gtest.h"
#include "verible/common/analysis/matcher/matcher-builders.h"
#include "verible/common/analysis/syntax-tree-search-test-utils.h"
#include "verible/common/analysis/syntax-tree-search.h"
#include "verible/common/text/concrete-syntax-tree.h"
#include "verible/common/text/symbol.h"
#include "verible/common/text/text-structure.h"
#include "verible/common/text/tree-utils.h"
#include "verible/common/util/logging.h"
#include "verible/verilog/CST/match-test-utils.h"
#include "verible/verilog/CST/verilog-matchers.h"
#include "verible/verilog/CST/verilog-nonterminals.h"
#include "verible/verilog/analysis/verilog-analyzer.h"
#undef EXPECT_OK
#define EXPECT_OK(value) EXPECT_TRUE((value).ok())
#undef ASSERT_OK
#define ASSERT_OK(value) ASSERT_TRUE((value).ok())
namespace verilog {
namespace {
using verible::SymbolKind;
using verible::SymbolTag;
using verible::SyntaxTreeSearchTestCase;
using verible::TextStructureView;
using verible::TreeSearchMatch;
struct ControlStatementTestData {
NodeEnum expected_construct;
SyntaxTreeSearchTestCase token_data;
};
TEST(GetAnyControlStatementBodyTest, Various) {
constexpr int kTag = 1; // value doesn't matter
const ControlStatementTestData kTestCases[] = {
// each of these test cases should match exactly one statement body
{NodeEnum::kGenerateIfClause,
{"module m;\n"
" if (expr)\n",
{kTag, ";"}, // null generate item
"\n"
" else \n"
" bar foo;\n"
"endmodule\n"}},
{NodeEnum::kGenerateIfClause,
{"module m;\n"
" if (expr)\n"
" ",
{kTag, "foo bar;"},
"\n"
" else \n"
" bar foo;\n"
"endmodule\n"}},
{NodeEnum::kGenerateIfClause,
{"module m;\n"
" if (expr)\n"
" ",
{kTag, "begin\nfoo bar;end"},
"\n"
" else \n"
" bar foo;\n"
"endmodule\n"}},
{NodeEnum::kGenerateElseClause,
{"module m;\n"
" if (expr)\n"
" foo bar;\n"
" else \n",
{kTag, ";"}, // null generate item
"\n"
"endmodule\n"}},
{NodeEnum::kGenerateElseClause,
{"module m;\n"
" if (expr)\n"
" foo bar;\n"
" else \n",
{kTag, "bar#(1) foo;"},
"\n"
"endmodule\n"}},
{NodeEnum::kGenerateElseClause,
{"module m;\n"
" if (expr)\n"
" foo bar;\n"
" else \n",
{kTag, "begin \nbar#(1) foo; baz bam();\nend"},
"\n"
"endmodule\n"}},
{NodeEnum::kLoopGenerateConstruct,
{"module m;\n"
" for (genvar i=0; i<N; ++i)\n"
" ",
{kTag, ";"}, // null generate item
"\n"
"endmodule\n"}},
{NodeEnum::kLoopGenerateConstruct,
{"module m;\n"
" for (genvar i=0; i<N; ++i)\n"
" ",
{kTag, "foo#(.N(i)) bar;"},
"\n"
"endmodule\n"}},
{NodeEnum::kLoopGenerateConstruct,
{"module m;\n"
" for (genvar i=0; i<N; ++i)\n"
" ",
{kTag, "begin:l1\n foo#(.N(i)) bar;\n end : l1"},
"\n"
"endmodule\n"}},
{NodeEnum::kIfClause,
{"function f;\n"
" if (expr)\n",
{kTag, ";"}, // null statement
"\n"
" else \n"
" bar=foo;\n"
"endfunction\n"}},
{NodeEnum::kIfClause,
{"function f;\n"
" if (expr)\n"
" ",
{kTag, "foo=bar;"},
"\n"
" else \n"
" bar=foo;\n"
"endfunction\n"}},
{NodeEnum::kIfClause,
{"task t;\n"
" if (expr)\n"
" ",
{kTag, "begin\nfoo=bar; bar=1;\nend"},
"\n"
" else \n"
" bar=foo;\n"
"endtask\n"}},
{NodeEnum::kElseClause,
{"task t;\n"
" if (expr)\n"
" foo =bar;\n"
"\n"
" else\n",
{kTag, "bar=foo;"},
"endtask\n"}},
{NodeEnum::kElseClause,
{"task t;\n"
" if (expr)\n"
" foo =bar;\n"
"\n"
" else\n",
{kTag, ";"}, // null statement
"endtask\n"}},
{NodeEnum::kElseClause,
{"function f;\n"
" if (expr)\n"
" foo =bar;\n"
" else\n",
{kTag, "begin:bb bar=foo(baz);\n\nend :\nbb"},
"\nendfunction\n"}},
{NodeEnum::kForLoopStatement,
{"function f;\n"
" for (int j=N; expr; --j)\n"
" ",
{kTag, ";"}, // null statement
"\n"
"endfunction\n"}},
{NodeEnum::kForLoopStatement,
{"function f;\n"
" for (int j=N; expr; --j)\n"
" ",
{kTag, "foo=bar;"},
"\n"
"endfunction\n"}},
{NodeEnum::kForLoopStatement,
{"task t;\n"
" for (int j=N; expr; --j)\n"
" ",
{kTag, "begin\nfoo=bar; bar=1;\nend"},
"\n"
"endtask\n"}},
{NodeEnum::kDoWhileLoopStatement,
{"function f;\n"
" do\n",
{kTag, ";"}, // null statement
" while (expr);\n"
"endfunction\n"}},
{NodeEnum::kDoWhileLoopStatement,
{"function f;\n"
" do\n",
{kTag, "foo=bar;"},
" while (expr);\n"
"endfunction\n"}},
{NodeEnum::kDoWhileLoopStatement,
{"task t;\n"
" do ",
{kTag, "begin\nfoo=bar; bar=1;\nend"},
" while (expr);\n"
"endtask\n"}},
{NodeEnum::kForeverLoopStatement,
{"function f;\n"
" forever\n",
{kTag, ";"}, // null statement
"\n"
"endfunction\n"}},
{NodeEnum::kForeverLoopStatement,
{"function f;\n"
" forever\n"
" ",
{kTag, "foo=bar;"},
"\n"
"endfunction\n"}},
{NodeEnum::kForeverLoopStatement,
{"task t;\n"
" forever\n"
" ",
{kTag, "begin\nfoo=bar; bar=1;\nend"},
"\n"
"endtask\n"}},
{NodeEnum::kForeachLoopStatement,
{"function f;\n"
" foreach (x[i])\n",
{kTag, ";"}, // null statement
"\n"
"endfunction\n"}},
{NodeEnum::kForeachLoopStatement,
{"function f;\n"
" foreach (x[i])\n"
" ",
{kTag, "foo=bar;"},
"\n"
"endfunction\n"}},
{NodeEnum::kForeachLoopStatement,
{"task t;\n"
" foreach (x[i])\n"
" ",
{kTag, "begin\nfoo=bar; bar=1;\nend"},
"\n"
"endtask\n"}},
{NodeEnum::kRepeatLoopStatement,
{"function f;\n"
" repeat (8)\n",
{kTag, ";"}, // null statement
"\n"
"endfunction\n"}},
{NodeEnum::kRepeatLoopStatement,
{"function f;\n"
" repeat (8)\n"
" ",
{kTag, "foo=bar;"},
"\n"
"endfunction\n"}},
{NodeEnum::kRepeatLoopStatement,
{"task t;\n"
" repeat (9)\n"
" ",
{kTag, "begin\nfoo=bar; bar=1;\nend"},
"\n"
"endtask\n"}},
{NodeEnum::kWhileLoopStatement,
{"function f;\n"
" while (expr)\n",
{kTag, ";"}, // null statement
"\n"
"endfunction\n"}},
{NodeEnum::kWhileLoopStatement,
{"function f;\n"
" while (expr)\n"
" ",
{kTag, "foo=bar;"},
"\n"
"endfunction\n"}},
{NodeEnum::kWhileLoopStatement,
{"task t;\n"
" while (expr)\n"
" ",
{kTag, "begin\nfoo=bar; bar=1;\nend"},
"\n"
"endtask\n"}},
{NodeEnum::kProceduralTimingControlStatement,
{"module m;\n"
" always @(negedge c)\n",
{kTag, ";"}, // null statement
"\n"
"endmodule\n"}},
{NodeEnum::kProceduralTimingControlStatement,
{"module m;\n"
" always @(negedge c)\n",
{kTag, "foo=bar;"},
"\n"
"endmodule\n"}},
{NodeEnum::kProceduralTimingControlStatement,
{"module m;\n"
" always @(negedge c)\n",
{kTag, "begin\nfoo=bar; bar=1;\nend"},
"\n"
"endmodule\n"}},
{NodeEnum::kAssertionClause,
{"task t;\n"
" assert (expr)\n",
{kTag, ";"}, // null statement
"\n"
"endtask\n"}},
{NodeEnum::kAssertionClause,
{"task t;\n"
" assert (expr)\n",
{kTag, "action();"},
"\n"
"endtask\n"}},
{NodeEnum::kAssertionClause,
{"task t;\n"
" assert (expr)\n",
{kTag, "begin action(); end"},
"\n"
"endtask\n"}},
{NodeEnum::kAssumeClause,
{"task t;\n"
" assume (expr)\n",
{kTag, ";"}, // null statement
"\n"
"endtask\n"}},
{NodeEnum::kAssumeClause,
{"task t;\n"
" assume (expr)\n",
{kTag, "action();"},
"\n"
"endtask\n"}},
{NodeEnum::kAssumeClause,
{"task t;\n"
" assume (expr)\n",
{kTag, "begin action(); end"},
"\n"
"endtask\n"}},
{NodeEnum::kWaitStatement,
{"task t;\n"
" wait (expr)\n",
{kTag, ";"}, // null statement
"\n"
"endtask\n"}},
{NodeEnum::kWaitStatement,
{"task t;\n"
" wait (expr)\n",
{kTag, "snooze();"},
"\n"
"endtask\n"}},
{NodeEnum::kWaitStatement,
{"task t;\n"
" wait (expr)\n",
{kTag, "begin snooze(); end"},
"\n"
"endtask\n"}},
{NodeEnum::kCoverStatement,
{"task t;\n"
" cover (expr)\n",
{kTag, ";"}, // null statement
"\n"
"endtask\n"}},
{NodeEnum::kCoverStatement,
{"task t;\n"
" cover (expr)\n",
{kTag, "snooze();"},
"\n"
"endtask\n"}},
{NodeEnum::kCoverStatement,
{"task t;\n"
" cover (expr)\n",
{kTag, "begin snooze(); end"},
"\n"
"endtask\n"}},
{NodeEnum::kAssertPropertyClause,
{"task t;\n"
" assert property (p_expr)\n",
{kTag, ";"}, // null statement
"\n"
"endtask\n"}},
{NodeEnum::kAssertPropertyClause,
{"task t;\n"
" assert property (p_expr)\n",
{kTag, "action();"},
"\n"
"endtask\n"}},
{NodeEnum::kAssertPropertyClause,
{"task t;\n"
" assert property (p_expr)\n",
{kTag, "begin action(); end"},
"\n"
"endtask\n"}},
{NodeEnum::kAssumePropertyClause,
{"task t;\n"
" assume property (p_expr)\n",
{kTag, ";"}, // null statement
"\n"
"endtask\n"}},
{NodeEnum::kAssumePropertyClause,
{"task t;\n"
" assume property (p_expr)\n",
{kTag, "action();"},
"\n"
"endtask\n"}},
{NodeEnum::kAssumePropertyClause,
{"task t;\n"
" assume property (p_expr)\n",
{kTag, "begin action(); end"},
"\n"
"endtask\n"}},
{NodeEnum::kExpectPropertyClause,
{"task t;\n"
" expect (p_expr)\n",
{kTag, ";"}, // null statement
"\n"
"endtask\n"}},
{NodeEnum::kExpectPropertyClause,
{"task t;\n"
" expect (p_expr)\n",
{kTag, "action();"},
"\n"
"endtask\n"}},
{NodeEnum::kExpectPropertyClause,
{"task t;\n"
" expect (p_expr)\n",
{kTag, "begin action(); end"},
"\n"
"endtask\n"}},
{NodeEnum::kCoverPropertyStatement,
{"task t;\n"
" cover property (p_expr)\n",
{kTag, ";"}, // null statement
"\n"
"endtask\n"}},
{NodeEnum::kCoverPropertyStatement,
{"task t;\n"
" cover property (p_expr)\n",
{kTag, "action();"},
"\n"
"endtask\n"}},
{NodeEnum::kCoverPropertyStatement,
{"task t;\n"
" cover property (p_expr)\n",
{kTag, "begin action(); end"},
"\n"
"endtask\n"}},
{NodeEnum::kCoverSequenceStatement,
{"task t;\n"
" cover sequence (s_expr)\n",
{kTag, ";"}, // null statement
"\n"
"endtask\n"}},
{NodeEnum::kCoverSequenceStatement,
{"task t;\n"
" cover sequence (s_expr)\n",
{kTag, "action();"},
"\n"
"endtask\n"}},
{NodeEnum::kCoverSequenceStatement,
{"task t;\n"
" cover sequence (s_expr)\n",
{kTag, "begin action(); end"},
"\n"
"endtask\n"}},
};
for (const auto &test : kTestCases) {
TestVerilogSyntaxRangeMatches(
__FUNCTION__, test.token_data,
[&test](const TextStructureView &text_structure) {
const auto &root = text_structure.SyntaxTree();
// Grab outer statement constructs.
const auto statements = verible::SearchSyntaxTree(
*ABSL_DIE_IF_NULL(root),
verible::matcher::DynamicTagMatchBuilder(
SymbolTag{SymbolKind::kNode,
static_cast<int>(test.expected_construct)})());
// Extract subtree of interest.
std::vector<TreeSearchMatch> bodies;
for (const auto &statement : statements) {
const auto *body = GetAnyControlStatementBody(*statement.match);
bodies.push_back(TreeSearchMatch{body, {/* ignored context */}});
}
return bodies;
});
}
}
TEST(GetAnyConditionalIfClauseTest, Various) {
const ControlStatementTestData kTestCases[] = {
// each of these test cases should match exactly one statement body
{NodeEnum::kConditionalGenerateConstruct,
{"module m;\n",
{static_cast<int>(NodeEnum::kGenerateIfClause), "if (expr);"},
"\n"
"endmodule\n"}},
{NodeEnum::kConditionalGenerateConstruct,
{"module m;\n",
{static_cast<int>(NodeEnum::kGenerateIfClause), "if (expr) foo bar;"},
"\n"
"endmodule\n"}},
{NodeEnum::kConditionalGenerateConstruct,
{"module m;\n",
{static_cast<int>(NodeEnum::kGenerateIfClause), "if (expr) foo bar;"},
"\n"
" else \n"
" bar foo;\n"
"endmodule\n"}},
{NodeEnum::kConditionalGenerateConstruct,
{"module m;\n",
{static_cast<int>(NodeEnum::kGenerateIfClause),
"if (expr) begin\nfoo bar;end"},
"\n"
" else \n"
" bar foo;\n"
"endmodule\n"}},
{NodeEnum::kConditionalStatement,
{"function f;\n",
{static_cast<int>(NodeEnum::kIfClause), "if ( expr );"},
"\n"
"endfunction\n"}},
{NodeEnum::kConditionalStatement,
{"function f;\n",
{static_cast<int>(NodeEnum::kIfClause), "if ( expr ) foo=bar;"},
"\n"
"endfunction\n"}},
{NodeEnum::kConditionalStatement,
{"function f;\n",
{static_cast<int>(NodeEnum::kIfClause), "if ( expr ) foo=bar;"},
"\n"
" else \n"
" bar=foo;\n"
"endfunction\n"}},
{NodeEnum::kConditionalStatement,
{"task t;\n",
{static_cast<int>(NodeEnum::kIfClause),
"if (expr)begin\nfoo=bar; bar=1;\nend"},
"\n"
" else \n"
" bar=foo;\n"
"endtask\n"}},
{NodeEnum::kAssertionStatement,
{"function f;\n",
{static_cast<int>(NodeEnum::kAssertionClause),
"assert ( expr );"}, // null statement
"\n"
"endfunction\n"}},
{NodeEnum::kAssertionStatement,
{"function f;\n",
{static_cast<int>(NodeEnum::kAssertionClause),
"assert ( expr ) foo=bar;"},
"\n"
"endfunction\n"}},
{NodeEnum::kAssertionStatement,
{"function f;\n",
{static_cast<int>(NodeEnum::kAssertionClause),
"assert ( expr ) foo=bar;"},
"\n"
" else \n"
" bar=foo;\n"
"endfunction\n"}},
{NodeEnum::kAssertionStatement,
{"task t;\n",
{static_cast<int>(NodeEnum::kAssertionClause),
"assert (expr)begin\nfoo=bar; bar=1;\nend"},
"\n"
" else \n"
" bar=foo;\n"
"endtask\n"}},
{NodeEnum::kAssumeStatement,
{"function f;\n",
{static_cast<int>(NodeEnum::kAssumeClause), "assume ( expr );"},
"\n"
"endfunction\n"}},
{NodeEnum::kAssumeStatement,
{"function f;\n",
{static_cast<int>(NodeEnum::kAssumeClause), "assume ( expr ) foo=bar;"},
"\n"
"endfunction\n"}},
{NodeEnum::kAssumeStatement,
{"function f;\n",
{static_cast<int>(NodeEnum::kAssumeClause), "assume ( expr ) foo=bar;"},
"\n"
" else \n"
" bar=foo;\n"
"endfunction\n"}},
{NodeEnum::kAssumeStatement,
{"task t;\n",
{static_cast<int>(NodeEnum::kAssumeClause),
"assume (expr)begin\nfoo=bar; bar=1;\nend"},
"\n"
" else \n"
" bar=foo;\n"
"endtask\n"}},
{NodeEnum::kAssertPropertyStatement,
{"task t;\n",
{static_cast<int>(NodeEnum::kAssertPropertyClause),
"assert property ( p_expr );"}, // null statement
"\n"
"endtask\n"}},
{NodeEnum::kAssertPropertyStatement,
{"task t;\n",
{static_cast<int>(NodeEnum::kAssertPropertyClause),
"assert property ( p_expr ) foo=bar;"},
"\n"
"endtask\n"}},
{NodeEnum::kAssertPropertyStatement,
{"task t;\n",
{static_cast<int>(NodeEnum::kAssertPropertyClause),
"assert property ( p_expr ) foo=bar;"},
"\n"
" else \n"
" bar=foo;\n"
"endtask\n"}},
{NodeEnum::kAssertPropertyStatement,
{"task t;\n",
{static_cast<int>(NodeEnum::kAssertPropertyClause),
"assert property (p_expr)begin\nfoo=bar; bar=1;\nend"},
"\n"
" else \n"
" bar=foo;\n"
"endtask\n"}},
{NodeEnum::kAssumePropertyStatement,
{"task t;\n",
{static_cast<int>(NodeEnum::kAssumePropertyClause),
"assume property ( p_expr );"}, // null statement
"\n"
"endtask\n"}},
{NodeEnum::kAssumePropertyStatement,
{"task t;\n",
{static_cast<int>(NodeEnum::kAssumePropertyClause),
"assume property ( p_expr ) foo=bar;"},
"\n"
"endtask\n"}},
{NodeEnum::kAssumePropertyStatement,
{"task t;\n",
{static_cast<int>(NodeEnum::kAssumePropertyClause),
"assume property ( p_expr ) foo=bar;"},
"\n"
" else \n"
" bar=foo;\n"
"endtask\n"}},
{NodeEnum::kAssumePropertyStatement,
{"task t;\n",
{static_cast<int>(NodeEnum::kAssumePropertyClause),
"assume property (p_expr)begin\nfoo=bar; bar=1;\nend"},
"\n"
" else \n"
" bar=foo;\n"
"endtask\n"}},
{NodeEnum::kExpectPropertyStatement,
{"task t;\n",
{static_cast<int>(NodeEnum::kExpectPropertyClause),
"expect ( p_expr );"}, // null statement
"\n"
"endtask\n"}},
{NodeEnum::kExpectPropertyStatement,
{"task t;\n",
{static_cast<int>(NodeEnum::kExpectPropertyClause),
"expect ( p_expr ) foo=bar;"},
"\n"
"endtask\n"}},
{NodeEnum::kExpectPropertyStatement,
{"task t;\n",
{static_cast<int>(NodeEnum::kExpectPropertyClause),
"expect ( p_expr ) foo=bar;"},
"\n"
" else \n"
" bar=foo;\n"
"endtask\n"}},
{NodeEnum::kExpectPropertyStatement,
{"task t;\n",
{static_cast<int>(NodeEnum::kExpectPropertyClause),
"expect (p_expr)begin\nfoo=bar; bar=1;\nend"},
"\n"
" else \n"
" bar=foo;\n"
"endtask\n"}},
};
for (const auto &test : kTestCases) {
TestVerilogSyntaxRangeMatches(
__FUNCTION__, test.token_data,
[&test](const TextStructureView &text_structure) {
const auto &root = text_structure.SyntaxTree();
// Grab outer statement constructs.
const auto statements = verible::SearchSyntaxTree(
*ABSL_DIE_IF_NULL(root),
verible::matcher::DynamicTagMatchBuilder(
SymbolTag{SymbolKind::kNode,
static_cast<int>(test.expected_construct)})());
// Extract subtree of interest.
std::vector<TreeSearchMatch> bodies;
for (const auto &statement : statements) {
const auto *clause = GetAnyConditionalIfClause(*statement.match);
bodies.push_back(TreeSearchMatch{clause, {/* ignored context */}});
}
return bodies;
});
}
}
TEST(GetAnyConditionalElseClauseTest, NoElseClause) {
const ControlStatementTestData kTestCases[] = {
// each of these test cases should match exactly one statement body
{NodeEnum::kConditionalGenerateConstruct,
{"module m;\n",
{static_cast<int>(NodeEnum::kGenerateIfClause), "if (expr);"},
"\n"
"endmodule\n"}},
{NodeEnum::kConditionalGenerateConstruct,
{"module m;\n",
{static_cast<int>(NodeEnum::kGenerateIfClause), "if (expr) foo bar;"},
"\n"
"endmodule\n"}},
{NodeEnum::kConditionalStatement,
{"function f;\n",
{static_cast<int>(NodeEnum::kIfClause), "if ( expr );"},
"\n"
"endfunction\n"}},
{NodeEnum::kConditionalStatement,
{"function f;\n",
{static_cast<int>(NodeEnum::kIfClause), "if ( expr ) foo=bar;"},
"\n"
"endfunction\n"}},
{NodeEnum::kAssertionStatement,
{"task t;\n",
{static_cast<int>(NodeEnum::kAssertionClause), "assert ( expr );"},
"\n"
"endtask\n"}},
{NodeEnum::kAssertionStatement,
{"task t;\n",
{static_cast<int>(NodeEnum::kAssertionClause),
"assert ( expr ) foo=bar;"},
"\n"
"endtask\n"}},
{NodeEnum::kAssumeStatement,
{"task t;\n",
{static_cast<int>(NodeEnum::kAssumeClause), "assume ( expr );"},
"\n"
"endtask\n"}},
{NodeEnum::kAssumeStatement,
{"task t;\n",
{static_cast<int>(NodeEnum::kAssumeClause), "assume ( expr ) foo=bar;"},
"\n"
"endtask\n"}},
{NodeEnum::kAssertPropertyStatement,
{"task t;\n",
{static_cast<int>(NodeEnum::kAssertPropertyClause),
"assert property( expr );"},
"\n"
"endtask\n"}},
{NodeEnum::kAssertPropertyStatement,
{"task t;\n",
{static_cast<int>(NodeEnum::kAssertPropertyClause),
"assert property ( expr ) foo=bar;"},
"\n"
"endtask\n"}},
{NodeEnum::kAssumePropertyStatement,
{"task t;\n",
{static_cast<int>(NodeEnum::kAssumePropertyClause),
"assume property( expr );"},
"\n"
"endtask\n"}},
{NodeEnum::kAssumePropertyStatement,
{"task t;\n",
{static_cast<int>(NodeEnum::kAssumePropertyClause),
"assume property ( expr ) foo=bar;"},
"\n"
"endtask\n"}},
{NodeEnum::kExpectPropertyStatement,
{"task t;\n",
{static_cast<int>(NodeEnum::kExpectPropertyClause), "expect( expr );"},
"\n"
"endtask\n"}},
{NodeEnum::kExpectPropertyStatement,
{"task t;\n",
{static_cast<int>(NodeEnum::kExpectPropertyClause),
"expect ( expr ) foo=bar;"},
"\n"
"endtask\n"}},
};
for (const auto &test : kTestCases) {
const std::string_view code(test.token_data.code);
VerilogAnalyzer analyzer(code, "test-file");
ASSERT_OK(analyzer.Analyze()) << "failed on:\n" << code;
const auto &root = analyzer.Data().SyntaxTree();
const auto statements = verible::SearchSyntaxTree(
*ABSL_DIE_IF_NULL(root),
verible::matcher::DynamicTagMatchBuilder(SymbolTag{
SymbolKind::kNode, static_cast<int>(test.expected_construct)})());
ASSERT_EQ(statements.size(), 1);
const auto &statement = *statements.front().match;
const auto *clause = GetAnyConditionalElseClause(statement);
EXPECT_EQ(clause, nullptr);
}
}
TEST(GetAnyConditionalElseClauseTest, HaveElseClause) {
const ControlStatementTestData kTestCases[] = {
// each of these test cases should match exactly one statement body
{NodeEnum::kConditionalGenerateConstruct,
{"module m;\n",
"if (expr);\n",
{static_cast<int>(NodeEnum::kGenerateElseClause),
"else \n"
" ;"}, // null else body
"\n"
"endmodule\n"}},
{NodeEnum::kConditionalGenerateConstruct,
{"module m;\n",
"if (expr);\n",
{static_cast<int>(NodeEnum::kGenerateElseClause),
"else \n"
" bar foo;"},
"\n"
"endmodule\n"}},
{NodeEnum::kConditionalGenerateConstruct,
{"module m;\n",
"if (expr) foo bar;\n",
{static_cast<int>(NodeEnum::kGenerateElseClause),
"else \n"
" bar foo;"},
"\n"
"endmodule\n"}},
{NodeEnum::kConditionalGenerateConstruct,
{"module m;\n",
"if (expr) foo bar;\n",
{static_cast<int>(NodeEnum::kGenerateElseClause),
"else \n"
" begin bar foo;\nend"},
"\n"
"endmodule\n"}},
{NodeEnum::kConditionalStatement,
{"function f;\n",
"if ( expr );\n",
{static_cast<int>(NodeEnum::kElseClause),
"else \n"
" ;"}, // null else body
"\n"
"endfunction\n"}},
{NodeEnum::kConditionalStatement,
{"function f;\n",
"if ( expr );\n",
{static_cast<int>(NodeEnum::kElseClause),
"else \n"
" bar=foo;"},
"\n"
"endfunction\n"}},
{NodeEnum::kConditionalStatement,
{"function f;\n",
"if ( expr ) foo=bar;\n",
{static_cast<int>(NodeEnum::kElseClause),
"else \n"
" bar=foo;"},
"\n"
"endfunction\n"}},
{NodeEnum::kConditionalStatement,
{"function f;\n",
"if ( expr ) foo=bar;\n",
{static_cast<int>(NodeEnum::kElseClause),
"else \n"
" begin\nbar=foo;\nend"},
"\n"
"endfunction\n"}},
{NodeEnum::kAssertionStatement,
{"task t;\n",
"assert ( expr )\n", // no statement
{static_cast<int>(NodeEnum::kElseClause),
"else \n"
" bar=foo;"},
"\n"
"endtask\n"}},
{NodeEnum::kAssertionStatement,
{"task t;\n",
"assert ( expr );\n", // null statement
{static_cast<int>(NodeEnum::kElseClause),
"else \n"
" bar=foo;"},
"\n"
"endtask\n"}},
{NodeEnum::kAssertionStatement,
{"task t;\n",
"assert ( expr ) foo=bar;\n",
{static_cast<int>(NodeEnum::kElseClause),
"else \n"
" ;"}, // null else body
"\n"
"endtask\n"}},
{NodeEnum::kAssertionStatement,
{"task t;\n",
"assert ( expr ) foo=bar;\n",
{static_cast<int>(NodeEnum::kElseClause),
"else \n"
" bar=foo;"},
"\n"
"endtask\n"}},
{NodeEnum::kAssertionStatement,
{"task t;\n",
"assert ( expr ) foo=bar;\n",
{static_cast<int>(NodeEnum::kElseClause),
"else \n"
" begin\nbar=foo;\nend"},
"\n"
"endtask\n"}},
{NodeEnum::kAssumeStatement,
{"task t;\n",
"assume ( expr )\n", // no statement
{static_cast<int>(NodeEnum::kElseClause),
"else \n"
" bar=foo;"},
"\n"
"endtask\n"}},
{NodeEnum::kAssumeStatement,
{"task t;\n",
"assume ( expr );\n", // null statement
{static_cast<int>(NodeEnum::kElseClause),
"else \n"
" bar=foo;"},
"\n"
"endtask\n"}},
{NodeEnum::kAssumeStatement,
{"task t;\n",
"assume ( expr ) foo=bar;\n",
{static_cast<int>(NodeEnum::kElseClause),
"else \n"
" bar=foo;"},
"\n"
"endtask\n"}},
{NodeEnum::kAssumeStatement,
{"task t;\n",
"assume ( expr ) foo=bar;\n",
{static_cast<int>(NodeEnum::kElseClause),
"else \n"
" ;"}, // null else body
"\n"
"endtask\n"}},
{NodeEnum::kAssumeStatement,
{"task t;\n",
"assume ( expr ) foo=bar;\n",
{static_cast<int>(NodeEnum::kElseClause),
"else \n"
" begin\nbar=foo;\nend"},
"\n"
"endtask\n"}},
{NodeEnum::kAssertPropertyStatement,
{"task t;\n",
"assert property ( expr )\n", // no statement
{static_cast<int>(NodeEnum::kElseClause),
"else \n"
" bar=foo;"},
"\n"
"endtask\n"}},
{NodeEnum::kAssertPropertyStatement,
{"task t;\n",
"assert property ( expr );\n", // null statement
{static_cast<int>(NodeEnum::kElseClause),
"else \n"
" bar=foo;"},
"\n"
"endtask\n"}},
{NodeEnum::kAssertPropertyStatement,
{"task t;\n",
"assert property ( expr ) foo=bar;\n",
{static_cast<int>(NodeEnum::kElseClause),
"else \n"
" ;"}, // null else body
"\n"
"endtask\n"}},
{NodeEnum::kAssertPropertyStatement,
{"task t;\n",
"assert property ( expr ) foo=bar;\n",
{static_cast<int>(NodeEnum::kElseClause),
"else \n"
" bar=foo;"},
"\n"
"endtask\n"}},
{NodeEnum::kAssertPropertyStatement,
{"task t;\n",
"assert property ( expr ) foo=bar;\n",
{static_cast<int>(NodeEnum::kElseClause),
"else \n"
" begin\nbar=foo;\nend"},
"\n"
"endtask\n"}},
{NodeEnum::kAssumePropertyStatement,
{"task t;\n",
"assume property ( expr )\n", // no statement
{static_cast<int>(NodeEnum::kElseClause),
"else \n"
" bar=foo;"},
"\n"
"endtask\n"}},
{NodeEnum::kAssumePropertyStatement,
{"task t;\n",
"assume property ( expr );\n", // null statement
{static_cast<int>(NodeEnum::kElseClause),
"else \n"
" bar=foo;"},
"\n"
"endtask\n"}},
{NodeEnum::kAssumePropertyStatement,
{"task t;\n",
"assume property ( expr ) foo=bar;\n",
{static_cast<int>(NodeEnum::kElseClause),
"else \n"
" ;"}, // null else body
"\n"
"endtask\n"}},
{NodeEnum::kAssumePropertyStatement,
{"task t;\n",
"assume property ( expr ) foo=bar;\n",
{static_cast<int>(NodeEnum::kElseClause),
"else \n"
" bar=foo;"},
"\n"
"endtask\n"}},
{NodeEnum::kAssumePropertyStatement,
{"task t;\n",
"assume property ( expr ) foo=bar;\n",
{static_cast<int>(NodeEnum::kElseClause),
"else \n"
" begin\nbar=foo;\nend"},
"\n"
"endtask\n"}},
{NodeEnum::kExpectPropertyStatement,
{"task t;\n",
"expect ( expr )\n", // no statement
{static_cast<int>(NodeEnum::kElseClause),
"else \n"
" bar=foo;"},
"\n"
"endtask\n"}},
{NodeEnum::kExpectPropertyStatement,
{"task t;\n",
"expect ( expr );\n", // null statement
{static_cast<int>(NodeEnum::kElseClause),
"else \n"
" bar=foo;"},
"\n"
"endtask\n"}},
{NodeEnum::kExpectPropertyStatement,
{"task t;\n",
"expect ( expr ) foo=bar;\n",
{static_cast<int>(NodeEnum::kElseClause),
"else \n"
" ;"}, // null else body
"\n"
"endtask\n"}},
{NodeEnum::kExpectPropertyStatement,
{"task t;\n",
"expect ( expr ) foo=bar;\n",
{static_cast<int>(NodeEnum::kElseClause),
"else \n"
" bar=foo;"},
"\n"
"endtask\n"}},
{NodeEnum::kExpectPropertyStatement,
{"task t;\n",
"expect ( expr ) foo=bar;\n",
{static_cast<int>(NodeEnum::kElseClause),
"else \n"
" begin\nbar=foo;\nend"},
"\n"
"endtask\n"}},
};
for (const auto &test : kTestCases) {
TestVerilogSyntaxRangeMatches(
__FUNCTION__, test.token_data,
[&test](const TextStructureView &text_structure) {
const auto &root = text_structure.SyntaxTree();
// Grab outer statement constructs.
const auto statements = verible::SearchSyntaxTree(
*ABSL_DIE_IF_NULL(root),
verible::matcher::DynamicTagMatchBuilder(
SymbolTag{SymbolKind::kNode,
static_cast<int>(test.expected_construct)})());
// Extract subtree of interest.
std::vector<TreeSearchMatch> bodies;
for (const auto &statement : statements) {
const auto *clause = GetAnyConditionalElseClause(*statement.match);
bodies.push_back(TreeSearchMatch{clause, {/* ignored context */}});
}
return bodies;
});
}
}
TEST(FindAllForLoopsInitializations, FindForInitializationNames) {
constexpr int kTag = 1; // value doesn't matter
const SyntaxTreeSearchTestCase kTestCases[] = {
{""},
{"module m;\nendmodule\n"},
{"function int my_fun();\nint x = 0;\nfor (int ",
{kTag, "i"},
" = 0, ",
{kTag, "j"},
" = 0; i < 50; i++) begin\nx+=i;\nend\nreturn x;\nendfunction"},
{"module m();\nint x = 0;\ninitial begin\nfor (int ",
{kTag, "i"},
" = 0, ",
{kTag, "j"},
" = 0, bit ",
{kTag, "k"},
" = 0; i < 50; i++) begin\nx+=i;\nend\nend\nendmodule"},
};
for (const auto &test : kTestCases) {
TestVerilogSyntaxRangeMatches(
__FUNCTION__, test, [](const TextStructureView &text_structure) {
const auto &root = text_structure.SyntaxTree();
const auto &instances =
FindAllForLoopsInitializations(*ABSL_DIE_IF_NULL(root));
std::vector<TreeSearchMatch> names;
for (const auto &instance : instances) {
const auto *variable_name =
GetVariableNameFromForInitialization(*instance.match);
names.emplace_back(
TreeSearchMatch{variable_name, {/* ignored context */}});
}
return names;
});
}
}
TEST(FindAllForLoopsInitializations, FindForInitializationDataTypes) {
constexpr int kTag = 1; // value doesn't matter
const SyntaxTreeSearchTestCase kTestCases[] = {
{""},
{"module m;\nendmodule\n"},
{"function int my_fun();\nint x = 0;\nfor (",
{kTag, "int"},
" i = 0, j = 0; i < 50; i++) begin\nx+=i;\nend\nreturn x;\nendfunction"},
{"module m();\nint x = 0;\ninitial begin\nfor (",
{kTag, "int"},
" i = 0, j = 0, ",
{kTag, "bit"},
" k = 0; i < 50; i++) begin\nx+=i;\nend\nend\nendmodule"},
{"module m();\nint x = 0;\ninitial begin\nfor (",
{kTag, "int[x:y]"},
" i = 0, j = 0, ",
{kTag, "bit"},
" k = 0; i < 50; i++) begin\nx+=i;\nend\nend\nendmodule"},
};
for (const auto &test : kTestCases) {
TestVerilogSyntaxRangeMatches(
__FUNCTION__, test, [](const TextStructureView &text_structure) {
const auto &root = text_structure.SyntaxTree();
const auto &instances =
FindAllForLoopsInitializations(*ABSL_DIE_IF_NULL(root));
std::vector<TreeSearchMatch> types;
for (const auto &instance : instances) {
const auto *type =
GetDataTypeFromForInitialization(*instance.match);
if (type == nullptr) {
continue;
}
types.emplace_back(TreeSearchMatch{type, {/* ignored context */}});
}
return types;
});
}
}
TEST(FindAllForLoopsInitializations, FindForInitializationExpressions) {
constexpr int kTag = 1; // value doesn't matter
const SyntaxTreeSearchTestCase kTestCases[] = {
{""},
{"module m;\nendmodule\n"},
{"function int my_fun();\nint x = 0;\nfor (int i = ",
{kTag, "0"},
", j = ",
{kTag, "0"},
"; i < 50;i++) begin\nx+=i;\nend\nreturn x;\nendfunction"},
{"module m();\nint x = 0;\ninitial begin\nfor (int i = ",
{kTag, "0"},
", j = ",
{kTag, "y + x"},
", bit k = ",
{kTag, "0"},
"; i < 50;i++) begin\nx+=i;\nend\nend\nendmodule"},
};
for (const auto &test : kTestCases) {
TestVerilogSyntaxRangeMatches(
__FUNCTION__, test, [](const TextStructureView &text_structure) {
const auto &root = text_structure.SyntaxTree();
const auto &instances =
FindAllForLoopsInitializations(*ABSL_DIE_IF_NULL(root));
std::vector<TreeSearchMatch> expressions;
for (const auto &instance : instances) {
const auto *expression =
GetExpressionFromForInitialization(*instance.match);
expressions.emplace_back(
TreeSearchMatch{expression, {/* ignored context */}});
}
return expressions;
});
}
}
TEST(GetGenerateBlockBeginTest, Various) {
constexpr int kTag = 1; // value doesn't matter
const SyntaxTreeSearchTestCase kTestCases[] = {
{""},
{"module m;\nendmodule\n"},
{"module m;\n"
" wire k;\n"
"endmodule\n"},
{"module m;\n"
" if (1)\n"
" wire www;\n"
"endmodule\n"},
{"module m;\n"
" if (1) ",
{kTag, "begin"},
"\n"
" end\n"
"endmodule\n"},
{"module m;\n"
" if (1) ",
{kTag, "begin : my_label"},
"\n"
" end : my_label\n"
"endmodule\n"},
{"module m;\n"
" if (1) ",
{kTag, "begin"},
"\n"
" end else if (2) ",
{kTag, "begin:foo"},
"\n"
" end\n"
"endmodule\n"},
{"module m;\n"
" for (genvar i=0; i<N; ++i) ",
{kTag, "begin"},
"\n"
" end\n"
"endmodule\n"},
};
for (const auto &test : kTestCases) {
TestVerilogSyntaxRangeMatches(
__FUNCTION__, test, [](const TextStructureView &text_structure) {
const auto &root = text_structure.SyntaxTree();
const auto &blocks = FindAllGenerateBlocks(*ABSL_DIE_IF_NULL(root));
std::vector<TreeSearchMatch> begins;
for (const auto &block : blocks) {
const auto *begin = GetGenerateBlockBegin(*block.match);
begins.emplace_back(
TreeSearchMatch{begin, {/* ignored context */}});
}
return begins;
});
}
}
TEST(GetGenerateBlockEndTest, Various) {
constexpr int kTag = 1; // value doesn't matter
const SyntaxTreeSearchTestCase kTestCases[] = {
{""},
{"module m;\nendmodule\n"},
{"module m;\n"
" wire k;\n"
"endmodule\n"},
{"module m;\n"
" if (1)\n"
" wire www;\n"
"endmodule\n"},
{"module m;\n"
" if (1) begin\n"
" ",
{kTag, "end"},
"\n"
"endmodule\n"},
{"module m;\n"
" if (1) begin : my_label\n"
" ",
{kTag, "end : my_label"},
"\n"
"endmodule\n"},
{"module m;\n"
" if (1) begin : my_label\n"
" ",
{kTag, "end : my_label"},
"\n"
" else if (2) begin : your_label\n"
" ",
{kTag, "end : your_label"},
"\n"
"endmodule\n"},
{"module m;\n"
" for (genvar i=0; i<N; ++i) begin\n"
" ",
{kTag, "end"},
"\n"
"endmodule\n"},
};
for (const auto &test : kTestCases) {
TestVerilogSyntaxRangeMatches(
__FUNCTION__, test, [](const TextStructureView &text_structure) {
const auto &root = text_structure.SyntaxTree();
const auto &blocks = FindAllGenerateBlocks(*ABSL_DIE_IF_NULL(root));
std::vector<TreeSearchMatch> ends;
for (const auto &block : blocks) {
const auto *end = GetGenerateBlockEnd(*block.match);
ends.emplace_back(TreeSearchMatch{end, {/* ignored context */}});
}
return ends;
});
}
}
TEST(FindAllNonBlockingAssignmentTest, Various) {
constexpr int kTag = 1; // value doesn't matter
const SyntaxTreeSearchTestCase kTestCases[] = {
{"module m;\n"
"always @(*) x = 1;\n"
"endmodule"},
{"module m;\n"
"always_ff @(posedge clk) ",
{kTag, "x <= 1;"},
"\nendmodule"},
{"module m;\n"
"always_ff @(posedge clk) begin\n",
{kTag, "a <= b;"},
"\n",
{kTag, "c <= d();"},
"\n",
{kTag, "e <= `F;"},
"\nend endmodule"},
{"module m;\n"
"always_latch ",
{kTag, "x <= 1;"},
"\nendmodule"},
{"module m;\n"
"always@(*) ",
{kTag, "x[1] <= y[2];"},
"\nendmodule"},
};
for (const auto &test : kTestCases) {
TestVerilogSyntaxRangeMatches(
__FUNCTION__, test, [](const TextStructureView &text_structure) {
const auto &root = text_structure.SyntaxTree();
return FindAllNonBlockingAssignments(*ABSL_DIE_IF_NULL(root));
});
}
}
TEST(GetNonBlockingAssignmentRhsTest, Various) {
constexpr int kTag = 1; // value doesn't matter
const SyntaxTreeSearchTestCase kTestCases[] = {
{"module m;\n"
"always @(*) x = 1;\n"
"endmodule"},
{"module m;\n"
"always_ff @(posedge clk) "
"x <= ",
{kTag, "1"},
";\nendmodule"},
{"module m;\n"
"always_ff @(posedge clk) begin\n"
"a <= ",
{kTag, "b"},
";\nc <= ",
{kTag, "d()"},
";\ne <= ",
{kTag, "`F"},
";\nend endmodule"},
{"module m;\n"
"always_latch "
"x <= ",
{kTag, "1"},
";\nendmodule"},
{"module m;\n"
"always@(*) x[1] <= ",
{kTag, "y[2]"},
";\nendmodule"},
};
for (const auto &test : kTestCases) {
TestVerilogSyntaxRangeMatches(
__FUNCTION__, test, [](const TextStructureView &text_structure) {
const auto &root = text_structure.SyntaxTree();
const auto &non_blocking_assignments =
FindAllNonBlockingAssignments(*ABSL_DIE_IF_NULL(root));
std::vector<TreeSearchMatch> right_hand_sides;
right_hand_sides.reserve(non_blocking_assignments.size());
for (const auto &assignment : non_blocking_assignments) {
const auto *rhs = GetNonBlockingAssignmentRhs(
verible::SymbolCastToNode(*assignment.match));
right_hand_sides.emplace_back(
TreeSearchMatch{rhs, {/* ignored context */}});
}
return right_hand_sides;
});
}
}
TEST(GetNonBlockingAssignmentLhsTest, Various) {
constexpr int kTag = 1; // value doesn't matter
const SyntaxTreeSearchTestCase kTestCases[] = {
{"module m;\n"
"always @(*) x = 1;\n"
"endmodule"},
{"module m;\n"
"always_ff @(posedge clk) ",
{kTag, "x"},
" <= 1;\n",
"endmodule"},
{"module m;\n"
"always_ff @(posedge clk) begin\n",
{kTag, "a"},
" <= b;\n",
{kTag, "c"},
" <= d();\n",
{kTag, "e"},
" <= `F;\n"
"end endmodule"},
{"module m;\n"
"always_latch ",
{kTag, "x"},
" <= 1;\n"
"endmodule"},
{"module m;\n"
"always@(*) ",
{kTag, "x[1]"},
" <= y[2];\n"
"endmodule"},
};
for (const auto &test : kTestCases) {
TestVerilogSyntaxRangeMatches(
__FUNCTION__, test, [](const TextStructureView &text_structure) {
const auto &root = text_structure.SyntaxTree();
const auto &non_blocking_assignments =
FindAllNonBlockingAssignments(*ABSL_DIE_IF_NULL(root));
std::vector<TreeSearchMatch> left_hand_sides;
left_hand_sides.reserve(non_blocking_assignments.size());
for (const auto &assignment : non_blocking_assignments) {
const auto *lhs = GetNonBlockingAssignmentLhs(
verible::SymbolCastToNode(*assignment.match));
left_hand_sides.emplace_back(
TreeSearchMatch{lhs, {/* ignored context */}});
}
return left_hand_sides;
});
}
}
TEST(GetIfClauseHeaderTest, Various) {
constexpr int kTag = 1; // value doesn't matter
const SyntaxTreeSearchTestCase kTestCases[] = {
{"module m;\n"
"always @(*)\n",
{kTag, "if (y)"},
" x = 1;\n"
"endmodule"},
{"module m;\n"
"always @(*) x = 1;\n"
"endmodule"},
};
for (const auto &test : kTestCases) {
TestVerilogSyntaxRangeMatches(
__FUNCTION__, test, [](const TextStructureView &text_structure) {
const auto &root = text_structure.SyntaxTree();
const auto &if_clauses =
verible::SearchSyntaxTree(*root, NodekIfClause());
std::vector<TreeSearchMatch> if_headers;
if_headers.reserve(if_clauses.size());
for (const auto &if_clause : if_clauses) {
const auto *header =
GetIfClauseHeader(verible::SymbolCastToNode(*if_clause.match));
if_headers.emplace_back(
TreeSearchMatch{header, {/* ignored context */}});
}
return if_headers;
});
}
}
TEST(GetIfClauseExpressionTest, Various) {
constexpr int kTag = 1; // value doesn't matter
const SyntaxTreeSearchTestCase kTestCases[] = {
{"module m;\n"
"always @(*) if(",
{kTag, "y"},
")\n "
"x = 1;\n"
"endmodule"},
{"module m;\n"
"always @(*) if(",
{kTag, "func()"},
") x = 1;\n"
"endmodule"},
{"module m;\n"
"always @(*) if(",
{kTag, "y"},
") begin\n",
"if(",
{kTag, "z"},
") x = 1;\n"
"end endmodule"},
{"module m;\n"
"always @(*) x = 1;\n"
"endmodule"},
};
for (const auto &test : kTestCases) {
TestVerilogSyntaxRangeMatches(
__FUNCTION__, test, [](const TextStructureView &text_structure) {
const auto &root = text_structure.SyntaxTree();
const auto &if_headers =
verible::SearchSyntaxTree(*root, NodekIfHeader());
std::vector<TreeSearchMatch> if_expressions;
if_expressions.reserve(if_headers.size());
for (const auto &if_header : if_headers) {
const auto *expression = GetIfHeaderExpression(
verible::SymbolCastToNode(*if_header.match));
if_expressions.emplace_back(
TreeSearchMatch{expression, {/* ignored context */}});
}
return if_expressions;
});
}
}
} // namespace
} // namespace verilog