blob: 7b880a8e59caba3985ce2f2bbd67fa899f00699c [file] [log] [blame]
// Copyright 2017-2022 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/analysis/flow_tree.h"
#include <vector>
#include "absl/status/status.h"
#include "absl/strings/string_view.h"
#include "common/text/token_stream_view.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "verilog/parser/verilog_lexer.h"
namespace verilog {
namespace {
using testing::StartsWith;
// Lexes a SystemVerilog source code, and returns a TokenSequence.
verible::TokenSequence LexToSequence(absl::string_view source_contents) {
verible::TokenSequence lexed_sequence;
VerilogLexer lexer(source_contents);
for (lexer.DoNextToken(); !lexer.GetLastToken().isEOF();
lexer.DoNextToken()) {
if (verilog::VerilogLexer::KeepSyntaxTreeTokens(lexer.GetLastToken())) {
lexed_sequence.push_back(lexer.GetLastToken());
}
}
return lexed_sequence;
}
TEST(FlowTree, MultipleConditionalsSameMacro) {
const absl::string_view test_case =
R"(
`ifdef A
A_TRUE_1
`else
A_FALSE_1
`endif
`ifdef A
A_TRUE_2
`else
A_FALSE_2
`endif
`ifndef A
A_FALSE_3
`else
A_TRUE_3
`endif)";
FlowTree tree_test(LexToSequence(test_case));
std::vector<FlowTree::Variant> variants;
auto status =
tree_test.GenerateVariants([&variants](const FlowTree::Variant& variant) {
variants.push_back(variant);
return true;
});
EXPECT_TRUE(status.ok());
EXPECT_EQ(variants.size(), 2);
const auto& used_macros = tree_test.GetUsedMacros();
EXPECT_EQ(used_macros.size(), 1);
EXPECT_THAT(used_macros[0]->text(), "A");
// First variant: A is defined.
EXPECT_TRUE(variants[0].macros_mask.test(0));
EXPECT_TRUE(variants[0].visited.test(0));
EXPECT_THAT(variants[0].sequence[0].text(), "A_TRUE_1");
EXPECT_THAT(variants[0].sequence[1].text(), "A_TRUE_2");
EXPECT_THAT(variants[0].sequence[2].text(), "A_TRUE_3");
// Second variant: A is undefined.
EXPECT_FALSE(variants[1].macros_mask.test(0));
EXPECT_TRUE(variants[1].visited.test(0));
EXPECT_THAT(variants[1].sequence[0].text(), "A_FALSE_1");
EXPECT_THAT(variants[1].sequence[1].text(), "A_FALSE_2");
EXPECT_THAT(variants[1].sequence[2].text(), "A_FALSE_3");
}
TEST(FlowTree, UnmatchedElses) {
const absl::string_view test_cases[] = {
R"(
`elsif A
A_TRUE
`endif
)",
R"(
`ifdef A
A_TRUE
`endif
`elsif B
B_TRUE
`endif
)",
R"(
`else
A_TRUE
`endif
)",
R"(
`ifdef A
`elsif B
B_TRUE
`endif
`endif
)",
R"(
`endif
)"};
for (auto test : test_cases) {
FlowTree tree_test(LexToSequence(test));
auto status = tree_test.GenerateVariants(
[](const FlowTree::Variant& variant) { return true; });
EXPECT_FALSE(status.ok());
EXPECT_THAT(status.message(), StartsWith("ERROR: Unmatched"));
}
}
TEST(FlowTree, UnvalidConditionals) {
const absl::string_view test_cases[] = {
R"(
`ifdef A
A_TRUE
`elsif
A_FALSE
)",
R"(
`ifdef
A_TRUE
`else
A_FALSE
)",
R"(
`ifndef
A_TRUE
`else
A_FALSE
`endif
)"};
for (auto test : test_cases) {
FlowTree tree_test(LexToSequence(test));
auto status = tree_test.GenerateVariants(
[](const FlowTree::Variant& variant) { return true; });
EXPECT_FALSE(status.ok());
}
}
TEST(FlowTree, UncompletedConditionals) {
const absl::string_view test_cases[] = {
R"(
`ifdef A
A_TRUE
`else
A_FALSE
)",
R"(
`ifdef A
A_TRUE
`ifndef B
B_FALSE
`else
B_TRUE
`endif
)"};
for (auto test : test_cases) {
FlowTree tree_test(LexToSequence(test));
auto status = tree_test.GenerateVariants(
[](const FlowTree::Variant& variant) { return true; });
EXPECT_FALSE(status.ok());
EXPECT_THAT(status.message(), StartsWith("ERROR: Uncompleted"));
}
}
TEST(FlowTree, NestedConditionals) {
const absl::string_view test_cases[] = {
R"(
`ifdef A
`ifdef B
A_B
`else
A_nB
`endif
`else
nA_B_or_nA_nB
`endif)"};
for (auto test : test_cases) {
FlowTree tree_test(LexToSequence(test));
std::vector<FlowTree::Variant> variants;
auto status = tree_test.GenerateVariants(
[&variants](const FlowTree::Variant& variant) {
variants.push_back(variant);
return true;
});
EXPECT_TRUE(status.ok());
EXPECT_EQ(variants.size(), 3);
for (const auto& variant : variants) {
EXPECT_EQ(variant.sequence.size(), 1);
if (variant.macros_mask.test(0) == 0) {
// Check that if A is undefined, then B is not visited.
EXPECT_FALSE(variant.visited.test(1));
} else {
// Check that if A is defined, then B is visited.
EXPECT_TRUE(variant.visited.test(1));
}
}
const auto& used_macros = tree_test.GetUsedMacros();
EXPECT_EQ(used_macros.size(), 2);
EXPECT_THAT(used_macros[0]->text(), "A");
EXPECT_THAT(used_macros[1]->text(), "B");
}
}
TEST(FlowTree, MultipleElseIfs) {
const absl::string_view test_case =
R"(
`ifdef A
A_TRUE
`elsif B
B_TRUE
`elsif EMPTY
`elsif C
C_TRUE
`endif)";
FlowTree tree_test(LexToSequence(test_case));
std::vector<FlowTree::Variant> variants;
auto status =
tree_test.GenerateVariants([&variants](const FlowTree::Variant& variant) {
variants.push_back(variant);
return true;
});
EXPECT_TRUE(status.ok());
EXPECT_EQ(variants.size(), 5);
const auto& used_macros = tree_test.GetUsedMacros();
EXPECT_EQ(used_macros.size(), 4);
EXPECT_THAT(used_macros[0]->text(), "A");
EXPECT_THAT(used_macros[1]->text(), "B");
EXPECT_THAT(used_macros[2]->text(), "EMPTY");
EXPECT_THAT(used_macros[3]->text(), "C");
// A is defined.
EXPECT_TRUE(variants[0].macros_mask.test(0));
EXPECT_THAT(variants[0].sequence[0].text(), "A_TRUE");
// B is defined.
EXPECT_TRUE(variants[1].macros_mask.test(1));
EXPECT_THAT(variants[1].sequence[0].text(), "B_TRUE");
// EMPTY is defined.
EXPECT_TRUE(variants[2].macros_mask.test(2));
EXPECT_TRUE(variants[2].sequence.empty());
// C is defined.
EXPECT_TRUE(variants[3].macros_mask.test(3));
EXPECT_THAT(variants[3].sequence[0].text(), "C_TRUE");
}
TEST(FlowTree, SwappedNegatedIfs) {
const absl::string_view test_case =
R"(
`ifndef A
A_FALSE
`elsif B
B_TRUE
`endif
`ifndef B
B_FALSE
`elsif A
A_TRUE
`endif)";
FlowTree tree_test(LexToSequence(test_case));
std::vector<FlowTree::Variant> variants;
auto status =
tree_test.GenerateVariants([&variants](const FlowTree::Variant& variant) {
variants.push_back(variant);
return true;
});
EXPECT_TRUE(status.ok());
EXPECT_EQ(variants.size(), 4);
const auto& used_macros = tree_test.GetUsedMacros();
EXPECT_EQ(used_macros.size(), 2);
EXPECT_THAT(used_macros[0]->text(), "A");
EXPECT_THAT(used_macros[1]->text(), "B");
EXPECT_THAT(variants[0].sequence[0].text(), "A_FALSE");
EXPECT_THAT(variants[0].sequence[1].text(), "B_FALSE");
EXPECT_THAT(variants[1].sequence[0].text(), "A_FALSE");
EXPECT_THAT(variants[2].sequence[0].text(), "B_TRUE");
EXPECT_THAT(variants[2].sequence[1].text(), "A_TRUE");
EXPECT_THAT(variants[3].sequence[0].text(), "B_FALSE");
}
TEST(FlowTree, CompleteConditional) {
const absl::string_view test_case =
R"(
`ifdef A
A_TRUE
`elsif B
B_TRUE
`elsif C
C_TRUE
`else
ALL_FALSE
`endif)";
FlowTree tree_test(LexToSequence(test_case));
std::vector<FlowTree::Variant> variants;
auto status =
tree_test.GenerateVariants([&variants](const FlowTree::Variant& variant) {
variants.push_back(variant);
return true;
});
EXPECT_TRUE(status.ok());
EXPECT_EQ(variants.size(), 4);
const auto& used_macros = tree_test.GetUsedMacros();
EXPECT_EQ(used_macros.size(), 3);
EXPECT_THAT(used_macros[0]->text(), "A");
EXPECT_THAT(used_macros[1]->text(), "B");
EXPECT_THAT(used_macros[2]->text(), "C");
EXPECT_TRUE(variants[0].macros_mask.test(0));
EXPECT_THAT(variants[0].sequence[0].text(), "A_TRUE");
EXPECT_TRUE(variants[1].macros_mask.test(1));
EXPECT_THAT(variants[1].sequence[0].text(), "B_TRUE");
EXPECT_TRUE(variants[2].macros_mask.test(2));
EXPECT_THAT(variants[2].sequence[0].text(), "C_TRUE");
EXPECT_THAT(variants[3].sequence[0].text(), "ALL_FALSE");
}
} // namespace
} // namespace verilog