blob: bf72d157b0f4434fcd09f3e4be44626ea7765323 [file] [log] [blame] [edit]
// 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/statement.h"
#include <memory>
#include <vector>
#include "gmock/gmock.h"
#include "gtest/gtest.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_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/util/casts.h"
#include "common/util/logging.h"
#include "common/util/range.h"
#include "verilog/CST/verilog_matchers.h"
#include "verilog/CST/verilog_nonterminals.h"
#include "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::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) {
const absl::string_view code(test.token_data.code);
VerilogAnalyzer analyzer(code, "test-file");
const auto code_copy = analyzer.Data().Contents();
ASSERT_OK(analyzer.Analyze()) << "failed on:\n" << code;
const auto& root = analyzer.Data().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 */}});
}
// Compare sets of text ranges.
std::ostringstream diffs;
EXPECT_TRUE(test.token_data.ExactMatchFindings(bodies, code_copy, &diffs))
<< "failed on:\n"
<< code << "\ndiffs:\n"
<< diffs.str();
}
}
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) {
const absl::string_view code(test.token_data.code);
VerilogAnalyzer analyzer(code, "test-file");
const auto code_copy = analyzer.Data().Contents();
ASSERT_OK(analyzer.Analyze()) << "failed on:\n" << code;
const auto& root = analyzer.Data().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 */}});
}
// Compare sets of text ranges.
std::ostringstream diffs;
EXPECT_TRUE(test.token_data.ExactMatchFindings(bodies, code_copy, &diffs))
<< "failed on:\n"
<< code << "\ndiffs:\n"
<< diffs.str();
}
}
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 absl::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) {
const absl::string_view code(test.token_data.code);
VerilogAnalyzer analyzer(code, "test-file");
const auto code_copy = analyzer.Data().Contents();
ASSERT_OK(analyzer.Analyze()) << "failed on:\n" << code;
const auto& root = analyzer.Data().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 */}});
}
// Compare sets of text ranges.
std::ostringstream diffs;
EXPECT_TRUE(test.token_data.ExactMatchFindings(bodies, code_copy, &diffs))
<< "failed on:\n"
<< code << "\ndiffs:\n"
<< diffs.str();
}
}
} // namespace
} // namespace verilog