| // 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/preprocessor/verilog_preprocess.h" |
| |
| #include <map> |
| #include <vector> |
| |
| #include "gmock/gmock.h" |
| #include "gtest/gtest.h" |
| #include "absl/status/status.h" |
| #include "common/text/macro_definition.h" |
| #include "common/text/token_info.h" |
| #include "common/util/container_util.h" |
| #include "verilog/analysis/verilog_analyzer.h" |
| |
| namespace verilog { |
| namespace { |
| |
| using testing::ElementsAre; |
| using testing::Pair; |
| using verible::container::FindOrNull; |
| |
| class PreprocessorTester { |
| public: |
| explicit PreprocessorTester(const char* text) |
| : analyzer_(text, "<<inline-file>>"), status_() { |
| status_ = analyzer_.Analyze(); |
| } |
| |
| const VerilogPreprocessData& PreprocessorData() const { |
| return analyzer_.PreprocessorData(); |
| } |
| |
| const absl::Status& Status() const { return status_; } |
| |
| const VerilogAnalyzer& Analyzer() const { return analyzer_; } |
| |
| private: |
| VerilogAnalyzer analyzer_; |
| absl::Status status_; |
| }; |
| |
| struct FailTest { |
| const char* input; |
| int offset; |
| }; |
| |
| TEST(VerilogPreprocessTest, InvalidPreprocessorInputs) { |
| const FailTest test_cases[] = { |
| {"`define\n", 8}, // unterminated macro definition |
| {"\n\n`define\n", 10}, // unterminated macro definition |
| {"`define 789\n", 8}, // expect identifier for macro name |
| {"`define 789 non-sense\n", 8}, // expect identifier for macro name |
| {"`define 789 \\\nnon-sense\n", 8}, // expect identifier for macro name |
| {"`define FOO(\n", 13}, // unterminated parameter list |
| {"`define FOO(234\n", 12}, // invalid parameter name |
| {"`define FOO(234)\n", 12}, // invalid parameter name |
| {"`define FOO(aaa\n", 16}, // unterminated parameter list |
| {"`define FOO(aaa;\n", 15}, // bad parameter separator |
| {"`define FOO(aaa bbb\n", 16}, // bad parameter separator |
| {"`define FOO(aaa bbb)\n", 16}, // bad parameter separator |
| {"`define FOO(aaa+bbb)\n", 15}, // bad parameter separator |
| {"`define FOO(aaa.zzz\n", 15}, // bad parameter separator |
| {"`define FOO(aaa.zzz)\n", 15}, // bad parameter separator |
| {"`define FOO(aaa,\n", 17}, // unterminated parameter list |
| {"`define FOO(aaa,)\n", 16}, // missing parameter name |
| {"`define FOO(,,)\n", 12}, // missing parameter name |
| {"`define FOO(aaa, 345)\n", 17}, // invalid parameter name |
| {"`define FOO(aaa=\n", 17}, // unterminated default parameter |
| {"`define FOO(aaa =\n", 18}, // unterminated default parameter |
| {"`define FOO(aaa = 9\n", 20}, // expecting ',' or ')' |
| {"`define FOO(aaa = 9, bbb =\n", 27}, // unterminated parameter list |
| {"`define FOO(aa = 9, bb = 2\n", 27}, // expecting ',' or ')' |
| }; |
| for (const auto& test_case : test_cases) { |
| PreprocessorTester tester(test_case.input); |
| EXPECT_FALSE(tester.Status().ok()) |
| << "Expected preprocess to fail on invalid input: \"" << test_case.input |
| << "\""; |
| const auto& rejected_tokens = tester.Analyzer().GetRejectedTokens(); |
| ASSERT_FALSE(rejected_tokens.empty()) |
| << "on invalid input: \"" << test_case.input << "\""; |
| const int rejected_token_offset = |
| rejected_tokens[0].token_info.left(tester.Analyzer().Data().Contents()); |
| EXPECT_EQ(rejected_token_offset, test_case.offset) |
| << "on invalid input: \"" << test_case.input << "\""; |
| } |
| } |
| |
| // Verify that VerilogPreprocess works without any directives. |
| TEST(VerilogPreprocessTest, WorksWithoutDefinitions) { |
| const char* test_cases[] = { |
| "", |
| "\n", |
| "module foo;\nendmodule\n", |
| "module foo(input x, output y);\nendmodule\n", |
| }; |
| for (const auto& test_case : test_cases) { |
| PreprocessorTester tester(test_case); |
| EXPECT_TRUE(tester.Status().ok()); |
| const auto& definitions = tester.PreprocessorData().macro_definitions; |
| EXPECT_TRUE(definitions.empty()); |
| EXPECT_TRUE(tester.PreprocessorData().errors.empty()); |
| EXPECT_TRUE(tester.Analyzer().GetRejectedTokens().empty()); |
| } |
| } |
| |
| TEST(VerilogPreprocessTest, OneMacroDefinitionNoParamsNoValue) { |
| const char* test_cases[] = { |
| "`define FOOOO\n", |
| "`define FOOOO\n", |
| "module foo;\nendmodule\n" |
| "`define FOOOO\n", |
| "`define FOOOO\n" |
| "module foo;\nendmodule\n", |
| }; |
| for (const auto& test_case : test_cases) { |
| PreprocessorTester tester(test_case); |
| EXPECT_TRUE(tester.Status().ok()); |
| EXPECT_TRUE(tester.PreprocessorData().errors.empty()); |
| EXPECT_TRUE(tester.Analyzer().GetRejectedTokens().empty()); |
| const auto& definitions = tester.PreprocessorData().macro_definitions; |
| EXPECT_THAT(definitions, ElementsAre(Pair("FOOOO", testing::_))); |
| auto macro = FindOrNull(definitions, "FOOOO"); |
| ASSERT_NE(macro, nullptr); |
| EXPECT_EQ(macro->DefinitionText().text(), ""); |
| EXPECT_FALSE(macro->IsCallable()); |
| EXPECT_TRUE(macro->Parameters().empty()); |
| } |
| } |
| |
| TEST(VerilogPreprocessTest, OneMacroDefinitionNoParamsSimpleValue) { |
| PreprocessorTester tester( |
| "module foo;\nendmodule\n" |
| "`define FOOOO \"bar\"\n"); |
| const auto& definitions = tester.PreprocessorData().macro_definitions; |
| EXPECT_TRUE(tester.Status().ok()) << "Unexpected analyzer failure."; |
| EXPECT_TRUE(tester.PreprocessorData().errors.empty()); |
| EXPECT_TRUE(tester.Analyzer().GetRejectedTokens().empty()); |
| EXPECT_THAT(definitions, ElementsAre(Pair("FOOOO", testing::_))); |
| auto macro = FindOrNull(definitions, "FOOOO"); |
| ASSERT_NE(macro, nullptr); |
| EXPECT_EQ(macro->DefinitionText().text(), "\"bar\""); |
| EXPECT_FALSE(macro->IsCallable()); |
| EXPECT_TRUE(macro->Parameters().empty()); |
| } |
| |
| TEST(VerilogPreprocessTest, OneMacroDefinitionOneParamWithValue) { |
| PreprocessorTester tester( |
| "module foo;\nendmodule\n" |
| "`define FOOOO(x) (x+1)\n"); |
| const auto& definitions = tester.PreprocessorData().macro_definitions; |
| EXPECT_TRUE(tester.Status().ok()) << "Unexpected analyzer failure."; |
| EXPECT_TRUE(tester.PreprocessorData().errors.empty()); |
| EXPECT_TRUE(tester.Analyzer().GetRejectedTokens().empty()); |
| EXPECT_THAT(definitions, ElementsAre(Pair("FOOOO", testing::_))); |
| auto macro = FindOrNull(definitions, "FOOOO"); |
| ASSERT_NE(macro, nullptr); |
| EXPECT_EQ(macro->DefinitionText().text(), "(x+1)"); |
| EXPECT_TRUE(macro->IsCallable()); |
| const auto& params = macro->Parameters(); |
| EXPECT_EQ(params.size(), 1); |
| const auto& param(params[0]); |
| EXPECT_EQ(param.name.text(), "x"); |
| EXPECT_FALSE(param.HasDefaultText()); |
| } |
| |
| TEST(VerilogPreprocessTest, OneMacroDefinitionOneParamDefaultWithValue) { |
| PreprocessorTester tester( |
| "module foo;\nendmodule\n" |
| "`define FOOOO(x=22) (x+3)\n"); |
| const auto& definitions = tester.PreprocessorData().macro_definitions; |
| EXPECT_TRUE(tester.Status().ok()) << "Unexpected analyzer failure."; |
| EXPECT_TRUE(tester.PreprocessorData().errors.empty()); |
| EXPECT_TRUE(tester.Analyzer().GetRejectedTokens().empty()); |
| EXPECT_THAT(definitions, ElementsAre(Pair("FOOOO", testing::_))); |
| auto macro = FindOrNull(definitions, "FOOOO"); |
| ASSERT_NE(macro, nullptr); |
| EXPECT_EQ(macro->DefinitionText().text(), "(x+3)"); |
| EXPECT_TRUE(macro->IsCallable()); |
| const auto& params = macro->Parameters(); |
| EXPECT_EQ(params.size(), 1); |
| const auto& param(params[0]); |
| EXPECT_EQ(param.name.text(), "x"); |
| EXPECT_TRUE(param.HasDefaultText()); |
| EXPECT_EQ(param.default_value.text(), "22"); |
| } |
| |
| TEST(VerilogPreprocessTest, TwoMacroDefinitions) { |
| PreprocessorTester tester( |
| "`define BAAAAR(y, z) (y*z)\n" |
| "`define FOOOO(x=22) (x+3)\n"); |
| const auto& definitions = tester.PreprocessorData().macro_definitions; |
| EXPECT_TRUE(tester.Status().ok()) << "Unexpected analyzer failure."; |
| EXPECT_TRUE(tester.PreprocessorData().errors.empty()); |
| EXPECT_TRUE(tester.Analyzer().GetRejectedTokens().empty()); |
| EXPECT_THAT(definitions, ElementsAre(Pair("BAAAAR", testing::_), |
| Pair("FOOOO", testing::_))); |
| { |
| auto macro = FindOrNull(definitions, "BAAAAR"); |
| ASSERT_NE(macro, nullptr); |
| EXPECT_TRUE(macro->IsCallable()); |
| const auto& params = macro->Parameters(); |
| EXPECT_EQ(params.size(), 2); |
| } |
| { |
| auto macro = FindOrNull(definitions, "FOOOO"); |
| ASSERT_NE(macro, nullptr); |
| EXPECT_TRUE(macro->IsCallable()); |
| const auto& params = macro->Parameters(); |
| EXPECT_EQ(params.size(), 1); |
| } |
| } |
| |
| } // namespace |
| } // namespace verilog |