remove most remaining unwraps

This commit is contained in:
ConnorSkees 2020-06-18 03:09:24 -04:00
parent de8e7048d8
commit 5fc3748472
17 changed files with 159 additions and 79 deletions

View File

@ -15,7 +15,10 @@ use super::Parser;
impl<'a> Parser<'a> { impl<'a> Parser<'a> {
pub(super) fn parse_func_args(&mut self) -> SassResult<FuncArgs> { pub(super) fn parse_func_args(&mut self) -> SassResult<FuncArgs> {
let mut args: Vec<FuncArg> = Vec::new(); let mut args: Vec<FuncArg> = Vec::new();
let mut close_paren_span: Span = self.toks.peek().unwrap().pos(); let mut close_paren_span: Span = match self.toks.peek() {
Some(Token { pos, .. }) => *pos,
None => return Err(("expected \")\".", self.span_before).into()),
};
self.whitespace(); self.whitespace();
while let Some(Token { kind, pos }) = self.toks.next() { while let Some(Token { kind, pos }) = self.toks.next() {
@ -130,9 +133,10 @@ impl<'a> Parser<'a> {
.ok_or(("expected \")\".", self.span_before))? .ok_or(("expected \")\".", self.span_before))?
.pos(); .pos();
loop { loop {
match self.toks.peek() { match self.toks.peek().cloned() {
Some(Token { kind: '$', .. }) => { Some(Token { kind: '$', pos }) => {
let Token { pos, .. } = self.toks.next().unwrap(); span = span.merge(pos);
self.toks.next();
let v = self.parse_identifier_no_interpolation(false)?; let v = self.parse_identifier_no_interpolation(false)?;
let whitespace = self.whitespace_or_comment(); let whitespace = self.whitespace_or_comment();
if let Some(Token { kind: ':', .. }) = self.toks.peek() { if let Some(Token { kind: ':', .. }) = self.toks.peek() {

View File

@ -35,7 +35,10 @@ impl<'a> Parser<'a> {
self.whitespace(); self.whitespace();
let mut body = read_until_closing_curly_brace(self.toks)?; let mut body = read_until_closing_curly_brace(self.toks)?;
body.push(self.toks.next().unwrap()); body.push(match self.toks.next() {
Some(tok) => tok,
None => return Err(("expected \"}\".", self.span_before).into()),
});
self.whitespace(); self.whitespace();
let function = Function::new(self.scopes.last().clone(), args, body, span); let function = Function::new(self.scopes.last().clone(), args, body, span);

View File

@ -29,7 +29,10 @@ impl<'a> Parser<'a> {
self.whitespace(); self.whitespace();
let mut body = read_until_closing_curly_brace(self.toks)?; let mut body = read_until_closing_curly_brace(self.toks)?;
body.push(self.toks.next().unwrap()); body.push(match self.toks.next() {
Some(tok) => tok,
None => return Err(("expected \"}\".", self.span_before).into()),
});
// todo: `@include` can only give content when `@content` is present within the body // todo: `@include` can only give content when `@content` is present within the body
// if `@content` is *not* present and `@include` attempts to give a body, we throw an error // if `@content` is *not* present and `@include` attempts to give a body, we throw an error

View File

@ -171,8 +171,9 @@ impl<'a> Parser<'a> {
span, span,
} = self.parse_value_from_vec(toks)?; } = self.parse_value_from_vec(toks)?;
span.merge(kind_string.span); span.merge(kind_string.span);
if let Some(Token { kind: ';', .. }) = self.toks.peek() { if let Some(Token { kind: ';', pos }) = self.toks.peek() {
kind_string.span.merge(self.toks.next().unwrap().pos()); kind_string.span.merge(*pos);
self.toks.next();
} }
self.warn(&Spanned { self.warn(&Spanned {
node: message.to_css_string(span)?, node: message.to_css_string(span)?,
@ -186,8 +187,9 @@ impl<'a> Parser<'a> {
span, span,
} = self.parse_value_from_vec(toks)?; } = self.parse_value_from_vec(toks)?;
span.merge(kind_string.span); span.merge(kind_string.span);
if let Some(Token { kind: ';', .. }) = self.toks.peek() { if let Some(Token { kind: ';', pos }) = self.toks.peek() {
kind_string.span.merge(self.toks.next().unwrap().pos()); kind_string.span.merge(*pos);
self.toks.next();
} }
self.debug(&Spanned { self.debug(&Spanned {
node: message.inspect(span)?, node: message.inspect(span)?,
@ -529,7 +531,11 @@ impl<'a> Parser<'a> {
if let Some(tok) = self.toks.next() { if let Some(tok) = self.toks.next() {
self.whitespace(); self.whitespace();
match tok.kind.to_ascii_lowercase() { match tok.kind.to_ascii_lowercase() {
'i' if self.toks.next().unwrap().kind.to_ascii_lowercase() == 'f' => { 'i' if matches!(
self.toks.peek(),
Some(Token { kind: 'f', .. }) | Some(Token { kind: 'F', .. })
) =>
{
self.toks.next(); self.toks.next();
let cond = read_until_open_curly_brace(self.toks)?; let cond = read_until_open_curly_brace(self.toks)?;
self.toks.next(); self.toks.next();
@ -610,10 +616,10 @@ impl<'a> Parser<'a> {
_ => return Err(("expected \"$\".", self.span_before).into()), _ => return Err(("expected \"$\".", self.span_before).into()),
}; };
self.whitespace(); self.whitespace();
if self.toks.peek().is_none() { self.span_before = match self.toks.peek() {
return Err(("Expected \"from\".", var.span).into()); Some(tok) => tok.pos,
} None => return Err(("Expected \"from\".", var.span).into()),
self.span_before = self.toks.peek().unwrap().pos; };
if self.parse_identifier()?.node.to_ascii_lowercase() != "from" { if self.parse_identifier()?.node.to_ascii_lowercase() != "from" {
return Err(("Expected \"from\".", var.span).into()); return Err(("Expected \"from\".", var.span).into());
} }
@ -644,7 +650,10 @@ impl<'a> Parser<'a> {
'{' => { '{' => {
return Err(("Expected \"to\" or \"through\".", tok.pos()).into()); return Err(("Expected \"to\" or \"through\".", tok.pos()).into());
} }
_ => from_toks.push(self.toks.next().unwrap()), _ => {
from_toks.push(tok);
self.toks.next();
}
} }
} }
self.whitespace(); self.whitespace();
@ -766,7 +775,10 @@ impl<'a> Parser<'a> {
let mut body = read_until_closing_curly_brace(self.toks)?; let mut body = read_until_closing_curly_brace(self.toks)?;
body.push(self.toks.next().unwrap()); body.push(match self.toks.next() {
Some(tok) => tok,
None => return Err(("expected \"}\".", self.span_before).into()),
});
self.whitespace(); self.whitespace();
@ -860,7 +872,10 @@ impl<'a> Parser<'a> {
self.toks.next(); self.toks.next();
self.whitespace(); self.whitespace();
let mut body = read_until_closing_curly_brace(self.toks)?; let mut body = read_until_closing_curly_brace(self.toks)?;
body.push(self.toks.next().unwrap()); body.push(match self.toks.next() {
Some(tok) => tok,
None => return Err(("expected \"}\".", self.span_before).into()),
});
self.whitespace(); self.whitespace();
let mut stmts = Vec::new(); let mut stmts = Vec::new();
@ -1102,7 +1117,10 @@ impl<'a> Parser<'a> {
self.whitespace(); self.whitespace();
let mut body = read_until_closing_curly_brace(self.toks)?; let mut body = read_until_closing_curly_brace(self.toks)?;
body.push(self.toks.next().unwrap()); body.push(match self.toks.next() {
Some(tok) => tok,
None => return Err(("expected \"}\".", self.span_before).into()),
});
self.whitespace(); self.whitespace();

View File

@ -710,7 +710,7 @@ impl<'a> Parser<'a> {
} }
fn peek_interpolation(&mut self) -> SassResult<(Spanned<Value>, usize)> { fn peek_interpolation(&mut self) -> SassResult<(Spanned<Value>, usize)> {
let vec = peek_until_closing_curly_brace(self.toks); let vec = peek_until_closing_curly_brace(self.toks)?;
let peek_counter = vec.len(); let peek_counter = vec.len();
self.toks.move_forward(1); self.toks.move_forward(1);
let val = self.parse_value_from_vec(vec)?; let val = self.parse_value_from_vec(vec)?;

View File

@ -51,11 +51,7 @@ pub(crate) fn eat_number<I: Iterator<Item = Token>>(
) -> SassResult<Spanned<ParsedNumber>> { ) -> SassResult<Spanned<ParsedNumber>> {
let mut whole = String::with_capacity(1); let mut whole = String::with_capacity(1);
// TODO: merge this span with chars // TODO: merge this span with chars
let span = if let Some(tok) = toks.peek() { let span = toks.peek().unwrap().pos;
tok.pos()
} else {
todo!()
};
eat_whole_number(toks, &mut whole); eat_whole_number(toks, &mut whole);
if toks.peek().is_none() { if toks.peek().is_none() {

View File

@ -8,19 +8,19 @@ use super::{as_hex, hex_char_for, is_name, is_name_start, IsWhitespace};
pub(crate) fn peek_until_closing_curly_brace<I: Iterator<Item = Token>>( pub(crate) fn peek_until_closing_curly_brace<I: Iterator<Item = Token>>(
toks: &mut PeekMoreIterator<I>, toks: &mut PeekMoreIterator<I>,
) -> Vec<Token> { ) -> SassResult<Vec<Token>> {
let mut t = Vec::new(); let mut t = Vec::new();
let mut nesting = 0; let mut nesting = 0;
while let Some(tok) = toks.peek() { while let Some(tok) = toks.peek().cloned() {
match tok.kind { match tok.kind {
q @ '"' | q @ '\'' => { q @ '"' | q @ '\'' => {
t.push(*toks.peek().unwrap()); t.push(tok);
toks.move_forward(1); toks.move_forward(1);
t.extend(peek_until_closing_quote(toks, q)); t.extend(peek_until_closing_quote(toks, q)?);
} }
'{' => { '{' => {
nesting += 1; nesting += 1;
t.push(*toks.peek().unwrap()); t.push(tok);
toks.move_forward(1); toks.move_forward(1);
} }
'}' => { '}' => {
@ -28,62 +28,71 @@ pub(crate) fn peek_until_closing_curly_brace<I: Iterator<Item = Token>>(
break; break;
} else { } else {
nesting -= 1; nesting -= 1;
t.push(*toks.peek().unwrap()); t.push(tok);
toks.move_forward(1); toks.move_forward(1);
} }
} }
'/' => { '/' => {
let next = *toks.peek_forward(1).unwrap(); let next = *toks
match toks.peek().unwrap().kind { .peek_forward(1)
'/' => peek_until_newline(toks), .ok_or(("Expected expression.", tok.pos))?;
match toks.peek() {
Some(Token { kind: '/', .. }) => peek_until_newline(toks),
_ => t.push(next), _ => t.push(next),
}; };
continue; continue;
} }
_ => { _ => {
t.push(*toks.peek().unwrap()); t.push(tok);
toks.move_forward(1); toks.move_forward(1);
} }
} }
} }
peek_whitespace(toks); peek_whitespace(toks);
t Ok(t)
} }
fn peek_until_closing_quote<I: Iterator<Item = Token>>( fn peek_until_closing_quote<I: Iterator<Item = Token>>(
toks: &mut PeekMoreIterator<I>, toks: &mut PeekMoreIterator<I>,
q: char, q: char,
) -> Vec<Token> { ) -> SassResult<Vec<Token>> {
let mut t = Vec::new(); let mut t = Vec::new();
while let Some(tok) = toks.peek() { while let Some(tok) = toks.peek().cloned() {
match tok.kind { match tok.kind {
'"' if q == '"' => { '"' if q == '"' => {
t.push(*tok); t.push(tok);
toks.move_forward(1); toks.move_forward(1);
break; break;
} }
'\'' if q == '\'' => { '\'' if q == '\'' => {
t.push(*tok); t.push(tok);
toks.move_forward(1); toks.move_forward(1);
break; break;
} }
'\\' => { '\\' => {
t.push(*tok); t.push(tok);
t.push(*toks.peek_forward(1).unwrap()); t.push(match toks.peek_forward(1) {
Some(tok) => *tok,
None => return Err((format!("Expected {}.", q), tok.pos).into()),
});
} }
'#' => { '#' => {
t.push(*tok); t.push(tok);
let next = toks.peek().unwrap(); let next = match toks.peek() {
Some(tok) => tok,
None => return Err((format!("Expected {}.", q), tok.pos).into()),
};
if next.kind == '{' { if next.kind == '{' {
t.push(*toks.peek_forward(1).unwrap()); t.push(*next);
t.append(&mut peek_until_closing_curly_brace(toks)); toks.peek_forward(1);
t.append(&mut peek_until_closing_curly_brace(toks)?);
} }
} }
_ => t.push(*tok), _ => t.push(tok),
} }
toks.move_forward(1); toks.move_forward(1);
} }
t Ok(t)
} }
fn peek_until_newline<I: Iterator<Item = Token>>(toks: &mut PeekMoreIterator<I>) { fn peek_until_newline<I: Iterator<Item = Token>>(toks: &mut PeekMoreIterator<I>) {
@ -166,13 +175,13 @@ pub(crate) fn peek_ident_no_interpolation<I: Iterator<Item = Token>>(
.ok_or(("Expected identifier.", span_before))? .ok_or(("Expected identifier.", span_before))?
.pos(); .pos();
let mut text = String::new(); let mut text = String::new();
if toks.peek().unwrap().kind == '-' { if let Some(Token { kind: '-', .. }) = toks.peek() {
toks.peek_forward(1); toks.peek_forward(1);
text.push('-'); text.push('-');
if toks.peek().is_none() { if toks.peek().is_none() {
return Ok(Spanned { node: text, span }); return Ok(Spanned { node: text, span });
} }
if toks.peek().unwrap().kind == '-' { if let Some(Token { kind: '-', .. }) = toks.peek() {
toks.peek_forward(1); toks.peek_forward(1);
text.push('-'); text.push('-');
text.push_str(&peek_ident_body_no_interpolation(toks, unit, span)?.node); text.push_str(&peek_ident_body_no_interpolation(toks, unit, span)?.node);

View File

@ -28,10 +28,10 @@ pub(crate) fn read_until_open_curly_brace<I: Iterator<Item = Token>>(
} }
'\\' => { '\\' => {
t.push(toks.next().unwrap()); t.push(toks.next().unwrap());
if toks.peek().is_some() { t.push(match toks.next() {
t.push(toks.next().unwrap()); Some(tok) => tok,
} None => continue,
continue; });
} }
q @ '"' | q @ '\'' => { q @ '"' | q @ '\'' => {
t.push(toks.next().unwrap()); t.push(toks.next().unwrap());
@ -89,9 +89,10 @@ pub(crate) fn read_until_closing_curly_brace<I: Iterator<Item = Token>>(
} }
'\\' => { '\\' => {
t.push(toks.next().unwrap()); t.push(toks.next().unwrap());
if toks.peek().is_some() { t.push(match toks.next() {
t.push(toks.next().unwrap()); Some(tok) => tok,
} None => continue,
});
} }
_ => t.push(toks.next().unwrap()), _ => t.push(toks.next().unwrap()),
} }
@ -120,16 +121,21 @@ pub(crate) fn read_until_closing_quote<I: Iterator<Item = Token>>(
} }
'\\' => { '\\' => {
t.push(tok); t.push(tok);
if toks.peek().is_some() { t.push(match toks.next() {
t.push(toks.next().unwrap()); Some(tok) => tok,
} None => return Err((format!("Expected {}.", q), tok.pos).into()),
});
} }
'#' => { '#' => {
t.push(tok); t.push(tok);
let next = toks.peek().unwrap(); match toks.peek() {
if next.kind == '{' { Some(tok @ Token { kind: '{', .. }) => {
t.push(toks.next().unwrap()); t.push(*tok);
t.append(&mut read_until_closing_curly_brace(toks)?); toks.next();
t.append(&mut read_until_closing_curly_brace(toks)?);
}
Some(..) => continue,
None => return Err((format!("Expected {}.", q), tok.pos).into()),
} }
} }
_ => t.push(tok), _ => t.push(tok),
@ -156,9 +162,10 @@ pub(crate) fn read_until_semicolon_or_closing_curly_brace<I: Iterator<Item = Tok
} }
'\\' => { '\\' => {
t.push(toks.next().unwrap()); t.push(toks.next().unwrap());
if toks.peek().is_some() { t.push(match toks.next() {
t.push(toks.next().unwrap()); Some(tok) => tok,
} None => continue,
});
} }
'"' | '\'' => { '"' | '\'' => {
let quote = toks.next().unwrap(); let quote = toks.next().unwrap();
@ -217,11 +224,11 @@ pub(crate) fn read_until_closing_paren<I: Iterator<Item = Token>>(
continue; continue;
} }
'\\' => { '\\' => {
t.push(tok); t.push(toks.next().unwrap());
if toks.peek().is_some() { t.push(match toks.next() {
t.push(toks.next().unwrap()); Some(tok) => tok,
} None => continue,
continue; });
} }
_ => {} _ => {}
} }
@ -253,10 +260,11 @@ pub(crate) fn read_until_closing_square_brace<I: Iterator<Item = Token>>(
continue; continue;
} }
'\\' => { '\\' => {
t.push(tok); t.push(toks.next().unwrap());
if toks.peek().is_some() { t.push(match toks.next() {
t.push(toks.next().unwrap()); Some(tok) => tok,
} None => continue,
});
} }
_ => {} _ => {}
} }

View File

@ -148,7 +148,7 @@ impl Value {
Self::List(vals, sep, brackets) => match brackets { Self::List(vals, sep, brackets) => match brackets {
Brackets::None => Cow::owned( Brackets::None => Cow::owned(
vals.iter() vals.iter()
.filter(|x| !x.clone().is_null(span).unwrap_or(false)) .filter(|x| !x.is_null(span).unwrap_or(false))
.map(|x| x.to_css_string(span)) .map(|x| x.to_css_string(span))
.collect::<SassResult<Vec<Cow<'static, str>>>>()? .collect::<SassResult<Vec<Cow<'static, str>>>>()?
.join(sep.as_str()), .join(sep.as_str()),

View File

@ -46,7 +46,11 @@ test!(
"a {\n color: 2;\n}\n" "a {\n color: 2;\n}\n"
); );
error!( error!(
#[ignore = "does not fail"] #[ignore = "expects incorrect char, '{'"]
nothing_after_open, nothing_after_open,
"a { color:rgb(; }", "Error: expected \")\"." "a { color:rgb(; }", "Error: expected \")\"."
); );
error!(
nothing_after_open_paren_in_fn_args,
"@function foo(", "Error: expected \")\"."
);

View File

@ -68,3 +68,7 @@ test!(
"a {}\n\n@at-root {\n @-ms-viewport { width: device-width; }\n}\n", "a {}\n\n@at-root {\n @-ms-viewport { width: device-width; }\n}\n",
"@-ms-viewport {\n width: device-width;\n}\n" "@-ms-viewport {\n width: device-width;\n}\n"
); );
error!(
missing_closing_curly_brace,
"@at-root {", "Error: expected \"}\"."
);

View File

@ -48,3 +48,7 @@ test!(
"a {\n @each $i in 1 2 3 {\n color: type-of($i);\n }\n}\n", "a {\n @each $i in 1 2 3 {\n color: type-of($i);\n }\n}\n",
"a {\n color: number;\n color: number;\n color: number;\n}\n" "a {\n color: number;\n color: number;\n color: number;\n}\n"
); );
error!(
missing_closing_curly_brace,
"@each $i in 1 {", "Error: expected \"}\"."
);

View File

@ -95,3 +95,7 @@ error!(
double_comma_args, double_comma_args,
"@function foo($a,$b,,) {}", "Error: expected \")\"." "@function foo($a,$b,,) {}", "Error: expected \")\"."
); );
error!(
body_missing_closing_curly_brace,
"@function foo() {", "Error: expected \"}\"."
);

View File

@ -149,3 +149,7 @@ error!(unclosed_sgl_quote, "@if true ' {}", "Error: Expected '.");
error!(unclosed_call_args, "@if a({}", "Error: expected \")\"."); error!(unclosed_call_args, "@if a({}", "Error: expected \")\".");
error!(nothing_after_div, "@if a/", "Error: Expected expression."); error!(nothing_after_div, "@if a/", "Error: Expected expression.");
error!(multiline_error, "@if \"\n\"{}", "Error: Expected \"."); error!(multiline_error, "@if \"\n\"{}", "Error: Expected \".");
error!(
nothing_after_i_after_else,
"@if true {} @else i", "Error: expected \"{\"."
);

View File

@ -233,6 +233,10 @@ error!(
undefined_mixin, undefined_mixin,
"a {@include foo;}", "Error: Undefined mixin." "a {@include foo;}", "Error: Undefined mixin."
); );
error!(
body_missing_closing_curly_brace,
"@mixin foo() {", "Error: expected \"}\"."
);
test!( test!(
include_empty_args_no_semicolon, include_empty_args_no_semicolon,
"@mixin c {}\n\na {\n @include c()\n}\n", "@mixin c {}\n\na {\n @include c()\n}\n",

View File

@ -134,3 +134,15 @@ test!(
"a {\n color: UrL(http://foo);\n}\n", "a {\n color: UrL(http://foo);\n}\n",
"a {\n color: url(http://foo);\n}\n" "a {\n color: url(http://foo);\n}\n"
); );
error!(
url_nothing_after_forward_slash_in_interpolation,
"a { color: url(#{/", "Error: Expected expression."
);
error!(
url_nothing_after_backslash_in_interpolation_in_quote,
"a { color: url(#{\"\\", "Error: Expected \"."
);
error!(
url_nothing_after_hash_in_interpolation_in_quote,
"a { color: url(#{\"#", "Error: Expected \"."
);

View File

@ -107,7 +107,6 @@ test!(
}", }",
"result {\n root_default: initial;\n root_implicit: inner;\n root_explicit: inner;\n}\n" "result {\n root_default: initial;\n root_implicit: inner;\n root_explicit: inner;\n}\n"
); );
test!( test!(
if_inside_while, if_inside_while,
"$continue_outer: true; "$continue_outer: true;
@ -126,3 +125,7 @@ test!(
}", }",
"a {\n color: red;\n}\n\na {\n color: blue;\n}\n" "a {\n color: red;\n}\n\na {\n color: blue;\n}\n"
); );
error!(
missing_closing_curly_brace,
"@while true {", "Error: expected \"}\"."
);