| // Copyright 2017-2021 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/checkers/truncated_numeric_literal_rule.h" | |
| #include <initializer_list> | |
| #include "common/analysis/linter_test_utils.h" | |
| #include "common/analysis/syntax_tree_linter_test_utils.h" | |
| #include "common/text/symbol.h" | |
| #include "gtest/gtest.h" | |
| #include "verilog/CST/verilog_nonterminals.h" | |
| #include "verilog/analysis/verilog_analyzer.h" | |
| #include "verilog/parser/verilog_token_enum.h" | |
| namespace verilog { | |
| namespace analysis { | |
| namespace { | |
| using verible::LintTestCase; | |
| using verible::RunConfiguredLintTestCases; | |
| TEST(TruncatedNumericLiteralRuleTest, TruncatedBinaryNumbers) { | |
| constexpr int kToken = TK_BinDigits; | |
| const std::initializer_list<LintTestCase> kTestCases = { | |
| {""}, | |
| {"localparam x = 0;"}, | |
| {"localparam x = 1;"}, | |
| {"localparam x = 1'b?;"}, | |
| {"localparam x = 1'bz;"}, | |
| {"localparam x = 1'bx;"}, | |
| {"localparam x = 1'b", {kToken, "zz"}, ";"}, | |
| {"localparam x = 1'b", {kToken, "xx"}, ";"}, | |
| {"localparam x = 1'b", {kToken, "??"}, ";"}, | |
| // Not doing macro expansion yet, but we know that it uses at least 1 bit | |
| {"localparam x = 1'b`SOME_MACRO;"}, | |
| {"localparam x = 0'b", {MacroIdentifier, "`SOME_MACRO"}, ";"}, | |
| {"localparam x = 0'b", {kToken, "0"}, ";"}, // Even a zero uses one bit | |
| {"localparam x = 0'b", {kToken, "1"}, ";"}, | |
| {"localparam x = 3'b111;"}, | |
| {"localparam x = 3'b00000111;"}, | |
| {"localparam x = 3'b11_1;"}, | |
| {"localparam x = 3'b", {kToken, "1111"}, ";"}, | |
| {"localparam x = 3'b", {kToken, "00001111"}, ";"}, | |
| }; | |
| RunConfiguredLintTestCases<VerilogAnalyzer, TruncatedNumericLiteralRule>( | |
| kTestCases, ""); | |
| } | |
| TEST(TruncatedNumericLiteralRuleTest, TooShortHexNumbers) { | |
| constexpr int kToken = TK_HexDigits; | |
| const std::string superlong(1001, 'F'); | |
| const std::string exp = absl::StrCat("localparam x = 4004'h", superlong, ";"); | |
| const absl::string_view good_long_expression = exp; | |
| const std::initializer_list<LintTestCase> kTestCases = { | |
| {"localparam x = 1'h1;"}, | |
| {"localparam x = 0'h", {kToken, "0"}, ";"}, | |
| {"localparam x = 0'h", {kToken, "1"}, ";"}, | |
| {"localparam x = 4'h?;"}, | |
| {"localparam x = 3'h?;"}, | |
| {"localparam x = 1'h?;"}, // ? can mean anything 1..4 bits, all ok. | |
| {"localparam x = 4'h", {kToken, "??"}, ";"}, // Two digits exceed 4 bit | |
| {"localparam x = 5'h??;"}, // Minimum that two digits use is 5 bits | |
| // Same spiel with z and x | |
| {"localparam x = 3'hz;"}, | |
| {"localparam x = 4'h", {kToken, "zz"}, ";"}, | |
| {"localparam x = 5'hzz;"}, | |
| {"localparam x = 3'hx;"}, | |
| {"localparam x = 4'h", {kToken, "xx"}, ";"}, | |
| {"localparam x = 5'hxx;"}, | |
| {"localparam x = 4'h", {kToken, "xz"}, ";"}, // or a mix of these | |
| // Not doing macro expansion yet, but we know that it uses at least 1 bit | |
| {"localparam x = 1'h`SOME_MACRO;"}, | |
| {"localparam x = 0'h", {MacroIdentifier, "`SOME_MACRO"}, ";"}, | |
| {"localparam x = 4'hf;"}, | |
| {"localparam x = 6'h2f;"}, | |
| {"localparam x = 6'h2_f;"}, | |
| {"localparam x = 6'h0000000002f;"}, // MSB fits ? good. | |
| {"localparam x = 5'h", {kToken, "2f"}, ";"}, | |
| {"localparam x = 5'h", {kToken, "00000002f"}, ";"}, | |
| {"localparam x = 5'h1f;"}, | |
| {"localparam x = 16'habcd;"}, | |
| {"localparam x = 15'h", {kToken, "abcd"}, ";"}, | |
| {"localparam x = 16'hab_cd;"}, | |
| {good_long_expression}, | |
| {"localparam x = 4003'h", {kToken, superlong}, ";"}, | |
| {"localparam x = -16'hffff;"}, // TODO: should we complain about -(-1)? | |
| }; | |
| RunConfiguredLintTestCases<VerilogAnalyzer, TruncatedNumericLiteralRule>( | |
| kTestCases, ""); | |
| } | |
| TEST(TruncatedNumericLiteralRuleTest, TruncatedOctalNumbers) { | |
| constexpr int kToken = TK_OctDigits; | |
| const std::initializer_list<LintTestCase> kTestCases = { | |
| {"localparam x = 1'o1;"}, | |
| {"localparam x = 3'o7;"}, | |
| {"localparam x = 2'o3;"}, | |
| {"localparam x = 2'o", {kToken, "4"}, ";"}, | |
| {"localparam x = 2'o", {kToken, "7"}, ";"}, | |
| {"localparam x = 8'o377;"}, | |
| {"localparam x = 8'o000000377;"}, | |
| {"localparam x = 8'o", {kToken, "477"}, ";"}, | |
| }; | |
| RunConfiguredLintTestCases<VerilogAnalyzer, TruncatedNumericLiteralRule>( | |
| kTestCases, ""); | |
| } | |
| TEST(TruncatedNumericLiteralRuleTest, TruncatedDecimalNumbers) { | |
| constexpr int kToken = TK_DecDigits; | |
| // A number longer than can be parsed as double to force fallback of fallback. | |
| const std::string superlong(500, '9'); | |
| const std::string exp = absl::StrCat("localparam x = 1661'd", superlong, ";"); | |
| const absl::string_view good_long_expression = exp; | |
| const std::initializer_list<LintTestCase> kTestCases = { | |
| {"localparam x = 1'd1;"}, | |
| {"localparam x = 0'd", {kToken, "0"}, ";"}, | |
| {"localparam x = 0'd", {kToken, "1"}, ";"}, | |
| {"localparam x = 1'dz;"}, // Not dealing with special digits | |
| {"localparam x = 1'dx;"}, | |
| {"localparam x = 1'd?;"}, | |
| // Negative numbers: we really only look that the literal bits fit | |
| {"localparam x = -4'd15;"}, | |
| {"localparam x = -4'd", {kToken, "16"}, ";"}, | |
| // 16 bit boundary | |
| {"localparam x = 16'd65535;"}, | |
| {"localparam x = 16'd", {kToken, "65536"}, ";"}, | |
| {"localparam x = 17'd65536;"}, | |
| // TODO: should we warn about implicit negative numbers ? | |
| {"localparam x = -16'd65535;"}, | |
| // 32 Bit. | |
| {"localparam x = 32'd4294967295;"}, // 2^32-1 | |
| {"localparam x = 32'd", {kToken, "4294967296"}, ";"}, // too long | |
| {"localparam x = 33'd4294967296;"}, // needs one more bit | |
| // 64 bit | |
| {"localparam x = 64'd18446744073709551615;"}, // 2^64-1 | |
| {"localparam x = 64'd", {kToken, "18446744073709551616"}, ";"}, | |
| {"localparam x = 65'd18446744073709551616;"}, | |
| // 2^100-1. | |
| {"localparam x = 100'd1267650600228229401496703205375;"}, | |
| {"localparam x = 100'd", // Value +1 doesn't fit in 100 bits. | |
| {kToken, "1267650600228229401496703205376"}, | |
| ";"}, | |
| // 2^128-1 | |
| {"localparam x = 128'd340282366920938463463374607431768211455;"}, | |
| {"localparam x = 127'd", // ... but doesn't fit in 127 bits. | |
| {kToken, "340282366920938463463374607431768211455"}, | |
| ";"}, | |
| /* | |
| * For larger than 128 bit numbers, we only do best effort, but making | |
| * sure to not give false positives, only false negatives. | |
| * | |
| * In practice, super long decimals are probably not something someone | |
| * would use in code anyway, so best effort is probably good enough. | |
| */ | |
| // This number (2^128, so 1<<129) is dealt with by the heuristic, | |
| // and accurately detected as not fitting into 128 bits. | |
| // The heuristic would estimate 128 bits, but we also know that | |
| // we need to be at least 129 bits if we entered the heuristic realm :) | |
| {"localparam x = 128'd", | |
| {kToken, "340282366920938463463374607431768211456"}, | |
| ";"}, | |
| {"localparam x = 129'd340282366920938463463374607431768211456;"}, | |
| /* larger numbers will only be somewhat accurate and we underestimate | |
| * the number of bits, so there will be some non-reported issues, | |
| * false negatives. | |
| */ | |
| // This number, 2^145-1 is accepted with 145 bit precision. | |
| {"localparam x = 145'd44601490397061246283071436545296723011960831;"}, | |
| // We correctly recognize that this can't be represented in 144 bits. | |
| {"localparam x = 144'd", | |
| {kToken, "44601490397061246283071436545296723011960831"}, | |
| ";"}, | |
| // However, due to our heuristic, we don't actually recognize that this | |
| // one-more-bit number 2^145 will need 146 bits... | |
| {"localparam x = 145'd44601490397061246283071436545296723011960832;"}, | |
| // In fact, we don't really notice any change beyond the first 15 or 16 | |
| // first digits or so (as we internally parse it as double) and still | |
| // accept this as 145 bits even though it really needs 146 bits by now. | |
| // Erring on the side of not complaining. | |
| // And who describes huge numbers in decimal anyway... | |
| {"localparam x = 145'd", | |
| {kToken, "44601490397061700000000000000000000000000000"}, | |
| //----------------------^ this needed to change from 2 to >= 6 | |
| ";"}, | |
| // This one should be ceil(log(10^500-1)/log(2)) = 1661 bits | |
| // long, but we only start to complain below 1658 bits. | |
| {good_long_expression}, | |
| {"localparam x = 1657'd", {kToken, superlong}, ";"}, | |
| }; | |
| RunConfiguredLintTestCases<VerilogAnalyzer, TruncatedNumericLiteralRule>( | |
| kTestCases, ""); | |
| } | |
| } // namespace | |
| } // namespace analysis | |
| } // namespace verilog |