improve span information in values

This commit is contained in:
ConnorSkees 2020-05-21 11:48:38 -04:00
parent 0c144e22ba
commit 709b95d035
4 changed files with 163 additions and 173 deletions

View File

@ -96,9 +96,9 @@ use crate::style::Style;
pub use crate::stylesheet::StyleSheet; pub use crate::stylesheet::StyleSheet;
pub(crate) use crate::token::Token; pub(crate) use crate::token::Token;
use crate::utils::{ use crate::utils::{
devour_whitespace, eat_comment, eat_ident, eat_variable_value, devour_whitespace, eat_comment, eat_ident, eat_variable_value, peek_ident_no_interpolation,
read_until_closing_curly_brace, read_until_closing_paren, read_until_newline, VariableDecl, peek_whitespace, read_until_closing_curly_brace, read_until_closing_paren, read_until_newline,
peek_whitespace, peek_ident_no_interpolation VariableDecl,
}; };
use crate::value::Value; use crate::value::Value;
@ -290,7 +290,8 @@ pub(crate) fn eat_expr<I: Iterator<Item = Token>>(
let whitespace = peek_whitespace(toks); let whitespace = peek_whitespace(toks);
if toks.peek().ok_or(("expected \":\".", name.span))?.kind == ':' { if toks.peek().ok_or(("expected \":\".", name.span))?.kind == ':' {
toks.take(name.node.chars().count() + whitespace + 1).for_each(drop); toks.take(name.node.chars().count() + whitespace + 1)
.for_each(drop);
devour_whitespace(toks); devour_whitespace(toks);
let VariableDecl { let VariableDecl {
val, val,

View File

@ -137,16 +137,16 @@ struct IntermediateValueIterator<'a, I: Iterator<Item = Token>> {
} }
impl<'a, I: Iterator<Item = Token>> Iterator for IntermediateValueIterator<'a, I> { impl<'a, I: Iterator<Item = Token>> Iterator for IntermediateValueIterator<'a, I> {
type Item = SassResult<IntermediateValue>; type Item = SassResult<Spanned<IntermediateValue>>;
fn next(&mut self) -> Option<Self::Item> { fn next(&mut self) -> Option<Self::Item> {
Value::parse_intermediate_value(self.toks, self.scope, self.super_selector) Value::parse_intermediate_value(self.toks, self.scope, self.super_selector)
} }
} }
impl IsWhitespace for SassResult<IntermediateValue> { impl IsWhitespace for SassResult<Spanned<IntermediateValue>> {
fn is_whitespace(&self) -> bool { fn is_whitespace(&self) -> bool {
match self { match self {
Ok(v) => v.is_whitespace(), Ok(v) => v.node.is_whitespace(),
_ => false, _ => false,
} }
} }
@ -154,14 +154,20 @@ impl IsWhitespace for SassResult<IntermediateValue> {
#[derive(Clone, Debug, Eq, PartialEq)] #[derive(Clone, Debug, Eq, PartialEq)]
enum IntermediateValue { enum IntermediateValue {
Value(Spanned<Value>), Value(Value),
Op(Spanned<Op>), Op(Op),
Bracketed(Spanned<Vec<Token>>), Bracketed(Vec<Token>),
Paren(Spanned<Vec<Token>>), Paren(Vec<Token>),
Comma, Comma,
Whitespace, Whitespace,
} }
impl IntermediateValue {
fn span(self, span: Span) -> Spanned<Self> {
Spanned { node: self, span }
}
}
impl IsWhitespace for IntermediateValue { impl IsWhitespace for IntermediateValue {
fn is_whitespace(&self) -> bool { fn is_whitespace(&self) -> bool {
if self == &IntermediateValue::Whitespace { if self == &IntermediateValue::Whitespace {
@ -337,23 +343,24 @@ fn single_value<I: Iterator<Item = Token>>(
super_selector: &Selector, super_selector: &Selector,
span: Span, span: Span,
) -> SassResult<Spanned<Value>> { ) -> SassResult<Spanned<Value>> {
Ok(match iter.next().ok_or(("Expected expression.", span))?? { let next = iter.next().ok_or(("Expected expression.", span))??;
IntermediateValue::Value(v) => v, Ok(match next.node {
IntermediateValue::Op(op) => match op.node { IntermediateValue::Value(v) => v.span(next.span),
IntermediateValue::Op(op) => match op {
Op::Minus => { Op::Minus => {
devour_whitespace(iter); devour_whitespace(iter);
let val = single_value(iter, scope, super_selector, span)?; let val = single_value(iter, scope, super_selector, span)?;
Spanned { Spanned {
node: val.node.neg(val.span)?, node: val.node.neg(val.span)?,
span: op.span.merge(val.span), span: next.span.merge(val.span),
} }
} }
Op::Not => { Op::Not => {
devour_whitespace(iter); devour_whitespace(iter);
let val = single_value(iter, scope, super_selector, span)?; let val = single_value(iter, scope, super_selector, span)?;
Spanned { Spanned {
node: Value::UnaryOp(op.node, Box::new(val.node)), node: Value::UnaryOp(Op::Not, Box::new(val.node)),
span: op.span.merge(val.span), span: next.span.merge(val.span),
} }
} }
Op::Plus => { Op::Plus => {
@ -368,7 +375,7 @@ fn single_value<I: Iterator<Item = Token>>(
format!("/{}", val.node.to_css_string(val.span)?), format!("/{}", val.node.to_css_string(val.span)?),
QuoteKind::None, QuoteKind::None,
), ),
span: op.span.merge(val.span), span: next.span.merge(val.span),
} }
} }
_ => todo!(), _ => todo!(),
@ -376,7 +383,7 @@ fn single_value<I: Iterator<Item = Token>>(
IntermediateValue::Whitespace => unreachable!(), IntermediateValue::Whitespace => unreachable!(),
IntermediateValue::Comma => return Err(("Expected expression.", span).into()), IntermediateValue::Comma => return Err(("Expected expression.", span).into()),
IntermediateValue::Bracketed(t) => { IntermediateValue::Bracketed(t) => {
let v = Value::from_vec(t.node, scope, super_selector)?; let v = Value::from_vec(t, scope, super_selector)?;
match v.node { match v.node {
Value::List(v, sep, Brackets::None) => Value::List(v, sep, Brackets::Bracketed), Value::List(v, sep, Brackets::None) => Value::List(v, sep, Brackets::Bracketed),
v => Value::List(vec![v], ListSeparator::Space, Brackets::Bracketed), v => Value::List(vec![v], ListSeparator::Space, Brackets::Bracketed),
@ -384,7 +391,7 @@ fn single_value<I: Iterator<Item = Token>>(
.span(v.span) .span(v.span)
} }
IntermediateValue::Paren(t) => { IntermediateValue::Paren(t) => {
let val = Value::from_vec(t.node, scope, super_selector)?; let val = Value::from_vec(t, scope, super_selector)?;
Spanned { Spanned {
node: Value::Paren(Box::new(val.node)), node: Value::Paren(Box::new(val.node)),
span: val.span, span: val.span,
@ -414,17 +421,21 @@ impl Value {
} }
.peekmore(); .peekmore();
while let Some(val) = iter.next() { while let Some(val) = iter.next() {
match val? { let val = val?;
match val.node {
IntermediateValue::Value(v) => { IntermediateValue::Value(v) => {
last_was_whitespace = false; last_was_whitespace = false;
space_separated.push(v) space_separated.push(v.span(val.span))
} }
IntermediateValue::Op(op) => { IntermediateValue::Op(op) => {
eat_op( eat_op(
&mut iter, &mut iter,
scope, scope,
super_selector, super_selector,
op, Spanned {
node: op,
span: val.span,
},
&mut space_separated, &mut space_separated,
last_was_whitespace, last_was_whitespace,
)?; )?;
@ -435,10 +446,14 @@ impl Value {
} }
IntermediateValue::Comma => { IntermediateValue::Comma => {
last_was_whitespace = false; last_was_whitespace = false;
if space_separated.len() == 1 { if space_separated.len() == 1 {
comma_separated.push(space_separated.pop().unwrap()); comma_separated.push(space_separated.pop().unwrap());
} else { } else {
let mut span = space_separated.get(0).unwrap().span; let mut span = space_separated
.get(0)
.ok_or(("Expected expression.", val.span))?
.span;
comma_separated.push( comma_separated.push(
Value::List( Value::List(
mem::take(&mut space_separated) mem::take(&mut space_separated)
@ -457,26 +472,32 @@ impl Value {
} }
IntermediateValue::Bracketed(t) => { IntermediateValue::Bracketed(t) => {
last_was_whitespace = false; last_was_whitespace = false;
if t.node.is_empty() { if t.is_empty() {
space_separated.push( space_separated.push(
Value::List(Vec::new(), ListSeparator::Space, Brackets::Bracketed) Value::List(Vec::new(), ListSeparator::Space, Brackets::Bracketed)
.span(t.span), .span(val.span),
); );
continue; continue;
} }
space_separated.push( space_separated.push(match Value::from_vec(t, scope, super_selector)?.node {
match Value::from_vec(t.node, scope, super_selector)?.node {
Value::List(v, sep, Brackets::None) => { Value::List(v, sep, Brackets::None) => {
Value::List(v, sep, Brackets::Bracketed).span(t.span) Value::List(v, sep, Brackets::Bracketed).span(val.span)
} }
v => Value::List(vec![v], ListSeparator::Space, Brackets::Bracketed) v => Value::List(vec![v], ListSeparator::Space, Brackets::Bracketed)
.span(t.span), .span(val.span),
}, })
)
} }
IntermediateValue::Paren(t) => { IntermediateValue::Paren(t) => {
last_was_whitespace = false; last_was_whitespace = false;
parse_paren(t, scope, super_selector, &mut space_separated)?; parse_paren(
Spanned {
node: t,
span: val.span,
},
scope,
super_selector,
&mut space_separated,
)?;
} }
} }
} }
@ -525,7 +546,7 @@ impl Value {
scope: &Scope, scope: &Scope,
super_selector: &Selector, super_selector: &Selector,
span_before: Span, span_before: Span,
) -> SassResult<IntermediateValue> { ) -> SassResult<Spanned<IntermediateValue>> {
let Spanned { node: mut s, span } = eat_ident(toks, scope, super_selector, span_before)?; let Spanned { node: mut s, span } = eat_ident(toks, scope, super_selector, span_before)?;
let lower = s.to_ascii_lowercase(); let lower = s.to_ascii_lowercase();
@ -535,10 +556,10 @@ impl Value {
toks.next(); toks.next();
s.push(':'); s.push(':');
s.push_str(&eat_progid(toks, scope, super_selector)?); s.push_str(&eat_progid(toks, scope, super_selector)?);
return Ok(IntermediateValue::Value(Spanned { return Ok(Spanned {
node: Value::Ident(s, QuoteKind::None), node: IntermediateValue::Value(Value::Ident(s, QuoteKind::None)),
span, span,
})); });
} }
if let Some(Token { kind: '(', .. }) = toks.peek() { if let Some(Token { kind: '(', .. }) = toks.peek() {
@ -550,10 +571,12 @@ impl Value {
Ok(f) => f, Ok(f) => f,
Err(_) => match GLOBAL_FUNCTIONS.get(s.replace('_', "-").as_str()) { Err(_) => match GLOBAL_FUNCTIONS.get(s.replace('_', "-").as_str()) {
Some(f) => { Some(f) => {
return Ok(IntermediateValue::Value(Spanned { return Ok(IntermediateValue::Value(f.0(
node: f.0(eat_call_args(toks)?, scope, super_selector)?, eat_call_args(toks)?,
span, scope,
})) super_selector,
)?)
.span(span))
} }
None => { None => {
match lower.as_str() { match lower.as_str() {
@ -573,59 +596,56 @@ impl Value {
&eat_call_args(toks)?.to_css_string(scope, super_selector)?, &eat_call_args(toks)?.to_css_string(scope, super_selector)?,
), ),
} }
return Ok(IntermediateValue::Value(Spanned { return Ok(
node: Value::Ident(s, QuoteKind::None), IntermediateValue::Value(Value::Ident(s, QuoteKind::None)).span(span)
span, );
}));
} }
}, },
}; };
return Ok(IntermediateValue::Value( return Ok(IntermediateValue::Value(func.eval(
func.eval(eat_call_args(toks)?, scope, super_selector)? eat_call_args(toks)?,
.span(span), scope,
)); super_selector,
)?)
.span(span));
} }
if let Some(c) = NAMED_COLORS.get_by_name(&lower.as_str()) { if let Some(c) = NAMED_COLORS.get_by_name(&lower.as_str()) {
return Ok(IntermediateValue::Value(Spanned { return Ok(IntermediateValue::Value(Value::Color(Box::new(Color::new(
node: Value::Color(Box::new(Color::new(c[0], c[1], c[2], c[3], s))), c[0], c[1], c[2], c[3], s,
span, ))))
})); .span(span));
} }
Ok(match lower.as_str() { Ok(match lower.as_str() {
"true" => IntermediateValue::Value(Value::True.span(span)), "true" => IntermediateValue::Value(Value::True),
"false" => IntermediateValue::Value(Value::False.span(span)), "false" => IntermediateValue::Value(Value::False),
"null" => IntermediateValue::Value(Value::Null.span(span)), "null" => IntermediateValue::Value(Value::Null),
"not" => IntermediateValue::Op(Spanned { "not" => IntermediateValue::Op(Op::Not),
node: Op::Not, "and" => IntermediateValue::Op(Op::And),
span, "or" => IntermediateValue::Op(Op::Or),
}), _ => IntermediateValue::Value(Value::Ident(s, QuoteKind::None)),
"and" => IntermediateValue::Op(Spanned { }
node: Op::And, .span(span))
span,
}),
"or" => IntermediateValue::Op(Spanned { node: Op::Or, span }),
_ => IntermediateValue::Value(Spanned {
node: Value::Ident(s, QuoteKind::None),
span,
}),
})
} }
fn parse_intermediate_value<I: Iterator<Item = Token>>( fn parse_intermediate_value<I: Iterator<Item = Token>>(
toks: &mut PeekMoreIterator<I>, toks: &mut PeekMoreIterator<I>,
scope: &Scope, scope: &Scope,
super_selector: &Selector, super_selector: &Selector,
) -> Option<SassResult<IntermediateValue>> { ) -> Option<SassResult<Spanned<IntermediateValue>>> {
if devour_whitespace(toks) {
return Some(Ok(IntermediateValue::Whitespace));
}
let (kind, span) = match toks.peek() { let (kind, span) = match toks.peek() {
Some(v) => (v.kind, v.pos()), Some(v) => (v.kind, v.pos()),
None => return None, None => return None,
}; };
if devour_whitespace(toks) {
return Some(Ok(Spanned {
node: IntermediateValue::Whitespace,
span,
}));
}
let next_is_hypen = |toks: &mut PeekMoreIterator<I>| { let next_is_hypen = |toks: &mut PeekMoreIterator<I>| {
toks.peek_forward(1).is_some() toks.peek_forward(1).is_some()
&& matches!(toks.peek().unwrap().kind, '-' | '_' | 'a'..='z' | 'A'..='Z') && matches!(toks.peek().unwrap().kind, '-' | '_' | 'a'..='z' | 'A'..='Z')
@ -670,26 +690,32 @@ impl Value {
let n = if val.dec_len == 0 { let n = if val.dec_len == 0 {
if val.num.len() <= 18 && val.times_ten.is_empty() { if val.num.len() <= 18 && val.times_ten.is_empty() {
let n = Rational64::new_raw(val.num.parse::<i64>().unwrap(), 1); let n = Rational64::new_raw(val.num.parse::<i64>().unwrap(), 1);
return Some(Ok(IntermediateValue::Value( return Some(Ok(IntermediateValue::Value(Value::Dimension(
Value::Dimension(Number::new_machine(n), unit).span(span), Number::new_machine(n),
))); unit,
))
.span(span)));
} }
BigRational::new_raw(val.num.parse::<BigInt>().unwrap(), BigInt::one()) BigRational::new_raw(val.num.parse::<BigInt>().unwrap(), BigInt::one())
} else { } else {
if val.num.len() <= 18 && val.times_ten.is_empty() { if val.num.len() <= 18 && val.times_ten.is_empty() {
let n = let n =
Rational64::new(val.num.parse::<i64>().unwrap(), pow(10, val.dec_len)); Rational64::new(val.num.parse::<i64>().unwrap(), pow(10, val.dec_len));
return Some(Ok(IntermediateValue::Value( return Some(Ok(IntermediateValue::Value(Value::Dimension(
Value::Dimension(Number::new_machine(n), unit).span(span), Number::new_machine(n),
))); unit,
))
.span(span)));
} }
BigRational::new(val.num.parse().unwrap(), pow(BigInt::from(10), val.dec_len)) BigRational::new(val.num.parse().unwrap(), pow(BigInt::from(10), val.dec_len))
}; };
if val.times_ten.is_empty() { if val.times_ten.is_empty() {
return Some(Ok(IntermediateValue::Value( return Some(Ok(IntermediateValue::Value(Value::Dimension(
Value::Dimension(Number::new_big(n), unit).span(span), Number::new_big(n),
))); unit,
))
.span(span)));
} }
let times_ten = pow( let times_ten = pow(
@ -712,9 +738,8 @@ impl Value {
BigRational::new(BigInt::one(), times_ten) BigRational::new(BigInt::one(), times_ten)
}; };
IntermediateValue::Value( IntermediateValue::Value(Value::Dimension(Number::new_big(n * times_ten), unit))
Value::Dimension(Number::new_big(n * times_ten), unit).span(span), .span(span)
)
} }
'(' => { '(' => {
let mut span = toks.next().unwrap().pos(); let mut span = toks.next().unwrap().pos();
@ -727,14 +752,11 @@ impl Value {
} }
span = span.merge(last_tok.pos()); span = span.merge(last_tok.pos());
} }
IntermediateValue::Paren(Spanned { node: inner, span }) IntermediateValue::Paren(inner).span(span)
} }
'&' => { '&' => {
let span = toks.next().unwrap().pos(); let span = toks.next().unwrap().pos();
IntermediateValue::Value(Spanned { IntermediateValue::Value(super_selector.into_value()).span(span)
node: super_selector.into_value(),
span,
})
} }
'#' => { '#' => {
if let Some(Token { kind: '{', pos }) = toks.peek_forward(1) { if let Some(Token { kind: '{', pos }) = toks.peek_forward(1) {
@ -744,10 +766,11 @@ impl Value {
} }
toks.reset_view(); toks.reset_view();
toks.next(); toks.next();
IntermediateValue::Value(match parse_hex(toks, scope, super_selector, span) { let hex = match parse_hex(toks, scope, super_selector, span) {
Ok(v) => v, Ok(v) => v,
Err(e) => return Some(Err(e)), Err(e) => return Some(Err(e)),
}) };
IntermediateValue::Value(hex.node).span(hex.span)
} }
q @ '"' | q @ '\'' => { q @ '"' | q @ '\'' => {
let span_start = toks.next().unwrap().pos(); let span_start = toks.next().unwrap().pos();
@ -756,10 +779,7 @@ impl Value {
Ok(v) => v, Ok(v) => v,
Err(e) => return Some(Err(e)), Err(e) => return Some(Err(e)),
}; };
IntermediateValue::Value(Spanned { IntermediateValue::Value(node).span(span_start.merge(span))
node,
span: span_start.merge(span),
})
} }
'[' => { '[' => {
let mut span = toks.next().unwrap().pos(); let mut span = toks.next().unwrap().pos();
@ -771,7 +791,7 @@ impl Value {
} }
span = span.merge(last_tok.pos()); span = span.merge(last_tok.pos());
} }
IntermediateValue::Bracketed(Spanned { node: inner, span }) IntermediateValue::Bracketed(inner).span(span)
} }
'$' => { '$' => {
toks.next(); toks.next();
@ -779,51 +799,38 @@ impl Value {
Ok(v) => v, Ok(v) => v,
Err(e) => return Some(Err(e)), Err(e) => return Some(Err(e)),
}; };
IntermediateValue::Value(Spanned { IntermediateValue::Value(
node: match scope.get_var(val.clone()) { match scope.get_var(val.clone()) {
Ok(v) => v, Ok(v) => v,
Err(e) => return Some(Err(e)), Err(e) => return Some(Err(e)),
} }
.node, .node,
span: val.span, )
}) .span(val.span)
} }
'+' => { '+' => {
let span = toks.next().unwrap().pos(); let span = toks.next().unwrap().pos();
IntermediateValue::Op(Spanned { IntermediateValue::Op(Op::Plus).span(span)
node: Op::Plus,
span,
})
} }
'-' => { '-' => {
let span = toks.next().unwrap().pos(); let span = toks.next().unwrap().pos();
IntermediateValue::Op(Spanned { IntermediateValue::Op(Op::Minus).span(span)
node: Op::Minus,
span,
})
} }
'*' => { '*' => {
let span = toks.next().unwrap().pos(); let span = toks.next().unwrap().pos();
IntermediateValue::Op(Spanned { IntermediateValue::Op(Op::Mul).span(span)
node: Op::Mul,
span,
})
} }
'%' => { '%' => {
let span = toks.next().unwrap().pos(); let span = toks.next().unwrap().pos();
IntermediateValue::Op(Spanned { IntermediateValue::Op(Op::Rem).span(span)
node: Op::Rem,
span,
})
} }
',' => { ',' => {
toks.next(); toks.next();
IntermediateValue::Comma IntermediateValue::Comma.span(span)
} }
q @ '>' | q @ '<' => { q @ '>' | q @ '<' => {
let mut span = toks.next().unwrap().pos(); let mut span = toks.next().unwrap().pos();
IntermediateValue::Op(Spanned { IntermediateValue::Op(if toks.peek().unwrap().kind == '=' {
node: if toks.peek().unwrap().kind == '=' {
span = span.merge(toks.next().unwrap().pos()); span = span.merge(toks.next().unwrap().pos());
match q { match q {
'>' => Op::GreaterThanEqual, '>' => Op::GreaterThanEqual,
@ -836,18 +843,14 @@ impl Value {
'<' => Op::LessThan, '<' => Op::LessThan,
_ => unreachable!(), _ => unreachable!(),
} }
},
span,
}) })
.span(span)
} }
'=' => { '=' => {
let mut span = toks.next().unwrap().pos(); let mut span = toks.next().unwrap().pos();
if let Token { kind: '=', pos } = toks.next().unwrap() { if let Token { kind: '=', pos } = toks.next().unwrap() {
span = span.merge(pos); span = span.merge(pos);
IntermediateValue::Op(Spanned { IntermediateValue::Op(Op::Equal).span(span)
node: Op::Equal,
span,
})
} else { } else {
return Some(Err(("expected \"=\".", span).into())); return Some(Err(("expected \"=\".", span).into()));
} }
@ -856,10 +859,7 @@ impl Value {
let mut span = toks.next().unwrap().pos(); let mut span = toks.next().unwrap().pos();
if toks.peek().is_some() && toks.peek().unwrap().kind == '=' { if toks.peek().is_some() && toks.peek().unwrap().kind == '=' {
span = span.merge(toks.next().unwrap().pos()); span = span.merge(toks.next().unwrap().pos());
return Some(Ok(IntermediateValue::Op(Spanned { return Some(Ok(IntermediateValue::Op(Op::NotEqual).span(span)));
node: Op::NotEqual,
span,
})));
} }
devour_whitespace(toks); devour_whitespace(toks);
let v = match eat_ident(toks, scope, super_selector, span) { let v = match eat_ident(toks, scope, super_selector, span) {
@ -868,10 +868,7 @@ impl Value {
}; };
span = span.merge(v.span); span = span.merge(v.span);
if v.node.to_ascii_lowercase().as_str() == "important" { if v.node.to_ascii_lowercase().as_str() == "important" {
IntermediateValue::Value(Spanned { IntermediateValue::Value(Value::Important).span(span)
node: Value::Important,
span,
})
} else { } else {
return Some(Err(("Expected \"important\".", span).into())); return Some(Err(("Expected \"important\".", span).into()));
} }
@ -887,20 +884,19 @@ impl Value {
Ok(..) => {} Ok(..) => {}
Err(e) => return Some(Err(e)), Err(e) => return Some(Err(e)),
} }
IntermediateValue::Whitespace IntermediateValue::Whitespace.span(span)
} else if '/' == toks.peek().unwrap().kind { } else if '/' == toks.peek().unwrap().kind {
read_until_newline(toks); read_until_newline(toks);
devour_whitespace(toks); devour_whitespace(toks);
IntermediateValue::Whitespace IntermediateValue::Whitespace.span(span)
} else { } else {
IntermediateValue::Op(Spanned { IntermediateValue::Op(Op::Div).span(span)
node: Op::Div,
span,
})
} }
} }
';' | '}' | '{' => return None, ';' | '}' | '{' => return None,
':' | '?' | ')' | '@' | '^' | ']' => return Some(Err(("expected \";\".", span).into())), ':' | '?' | ')' | '@' | '^' | ']' => {
return Some(Err(("expected \";\".", span).into()))
}
v if v as u32 >= 0x80 || v.is_control() || v == '`' => { v if v as u32 >= 0x80 || v.is_control() || v == '`' => {
return Some(Err(("Expected expression.", span).into())) return Some(Err(("Expected expression.", span).into()))
} }

View File

@ -82,19 +82,14 @@ error!(
nothing_after_variable_in_style, nothing_after_variable_in_style,
"a {$a", "Error: expected \":\"." "a {$a", "Error: expected \":\"."
); );
error!( error!(toplevel_comma, "a {},", "Error: expected \"{\".");
toplevel_comma, error!(toplevel_exclamation, "! {}", "Error: expected \"{\".");
"a {},", "Error: expected \"{\"." error!(toplevel_backtick, "` {}", "Error: expected selector.");
);
error!(
toplevel_exclamation,
"! {}", "Error: expected \"{\"."
);
error!(
toplevel_backtick,
"` {}", "Error: expected selector."
);
error!( error!(
backtick_in_value, backtick_in_value,
"a {color:`red;}", "Error: Expected expression." "a {color:`red;}", "Error: Expected expression."
); );
error!(
comma_begins_value,
"a {color:,red;}", "Error: Expected expression."
);

View File

@ -15,13 +15,11 @@ test!(
); );
error!( error!(
uppercase_non_ident, uppercase_non_ident,
"a {\n color: to-upper-case(123);\n}\n", "a {\n color: to-upper-case(123);\n}\n", "Error: $string: 123 is not a string."
"Error: $string: 123 is not a string."
); );
error!( error!(
lowercase_non_ident, lowercase_non_ident,
"a {\n color: to-lower-case(123);\n}\n", "a {\n color: to-lower-case(123);\n}\n", "Error: $string: 123 is not a string."
"Error: $string: 123 is not a string."
); );
test!( test!(
uppercase_named_arg, uppercase_named_arg,