add more tests for malformed @for
test for capitalization of keywords, error messages, and regression tests for integer overflows resulting when `from` or `to` == `std::i32::MAX`
This commit is contained in:
parent
38a37a3997
commit
f33739aa0f
@ -156,7 +156,6 @@ impl<'a> Parser<'a> {
|
|||||||
|
|
||||||
pub(super) fn parse_for(&mut self) -> SassResult<Vec<Stmt>> {
|
pub(super) fn parse_for(&mut self) -> SassResult<Vec<Stmt>> {
|
||||||
self.whitespace_or_comment();
|
self.whitespace_or_comment();
|
||||||
// todo: test for error here
|
|
||||||
self.expect_char('$')?;
|
self.expect_char('$')?;
|
||||||
|
|
||||||
let var = self
|
let var = self
|
||||||
@ -236,14 +235,16 @@ impl<'a> Parser<'a> {
|
|||||||
self.whitespace_or_comment();
|
self.whitespace_or_comment();
|
||||||
let from_val = self.parse_value_from_vec(from_toks, true)?;
|
let from_val = self.parse_value_from_vec(from_toks, true)?;
|
||||||
let from = match from_val.node {
|
let from = match from_val.node {
|
||||||
Value::Dimension(Some(n), ..) => match n.to_integer().to_isize() {
|
Value::Dimension(Some(n), ..) => match n.to_i32() {
|
||||||
|
Some(std::i32::MAX) | None => {
|
||||||
|
return Err((format!("{} is not an int.", n), from_val.span).into())
|
||||||
|
}
|
||||||
Some(v) => v,
|
Some(v) => v,
|
||||||
None => return Err((format!("{} is not a int.", n), from_val.span).into()),
|
|
||||||
},
|
},
|
||||||
Value::Dimension(None, ..) => todo!(),
|
Value::Dimension(None, ..) => return Err(("NaN is not an int.", from_val.span).into()),
|
||||||
v => {
|
v => {
|
||||||
return Err((
|
return Err((
|
||||||
format!("{} is not an integer.", v.inspect(from_val.span)?),
|
format!("{} is not a number.", v.inspect(from_val.span)?),
|
||||||
from_val.span,
|
from_val.span,
|
||||||
)
|
)
|
||||||
.into())
|
.into())
|
||||||
@ -252,14 +253,16 @@ impl<'a> Parser<'a> {
|
|||||||
|
|
||||||
let to_val = self.parse_value(true, &|_| false)?;
|
let to_val = self.parse_value(true, &|_| false)?;
|
||||||
let to = match to_val.node {
|
let to = match to_val.node {
|
||||||
Value::Dimension(Some(n), ..) => match n.to_integer().to_isize() {
|
Value::Dimension(Some(n), ..) => match n.to_i32() {
|
||||||
|
Some(std::i32::MAX) | None => {
|
||||||
|
return Err((format!("{} is not an int.", n), to_val.span).into())
|
||||||
|
}
|
||||||
Some(v) => v,
|
Some(v) => v,
|
||||||
None => return Err((format!("{} is not a int.", n), to_val.span).into()),
|
|
||||||
},
|
},
|
||||||
Value::Dimension(None, ..) => todo!(),
|
Value::Dimension(None, ..) => return Err(("NaN is not an int.", from_val.span).into()),
|
||||||
v => {
|
v => {
|
||||||
return Err((
|
return Err((
|
||||||
format!("{} is not an integer.", v.to_css_string(to_val.span)?),
|
format!("{} is not a number.", v.to_css_string(to_val.span)?),
|
||||||
to_val.span,
|
to_val.span,
|
||||||
)
|
)
|
||||||
.into())
|
.into())
|
||||||
@ -269,12 +272,13 @@ impl<'a> Parser<'a> {
|
|||||||
self.expect_char('{')?;
|
self.expect_char('{')?;
|
||||||
|
|
||||||
let body = read_until_closing_curly_brace(self.toks)?;
|
let body = read_until_closing_curly_brace(self.toks)?;
|
||||||
self.toks.next();
|
|
||||||
|
self.expect_char('}')?;
|
||||||
|
|
||||||
let (mut x, mut y);
|
let (mut x, mut y);
|
||||||
// we can't use an inclusive range here
|
// we can't use an inclusive range here
|
||||||
#[allow(clippy::range_plus_one)]
|
#[allow(clippy::range_plus_one)]
|
||||||
let iter: &mut dyn Iterator<Item = isize> = if from < to {
|
let iter: &mut dyn Iterator<Item = i32> = if from < to {
|
||||||
x = from..(to + through);
|
x = from..(to + through);
|
||||||
&mut x
|
&mut x
|
||||||
} else {
|
} else {
|
||||||
|
@ -318,6 +318,42 @@ impl fmt::Debug for Number {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl ToPrimitive for Number {
|
||||||
|
fn to_u64(&self) -> Option<u64> {
|
||||||
|
match self {
|
||||||
|
Self::Small(n) => {
|
||||||
|
if !n.denom().is_one() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
n.to_u64()
|
||||||
|
}
|
||||||
|
Self::Big(n) => {
|
||||||
|
if !n.denom().is_one() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
n.to_u64()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn to_i64(&self) -> Option<i64> {
|
||||||
|
match self {
|
||||||
|
Self::Small(n) => {
|
||||||
|
if !n.denom().is_one() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
n.to_i64()
|
||||||
|
}
|
||||||
|
Self::Big(n) => {
|
||||||
|
if !n.denom().is_one() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
n.to_i64()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Display for Number {
|
impl Display for Number {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
let mut whole = self.to_integer().abs();
|
let mut whole = self.to_integer().abs();
|
||||||
|
48
tests/for.rs
48
tests/for.rs
@ -118,3 +118,51 @@ test!(
|
|||||||
" /**/ @for /**/ $i /**/ from /**/ 0 /**/ to /**/ 2 /**/ {} /**/ ",
|
" /**/ @for /**/ $i /**/ from /**/ 0 /**/ to /**/ 2 /**/ {} /**/ ",
|
||||||
"/**/\n/**/\n"
|
"/**/\n/**/\n"
|
||||||
);
|
);
|
||||||
|
test!(
|
||||||
|
uppercase_keywords,
|
||||||
|
"@for $i FROM 0 TO 2 {
|
||||||
|
@foo;
|
||||||
|
}",
|
||||||
|
"@foo;\n@foo;\n"
|
||||||
|
);
|
||||||
|
error!(
|
||||||
|
missing_keyword_from,
|
||||||
|
"@for $i fro", "Error: Expected \"from\"."
|
||||||
|
);
|
||||||
|
error!(missing_dollar_sign, "@for", "Error: expected \"$\".");
|
||||||
|
error!(
|
||||||
|
variable_missing_identifier,
|
||||||
|
"@for $", "Error: Expected identifier."
|
||||||
|
);
|
||||||
|
error!(
|
||||||
|
from_value_is_decimal,
|
||||||
|
"@for $i from 0.5 to 2 {}", "Error: 0.5 is not an int."
|
||||||
|
);
|
||||||
|
error!(
|
||||||
|
to_value_is_decimal,
|
||||||
|
"@for $i from 0 to 1.5 {}", "Error: 1.5 is not an int."
|
||||||
|
);
|
||||||
|
error!(
|
||||||
|
from_value_is_non_numeric,
|
||||||
|
"@for $i from red to 2 {}", "Error: red is not a number."
|
||||||
|
);
|
||||||
|
error!(
|
||||||
|
to_value_is_non_numeric,
|
||||||
|
"@for $i from 0 to red {}", "Error: red is not a number."
|
||||||
|
);
|
||||||
|
error!(
|
||||||
|
through_i32_max,
|
||||||
|
"@for $i from 0 through 2147483647 {}", "Error: 2147483647 is not an int."
|
||||||
|
);
|
||||||
|
error!(
|
||||||
|
from_i32_max,
|
||||||
|
"@for $i from 2147483647 through 0 {}", "Error: 2147483647 is not an int."
|
||||||
|
);
|
||||||
|
error!(
|
||||||
|
from_nan,
|
||||||
|
"@for $i from (0/0) through 0 {}", "Error: NaN is not an int."
|
||||||
|
);
|
||||||
|
error!(
|
||||||
|
to_nan,
|
||||||
|
"@for $i from 0 through (0/0) {}", "Error: NaN is not an int."
|
||||||
|
);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user