Skip to content

Commit 1ebf9c3

Browse files
committed
For LuaJIT, do not report an error for ULL integer literals that exceed int64.
1 parent 7f84964 commit 1ebf9c3

File tree

4 files changed

+96
-13
lines changed

4 files changed

+96
-13
lines changed

crates/emmylua_code_analysis/src/diagnostic/test/syntax_error_test.rs

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
#[cfg(test)]
22
mod test {
3-
use crate::{DiagnosticCode, VirtualWorkspace};
3+
use crate::{DiagnosticCode, EmmyrcLuaVersion, VirtualWorkspace};
44

55
#[test]
66
fn test_1() {
@@ -14,4 +14,18 @@ mod test {
1414
"#
1515
));
1616
}
17+
18+
#[test]
19+
fn test_luajit_ull() {
20+
let mut ws = VirtualWorkspace::new();
21+
let mut config = ws.get_emmyrc();
22+
config.runtime.version = EmmyrcLuaVersion::LuaJIT;
23+
ws.update_emmyrc(config);
24+
assert!(ws.check_code_for(
25+
DiagnosticCode::SyntaxError,
26+
r#"
27+
local d = 0xFFFFFFFFFFFFFFFFULL
28+
"#
29+
));
30+
}
1731
}

crates/emmylua_parser/src/syntax/node/token/number_analyzer.rs

Lines changed: 76 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,30 @@ enum IntegerRepr {
8686
Bin,
8787
}
8888

89-
pub fn int_token_value(token: &LuaSyntaxToken) -> Result<i64, LuaParseError> {
89+
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
90+
pub enum IntegerOrUnsigned {
91+
Int(i64),
92+
Uint(u64),
93+
}
94+
95+
impl IntegerOrUnsigned {
96+
pub fn is_unsigned(&self) -> bool {
97+
matches!(self, IntegerOrUnsigned::Uint(_))
98+
}
99+
100+
pub fn is_signed(&self) -> bool {
101+
matches!(self, IntegerOrUnsigned::Int(_))
102+
}
103+
104+
pub fn as_integer(&self) -> Option<i64> {
105+
match self {
106+
IntegerOrUnsigned::Int(value) => Some(*value),
107+
IntegerOrUnsigned::Uint(_) => None,
108+
}
109+
}
110+
}
111+
112+
pub fn int_token_value(token: &LuaSyntaxToken) -> Result<IntegerOrUnsigned, LuaParseError> {
90113
let text = token.text();
91114
let repr = if text.starts_with("0x") || text.starts_with("0X") {
92115
IntegerRepr::Hex
@@ -96,9 +119,24 @@ pub fn int_token_value(token: &LuaSyntaxToken) -> Result<i64, LuaParseError> {
96119
IntegerRepr::Normal
97120
};
98121

99-
let text = text.trim_end_matches(['u', 'l', 'U', 'L']);
122+
// 检查是否有无符号后缀并去除后缀
123+
let mut is_unsigned = false;
124+
let mut suffix_count = 0;
125+
for c in text.chars().rev() {
126+
if c == 'u' || c == 'U' {
127+
is_unsigned = true;
128+
suffix_count += 1;
129+
} else if c == 'l' || c == 'L' {
130+
suffix_count += 1;
131+
} else {
132+
break;
133+
}
134+
}
100135

101-
let value = match repr {
136+
let text = &text[..text.len() - suffix_count];
137+
138+
// 首先尝试解析为有符号整数
139+
let signed_value = match repr {
102140
IntegerRepr::Hex => {
103141
let text = &text[2..];
104142
i64::from_str_radix(text, 16)
@@ -109,18 +147,46 @@ pub fn int_token_value(token: &LuaSyntaxToken) -> Result<i64, LuaParseError> {
109147
}
110148
IntegerRepr::Normal => text.parse::<i64>(),
111149
};
112-
match value {
113-
Ok(value) => Ok(value),
150+
151+
match signed_value {
152+
Ok(value) => Ok(IntegerOrUnsigned::Int(value)),
114153
Err(e) => {
115154
let range = token.text_range();
116-
if *e.kind() == std::num::IntErrorKind::PosOverflow
117-
|| *e.kind() == std::num::IntErrorKind::NegOverflow
118-
{
155+
156+
// 如果是溢出错误,尝试解析为无符号整数
157+
if *e.kind() == std::num::IntErrorKind::PosOverflow && is_unsigned {
158+
let unsigned_value = match repr {
159+
IntegerRepr::Hex => {
160+
let text = &text[2..];
161+
u64::from_str_radix(text, 16)
162+
}
163+
IntegerRepr::Bin => {
164+
let text = &text[2..];
165+
u64::from_str_radix(text, 2)
166+
}
167+
IntegerRepr::Normal => text.parse::<u64>(),
168+
};
169+
170+
match unsigned_value {
171+
Ok(value) => Ok(IntegerOrUnsigned::Uint(value)),
172+
Err(_) => Err(LuaParseError::new(
173+
LuaParseErrorKind::SyntaxError,
174+
&t!(
175+
"The integer literal '%{text}' is too large to be represented",
176+
text = token.text()
177+
),
178+
range,
179+
)),
180+
}
181+
} else if matches!(
182+
*e.kind(),
183+
std::num::IntErrorKind::NegOverflow | std::num::IntErrorKind::PosOverflow
184+
) {
119185
Err(LuaParseError::new(
120186
LuaParseErrorKind::SyntaxError,
121187
&t!(
122188
"The integer literal '%{text}' is too large to be represented in type 'long'",
123-
text = text
189+
text = token.text()
124190
),
125191
range,
126192
))
@@ -129,7 +195,7 @@ pub fn int_token_value(token: &LuaSyntaxToken) -> Result<i64, LuaParseError> {
129195
LuaParseErrorKind::SyntaxError,
130196
&t!(
131197
"The integer literal '%{text}' is invalid, %{err}",
132-
text = text,
198+
text = token.text(),
133199
err = e
134200
),
135201
range,

crates/emmylua_parser/src/syntax/node/token/test.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -167,7 +167,7 @@ mod tests {
167167
fn $name() {
168168
let token = &get_token($code, LuaTokenKind::TkInt);
169169
let result = int_token_value(token);
170-
assert_eq!(result.unwrap(), $expected);
170+
assert_eq!(result.unwrap().as_integer().unwrap(), $expected);
171171
}
172172
};
173173
}

crates/emmylua_parser/src/syntax/node/token/tokens.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -151,7 +151,10 @@ impl LuaNumberToken {
151151
if !self.is_int() {
152152
return 0;
153153
}
154-
int_token_value(&self.token).unwrap_or_default()
154+
match int_token_value(&self.token) {
155+
Ok(value) => value.as_integer().unwrap_or(0),
156+
Err(_) => 0,
157+
}
155158
}
156159
}
157160

0 commit comments

Comments
 (0)