Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions Analysis/src/Linter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3107,6 +3107,14 @@ class LintIntegerParsing : AstVisitor
"Hexadecimal number literal exceeded available precision and was truncated to 2^64"
);
break;
case ConstantNumberParseResult::OctOverflow:
emitWarning(
*context,
LintWarning::Code_IntegerParsing,
node->location,
"Octal number literal exceeded available precision and was truncated to 2^64"
);
break;
}

return true;
Expand Down
1 change: 1 addition & 0 deletions Ast/include/Luau/Ast.h
Original file line number Diff line number Diff line change
Expand Up @@ -307,6 +307,7 @@ enum class ConstantNumberParseResult
Malformed,
BinOverflow,
HexOverflow,
OctOverflow,
};

class AstExprConstantNumber : public AstExpr
Expand Down
15 changes: 13 additions & 2 deletions Ast/src/Parser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3174,7 +3174,7 @@ AstExpr* Parser::parseAssertionExpr()

static ConstantNumberParseResult parseInteger(double& result, const char* data, int base)
{
LUAU_ASSERT(base == 2 || base == 16);
LUAU_ASSERT(base == 2 || base == 16 || base == 8);

char* end = nullptr;
unsigned long long value = strtoull(data, &end, base);
Expand All @@ -3192,7 +3192,14 @@ static ConstantNumberParseResult parseInteger(double& result, const char* data,
value = strtoull(data, &end, base);

if (errno == ERANGE)
return base == 2 ? ConstantNumberParseResult::BinOverflow : ConstantNumberParseResult::HexOverflow;
{
if (base == 2)
return ConstantNumberParseResult::BinOverflow;
else if (base == 8)
return ConstantNumberParseResult::OctOverflow;
else
return ConstantNumberParseResult::HexOverflow;
}
}

if (value >= (1ull << 53) && static_cast<unsigned long long>(result) != value)
Expand All @@ -3207,6 +3214,10 @@ static ConstantNumberParseResult parseDouble(double& result, const char* data)
if (data[0] == '0' && (data[1] == 'b' || data[1] == 'B') && data[2])
return parseInteger(result, data + 2, 2);

// octal literal
if (data[0] == '0' && (data[1] == 'o' || data[1] == 'O') && data[2])
return parseInteger(result, data + 2, 8);

// hexadecimal literal
if (data[0] == '0' && (data[1] == 'x' || data[1] == 'X') && data[2])
return parseInteger(result, data, 16); // pass in '0x' prefix, it's handled by 'strtoull'
Expand Down
4 changes: 3 additions & 1 deletion tests/Linter.test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2235,11 +2235,13 @@ TEST_CASE_FIXTURE(Fixture, "IntegerParsing")
LintResult result = lint(R"(
local _ = 0b10000000000000000000000000000000000000000000000000000000000000000
local _ = 0x10000000000000000
local _ = 0o2000000000000000000000
)");

REQUIRE(2 == result.warnings.size());
REQUIRE(3 == result.warnings.size());
CHECK_EQ(result.warnings[0].text, "Binary number literal exceeded available precision and was truncated to 2^64");
CHECK_EQ(result.warnings[1].text, "Hexadecimal number literal exceeded available precision and was truncated to 2^64");
CHECK_EQ(result.warnings[2].text, "Octal number literal exceeded available precision and was truncated to 2^64");
}

TEST_CASE_FIXTURE(Fixture, "IntegerParsingDecimalImprecise")
Expand Down
24 changes: 22 additions & 2 deletions tests/Parser.test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -701,6 +701,19 @@ TEST_CASE_FIXTURE(Fixture, "parse_numbers_binary")
CHECK_EQ(str->list.data[3]->as<AstExprConstantNumber>()->value, double(ULLONG_MAX));
}

TEST_CASE_FIXTURE(Fixture, "parse_numbers_octal")
{
AstStat* stat = parse("return 0o1, 0o0, 0o52, 0o1777777777777777777777");
REQUIRE(stat != nullptr);

AstStatReturn* str = stat->as<AstStatBlock>()->body.data[0]->as<AstStatReturn>();
CHECK(str->list.size == 4);
CHECK_EQ(str->list.data[0]->as<AstExprConstantNumber>()->value, 1);
CHECK_EQ(str->list.data[1]->as<AstExprConstantNumber>()->value, 0);
CHECK_EQ(str->list.data[2]->as<AstExprConstantNumber>()->value, 42);
CHECK_EQ(str->list.data[3]->as<AstExprConstantNumber>()->value, double(ULLONG_MAX));
}

TEST_CASE_FIXTURE(Fixture, "parse_numbers_error")
{
matchParseError("return 0b123", "Malformed number");
Expand All @@ -709,6 +722,7 @@ TEST_CASE_FIXTURE(Fixture, "parse_numbers_error")
matchParseError("return 0x0x123", "Malformed number");
matchParseError("return 0xffffffffffffffffffffllllllg", "Malformed number");
matchParseError("return 0x0xffffffffffffffffffffffffffff", "Malformed number");
matchParseError("return 0o898", "Malformed number");
}

TEST_CASE_FIXTURE(Fixture, "break_return_not_last_error")
Expand Down Expand Up @@ -1694,14 +1708,15 @@ return
.5,
12_34_56,
0x1234,
0b010101
0b010101,
0o777
)");

REQUIRE(stat != nullptr);

AstStatReturn* ret = stat->body.data[0]->as<AstStatReturn>();
REQUIRE(ret != nullptr);
CHECK(ret->list.size == 6);
CHECK(ret->list.size == 7);

AstExprConstantNumber* num;

Expand All @@ -1728,6 +1743,11 @@ return
num = ret->list.data[5]->as<AstExprConstantNumber>();
REQUIRE(num != nullptr);
CHECK_EQ(num->value, 0x15);

num = ret->list.data[6]->as<AstExprConstantNumber>();
REQUIRE(num != nullptr);
CHECK_EQ(num->value, 511);

}

TEST_CASE_FIXTURE(Fixture, "end_extent_of_functions_unions_and_intersections")
Expand Down
6 changes: 6 additions & 0 deletions tests/Transpiler.test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -582,6 +582,12 @@ TEST_CASE("binary_numbers")
CHECK_EQ(code, transpile(code).code);
}

TEST_CASE("octal_numbers")
{
const std::string code = R"( local a = 0o777 )";
CHECK_EQ(code, transpile(code).code);
}

TEST_CASE("single_quoted_strings")
{
const std::string code = R"( local a = 'hello world' )";
Expand Down
Loading