blob: f361e94baa0461948a70f916bb01040889531659 [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/expression.h"
#include <memory>
#include <utility>
#include "common/analysis/syntax_tree_search.h"
#include "common/analysis/syntax_tree_search_test_utils.h"
#include "common/text/symbol.h"
#include "common/text/tree_utils.h"
#include "common/util/logging.h"
#include "gtest/gtest.h"
#include "verilog/CST/match_test_utils.h"
#include "verilog/CST/verilog_nonterminals.h"
#include "verilog/CST/verilog_tree_print.h"
#include "verilog/analysis/verilog_analyzer.h"
#include "verilog/analysis/verilog_excerpt_parse.h"
namespace verilog {
namespace {
static constexpr VerilogPreprocess::Config kDefaultPreprocess;
using verible::SyntaxTreeSearchTestCase;
using verible::TextStructureView;
using verible::TreeSearchMatch;
TEST(IsZeroTest, NonZero) {
const char* kTestCases[] = {
"a",
"a0",
"1",
"-1",
// no constant expression evaluation yet
"1-1",
"a-a",
"0+0",
"(0)",
};
for (auto code : kTestCases) {
const auto analyzer_ptr =
AnalyzeVerilogExpression(code, "<file>", kDefaultPreprocess);
const auto& node = ABSL_DIE_IF_NULL(analyzer_ptr)->SyntaxTree();
const auto tag = node->Tag();
EXPECT_EQ(tag.kind, verible::SymbolKind::kNode);
EXPECT_EQ(NodeEnum(tag.tag), NodeEnum::kExpression);
EXPECT_FALSE(IsZero(*node));
}
}
TEST(IsZeroTest, Zero) {
const char* kTestCases[] = {
"0",
"00",
"00000",
"'0",
};
for (auto code : kTestCases) {
const auto analyzer_ptr =
AnalyzeVerilogExpression(code, "<file>", kDefaultPreprocess);
const auto& node = ABSL_DIE_IF_NULL(analyzer_ptr)->SyntaxTree();
const auto tag = node->Tag();
EXPECT_EQ(tag.kind, verible::SymbolKind::kNode);
EXPECT_EQ(NodeEnum(tag.tag), NodeEnum::kExpression);
EXPECT_TRUE(IsZero(*node));
}
}
TEST(ConstantIntegerValueTest, NotInteger) {
const char* kTestCases[] = {
"a",
"1+1",
"(2)",
};
for (auto code : kTestCases) {
const auto analyzer_ptr =
AnalyzeVerilogExpression(code, "<file>", kDefaultPreprocess);
const auto& node = ABSL_DIE_IF_NULL(analyzer_ptr)->SyntaxTree();
const auto tag = node->Tag();
EXPECT_EQ(tag.kind, verible::SymbolKind::kNode);
EXPECT_EQ(NodeEnum(tag.tag), NodeEnum::kExpression);
int value;
EXPECT_FALSE(ConstantIntegerValue(*node, &value));
}
}
TEST(ConstantIntegerValueTest, IsInteger) {
const std::pair<const char*, int> kTestCases[] = {
{"0", 0},
{"1", 1},
{"666", 666},
};
for (auto test : kTestCases) {
const auto analyzer_ptr =
AnalyzeVerilogExpression(test.first, "<file>", kDefaultPreprocess);
const auto& node = ABSL_DIE_IF_NULL(analyzer_ptr)->SyntaxTree();
const auto tag = node->Tag();
EXPECT_EQ(tag.kind, verible::SymbolKind::kNode);
EXPECT_EQ(NodeEnum(tag.tag), NodeEnum::kExpression);
int value;
EXPECT_TRUE(ConstantIntegerValue(*node, &value));
EXPECT_EQ(value, test.second);
}
}
TEST(AssociativeBinaryExpressionsTest, FlatTree) {
constexpr int kTag = 1; // value doesn't matter
const SyntaxTreeSearchTestCase kTestCases[] = {
{""},
{"module m; endmodule\n"},
{"module m;\ninitial begin end\nendmodule"},
{"class c; endclass\n"},
{"function f; endfunction\n"},
{"function f;\n", "a = ", {kTag, "b + c"}, "; endfunction\n"},
{"function f;\n", "a = ", {kTag, "b + c + d"}, "; endfunction\n"},
{"parameter product P = ", {kTag, "q*r"}, ";\n"},
{"parameter product P = ", {kTag, "q*r*s"}, ";\n"},
{"function f;\n", "a = ", {kTag, "b | c | d | e"}, "; endfunction\n"},
{"function f;\n", "a = ", {kTag, "b & c & d & e"}, "; endfunction\n"},
{"function f;\n", "a = ", {kTag, "b || c || d || e"}, "; endfunction\n"},
{"function f;\n", "a = ", {kTag, "b && c && d && e"}, "; endfunction\n"},
{"function f;\n", "a = ", {kTag, "b ^ c ^ d ^ e"}, "; endfunction\n"},
{"function f;\n", "a = ", {kTag, "b ~^ c ~^ d ~^ e"}, "; endfunction\n"},
};
for (const auto& test : kTestCases) {
TestVerilogSyntaxRangeMatches(
__FUNCTION__, test, [](const TextStructureView& text_structure) {
const auto& root = text_structure.SyntaxTree();
return FindAllBinaryOperations(*ABSL_DIE_IF_NULL(root));
});
}
}
TEST(AssociativeBinaryExpressionsTest, ThreeFlatOperands) {
constexpr int kTag = 1; // value doesn't matter
const SyntaxTreeSearchTestCase kTestCases[] = {
{"function f;\n", "a = ", {kTag, "b | c | d"}, "; endfunction\n"},
{"function f;\n",
"a = ",
{kTag, "b(x) | c(y) | d(z)"},
"; endfunction\n"},
{"function f;\n",
"a = ",
{kTag, "b(x) ^ c(y) ^ d(z)"},
"; endfunction\n"},
{"function f;\n",
"a = ",
{kTag, "b(x) + c(y) + d(z)"},
"; endfunction\n"},
{"function f;\n", "a = g(", {kTag, "b | c | d"}, "); endfunction\n"},
{"function f;\n",
"a = g(",
{kTag, "b|c|d"},
", ",
{kTag, "x*y*z"},
"); endfunction\n"},
};
for (const auto& test : kTestCases) {
TestVerilogSyntaxRangeMatches(
__FUNCTION__, test, [](const TextStructureView& text_structure) {
const auto& root = text_structure.SyntaxTree();
const auto matches = FindAllBinaryOperations(*ABSL_DIE_IF_NULL(root));
for (const auto& match : matches) {
// "A op B op C" is 5 sibling tokens, due to flattening
EXPECT_EQ(verible::SymbolCastToNode(*ABSL_DIE_IF_NULL(match.match))
.children()
.size(),
5);
}
return matches;
});
}
}
TEST(GetConditionExpressionPredicateTest, Various) {
constexpr int kTag = 1; // value doesn't matter
const SyntaxTreeSearchTestCase kTestCases[] = {
{""},
{"module m; endmodule\n"},
{"module m;\ninitial begin end\nendmodule"},
{"class c; endclass\n"},
{"function f; endfunction\n"},
{"function f;\n", "a = ", {kTag, "b"}, " ? c : d", "; endfunction\n"},
{"module m;\n",
" assign foo = ",
{kTag, "condition_a"},
" ? b : c",
";\n",
"endmodule\n"},
{"module m;\n",
"parameter foo = ",
{kTag, "condition_a"},
"? a : b",
";\nendmodule"},
{"module m;\n",
"always @(posedge clk) begin\n left <= ",
{kTag, "condition_a"},
"? a : b",
"; \nend",
"\nendmodule"},
{"function f;\n g = h(",
{kTag, "condition_a"},
"? a : b",
"); \nendfunction"},
{"module m;\n",
"always @(posedge clk)\n",
"case (",
{kTag, "condition_a"},
"? a : b",
")\n",
"default :;\n",
"endcase\n",
"\nendmodule"},
};
for (const auto& test : kTestCases) {
TestVerilogSyntaxRangeMatches(
__FUNCTION__, test, [](const TextStructureView& text_structure) {
const auto& root = text_structure.SyntaxTree();
const auto exprs =
FindAllConditionExpressions(*ABSL_DIE_IF_NULL(root));
std::vector<TreeSearchMatch> predicates;
for (const auto& expr : exprs) {
const auto* predicate =
GetConditionExpressionPredicate(*expr.match);
if (predicate != nullptr) {
predicates.push_back(
TreeSearchMatch{predicate, {/* ignored context */}});
} else {
EXPECT_NE(predicate, nullptr)
<< "predicate:\n"
<< verible::RawTreePrinter(*expr.match);
}
}
return predicates;
});
}
}
TEST(GetConditionExpressionTrueCaseTest, Various) {
constexpr int kTag = 1; // value doesn't matter
const SyntaxTreeSearchTestCase kTestCases[] = {
{""},
{"module m; endmodule\n"},
{"module m;\ninitial begin end\nendmodule"},
{"class c; endclass\n"},
{"function f; endfunction\n"},
{"function f;\n",
"a = ",
"b ? ",
{kTag, "c"},
" : d",
"; endfunction\n"},
{"function f;\n",
"a = ",
"b ? ",
{kTag, "(c + d)"},
" : d",
"; endfunction\n"},
{"function f;\n",
"a = ",
"b ? ",
{kTag, "(c << d)"},
" : d",
"; endfunction\n"},
{"module m;\n",
" assign foo = ",
"condition_a ? ",
{kTag, "b"},
" : c",
";\n",
"endmodule\n"},
{"module m;\n",
"parameter foo = ",
"condition_a ? ",
{kTag, "a"},
" : b",
";\nendmodule"},
{"module m;\n",
"always @(posedge clk) begin\n left <= ",
"condition_a ? ",
{kTag, "a"},
" : b",
"; \nend",
"\nendmodule"},
{"function f;\n g = h(",
"condition_a ? ",
{kTag, "a"},
" : b",
"); \nendfunction"},
{"module m;\n",
"always @(posedge clk)\n",
"case (",
"condition_a ? ",
{kTag, "a"},
" : b",
")\n",
"default :;\n",
"endcase\n",
"\nendmodule"},
};
for (const auto& test : kTestCases) {
TestVerilogSyntaxRangeMatches(
__FUNCTION__, test, [](const TextStructureView& text_structure) {
const auto& root = text_structure.SyntaxTree();
const auto exprs =
FindAllConditionExpressions(*ABSL_DIE_IF_NULL(root));
std::vector<TreeSearchMatch> predicates;
for (const auto& expr : exprs) {
const auto* predicate = GetConditionExpressionTrueCase(*expr.match);
if (predicate != nullptr) {
predicates.push_back(
TreeSearchMatch{predicate, {/* ignored context */}});
} else {
EXPECT_NE(predicate, nullptr)
<< "predicate:\n"
<< verible::RawTreePrinter(*expr.match);
}
}
return predicates;
});
}
}
TEST(GetConditionExpressionFalseCaseTest, Various) {
constexpr int kTag = 1; // value doesn't matter
const SyntaxTreeSearchTestCase kTestCases[] = {
{""},
{"module m; endmodule\n"},
{"module m;\ninitial begin end\nendmodule"},
{"class c; endclass\n"},
{"function f; endfunction\n"},
{"function f;\n",
"a = ",
"b ? ",
"c : ",
{kTag, "d"},
"; endfunction\n"},
{"function f;\n",
"a = ",
"b ? ",
"c : ",
{kTag, "(c + d)"},
"; endfunction\n"},
{"function f;\n",
"a = ",
"b ? ",
"c : ",
{kTag, "(c << d)"},
"; endfunction\n"},
{"module m;\n",
" assign foo = ",
"condition_a ? ",
"b : ",
{kTag, "c"},
";\n",
"endmodule\n"},
{"module m;\n",
"parameter foo = ",
"condition_a ? ",
"a : ",
{kTag, "b"},
";\nendmodule"},
{"module m;\n",
"always @(posedge clk) begin\n left <= ",
"condition_a ? ",
"a : ",
{kTag, "b"},
"; \nend",
"\nendmodule"},
{"function f;\n g = h(",
"condition_a ? ",
"a : ",
{kTag, "b"},
"); \nendfunction"},
{"module m;\n",
"always @(posedge clk)\n",
"case (",
"condition_a ? ",
"a : ",
{kTag, "b"},
")\n",
"default :;\n",
"endcase\n",
"\nendmodule"},
};
for (const auto& test : kTestCases) {
TestVerilogSyntaxRangeMatches(
__FUNCTION__, test, [](const TextStructureView& text_structure) {
const auto& root = text_structure.SyntaxTree();
const auto exprs =
FindAllConditionExpressions(*ABSL_DIE_IF_NULL(root));
std::vector<TreeSearchMatch> predicates;
for (const auto& expr : exprs) {
const auto* predicate =
GetConditionExpressionFalseCase(*expr.match);
if (predicate != nullptr) {
predicates.push_back(
TreeSearchMatch{predicate, {/* ignored context */}});
} else {
EXPECT_NE(predicate, nullptr)
<< "predicate:\n"
<< verible::RawTreePrinter(*expr.match);
}
}
return predicates;
});
}
}
TEST(FindAllConditionExpressionsTest, Various) {
constexpr int kTag = 1; // value doesn't matter
const SyntaxTreeSearchTestCase kTestCases[] = {
{""},
{"module m; endmodule\n"},
{"module m;\ninitial begin end\nendmodule"},
{"class c; endclass\n"},
{"function f; endfunction\n"},
{"function f;\n", "a = ", {kTag, "b ? c : d"}, "; endfunction\n"},
{"module m;\n",
" assign foo = ",
{kTag, "condition_a ? b : c"},
";\n",
"endmodule\n"},
{"module m;\n",
"parameter foo = ",
{kTag, "condition_a? a : b"},
";\nendmodule"},
{"module m;\n",
"always @(posedge clk) begin\n left <= ",
{kTag, "condition_a? a : b"},
"; \nend",
"\nendmodule"},
{"function f;\n g = h(",
{kTag, "condition_a? a : b"},
"); \nendfunction"},
{"module m;\n",
"always @(posedge clk)\n",
"case (",
{kTag, "condition_a? a : b"},
")\n",
"default :;\n",
"endcase\n",
"\nendmodule"},
};
for (const auto& test : kTestCases) {
TestVerilogSyntaxRangeMatches(
__FUNCTION__, test, [](const TextStructureView& text_structure) {
const auto& root = text_structure.SyntaxTree();
return FindAllConditionExpressions(*ABSL_DIE_IF_NULL(root));
});
}
}
TEST(FindAllReferenceExpressionsTest, Various) {
constexpr int kTag = 1; // value doesn't matter
const SyntaxTreeSearchTestCase kTestCases[] = {
{""},
{"module m; endmodule\n"},
{"class c; endclass\n"},
{"function f; endfunction\n"},
{"function f;\n", {kTag, "a"}, " = 1;\n", "endfunction\n"},
{"function f;\n",
{kTag, "a"},
" = ",
{kTag, "z"},
";\n",
"endfunction\n"},
{"function f;\n",
{kTag, "a.b"},
" = ",
{kTag, "z.y.x"},
";\n",
"endfunction\n"},
{"task t;\n",
{kTag, "a[0][0]"},
" = ",
{kTag, "z[1][1]"},
";\n",
"endtask\n"},
{"module m;\n"
" foo bar(",
{kTag, "v.w"},
");\n"
"endmodule\n"},
{"module m;\n"
" foo bar(.clk(",
{kTag, "v.w"},
"));\n"
"endmodule\n"},
{"module m;\n" // continuous assignment
" assign ",
{kTag, "v"},
" = 1'b1;\n"
"endmodule\n"},
{"module m;\n" // continuous assignment
" assign ",
{kTag, "v"},
" = ",
{kTag, "w"},
";\n"
"endmodule\n"},
{"module m;\n" // continuous assignment
" assign ",
{kTag, "v[1]"},
" = ",
{kTag, "w.d"},
";\n"
"endmodule\n"},
{"module m;\n" // blocking assignment
" initial ",
{kTag, "v"},
" = 1'b1;\n"
"endmodule\n"},
{"module m;\n" // blocking assignment
" initial ",
{kTag, "v"},
" = ",
{kTag, "zz"},
";\n"
"endmodule\n"},
{"module m;\n" // blocking assignment
" initial begin\n",
{kTag, "v[2]"},
" = ",
{kTag, "zz.f"},
";\n"
" end\n"
"endmodule\n"},
{"module m;\n" // non blocking assignment
" always @(posedge ",
{kTag, "clk"},
")\n",
{kTag, "v.g"},
" <= ",
{kTag, "zz[3][4]"},
";\n"
"endmodule\n"},
{"function f;\n",
" for (int i = 0; ",
{kTag, "i"},
"<",
{kTag, "N"},
"; ++",
{kTag, "i"},
")\n",
// Note: "int i = 0" is a declaration, where "i" also serves as a
// reference in an assignment, but is not tagged as such.
{kTag, "a"},
" = ",
{kTag, "z"},
" + ",
{kTag, "i"},
";\n",
"endfunction\n"},
// reference could contain other references like "a[a]", but the testing
// framework doesn't support nested expected ranges... yet.
};
for (const auto& test : kTestCases) {
TestVerilogSyntaxRangeMatches(
__FUNCTION__, test, [](const TextStructureView& text_structure) {
const auto& root = text_structure.SyntaxTree();
return FindAllReferenceFullExpressions(*ABSL_DIE_IF_NULL(root));
});
}
}
TEST(ReferenceIsSimpleTest, Simple) {
const char* kTestCases[] = {
"a",
"bbb",
"z1",
"_y",
};
for (auto code : kTestCases) {
const auto analyzer_ptr =
AnalyzeVerilogExpression(code, "<file>", kDefaultPreprocess);
const auto& node = ABSL_DIE_IF_NULL(analyzer_ptr)->SyntaxTree();
{
const auto status = analyzer_ptr->LexStatus();
ASSERT_TRUE(status.ok()) << status.message();
}
{
const auto status = analyzer_ptr->ParseStatus();
ASSERT_TRUE(status.ok()) << status.message();
}
const std::vector<TreeSearchMatch> refs(
FindAllReferenceFullExpressions(*ABSL_DIE_IF_NULL(node)));
for (const auto& ref : refs) {
const verible::TokenInfo* token = ReferenceIsSimpleIdentifier(*ref.match);
ASSERT_NE(token, nullptr) << "reference: " << code;
EXPECT_EQ(token->text(), code);
}
}
}
TEST(ReferenceIsSimpleTest, NotSimple) {
const char* kTestCases[] = {
"a[0]", "a[4][6]", "bbb[3:0]", "x.y", "x.y.z", "x[0].y[1].z[2]",
"x()", "x.y()", "x()[0]", "x(1)", "f(0,1)", "j[9].k(3, 2, 1)",
};
for (auto code : kTestCases) {
VLOG(1) << __FUNCTION__ << " test: " << code;
const auto analyzer_ptr =
AnalyzeVerilogExpression(code, "<file>", kDefaultPreprocess);
const auto& node = ABSL_DIE_IF_NULL(analyzer_ptr)->SyntaxTree();
{
const auto status = analyzer_ptr->LexStatus();
ASSERT_TRUE(status.ok()) << status.message();
}
{
const auto status = analyzer_ptr->ParseStatus();
ASSERT_TRUE(status.ok()) << status.message();
}
const std::vector<TreeSearchMatch> refs(
FindAllReferenceFullExpressions(*ABSL_DIE_IF_NULL(node)));
for (const auto& ref : refs) {
VLOG(1) << "match: " << verible::StringSpanOfSymbol(*ref.match);
EXPECT_FALSE(ReferenceIsSimpleIdentifier(*ref.match))
<< "reference: " << code;
}
}
}
} // namespace
} // namespace verilog