emit proper error on unclosed quote

This commit is contained in:
ConnorSkees 2020-05-24 10:04:30 -04:00
parent b653c55ad7
commit 737a6ba90d
16 changed files with 91 additions and 62 deletions

View File

@ -262,7 +262,7 @@ pub(crate) fn eat_func_args<I: Iterator<Item = Token>>(
}
'(' => {
default.push(toks.next().unwrap());
default.extend(read_until_closing_paren(toks));
default.extend(read_until_closing_paren(toks)?);
}
_ => default.push(toks.next().unwrap()),
}
@ -380,15 +380,15 @@ pub(crate) fn eat_call_args<I: Iterator<Item = Token>>(
',' => break,
'[' => {
val.push(tok);
val.extend(read_until_closing_square_brace(toks));
val.extend(read_until_closing_square_brace(toks)?);
}
'(' => {
val.push(tok);
val.extend(read_until_closing_paren(toks));
val.extend(read_until_closing_paren(toks)?);
}
'"' | '\'' => {
val.push(tok);
val.extend(read_until_closing_quote(toks, tok.kind));
val.extend(read_until_closing_quote(toks, tok.kind)?);
}
_ => val.push(tok),
}

View File

@ -119,7 +119,7 @@ pub(crate) fn parse_each<I: Iterator<Item = Token>>(
return Err(("Expected \"in\".", i.span).into());
}
devour_whitespace(toks);
let iter_val = Value::from_vec(read_until_open_curly_brace(toks), scope, super_selector)?;
let iter_val = Value::from_vec(read_until_open_curly_brace(toks)?, scope, super_selector)?;
let iter = match iter_val.node.eval(iter_val.span)?.node {
Value::List(v, ..) => v,
Value::Map(m) => m
@ -130,7 +130,7 @@ pub(crate) fn parse_each<I: Iterator<Item = Token>>(
};
toks.next();
devour_whitespace(toks);
let mut body = read_until_closing_curly_brace(toks);
let mut body = read_until_closing_curly_brace(toks)?;
body.push(toks.next().unwrap());
devour_whitespace(toks);

View File

@ -148,7 +148,7 @@ pub(crate) fn parse_for<I: Iterator<Item = Token>>(
}
};
let to_toks = read_until_open_curly_brace(toks);
let to_toks = read_until_open_curly_brace(toks)?;
toks.next();
let to_val = Value::from_vec(to_toks, scope, super_selector)?;
let to = match to_val.node.eval(to_val.span)?.node {
@ -164,7 +164,7 @@ pub(crate) fn parse_for<I: Iterator<Item = Token>>(
.into())
}
};
let body = read_until_closing_curly_brace(toks);
let body = read_until_closing_curly_brace(toks)?;
toks.next();
devour_whitespace(toks);

View File

@ -58,7 +58,7 @@ impl Function {
devour_whitespace(toks);
let mut body = read_until_closing_curly_brace(toks); //eat_stmts(toks, &mut scope.clone(), super_selector)?;
let mut body = read_until_closing_curly_brace(toks)?; //eat_stmts(toks, &mut scope.clone(), super_selector)?;
body.push(toks.next().unwrap());
devour_whitespace(toks);

View File

@ -41,13 +41,13 @@ impl If {
) -> SassResult<If> {
devour_whitespace_or_comment(toks)?;
let mut branches = Vec::new();
let init_toks = read_until_open_curly_brace(toks);
if init_toks.is_empty() || toks.next().is_none() {
let init_cond_toks = read_until_open_curly_brace(toks)?;
if init_cond_toks.is_empty() || toks.next().is_none() {
return Err(("Expected expression.", span_before).into());
}
let init_cond = Value::from_vec(init_toks, scope, super_selector)?;
let init_cond = Value::from_vec(init_cond_toks, scope, super_selector)?;
devour_whitespace_or_comment(toks)?;
let mut init_toks = read_until_closing_curly_brace(toks);
let mut init_toks = read_until_closing_curly_brace(toks)?;
if let Some(tok) = toks.next() {
init_toks.push(tok);
} else {
@ -80,18 +80,18 @@ impl If {
'i' if toks.next().unwrap().kind.to_ascii_lowercase() == 'f' => {
toks.next();
let cond = Value::from_vec(
read_until_open_curly_brace(toks),
read_until_open_curly_brace(toks)?,
scope,
super_selector,
)?;
toks.next();
devour_whitespace(toks);
branches.push(Branch::new(cond, read_until_closing_curly_brace(toks)));
branches.push(Branch::new(cond, read_until_closing_curly_brace(toks)?));
toks.next();
devour_whitespace(toks);
}
'{' => {
else_ = read_until_closing_curly_brace(toks);
else_ = read_until_closing_curly_brace(toks)?;
toks.next();
break;
}

View File

@ -49,7 +49,7 @@ impl Mixin {
devour_whitespace(toks);
let mut body = read_until_closing_curly_brace(toks);
let mut body = read_until_closing_curly_brace(toks)?;
body.push(toks.next().unwrap());
Ok(Spanned {

View File

@ -69,7 +69,7 @@ impl AtRule {
node: message,
span,
} = Value::from_vec(
read_until_semicolon_or_closing_curly_brace(toks),
read_until_semicolon_or_closing_curly_brace(toks)?,
scope,
super_selector,
)?;
@ -81,7 +81,7 @@ impl AtRule {
node: message,
span,
} = Value::from_vec(
read_until_semicolon_or_closing_curly_brace(toks),
read_until_semicolon_or_closing_curly_brace(toks)?,
scope,
super_selector,
)?;
@ -103,7 +103,7 @@ impl AtRule {
node: message,
span,
} = Value::from_vec(
read_until_semicolon_or_closing_curly_brace(toks),
read_until_semicolon_or_closing_curly_brace(toks)?,
scope,
super_selector,
)?;
@ -139,7 +139,7 @@ impl AtRule {
}
}
AtRuleKind::Return => {
let v = read_until_semicolon_or_closing_curly_brace(toks);
let v = read_until_semicolon_or_closing_curly_brace(toks)?;
if toks.peek().unwrap().kind == ';' {
toks.next();
}
@ -153,7 +153,7 @@ impl AtRule {
let mut selector = &Selector::replace(
super_selector,
Selector::from_tokens(
&mut read_until_open_curly_brace(toks).into_iter().peekmore(),
&mut read_until_open_curly_brace(toks)?.into_iter().peekmore(),
scope,
super_selector,
)?,
@ -165,7 +165,7 @@ impl AtRule {
}
toks.next();
devour_whitespace(toks);
let mut body = read_until_closing_curly_brace(toks);
let mut body = read_until_closing_curly_brace(toks)?;
body.push(toks.next().unwrap());
devour_whitespace(toks);
let mut styles = Vec::new();
@ -201,7 +201,7 @@ impl AtRule {
}
}
AtRuleKind::Charset => {
read_until_semicolon_or_closing_curly_brace(toks);
read_until_semicolon_or_closing_curly_brace(toks)?;
if toks.peek().unwrap().kind == ';' {
toks.next();
}

View File

@ -50,7 +50,7 @@ pub(crate) fn parse_while<I: Iterator<Item = Token>>(
span: Span,
) -> SassResult<Spanned<AtRule>> {
devour_whitespace(toks);
let cond = read_until_open_curly_brace(toks);
let cond = read_until_open_curly_brace(toks)?;
if cond.is_empty() {
return Err(("Expected expression.", span).into());
@ -58,7 +58,7 @@ pub(crate) fn parse_while<I: Iterator<Item = Token>>(
toks.next();
let mut body = read_until_closing_curly_brace(toks);
let mut body = read_until_closing_curly_brace(toks)?;
body.push(toks.next().unwrap());

View File

@ -375,7 +375,7 @@ pub(crate) fn eat_expr<I: Iterator<Item = Token>>(
Some(Token { kind: '{', .. }) => {
let next = toks.next().unwrap();
values.push(next);
values.extend(read_until_closing_curly_brace(toks));
values.extend(read_until_closing_curly_brace(toks)?);
if let Some(tok) = toks.next() {
values.push(tok);
} else {
@ -395,7 +395,7 @@ pub(crate) fn eat_expr<I: Iterator<Item = Token>>(
// it is causing us to emit nothing on malformed input
'(' => {
values.push(toks.next().unwrap());
values.extend(read_until_closing_paren(toks));
values.extend(read_until_closing_paren(toks)?);
}
_ => values.push(toks.next().unwrap()),
};

View File

@ -403,7 +403,12 @@ impl Selector {
super_selector: &Selector,
span_before: Span,
) -> SassResult<SelectorKind> {
let is_pseudo_element = if toks.peek().ok_or(("Expected identifier.", span_before))?.kind == ':' {
let is_pseudo_element = if toks
.peek()
.ok_or(("Expected identifier.", span_before))?
.kind
== ':'
{
toks.next();
true
} else {
@ -419,7 +424,7 @@ impl Selector {
Ok(
if toks.peek().is_some() && toks.peek().unwrap().kind == '(' {
toks.next();
let mut inner_toks = read_until_closing_paren(toks);
let mut inner_toks = read_until_closing_paren(toks)?;
inner_toks.pop();
let inner = Selector::from_tokens(
&mut inner_toks.into_iter().peekmore(),

View File

@ -4,6 +4,7 @@ use peekmore::PeekMoreIterator;
use super::read_until_closing_quote;
use crate::error::SassResult;
use crate::Token;
/// Reads until the char is found, consuming the char,
@ -11,13 +12,13 @@ use crate::Token;
pub(crate) fn read_until_char<I: Iterator<Item = Token>>(
toks: &mut PeekMoreIterator<I>,
c: char,
) -> Vec<Token> {
) -> SassResult<Vec<Token>> {
let mut v = Vec::new();
while let Some(tok) = toks.next() {
match tok.kind {
'"' | '\'' => {
v.push(tok);
v.extend(read_until_closing_quote(toks, tok.kind));
v.extend(read_until_closing_quote(toks, tok.kind)?);
continue;
}
t if t == c => break,
@ -25,7 +26,7 @@ pub(crate) fn read_until_char<I: Iterator<Item = Token>>(
}
v.push(tok)
}
v
Ok(v)
}
pub(crate) fn hex_char_for(number: u32) -> char {

View File

@ -16,7 +16,7 @@ pub(crate) fn parse_interpolation<I: Iterator<Item = Token>>(
scope: &Scope,
super_selector: &Selector,
) -> SassResult<Spanned<Value>> {
let val = Value::from_vec(read_until_closing_curly_brace(toks), scope, super_selector)?;
let val = Value::from_vec(read_until_closing_curly_brace(toks)?, scope, super_selector)?;
toks.next();
Ok(Spanned {
node: val.node.eval(val.span)?.node.unquote(),

View File

@ -2,6 +2,7 @@ use std::iter::Iterator;
use peekmore::PeekMoreIterator;
use crate::error::SassResult;
use crate::Token;
use super::{devour_whitespace, read_until_newline};
@ -11,7 +12,7 @@ use super::{devour_whitespace, read_until_newline};
// Does not consume the open curly brace
pub(crate) fn read_until_open_curly_brace<I: Iterator<Item = Token>>(
toks: &mut PeekMoreIterator<I>,
) -> Vec<Token> {
) -> SassResult<Vec<Token>> {
let mut t = Vec::new();
let mut n = 0;
while let Some(tok) = toks.peek() {
@ -33,6 +34,11 @@ pub(crate) fn read_until_open_curly_brace<I: Iterator<Item = Token>>(
}
continue;
}
q @ '"' | q @ '\'' => {
t.push(toks.next().unwrap());
t.extend(read_until_closing_quote(toks, q)?);
continue;
}
_ => {}
}
if n == 1 {
@ -41,19 +47,19 @@ pub(crate) fn read_until_open_curly_brace<I: Iterator<Item = Token>>(
t.push(toks.next().unwrap());
}
t
Ok(t)
}
pub(crate) fn read_until_closing_curly_brace<I: Iterator<Item = Token>>(
toks: &mut PeekMoreIterator<I>,
) -> Vec<Token> {
) -> SassResult<Vec<Token>> {
let mut t = Vec::new();
let mut nesting = 0;
while let Some(tok) = toks.peek() {
match tok.kind {
q @ '"' | q @ '\'' => {
t.push(toks.next().unwrap());
t.extend(read_until_closing_quote(toks, q));
t.extend(read_until_closing_quote(toks, q)?);
}
'{' => {
nesting += 1;
@ -80,7 +86,7 @@ pub(crate) fn read_until_closing_curly_brace<I: Iterator<Item = Token>>(
}
'(' => {
t.push(toks.next().unwrap());
t.extend(read_until_closing_paren(toks));
t.extend(read_until_closing_paren(toks)?);
}
'\\' => {
t.push(toks.next().unwrap());
@ -92,13 +98,16 @@ pub(crate) fn read_until_closing_curly_brace<I: Iterator<Item = Token>>(
}
}
devour_whitespace(toks);
t
Ok(t)
}
/// Read tokens into a vector until a matching closing quote is found
///
/// The closing quote is included in the output
pub(crate) fn read_until_closing_quote<I: Iterator<Item = Token>>(
toks: &mut PeekMoreIterator<I>,
q: char,
) -> Vec<Token> {
) -> SassResult<Vec<Token>> {
let mut t = Vec::new();
while let Some(tok) = toks.next() {
match tok.kind {
@ -121,18 +130,24 @@ pub(crate) fn read_until_closing_quote<I: Iterator<Item = Token>>(
let next = toks.peek().unwrap();
if next.kind == '{' {
t.push(toks.next().unwrap());
t.append(&mut read_until_closing_curly_brace(toks));
t.append(&mut read_until_closing_curly_brace(toks)?);
}
}
_ => t.push(tok),
}
}
t
if let Some(tok) = t.pop() {
if tok.kind != q {
return Err((format!("Expected {}.", q), tok.pos).into());
}
t.push(tok);
}
Ok(t)
}
pub(crate) fn read_until_semicolon_or_closing_curly_brace<I: Iterator<Item = Token>>(
toks: &mut PeekMoreIterator<I>,
) -> Vec<Token> {
) -> SassResult<Vec<Token>> {
let mut t = Vec::new();
let mut nesting = 0;
while let Some(tok) = toks.peek() {
@ -149,7 +164,7 @@ pub(crate) fn read_until_semicolon_or_closing_curly_brace<I: Iterator<Item = Tok
'"' | '\'' => {
let quote = toks.next().unwrap();
t.push(quote);
t.extend(read_until_closing_quote(toks, quote.kind));
t.extend(read_until_closing_quote(toks, quote.kind)?);
}
'{' => {
nesting += 1;
@ -178,12 +193,12 @@ pub(crate) fn read_until_semicolon_or_closing_curly_brace<I: Iterator<Item = Tok
}
}
devour_whitespace(toks);
t
Ok(t)
}
pub(crate) fn read_until_closing_paren<I: Iterator<Item = Token>>(
toks: &mut PeekMoreIterator<I>,
) -> Vec<Token> {
) -> SassResult<Vec<Token>> {
let mut t = Vec::new();
let mut scope = 0;
while let Some(tok) = toks.next() {
@ -191,7 +206,7 @@ pub(crate) fn read_until_closing_paren<I: Iterator<Item = Token>>(
')' => {
if scope < 1 {
t.push(tok);
return t;
return Ok(t);
} else {
scope -= 1;
}
@ -199,7 +214,7 @@ pub(crate) fn read_until_closing_paren<I: Iterator<Item = Token>>(
'(' => scope += 1,
'"' | '\'' => {
t.push(tok);
t.extend(read_until_closing_quote(toks, tok.kind));
t.extend(read_until_closing_quote(toks, tok.kind)?);
continue;
}
'\\' => {
@ -213,12 +228,12 @@ pub(crate) fn read_until_closing_paren<I: Iterator<Item = Token>>(
}
t.push(tok)
}
t
Ok(t)
}
pub(crate) fn read_until_closing_square_brace<I: Iterator<Item = Token>>(
toks: &mut PeekMoreIterator<I>,
) -> Vec<Token> {
) -> SassResult<Vec<Token>> {
let mut t = Vec::new();
let mut scope = 0;
while let Some(tok) = toks.next() {
@ -227,7 +242,7 @@ pub(crate) fn read_until_closing_square_brace<I: Iterator<Item = Token>>(
']' => {
if scope < 1 {
t.push(tok);
return t;
return Ok(t);
} else {
scope -= 1;
}
@ -235,7 +250,7 @@ pub(crate) fn read_until_closing_square_brace<I: Iterator<Item = Token>>(
'[' => scope += 1,
'"' | '\'' => {
t.push(tok);
t.extend(read_until_closing_quote(toks, tok.kind));
t.extend(read_until_closing_quote(toks, tok.kind)?);
continue;
}
'\\' => {
@ -248,5 +263,5 @@ pub(crate) fn read_until_closing_square_brace<I: Iterator<Item = Token>>(
}
t.push(tok)
}
t
Ok(t)
}

View File

@ -56,7 +56,7 @@ pub(crate) fn eat_variable_value<I: Iterator<Item = Token>>(
'"' | '\'' => {
let quote = toks.next().unwrap();
val_toks.push(quote);
val_toks.extend(read_until_closing_quote(toks, quote.kind));
val_toks.extend(read_until_closing_quote(toks, quote.kind)?);
}
'#' => {
val_toks.push(toks.next().unwrap());
@ -93,7 +93,7 @@ pub(crate) fn eat_variable_value<I: Iterator<Item = Token>>(
}
'(' => {
val_toks.push(toks.next().unwrap());
val_toks.extend(read_until_closing_paren(toks));
val_toks.extend(read_until_closing_paren(toks)?);
}
'!' => {
let pos = tok.pos();

View File

@ -189,7 +189,7 @@ fn parse_paren(
let paren_toks = &mut t.node.into_iter().peekmore();
let mut map = SassMap::new();
let key = Value::from_vec(read_until_char(paren_toks, ':'), scope, super_selector)?;
let key = Value::from_vec(read_until_char(paren_toks, ':')?, scope, super_selector)?;
if paren_toks.peek().is_none() {
return Ok(Spanned {
@ -198,7 +198,7 @@ fn parse_paren(
});
}
let val = Value::from_vec(read_until_char(paren_toks, ','), scope, super_selector)?;
let val = Value::from_vec(read_until_char(paren_toks, ',')?, scope, super_selector)?;
map.insert(key.node, val.node);
@ -212,9 +212,9 @@ fn parse_paren(
let mut span = key.span;
loop {
let key = Value::from_vec(read_until_char(paren_toks, ':'), scope, super_selector)?;
let key = Value::from_vec(read_until_char(paren_toks, ':')?, scope, super_selector)?;
devour_whitespace(paren_toks);
let val = Value::from_vec(read_until_char(paren_toks, ','), scope, super_selector)?;
let val = Value::from_vec(read_until_char(paren_toks, ',')?, scope, super_selector)?;
span = span.merge(val.span);
devour_whitespace(paren_toks);
if map.insert(key.node, val.node) {
@ -755,7 +755,10 @@ impl Value {
}
'(' => {
let mut span = toks.next().unwrap().pos();
let mut inner = read_until_closing_paren(toks);
let mut inner = match read_until_closing_paren(toks) {
Ok(v) => v,
Err(e) => return Some(Err(e.into())),
};
// todo: the above shouldn't eat the closing paren
if let Some(last_tok) = inner.pop() {
if last_tok.kind != ')' {
@ -794,7 +797,10 @@ impl Value {
}
'[' => {
let mut span = toks.next().unwrap().pos();
let mut inner = read_until_closing_square_brace(toks);
let mut inner = match read_until_closing_square_brace(toks) {
Ok(v) => v,
Err(e) => return Some(Err(e.into())),
};
if let Some(last_tok) = inner.pop() {
if last_tok.kind != ']' {
return Some(Err(("expected \"]\".", span).into()));

View File

@ -126,3 +126,5 @@ error!(
nothing_after_escape,
"@if \\", "Error: Expected expression."
);
error!(unclosed_dbl_quote, "@if true \" {}", "Error: Expected \".");
error!(unclosed_sgl_quote, "@if true ' {}", "Error: Expected '.");