blob: 0e3cf8d0c7eaceb503e784e66b0f7ccd8796668c [file] [log] [blame]
// 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