| // 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 "common/analysis/lint_waiver.h" |
| |
| #include <cstddef> |
| #include <vector> |
| |
| #include "common/strings/line_column_map.h" |
| #include "common/text/text_structure_test_utils.h" |
| #include "common/text/token_info.h" |
| #include "common/text/token_stream_view.h" |
| #include "common/util/iterator_range.h" |
| #include "gtest/gtest.h" |
| |
| #undef EXPECT_OK |
| #undef EXPECT_NOK |
| #define EXPECT_OK(expr) EXPECT_TRUE((expr).ok()) |
| #define EXPECT_NOK(expr) EXPECT_FALSE((expr).ok()) |
| |
| namespace verible { |
| namespace { |
| |
| // Tests that an empty LintWaiver waives nothing. |
| TEST(LintWaiverTest, NoWaivers) { |
| LintWaiver lint_waiver; |
| EXPECT_TRUE(lint_waiver.Empty()); |
| EXPECT_FALSE(lint_waiver.RuleIsWaivedOnLine("foo", 0)); |
| EXPECT_FALSE(lint_waiver.RuleIsWaivedOnLine("foo", 1)); |
| EXPECT_FALSE(lint_waiver.RuleIsWaivedOnLine("bar", 1)); |
| } |
| |
| // Tests that only one line is waived for one rule. |
| TEST(LintWaiverTest, WaiveOneLineOneRule) { |
| LintWaiver lint_waiver; |
| auto rule_name = "xyz-rule"; |
| lint_waiver.WaiveOneLine(rule_name, 14); |
| EXPECT_FALSE(lint_waiver.Empty()); |
| EXPECT_FALSE(lint_waiver.RuleIsWaivedOnLine(rule_name, 13)); |
| EXPECT_TRUE(lint_waiver.RuleIsWaivedOnLine(rule_name, 14)); |
| EXPECT_FALSE(lint_waiver.RuleIsWaivedOnLine(rule_name, 15)); |
| EXPECT_FALSE(lint_waiver.RuleIsWaivedOnLine("other-rule", 14)); |
| } |
| |
| // Tests that re-waiving the same line has no additional effect. |
| TEST(LintWaiverTest, ReWaiveOneLineOneRule) { |
| LintWaiver lint_waiver; |
| auto rule_name = "xyz-rule"; |
| lint_waiver.WaiveOneLine(rule_name, 14); |
| EXPECT_FALSE(lint_waiver.Empty()); |
| EXPECT_FALSE(lint_waiver.RuleIsWaivedOnLine(rule_name, 13)); |
| EXPECT_TRUE(lint_waiver.RuleIsWaivedOnLine(rule_name, 14)); |
| EXPECT_TRUE(lint_waiver.RuleIsWaivedOnLine(rule_name, 14)); // yes, repeat |
| EXPECT_FALSE(lint_waiver.RuleIsWaivedOnLine(rule_name, 15)); |
| EXPECT_FALSE(lint_waiver.RuleIsWaivedOnLine("other-rule", 14)); |
| } |
| |
| // Tests that only two lines are waived properly. |
| TEST(LintWaiverTest, WaiveTwoLinesOneRule) { |
| LintWaiver lint_waiver; |
| auto rule_name = "aaa-rule"; |
| lint_waiver.WaiveOneLine(rule_name, 14); |
| lint_waiver.WaiveOneLine(rule_name, 10); |
| EXPECT_FALSE(lint_waiver.Empty()); |
| EXPECT_FALSE(lint_waiver.RuleIsWaivedOnLine(rule_name, 9)); |
| EXPECT_TRUE(lint_waiver.RuleIsWaivedOnLine(rule_name, 10)); |
| EXPECT_FALSE(lint_waiver.RuleIsWaivedOnLine(rule_name, 11)); |
| EXPECT_FALSE(lint_waiver.RuleIsWaivedOnLine(rule_name, 13)); |
| EXPECT_TRUE(lint_waiver.RuleIsWaivedOnLine(rule_name, 14)); |
| EXPECT_FALSE(lint_waiver.RuleIsWaivedOnLine(rule_name, 15)); |
| } |
| |
| // Tests that only one range is waived for one rule. |
| TEST(LintWaiverTest, WaiveRangeOneRule) { |
| LintWaiver lint_waiver; |
| auto rule_name = "www-rule"; |
| lint_waiver.WaiveLineRange(rule_name, 5, 9); |
| EXPECT_FALSE(lint_waiver.Empty()); |
| EXPECT_FALSE(lint_waiver.RuleIsWaivedOnLine(rule_name, 4)); |
| EXPECT_TRUE(lint_waiver.RuleIsWaivedOnLine(rule_name, 5)); |
| EXPECT_TRUE(lint_waiver.RuleIsWaivedOnLine(rule_name, 8)); |
| EXPECT_FALSE(lint_waiver.RuleIsWaivedOnLine(rule_name, 9)); |
| EXPECT_FALSE(lint_waiver.RuleIsWaivedOnLine("other-rule", 7)); |
| } |
| |
| // Tests that two disjoint ranges are waived for one rule. |
| TEST(LintWaiverTest, WaiveTwoDisjointRangesOneRule) { |
| LintWaiver lint_waiver; |
| auto rule_name = "zzz-rule"; |
| lint_waiver.WaiveLineRange(rule_name, 5, 7); |
| lint_waiver.WaiveLineRange(rule_name, 9, 11); |
| EXPECT_FALSE(lint_waiver.Empty()); |
| EXPECT_FALSE(lint_waiver.RuleIsWaivedOnLine(rule_name, 4)); |
| EXPECT_TRUE(lint_waiver.RuleIsWaivedOnLine(rule_name, 5)); |
| EXPECT_TRUE(lint_waiver.RuleIsWaivedOnLine(rule_name, 6)); |
| EXPECT_FALSE(lint_waiver.RuleIsWaivedOnLine(rule_name, 7)); |
| EXPECT_FALSE(lint_waiver.RuleIsWaivedOnLine(rule_name, 8)); |
| EXPECT_TRUE(lint_waiver.RuleIsWaivedOnLine(rule_name, 9)); |
| EXPECT_TRUE(lint_waiver.RuleIsWaivedOnLine(rule_name, 10)); |
| EXPECT_FALSE(lint_waiver.RuleIsWaivedOnLine(rule_name, 11)); |
| } |
| |
| // Tests that fused ranges are waived for one rule. |
| TEST(LintWaiverTest, WaiveFusedRangesOneRule) { |
| LintWaiver lint_waiver; |
| auto rule_name = "yy-rule"; |
| lint_waiver.WaiveLineRange(rule_name, 5, 9); |
| lint_waiver.WaiveLineRange(rule_name, 7, 11); // overlaps |
| EXPECT_FALSE(lint_waiver.Empty()); |
| EXPECT_FALSE(lint_waiver.RuleIsWaivedOnLine(rule_name, 4)); |
| EXPECT_TRUE(lint_waiver.RuleIsWaivedOnLine(rule_name, 5)); |
| EXPECT_TRUE(lint_waiver.RuleIsWaivedOnLine(rule_name, 6)); |
| EXPECT_TRUE(lint_waiver.RuleIsWaivedOnLine(rule_name, 7)); |
| EXPECT_TRUE(lint_waiver.RuleIsWaivedOnLine(rule_name, 8)); |
| EXPECT_TRUE(lint_waiver.RuleIsWaivedOnLine(rule_name, 9)); |
| EXPECT_TRUE(lint_waiver.RuleIsWaivedOnLine(rule_name, 10)); |
| EXPECT_FALSE(lint_waiver.RuleIsWaivedOnLine(rule_name, 11)); |
| } |
| |
| // Token type enumerations. |
| // For convenience, using plain int avoids static_cast-ing everywhere. |
| constexpr int kSpace = 0; |
| constexpr int kComment = 1; |
| constexpr int kOther = 2; |
| constexpr int kNewline = 3; |
| constexpr char kLinterName[] = "mylinter"; |
| constexpr char kWaiveLineCommand[] = "waive"; |
| constexpr char kWaiveStartCommand[] = "waive-begin"; |
| constexpr char kWaiveStopCommand[] = "waive-end"; |
| |
| // Helper class for testing an example of a waiver. |
| class LintWaiverBuilderTest : public testing::Test, public LintWaiverBuilder { |
| public: |
| LintWaiverBuilderTest() |
| : LintWaiverBuilder( |
| [](const TokenInfo& token) { |
| return token.token_enum() == kComment; |
| }, |
| [](const TokenInfo& token) { |
| return token.token_enum() == kSpace || |
| token.token_enum() == kNewline; |
| }, |
| kLinterName, kWaiveLineCommand, kWaiveStartCommand, |
| kWaiveStopCommand) {} |
| |
| // Convenient sequence adapter to iterator range. |
| void ProcessLine(const TokenSequence& tokens, size_t line_number) { |
| LintWaiverBuilder::ProcessLine(make_range(tokens.begin(), tokens.end()), |
| line_number); |
| } |
| }; |
| |
| // Tests that initial state contains no line waivers. |
| TEST_F(LintWaiverBuilderTest, PostConstruction) { |
| const LintWaiver& waiver = GetLintWaiver(); |
| EXPECT_TRUE(waiver.Empty()); |
| EXPECT_FALSE(waiver.RuleIsWaivedOnLine("some-rule", 0)); |
| EXPECT_FALSE(waiver.RuleIsWaivedOnLine("some-rule", 1)); |
| EXPECT_FALSE(waiver.RuleIsWaivedOnLine("another-rule", 1)); |
| } |
| |
| // Tests that an empty line waives nothing. |
| TEST_F(LintWaiverBuilderTest, EmptyLine) { |
| const TokenSequence tokens{}; |
| ProcessLine(tokens, 0); |
| const LintWaiver& waiver = GetLintWaiver(); |
| EXPECT_TRUE(waiver.Empty()); |
| EXPECT_FALSE(waiver.RuleIsWaivedOnLine("some-rule", 0)); |
| EXPECT_FALSE(waiver.RuleIsWaivedOnLine("some-rule", 1)); |
| EXPECT_FALSE(waiver.RuleIsWaivedOnLine("another-rule", 1)); |
| } |
| |
| // Tests that a comment-only line waives the next line. |
| TEST_F(LintWaiverBuilderTest, OneCommentOnly) { |
| const TokenSequence lines[] = { |
| {TokenInfo(kComment, |
| "// mylinter waive x-rule")}, // token locations do not matter |
| {TokenInfo(kOther, "hello")}}; |
| ProcessLine(lines[0], 2); |
| ProcessLine(lines[1], 3); |
| const LintWaiver& waiver = GetLintWaiver(); |
| EXPECT_FALSE(waiver.Empty()); |
| EXPECT_FALSE(waiver.RuleIsWaivedOnLine("x-rule", 2)); |
| EXPECT_TRUE(waiver.RuleIsWaivedOnLine("x-rule", 3)); |
| } |
| |
| // Tests that a next-line on the last line does nothing. |
| TEST_F(LintWaiverBuilderTest, LastLineWaiveNextLine) { |
| const TokenSequence lines[] = { |
| {TokenInfo(kOther, "hello")}, |
| {TokenInfo(kComment, "// mylinter waive z-rule")}}; |
| ProcessLine(lines[0], 2); |
| ProcessLine(lines[1], 3); |
| const LintWaiver& waiver = GetLintWaiver(); |
| EXPECT_FALSE(waiver.RuleIsWaivedOnLine("z-rule", 2)); |
| EXPECT_FALSE(waiver.RuleIsWaivedOnLine("z-rule", 3)); |
| // Does nothing with next line until next line is actually encountered. |
| EXPECT_FALSE(waiver.RuleIsWaivedOnLine("z-rule", 4)); |
| // At this point example looks like waiver comment is last line. |
| // As a small extension, we can verify that next line would be waived. |
| ProcessLine(lines[0], 4); |
| EXPECT_TRUE(waiver.RuleIsWaivedOnLine("z-rule", 4)); |
| } |
| |
| // Tests that a comment-only with no waive command does nothing. |
| TEST_F(LintWaiverBuilderTest, OneCommentOnlyMissingWaiveCommand) { |
| const TokenSequence lines[] = { |
| {TokenInfo(kComment, |
| "// mylinter x-rule")}, // missing 'waive' does nothing |
| {TokenInfo(kOther, "hello")}}; |
| ProcessLine(lines[0], 2); |
| ProcessLine(lines[1], 3); |
| const LintWaiver& waiver = GetLintWaiver(); |
| EXPECT_TRUE(waiver.Empty()); |
| EXPECT_FALSE(waiver.RuleIsWaivedOnLine("x-rule", 2)); |
| EXPECT_FALSE(waiver.RuleIsWaivedOnLine("x-rule", 3)); |
| } |
| |
| // Tests that a comment-only with the wrong waive command does nothing. |
| TEST_F(LintWaiverBuilderTest, OneCommentOnlyWrongWaiveCommand) { |
| const TokenSequence lines[] = { |
| {TokenInfo(kComment, |
| "// mylinter wave x-rule")}, // only 'waive' does something |
| {TokenInfo(kOther, "hello")}}; |
| ProcessLine(lines[0], 2); |
| ProcessLine(lines[1], 3); |
| const LintWaiver& waiver = GetLintWaiver(); |
| EXPECT_TRUE(waiver.Empty()); |
| EXPECT_FALSE(waiver.RuleIsWaivedOnLine("x-rule", 2)); |
| EXPECT_FALSE(waiver.RuleIsWaivedOnLine("x-rule", 3)); |
| } |
| |
| // Tests that a comment-only line with extra text waives the next line. |
| TEST_F(LintWaiverBuilderTest, OneCommentOnlyExtraTextIgnored) { |
| const TokenSequence lines[] = { |
| {TokenInfo(kComment, "// mylinter waive x-rule // yay, waiver!")}, |
| {TokenInfo(kOther, "hello")}}; |
| ProcessLine(lines[0], 0); |
| ProcessLine(lines[1], 1); |
| const LintWaiver& waiver = GetLintWaiver(); |
| EXPECT_FALSE(waiver.Empty()); |
| EXPECT_FALSE(waiver.RuleIsWaivedOnLine("x-rule", 0)); |
| EXPECT_TRUE(waiver.RuleIsWaivedOnLine("x-rule", 1)); |
| } |
| |
| // Tests that a comment-only line waives the next line. |
| TEST_F(LintWaiverBuilderTest, OneCommentOnlyOddSpacing) { |
| const TokenSequence lines[] = { |
| {TokenInfo(kComment, "//mylinter waive y-rule ")}, |
| {TokenInfo(kOther, "hello")}}; |
| ProcessLine(lines[0], 0); |
| ProcessLine(lines[1], 1); |
| const LintWaiver& waiver = GetLintWaiver(); |
| EXPECT_FALSE(waiver.Empty()); |
| EXPECT_FALSE(waiver.RuleIsWaivedOnLine("y-rule", 0)); |
| EXPECT_TRUE(waiver.RuleIsWaivedOnLine("y-rule", 1)); |
| } |
| |
| // Tests that a comment-only line with leading space waives the next line. |
| TEST_F(LintWaiverBuilderTest, OneCommentOnlyLeadingSpace) { |
| const TokenSequence lines[] = { |
| {TokenInfo(kSpace, " "), // leading space |
| TokenInfo( |
| kComment, |
| "// mylinter waive xx-rule")}, // token locations do not matter |
| {TokenInfo(kOther, "hello"), TokenInfo(kOther, "world")}}; |
| ProcessLine(lines[0], 0); |
| ProcessLine(lines[1], 1); |
| const LintWaiver& waiver = GetLintWaiver(); |
| EXPECT_FALSE(waiver.Empty()); |
| EXPECT_FALSE(waiver.RuleIsWaivedOnLine("xx-rule", 0)); |
| EXPECT_TRUE(waiver.RuleIsWaivedOnLine("xx-rule", 1)); |
| } |
| |
| // Tests that a block-style comment-only line space waives the next line. |
| TEST_F(LintWaiverBuilderTest, OneCommentOnlyBlockStyle) { |
| const TokenSequence lines[] = { |
| { |
| TokenInfo(kSpace, " "), // leading space |
| TokenInfo(kComment, "/* mylinter waive xx-rule */"), |
| TokenInfo(kSpace, " ") // trailing space |
| }, |
| {TokenInfo(kOther, "hello"), TokenInfo(kOther, "world")}}; |
| ProcessLine(lines[0], 0); |
| ProcessLine(lines[1], 1); |
| const LintWaiver& waiver = GetLintWaiver(); |
| EXPECT_FALSE(waiver.Empty()); |
| EXPECT_FALSE(waiver.RuleIsWaivedOnLine("xx-rule", 0)); |
| EXPECT_TRUE(waiver.RuleIsWaivedOnLine("xx-rule", 1)); |
| } |
| |
| // Tests that a comment-only line with leading space waives the next line. |
| TEST_F(LintWaiverBuilderTest, CommentWaiverCanceledByBlankLine) { |
| const TokenSequence lines[] = { |
| {TokenInfo(kComment, "// mylinter waive xx-rule")}, |
| {}, // blank line |
| {TokenInfo(kOther, "hello"), TokenInfo(kOther, "world")}}; |
| ProcessLine(lines[0], 0); |
| ProcessLine(lines[1], 1); |
| ProcessLine(lines[2], 2); |
| const LintWaiver& waiver = GetLintWaiver(); |
| EXPECT_TRUE(waiver.Empty()); |
| EXPECT_FALSE(waiver.RuleIsWaivedOnLine("xx-rule", 0)); |
| EXPECT_FALSE(waiver.RuleIsWaivedOnLine("xx-rule", 1)); |
| EXPECT_FALSE(waiver.RuleIsWaivedOnLine("xx-rule", 2)); |
| } |
| |
| // Tests that a comment carry waivers to the next non-comment line. |
| TEST_F(LintWaiverBuilderTest, CommentWaiverCarriedToNextLine) { |
| const TokenSequence lines[] = { |
| {TokenInfo(kComment, "// mylinter waive xx-rule")}, |
| {TokenInfo(kComment, "//")}, // comment line, carry waiver to next line |
| {TokenInfo(kOther, "hello"), TokenInfo(kOther, "world")}}; |
| ProcessLine(lines[0], 0); |
| ProcessLine(lines[1], 1); |
| ProcessLine(lines[2], 2); |
| const LintWaiver& waiver = GetLintWaiver(); |
| EXPECT_FALSE(waiver.Empty()); |
| EXPECT_FALSE(waiver.RuleIsWaivedOnLine("xx-rule", 0)); |
| EXPECT_FALSE(waiver.RuleIsWaivedOnLine("xx-rule", 1)); |
| EXPECT_TRUE(waiver.RuleIsWaivedOnLine("xx-rule", 2)); |
| EXPECT_FALSE(waiver.RuleIsWaivedOnLine("xx-rule", 3)); |
| } |
| |
| // Tests that a comment carry waivers to the next non-comment line (with |
| // spaces). |
| TEST_F(LintWaiverBuilderTest, CommentWaiverCarriedToNextLineLeadingSpaces) { |
| const TokenSequence lines[] = { |
| {TokenInfo(kSpace, "\t"), |
| TokenInfo(kComment, "// mylinter waive xx-rule")}, |
| {TokenInfo(kSpace, "\t"), |
| TokenInfo(kComment, "//")}, // comment line, carry waiver to next line |
| {TokenInfo(kOther, "hello"), TokenInfo(kOther, "world")}}; |
| ProcessLine(lines[0], 3); |
| ProcessLine(lines[1], 4); |
| ProcessLine(lines[2], 5); |
| const LintWaiver& waiver = GetLintWaiver(); |
| EXPECT_FALSE(waiver.Empty()); |
| EXPECT_FALSE(waiver.RuleIsWaivedOnLine("xx-rule", 3)); |
| EXPECT_FALSE(waiver.RuleIsWaivedOnLine("xx-rule", 4)); |
| EXPECT_TRUE(waiver.RuleIsWaivedOnLine("xx-rule", 5)); |
| EXPECT_FALSE(waiver.RuleIsWaivedOnLine("xx-rule", 6)); |
| } |
| |
| // Tests that a comment carry waivers to the next non-comment line. |
| TEST_F(LintWaiverBuilderTest, MultipleNextLineWaiversAccumulate) { |
| const TokenSequence lines[] = { |
| {TokenInfo(kComment, "// mylinter waive aa-rule")}, |
| {TokenInfo(kComment, "// mylinter waive bb-rule")}, |
| {TokenInfo(kOther, "hello"), TokenInfo(kOther, "world")}}; |
| ProcessLine(lines[0], 0); |
| ProcessLine(lines[1], 1); |
| ProcessLine(lines[2], 2); |
| const LintWaiver& waiver = GetLintWaiver(); |
| EXPECT_FALSE(waiver.Empty()); |
| EXPECT_FALSE(waiver.RuleIsWaivedOnLine("aa-rule", 0)); |
| EXPECT_FALSE(waiver.RuleIsWaivedOnLine("aa-rule", 1)); |
| EXPECT_TRUE(waiver.RuleIsWaivedOnLine("aa-rule", 2)); |
| EXPECT_FALSE(waiver.RuleIsWaivedOnLine("aa-rule", 3)); |
| EXPECT_FALSE(waiver.RuleIsWaivedOnLine("bb-rule", 0)); |
| EXPECT_FALSE(waiver.RuleIsWaivedOnLine("bb-rule", 1)); |
| EXPECT_TRUE(waiver.RuleIsWaivedOnLine("bb-rule", 2)); |
| EXPECT_FALSE(waiver.RuleIsWaivedOnLine("bb-rule", 3)); |
| } |
| |
| // Tests that same-line waiver works. |
| TEST_F(LintWaiverBuilderTest, ThisLineWaiver) { |
| const TokenSequence lines[] = { |
| {TokenInfo(kOther, "blah blah")}, |
| {TokenInfo(kOther, "hello"), TokenInfo(kOther, "world"), |
| TokenInfo(kComment, "// mylinter waive bb-rule")}}; |
| ProcessLine(lines[0], 8); |
| ProcessLine(lines[1], 9); |
| const LintWaiver& waiver = GetLintWaiver(); |
| EXPECT_FALSE(waiver.Empty()); |
| EXPECT_FALSE(waiver.RuleIsWaivedOnLine("bb-rule", 8)); |
| EXPECT_TRUE(waiver.RuleIsWaivedOnLine("bb-rule", 9)); |
| EXPECT_FALSE(waiver.RuleIsWaivedOnLine("bb-rule", 10)); |
| } |
| |
| // Tests that a next-line and same-line waivers accumulate. |
| TEST_F(LintWaiverBuilderTest, NextLineAndThisLineWaiversCombine) { |
| const TokenSequence lines[] = { |
| {TokenInfo(kComment, "// mylinter waive aa-rule")}, |
| {TokenInfo(kOther, "hello"), TokenInfo(kOther, "world"), |
| TokenInfo(kComment, "// mylinter waive bb-rule")}}; |
| ProcessLine(lines[0], 0); |
| ProcessLine(lines[1], 1); |
| const LintWaiver& waiver = GetLintWaiver(); |
| EXPECT_FALSE(waiver.Empty()); |
| EXPECT_FALSE(waiver.RuleIsWaivedOnLine("aa-rule", 0)); |
| EXPECT_TRUE(waiver.RuleIsWaivedOnLine("aa-rule", 1)); |
| EXPECT_FALSE(waiver.RuleIsWaivedOnLine("aa-rule", 2)); |
| EXPECT_FALSE(waiver.RuleIsWaivedOnLine("bb-rule", 0)); |
| EXPECT_TRUE(waiver.RuleIsWaivedOnLine("bb-rule", 1)); |
| EXPECT_FALSE(waiver.RuleIsWaivedOnLine("bb-rule", 2)); |
| } |
| |
| // Tests that multiple same-line waivers work. |
| TEST_F(LintWaiverBuilderTest, MultipleThisLineWaiver) { |
| const TokenSequence lines[] = { |
| {TokenInfo(kOther, "blah blah")}, |
| {TokenInfo(kOther, "hello"), TokenInfo(kOther, "world"), |
| TokenInfo(kComment, "/* mylinter waive bb-rule */"), |
| TokenInfo(kComment, "/* mylinter waive cc-rule */")}}; |
| ProcessLine(lines[0], 8); |
| ProcessLine(lines[1], 9); |
| const LintWaiver& waiver = GetLintWaiver(); |
| EXPECT_FALSE(waiver.Empty()); |
| EXPECT_FALSE(waiver.RuleIsWaivedOnLine("bb-rule", 8)); |
| EXPECT_TRUE(waiver.RuleIsWaivedOnLine("bb-rule", 9)); |
| EXPECT_FALSE(waiver.RuleIsWaivedOnLine("bb-rule", 10)); |
| EXPECT_FALSE(waiver.RuleIsWaivedOnLine("cc-rule", 8)); |
| EXPECT_TRUE(waiver.RuleIsWaivedOnLine("cc-rule", 9)); |
| EXPECT_FALSE(waiver.RuleIsWaivedOnLine("cc-rule", 10)); |
| } |
| |
| // Tests that waived line range works with end-line comments. |
| TEST_F(LintWaiverBuilderTest, SingleRangeWaiver) { |
| const TokenSequence lines[] = { |
| {TokenInfo(kOther, "blah blah")}, |
| {TokenInfo(kOther, "hello"), TokenInfo(kOther, "world"), |
| TokenInfo(kComment, "// mylinter waive-begin cc-rule")}, |
| {TokenInfo(kOther, "hello"), TokenInfo(kOther, "world"), |
| TokenInfo(kComment, "// mylinter waive-end cc-rule")}, |
| }; |
| ProcessLine(lines[0], 2); |
| ProcessLine(lines[1], 3); |
| ProcessLine(lines[2], 4); |
| const LintWaiver& waiver = GetLintWaiver(); |
| EXPECT_FALSE(waiver.Empty()); |
| EXPECT_FALSE(waiver.RuleIsWaivedOnLine("cc-rule", 2)); |
| EXPECT_TRUE(waiver.RuleIsWaivedOnLine("cc-rule", 3)); |
| EXPECT_FALSE(waiver.RuleIsWaivedOnLine("cc-rule", 4)); |
| } |
| |
| // Tests that waived line range mis-matched end has no effect. |
| TEST_F(LintWaiverBuilderTest, EndRangeWaiverNoEffect) { |
| const TokenSequence lines[] = { |
| {TokenInfo(kOther, "blah blah")}, |
| {TokenInfo(kOther, "hello"), TokenInfo(kOther, "world"), |
| TokenInfo(kComment, "// mylinter waive-end xx-rule")}, |
| }; |
| ProcessLine(lines[0], 12); |
| ProcessLine(lines[1], 15); |
| const LintWaiver& waiver = GetLintWaiver(); |
| EXPECT_TRUE(waiver.Empty()); |
| } |
| |
| // Tests that waived line range works with end-line comments on their own line. |
| TEST_F(LintWaiverBuilderTest, SingleRangeWaiverDirectivesOnOwnLine) { |
| const TokenSequence lines[] = { |
| {TokenInfo(kOther, "blah blah")}, |
| {TokenInfo(kComment, "// mylinter waive-begin cc-rule")}, |
| {TokenInfo(kComment, "// mylinter waive-end cc-rule")}, |
| }; |
| ProcessLine(lines[0], 4); |
| ProcessLine(lines[1], 5); |
| ProcessLine(lines[2], 6); |
| const LintWaiver& waiver = GetLintWaiver(); |
| EXPECT_FALSE(waiver.Empty()); |
| EXPECT_FALSE(waiver.RuleIsWaivedOnLine("cc-rule", 4)); |
| EXPECT_TRUE(waiver.RuleIsWaivedOnLine("cc-rule", 5)); |
| EXPECT_FALSE(waiver.RuleIsWaivedOnLine("cc-rule", 6)); |
| } |
| |
| // Tests that waived line range works on a longer range. |
| TEST_F(LintWaiverBuilderTest, SingleRangeWaiverLonger) { |
| const TokenSequence lines[] = { |
| {TokenInfo(kOther, "blah blah")}, |
| {TokenInfo(kOther, "hello"), TokenInfo(kOther, "world"), |
| TokenInfo(kComment, "// mylinter waive-begin cc-rule")}, |
| {TokenInfo(kOther, "hello"), TokenInfo(kOther, "world"), |
| TokenInfo(kComment, "// mylinter waive-end cc-rule")}, |
| }; |
| ProcessLine(lines[0], 2); |
| ProcessLine(lines[1], 3); |
| ProcessLine(lines[2], 8); |
| const LintWaiver& waiver = GetLintWaiver(); |
| EXPECT_FALSE(waiver.Empty()); |
| EXPECT_FALSE(waiver.RuleIsWaivedOnLine("cc-rule", 2)); |
| EXPECT_TRUE(waiver.RuleIsWaivedOnLine("cc-rule", 3)); |
| EXPECT_TRUE(waiver.RuleIsWaivedOnLine("cc-rule", 7)); |
| EXPECT_FALSE(waiver.RuleIsWaivedOnLine("cc-rule", 8)); |
| } |
| |
| // Tests that waived line range works with duplicate range-opens. |
| TEST_F(LintWaiverBuilderTest, SingleRangeWaiverDoubleOpenDoubleClose) { |
| const TokenSequence lines[] = { |
| {TokenInfo(kOther, "blah blah")}, |
| {TokenInfo(kOther, "hello"), TokenInfo(kOther, "world"), |
| TokenInfo(kComment, "// mylinter waive-begin cc-rule")}, |
| {TokenInfo(kOther, "hello"), TokenInfo(kOther, "world"), |
| TokenInfo(kComment, "// mylinter waive-end cc-rule")}, |
| }; |
| ProcessLine(lines[0], 2); |
| ProcessLine(lines[1], 3); |
| ProcessLine(lines[1], 4); // duplicate waive-begin (ignored) |
| ProcessLine(lines[2], 5); |
| ProcessLine(lines[2], 6); // duplicate waive-end (harmless) |
| const LintWaiver& waiver = GetLintWaiver(); |
| EXPECT_FALSE(waiver.Empty()); |
| EXPECT_FALSE(waiver.RuleIsWaivedOnLine("cc-rule", 2)); |
| EXPECT_TRUE(waiver.RuleIsWaivedOnLine("cc-rule", 3)); |
| EXPECT_TRUE(waiver.RuleIsWaivedOnLine("cc-rule", 4)); |
| EXPECT_FALSE(waiver.RuleIsWaivedOnLine("cc-rule", 5)); |
| EXPECT_FALSE(waiver.RuleIsWaivedOnLine("cc-rule", 6)); |
| } |
| |
| // Tests that multiple overlapping waived line ranges work. |
| TEST_F(LintWaiverBuilderTest, MultiRangeWaiver) { |
| const TokenSequence lines[] = { |
| {TokenInfo(kOther, "blah blah")}, |
| {TokenInfo(kComment, "// mylinter waive-begin cc-rule")}, |
| {TokenInfo(kComment, "// mylinter waive-begin dd-rule")}, |
| {TokenInfo(kComment, "// mylinter waive-end cc-rule")}, |
| {TokenInfo(kComment, "// mylinter waive-end dd-rule")}, |
| }; |
| ProcessLine(lines[0], 2); |
| ProcessLine(lines[1], 3); |
| ProcessLine(lines[2], 5); |
| ProcessLine(lines[3], 7); |
| ProcessLine(lines[4], 9); |
| const LintWaiver& waiver = GetLintWaiver(); |
| EXPECT_FALSE(waiver.Empty()); |
| EXPECT_FALSE(waiver.RuleIsWaivedOnLine("cc-rule", 2)); |
| EXPECT_TRUE(waiver.RuleIsWaivedOnLine("cc-rule", 3)); |
| EXPECT_TRUE(waiver.RuleIsWaivedOnLine("cc-rule", 6)); |
| EXPECT_FALSE(waiver.RuleIsWaivedOnLine("cc-rule", 7)); |
| EXPECT_FALSE(waiver.RuleIsWaivedOnLine("dd-rule", 4)); |
| EXPECT_TRUE(waiver.RuleIsWaivedOnLine("dd-rule", 5)); |
| EXPECT_TRUE(waiver.RuleIsWaivedOnLine("dd-rule", 8)); |
| EXPECT_FALSE(waiver.RuleIsWaivedOnLine("dd-rule", 9)); |
| } |
| |
| static const TokenInfo EOL(kNewline, "\n"); |
| |
| // Tests that empty lexical token structure constructs an empty LintWaiver. |
| TEST_F(LintWaiverBuilderTest, FromTextStructureEmptyFile) { |
| const TextStructureTokenized text_structure({}); // empty |
| ProcessTokenRangesByLine(text_structure.Data()); |
| const auto& lint_waiver = GetLintWaiver(); |
| EXPECT_TRUE(lint_waiver.Empty()); |
| EXPECT_FALSE(lint_waiver.RuleIsWaivedOnLine("abc-rule", 0)); |
| } |
| |
| // Tests that lexical token structure yields an empty LintWaiver. |
| TEST_F(LintWaiverBuilderTest, FromTextStructureNoWaivers) { |
| const TextStructureTokenized text_structure( |
| {{TokenInfo(kOther, "hello"), TokenInfo(kOther, ","), |
| TokenInfo(kSpace, " "), TokenInfo(kOther, "world"), EOL}, |
| {EOL}, |
| {TokenInfo(kOther, "hello"), TokenInfo(kOther, ","), |
| TokenInfo(kSpace, " "), TokenInfo(kOther, "world"), EOL}}); |
| ProcessTokenRangesByLine(text_structure.Data()); |
| const auto& lint_waiver = GetLintWaiver(); |
| EXPECT_TRUE(lint_waiver.Empty()); |
| EXPECT_FALSE(lint_waiver.RuleIsWaivedOnLine("abc-rule", 0)); |
| } |
| |
| // Tests that lexical token structure can waive the line after a comment. |
| TEST_F(LintWaiverBuilderTest, FromTextStructureOneWaiverNextLine) { |
| const TextStructureTokenized text_structure( |
| {{TokenInfo(kComment, "// mylinter waive abc-rule"), EOL}, |
| {TokenInfo(kOther, "hello"), EOL}}); |
| ProcessTokenRangesByLine(text_structure.Data()); |
| const auto& lint_waiver = GetLintWaiver(); |
| EXPECT_FALSE(lint_waiver.Empty()); |
| EXPECT_TRUE(lint_waiver.RuleIsWaivedOnLine("abc-rule", 1)); |
| } |
| |
| // Tests that lexical token structure can waive the line with a comment. |
| TEST_F(LintWaiverBuilderTest, FromTextStructureOneWaiverThisLine) { |
| const TextStructureTokenized text_structure( |
| {{TokenInfo(kOther, "text"), EOL}, // line[0] |
| {TokenInfo(kOther, "more-text"), EOL}, // line[1] |
| {TokenInfo(kOther, "hello"), // line[2] |
| TokenInfo(kComment, "// mylinter waive qq-rule"), EOL}, |
| {TokenInfo(kOther, "bye"), EOL}}); |
| ProcessTokenRangesByLine(text_structure.Data()); |
| const auto& lint_waiver = GetLintWaiver(); |
| EXPECT_FALSE(lint_waiver.Empty()); |
| EXPECT_TRUE(lint_waiver.RuleIsWaivedOnLine("qq-rule", 2)); |
| } |
| |
| // Tests that lexical token structure can waive a range of lines. |
| TEST_F(LintWaiverBuilderTest, FromTextStructureOneWaiverRange) { |
| const TextStructureTokenized text_structure({ |
| {TokenInfo(kOther, "text"), EOL}, // line[0] |
| {TokenInfo(kComment, "// mylinter waive-begin qq-rule"), EOL}, // line[1] |
| {TokenInfo(kOther, "more-text"), EOL}, // line[2] |
| {TokenInfo(kComment, "// mylinter waive-end qq-rule"), EOL}, // line[3] |
| {TokenInfo(kOther, "bye"), EOL} // line[4] |
| }); |
| ProcessTokenRangesByLine(text_structure.Data()); |
| const auto& lint_waiver = GetLintWaiver(); |
| EXPECT_FALSE(lint_waiver.Empty()); |
| EXPECT_FALSE(lint_waiver.RuleIsWaivedOnLine("qq-rule", 0)); |
| EXPECT_TRUE(lint_waiver.RuleIsWaivedOnLine("qq-rule", 1)); |
| EXPECT_TRUE(lint_waiver.RuleIsWaivedOnLine("qq-rule", 2)); |
| EXPECT_FALSE(lint_waiver.RuleIsWaivedOnLine("qq-rule", 3)); |
| } |
| |
| // Tests that lexical token structure can waive an open range of lines. |
| TEST_F(LintWaiverBuilderTest, FromTextStructureOneWaiverRangeOpened) { |
| const TextStructureTokenized text_structure({ |
| {TokenInfo(kOther, "text"), EOL}, // line[0] |
| {TokenInfo(kComment, "// mylinter waive-begin qq-rule"), EOL}, // line[1] |
| {TokenInfo(kOther, "more-text"), EOL}, // line[2] |
| {TokenInfo(kOther, "bye"), EOL} // line[3] |
| }); |
| ProcessTokenRangesByLine(text_structure.Data()); |
| const auto& lint_waiver = GetLintWaiver(); |
| EXPECT_FALSE(lint_waiver.Empty()); |
| EXPECT_FALSE(lint_waiver.RuleIsWaivedOnLine("qq-rule", 0)); |
| EXPECT_TRUE(lint_waiver.RuleIsWaivedOnLine("qq-rule", 1)); |
| EXPECT_TRUE(lint_waiver.RuleIsWaivedOnLine("qq-rule", 2)); |
| EXPECT_TRUE(lint_waiver.RuleIsWaivedOnLine("qq-rule", 3)); |
| } |
| |
| TEST_F(LintWaiverBuilderTest, ApplyExternalWaiversInvalidCases) { |
| std::set<absl::string_view> active_rules; |
| const absl::string_view user_file = "filename"; |
| const absl::string_view cfg_file = "waive_file.config"; |
| |
| // Completely invalid config |
| const absl::string_view cfg_inv = "inv config"; |
| EXPECT_NOK(ApplyExternalWaivers(active_rules, user_file, cfg_file, cfg_inv)); |
| |
| const absl::string_view cfg_inv_2 = "--line=1"; |
| EXPECT_NOK( |
| ApplyExternalWaivers(active_rules, user_file, cfg_file, cfg_inv_2)); |
| |
| // Valid command, invalid parameters |
| const absl::string_view cfg_inv_params = "waive --something"; |
| EXPECT_NOK( |
| ApplyExternalWaivers(active_rules, user_file, cfg_file, cfg_inv_params)); |
| |
| // Non-registered rule name |
| const absl::string_view cfg_inv_rule = "waive --rule=abc --line=1"; |
| EXPECT_NOK( |
| ApplyExternalWaivers(active_rules, user_file, cfg_file, cfg_inv_rule)); |
| |
| // register rule |
| const absl::string_view abc_rule = "abc"; |
| active_rules.insert(abc_rule); |
| |
| // Valid rule, missing params |
| const absl::string_view cfg_no_param = "waive --rule=abc"; |
| EXPECT_NOK( |
| ApplyExternalWaivers(active_rules, user_file, cfg_file, cfg_no_param)); |
| |
| // Valid rule, invalid line number |
| const absl::string_view cfg_inv_lineno = "waive --rule=abc --line=0"; |
| EXPECT_NOK( |
| ApplyExternalWaivers(active_rules, user_file, cfg_file, cfg_inv_lineno)); |
| |
| // Valid rule, invalid line range |
| const absl::string_view cfg_inv_range = "waive --rule=abc --line=1:0"; |
| EXPECT_NOK( |
| ApplyExternalWaivers(active_rules, user_file, cfg_file, cfg_inv_range)); |
| // Valid rule, invalid regex |
| const absl::string_view cfg_inv_regex = "waive --rule=abc --regex=\"(\""; |
| EXPECT_NOK( |
| ApplyExternalWaivers(active_rules, user_file, cfg_file, cfg_inv_regex)); |
| |
| // Valid rule, both regex and lines specified |
| const absl::string_view cfg_conflict = |
| "waive --rule=abc --regex=\".*\" --line=1"; |
| EXPECT_NOK( |
| ApplyExternalWaivers(active_rules, user_file, cfg_file, cfg_conflict)); |
| |
| // Missing rulename |
| const absl::string_view cfg_no_rule = "waive --line=1"; |
| EXPECT_NOK( |
| ApplyExternalWaivers(active_rules, user_file, cfg_file, cfg_no_rule)); |
| |
| // Check that even though some rules are invalid, the consecutive ones |
| // are still parsed and applied |
| const absl::string_view cfg_mixed = |
| "waive --line=1\ndasdasda\nwaive --rule=abc --line=10"; |
| EXPECT_NOK( |
| ApplyExternalWaivers(active_rules, user_file, cfg_file, cfg_mixed)); |
| EXPECT_FALSE(lint_waiver_.RuleIsWaivedOnLine("abc", 8)); |
| EXPECT_TRUE(lint_waiver_.RuleIsWaivedOnLine("abc", 9)); |
| EXPECT_FALSE(lint_waiver_.RuleIsWaivedOnLine("abc", 10)); |
| } |
| |
| TEST_F(LintWaiverBuilderTest, ApplyExternalWaiversValidCases) { |
| const std::set<absl::string_view> active_rules{"abc"}; |
| const absl::string_view user_file = "filename"; |
| const absl::string_view cfg_file = "waive_file.config"; |
| |
| const absl::string_view cfg_line = "waive --rule=abc --line=1"; |
| EXPECT_OK(ApplyExternalWaivers(active_rules, user_file, cfg_file, cfg_line)); |
| EXPECT_TRUE(lint_waiver_.RuleIsWaivedOnLine("abc", 0)); |
| EXPECT_FALSE(lint_waiver_.RuleIsWaivedOnLine("abc", 1)); |
| |
| const absl::string_view cfg_line_inv_ord = "waive --line=3 --rule=abc"; |
| EXPECT_OK(ApplyExternalWaivers(active_rules, user_file, cfg_file, |
| cfg_line_inv_ord)); |
| EXPECT_FALSE(lint_waiver_.RuleIsWaivedOnLine("abc", 1)); |
| EXPECT_TRUE(lint_waiver_.RuleIsWaivedOnLine("abc", 2)); |
| EXPECT_FALSE(lint_waiver_.RuleIsWaivedOnLine("abc", 3)); |
| |
| const absl::string_view cfg_quotes = "waive --rule=\"abc\" --line=5"; |
| EXPECT_OK( |
| ApplyExternalWaivers(active_rules, user_file, cfg_file, cfg_quotes)); |
| EXPECT_FALSE(lint_waiver_.RuleIsWaivedOnLine("abc", 3)); |
| EXPECT_TRUE(lint_waiver_.RuleIsWaivedOnLine("abc", 4)); |
| EXPECT_FALSE(lint_waiver_.RuleIsWaivedOnLine("abc", 5)); |
| |
| const absl::string_view cfg_line_range = "waive --rule=abc --line=7:9"; |
| EXPECT_OK( |
| ApplyExternalWaivers(active_rules, user_file, cfg_file, cfg_line_range)); |
| EXPECT_FALSE(lint_waiver_.RuleIsWaivedOnLine("abc", 5)); |
| EXPECT_TRUE(lint_waiver_.RuleIsWaivedOnLine("abc", 6)); |
| EXPECT_TRUE(lint_waiver_.RuleIsWaivedOnLine("abc", 7)); |
| EXPECT_TRUE(lint_waiver_.RuleIsWaivedOnLine("abc", 8)); |
| EXPECT_FALSE(lint_waiver_.RuleIsWaivedOnLine("abc", 9)); |
| |
| const absl::string_view cfg_line_range_i = "waive --rule=abc --line=11:11"; |
| EXPECT_OK(ApplyExternalWaivers(active_rules, user_file, cfg_file, |
| cfg_line_range_i)); |
| EXPECT_FALSE(lint_waiver_.RuleIsWaivedOnLine("abc", 9)); |
| EXPECT_TRUE(lint_waiver_.RuleIsWaivedOnLine("abc", 10)); |
| EXPECT_FALSE(lint_waiver_.RuleIsWaivedOnLine("abc", 11)); |
| |
| const absl::string_view cfg_regex = "waive --rule=abc --regex=abc"; |
| EXPECT_OK(ApplyExternalWaivers(active_rules, user_file, cfg_file, cfg_regex)); |
| |
| const absl::string_view cfg_regex_complex = |
| "waive --rule=abc --regex=\"abc .*\""; |
| EXPECT_OK(ApplyExternalWaivers(active_rules, user_file, cfg_file, |
| cfg_regex_complex)); |
| } |
| |
| TEST_F(LintWaiverBuilderTest, LocationOptionNarrowsTestedFile) { |
| const std::set<absl::string_view> active_rules{"abc"}; |
| const absl::string_view user_file = "some_fancy_fileName.sv"; |
| const absl::string_view cfg_file = "waive_file.config"; |
| |
| absl::string_view cfg_line = R"( |
| waive --rule=abc --line=100 |
| waive --rule=abc --line=200 --location=".*foo.*" |
| waive --rule=abc --line=300 --location=".*_fancy_.*" |
| )"; |
| |
| EXPECT_OK(ApplyExternalWaivers(active_rules, user_file, cfg_file, cfg_line)); |
| EXPECT_FALSE(lint_waiver_.RuleIsWaivedOnLine("abc", 0)); |
| EXPECT_TRUE(lint_waiver_.RuleIsWaivedOnLine("abc", 99)); // no location |
| EXPECT_FALSE(lint_waiver_.RuleIsWaivedOnLine("abc", 199)); // non-match loc |
| EXPECT_TRUE(lint_waiver_.RuleIsWaivedOnLine("abc", 299)); // matching loc |
| } |
| |
| TEST_F(LintWaiverBuilderTest, RegexToLinesSimple) { |
| const std::set<absl::string_view> active_rules{"rule-1"}; |
| const absl::string_view user_file = "filename"; |
| const absl::string_view cfg_file = "waive_file.config"; |
| |
| const absl::string_view cfg_regex = "waive --rule=rule-1 --regex=def"; |
| EXPECT_OK(ApplyExternalWaivers(active_rules, user_file, cfg_file, cfg_regex)); |
| |
| const absl::string_view file = "abc\ndef\nghi\n"; |
| const LineColumnMap line_map(file); |
| |
| lint_waiver_.RegexToLines(file, line_map); |
| |
| // The rule should be waived on the second line only (0-based indexing) |
| EXPECT_FALSE(lint_waiver_.RuleIsWaivedOnLine("rule-1", 0)); |
| EXPECT_TRUE(lint_waiver_.RuleIsWaivedOnLine("rule-1", 1)); |
| EXPECT_FALSE(lint_waiver_.RuleIsWaivedOnLine("rule-1", 2)); |
| } |
| |
| TEST_F(LintWaiverBuilderTest, RegexToLinesCatchAll) { |
| const std::set<absl::string_view> active_rules{"rule-1"}; |
| const absl::string_view user_file = "filename"; |
| const absl::string_view cfg_file = "waive_file.config"; |
| |
| const absl::string_view cfg_regex = "waive --rule=rule-1 --regex=\".*\""; |
| EXPECT_OK(ApplyExternalWaivers(active_rules, user_file, cfg_file, cfg_regex)); |
| |
| const absl::string_view file = "abc\ndef\nghi\n"; |
| const LineColumnMap line_map(file); |
| |
| lint_waiver_.RegexToLines(file, line_map); |
| |
| // The rule should be waived on all lines |
| EXPECT_TRUE(lint_waiver_.RuleIsWaivedOnLine("rule-1", 0)); |
| EXPECT_TRUE(lint_waiver_.RuleIsWaivedOnLine("rule-1", 1)); |
| EXPECT_TRUE(lint_waiver_.RuleIsWaivedOnLine("rule-1", 2)); |
| } |
| |
| TEST_F(LintWaiverBuilderTest, RegexToLinesMultipleMatches) { |
| const std::set<absl::string_view> active_rules{"rule-1"}; |
| const absl::string_view user_file = "filename"; |
| const absl::string_view cfg_file = "waive_file.config"; |
| |
| const absl::string_view cfg_regex = "waive --rule=rule-1 --regex=\"[0-9]\""; |
| EXPECT_OK(ApplyExternalWaivers(active_rules, user_file, cfg_file, cfg_regex)); |
| |
| const absl::string_view file = "abc1\ndef\ng2hi\n"; |
| const LineColumnMap line_map(file); |
| |
| lint_waiver_.RegexToLines(file, line_map); |
| |
| // The rule should be waived on all lines that contain any digits |
| EXPECT_TRUE(lint_waiver_.RuleIsWaivedOnLine("rule-1", 0)); |
| EXPECT_FALSE(lint_waiver_.RuleIsWaivedOnLine("rule-1", 1)); |
| EXPECT_TRUE(lint_waiver_.RuleIsWaivedOnLine("rule-1", 2)); |
| } |
| |
| } // namespace |
| } // namespace verible |