From b58ed29fd0cbfbfc857a2eef6281b89d5c37510e Mon Sep 17 00:00:00 2001 From: ConnorSkees <39542938+ConnorSkees@users.noreply.github.com> Date: Sun, 17 May 2020 00:35:07 -0400 Subject: [PATCH] add more span information this resolves a lot of panics that occurred when there was no more input --- src/args.rs | 2 +- src/atrule/each_rule.rs | 7 ++----- src/atrule/for_rule.rs | 13 ++++++++----- src/atrule/function.rs | 3 ++- src/atrule/if_rule.rs | 6 ++++-- src/atrule/mixin.rs | 8 +++++--- src/atrule/mod.rs | 13 ++++++++++--- src/lib.rs | 25 +++++++++++++++++-------- src/selector/attribute.rs | 15 ++++++++------- src/style.rs | 13 ++++++++----- src/stylesheet.rs | 19 +++++++++++++++---- src/utils/strings.rs | 7 +++++-- src/value/parse.rs | 14 ++++++++------ 13 files changed, 93 insertions(+), 52 deletions(-) diff --git a/src/args.rs b/src/args.rs index 51be667..6ca0ce8 100644 --- a/src/args.rs +++ b/src/args.rs @@ -221,7 +221,7 @@ pub(crate) fn eat_func_args>( devour_whitespace(toks); while let Some(Token { kind, pos }) = toks.next() { let name = match kind { - '$' => eat_ident(toks, scope, super_selector)?, + '$' => eat_ident(toks, scope, super_selector, pos)?, ')' => { close_paren_span = pos; break; diff --git a/src/atrule/each_rule.rs b/src/atrule/each_rule.rs index cea1bb4..6b4f3ef 100644 --- a/src/atrule/each_rule.rs +++ b/src/atrule/each_rule.rs @@ -98,7 +98,7 @@ pub(crate) fn parse_each>( let next = toks.next().ok_or(("expected \"$\".", span))?; span = next.pos(); match next.kind { - '$' => vars.push(eat_ident(toks, scope, super_selector)?), + '$' => vars.push(eat_ident(toks, scope, super_selector, next.pos)?), _ => return Err(("expected \"$\".", next.pos()).into()), } devour_whitespace(toks); @@ -114,10 +114,7 @@ pub(crate) fn parse_each>( break; } } - if toks.peek().is_none() { - todo!() - } - let i = eat_ident(toks, scope, super_selector)?; + let i = eat_ident(toks, scope, super_selector, span)?; if i.node.to_ascii_lowercase() != "in" { return Err(("Expected \"in\".", i.span).into()); } diff --git a/src/atrule/for_rule.rs b/src/atrule/for_rule.rs index b1e3a8b..06eec7a 100644 --- a/src/atrule/for_rule.rs +++ b/src/atrule/for_rule.rs @@ -89,14 +89,17 @@ pub(crate) fn parse_for>( span: Span, ) -> SassResult { devour_whitespace(toks); - let var = match toks.next().ok_or(("expected \"$\".", span))?.kind { - '$' => eat_ident(toks, scope, super_selector)?, + let next = toks.next().ok_or(("expected \"$\".", span))?; + let var = match next.kind { + '$' => eat_ident(toks, scope, super_selector, next.pos)?, _ => return Err(("expected \"$\".", span).into()), }; devour_whitespace(toks); - if toks.peek().is_none() - || eat_ident(toks, scope, super_selector)?.to_ascii_lowercase() != "from" - { + if toks.peek().is_none() { + return Err(("Expected \"from\".", var.span).into()); + } + let span_before = toks.peek().unwrap().pos; + if eat_ident(toks, scope, super_selector, span_before)?.to_ascii_lowercase() != "from" { return Err(("Expected \"from\".", var.span).into()); } devour_whitespace(toks); diff --git a/src/atrule/function.rs b/src/atrule/function.rs index d0fbc56..e50d492 100644 --- a/src/atrule/function.rs +++ b/src/atrule/function.rs @@ -44,8 +44,9 @@ impl Function { toks: &mut PeekMoreIterator, scope: Scope, super_selector: &Selector, + span_before: Span, ) -> SassResult<(String, Function)> { - let Spanned { node: name, span } = eat_ident(toks, &scope, super_selector)?; + let Spanned { node: name, span } = eat_ident(toks, &scope, super_selector, span_before)?; devour_whitespace(toks); let args = match toks.next() { Some(Token { kind: '(', .. }) => eat_func_args(toks, &scope, super_selector)?, diff --git a/src/atrule/if_rule.rs b/src/atrule/if_rule.rs index 8bf36f8..907f2d3 100644 --- a/src/atrule/if_rule.rs +++ b/src/atrule/if_rule.rs @@ -57,11 +57,13 @@ impl If { if first_char != 'e' && first_char != 'E' { break; } - toks.next(); } else { break; } - if eat_ident(toks, &Scope::new(), &Selector::new())?.to_ascii_lowercase() == "else" + let span_before = toks.next().unwrap().pos; + if eat_ident(toks, &Scope::new(), &Selector::new(), span_before)? + .to_ascii_lowercase() + == "else" { devour_whitespace(toks); if let Some(tok) = toks.next() { diff --git a/src/atrule/mixin.rs b/src/atrule/mixin.rs index 76d34b4..07476ec 100644 --- a/src/atrule/mixin.rs +++ b/src/atrule/mixin.rs @@ -1,6 +1,6 @@ use std::vec::IntoIter; -use codemap::Spanned; +use codemap::{Span, Spanned}; use peekmore::{PeekMore, PeekMoreIterator}; @@ -34,9 +34,10 @@ impl Mixin { toks: &mut PeekMoreIterator, scope: &Scope, super_selector: &Selector, + span_before: Span, ) -> SassResult> { devour_whitespace(toks); - let Spanned { node: name, span } = eat_ident(toks, scope, super_selector)?; + let Spanned { node: name, span } = eat_ident(toks, scope, super_selector, span_before)?; devour_whitespace(toks); let args = match toks.next() { Some(Token { kind: '(', .. }) => eat_func_args(toks, scope, super_selector)?, @@ -185,9 +186,10 @@ pub(crate) fn eat_include>( scope: &Scope, super_selector: &Selector, content: Option<&[Spanned]>, + span_before: Span, ) -> SassResult>> { devour_whitespace_or_comment(toks)?; - let name = eat_ident(toks, scope, super_selector)?; + let name = eat_ident(toks, scope, super_selector, span_before)?; devour_whitespace_or_comment(toks)?; diff --git a/src/atrule/mod.rs b/src/atrule/mod.rs index 931ebec..e78f6e8 100644 --- a/src/atrule/mod.rs +++ b/src/atrule/mod.rs @@ -121,14 +121,15 @@ impl AtRule { let Spanned { node: (name, mixin), span, - } = Mixin::decl_from_tokens(toks, scope, super_selector)?; + } = Mixin::decl_from_tokens(toks, scope, super_selector, kind_span)?; Spanned { node: AtRule::Mixin(name, Box::new(mixin)), span, } } AtRuleKind::Function => { - let (name, func) = Function::decl_from_tokens(toks, scope.clone(), super_selector)?; + let (name, func) = + Function::decl_from_tokens(toks, scope.clone(), super_selector, kind_span)?; Spanned { node: AtRule::Function(name, Box::new(func)), span: kind_span, @@ -236,7 +237,13 @@ impl AtRule { span: kind_span, }, AtRuleKind::Include => Spanned { - node: AtRule::Include(eat_include(toks, scope, super_selector, content)?), + node: AtRule::Include(eat_include( + toks, + scope, + super_selector, + content, + kind_span, + )?), span: kind_span, }, AtRuleKind::Import => todo!("@import not yet implemented"), diff --git a/src/lib.rs b/src/lib.rs index cdecd37..f838fb1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -197,6 +197,7 @@ pub(crate) fn eat_expr>( scope, super_selector, String::new(), + tok.unwrap().pos, )?; return Ok(Some(Spanned { node: Style::from_tokens(toks, scope, super_selector, prop)?, @@ -207,7 +208,7 @@ pub(crate) fn eat_expr>( } } ';' => { - toks.next(); + let span_before = toks.next().unwrap().pos; devour_whitespace(toks); // special edge case where there was no space between the colon // in a style, e.g. `color:red`. todo: refactor @@ -223,7 +224,13 @@ pub(crate) fn eat_expr>( span, })); } - let property = Style::parse_property(&mut v, scope, super_selector, String::new())?; + let property = Style::parse_property( + &mut v, + scope, + super_selector, + String::new(), + span_before, + )?; let value = Style::parse_value(&mut v, scope, super_selector)?; return Ok(Some(Spanned { node: Expr::Style(Box::new(Style { property, value })), @@ -244,8 +251,13 @@ pub(crate) fn eat_expr>( // and no semicolon following the style // in a style `color:red`. todo: refactor let mut v = values.into_iter().peekmore(); - let property = - Style::parse_property(&mut v, scope, super_selector, String::new())?; + let property = Style::parse_property( + &mut v, + scope, + super_selector, + String::new(), + tok.pos, + )?; let value = Style::parse_value(&mut v, scope, super_selector)?; return Ok(Some(Spanned { node: Expr::Style(Box::new(Style { property, value })), @@ -327,10 +339,7 @@ pub(crate) fn eat_expr>( } '@' => { let span = toks.next().unwrap().pos(); - if toks.peek().is_none() { - return Err(("Expected identifier.", span).into()); - } - let Spanned { node: ident, span } = eat_ident(toks, scope, super_selector)?; + let Spanned { node: ident, span } = eat_ident(toks, scope, super_selector, span)?; devour_whitespace(toks); let rule = AtRule::from_tokens( &AtRuleKind::from(ident.as_str()), diff --git a/src/selector/attribute.rs b/src/selector/attribute.rs index 30f11a8..e2b3524 100644 --- a/src/selector/attribute.rs +++ b/src/selector/attribute.rs @@ -35,15 +35,16 @@ fn attribute_name>( return Err(("expected \"|\".", pos).into()); } - toks.next(); + let span_before = toks.next().unwrap().pos(); - let ident = eat_ident(toks, scope, super_selector)?.node; + let ident = eat_ident(toks, scope, super_selector, span_before)?.node; return Ok(QualifiedName { ident, namespace: Some('*'.to_string()), }); } - let name_or_namespace = eat_ident(toks, scope, super_selector)?; + let span_before = next.pos; + let name_or_namespace = eat_ident(toks, scope, super_selector, span_before)?; match toks.peek() { Some(v) if v.kind != '|' => { return Ok(QualifiedName { @@ -67,8 +68,8 @@ fn attribute_name>( } None => return Err(("expected more input.", name_or_namespace.span).into()), } - toks.next(); - let ident = eat_ident(toks, scope, super_selector)?.node; + let span_before = toks.next().unwrap().pos(); + let ident = eat_ident(toks, scope, super_selector, span_before)?.node; Ok(QualifiedName { ident, namespace: Some(name_or_namespace.node), @@ -118,7 +119,7 @@ impl Attribute { devour_whitespace(toks); let peek = toks.peek().ok_or(("expected more input.", start))?; - + let span_before = peek.pos; let value = match peek.kind { q @ '\'' | q @ '"' => { toks.next(); @@ -127,7 +128,7 @@ impl Attribute { _ => unreachable!(), } } - _ => eat_ident(toks, scope, super_selector)?.node, + _ => eat_ident(toks, scope, super_selector, span_before)?.node, }; devour_whitespace(toks); diff --git a/src/style.rs b/src/style.rs index 66a4071..71aac9a 100644 --- a/src/style.rs +++ b/src/style.rs @@ -1,6 +1,6 @@ use peekmore::PeekMoreIterator; -use codemap::Spanned; +use codemap::{Span, Spanned}; use crate::error::SassResult; use crate::scope::Scope; @@ -22,8 +22,9 @@ impl Style { scope: &Scope, super_selector: &Selector, super_property: String, + span_before: Span, ) -> SassResult { - StyleParser::new(scope, super_selector).parse_property(toks, super_property) + StyleParser::new(scope, super_selector).parse_property(toks, super_property, span_before) } pub fn to_string(&self) -> SassResult { @@ -95,10 +96,11 @@ impl<'a> StyleParser<'a> { while let Some(tok) = toks.peek() { match tok.kind { '{' => { - toks.next(); + let span_before = toks.next().unwrap().pos; devour_whitespace(toks); loop { - let property = self.parse_property(toks, super_property.clone())?; + let property = + self.parse_property(toks, super_property.clone(), span_before)?; if let Some(tok) = toks.peek() { if tok.kind == '{' { match self.eat_style_group(toks, property, scope)? { @@ -194,9 +196,10 @@ impl<'a> StyleParser<'a> { &self, toks: &mut PeekMoreIterator, mut super_property: String, + span_before: Span, ) -> SassResult { devour_whitespace(toks); - let property = eat_ident(toks, self.scope, self.super_selector)?.node; + let property = eat_ident(toks, self.scope, self.super_selector, span_before)?.node; devour_whitespace_or_comment(toks)?; if toks.peek().is_some() && toks.peek().unwrap().kind == ':' { toks.next(); diff --git a/src/stylesheet.rs b/src/stylesheet.rs index d25a291..87ef088 100644 --- a/src/stylesheet.rs +++ b/src/stylesheet.rs @@ -164,8 +164,13 @@ impl<'a> StyleSheetParser<'a> { continue; } '$' => { - self.lexer.next(); - let name = eat_ident(&mut self.lexer, &Scope::new(), &Selector::new())?; + let span_before = self.lexer.next().unwrap().pos(); + let name = eat_ident( + &mut self.lexer, + &Scope::new(), + &Selector::new(), + span_before + )?; devour_whitespace(&mut self.lexer); let Token { kind, pos } = self .lexer @@ -194,8 +199,13 @@ impl<'a> StyleSheetParser<'a> { } } '@' => { - self.lexer.next(); - let Spanned { node: at_rule_kind, span } = eat_ident(&mut self.lexer, &Scope::new(), &Selector::new())?; + let span_before = self.lexer.next().unwrap().pos(); + let Spanned { node: at_rule_kind, span } = eat_ident( + &mut self.lexer, + &Scope::new(), + &Selector::new(), + span_before + )?; if at_rule_kind.is_empty() { return Err(("Expected identifier.", span).into()); } @@ -205,6 +215,7 @@ impl<'a> StyleSheetParser<'a> { &Scope::new(), &Selector::new(), None, + span )?), AtRuleKind::Import => { devour_whitespace(&mut self.lexer); diff --git a/src/utils/strings.rs b/src/utils/strings.rs index dd38649..f6abfa2 100644 --- a/src/utils/strings.rs +++ b/src/utils/strings.rs @@ -169,9 +169,12 @@ pub(crate) fn eat_ident>( toks: &mut PeekMoreIterator, scope: &Scope, super_selector: &Selector, + span_before: Span, ) -> SassResult> { - // TODO: take span as param because we use unwrap here - let mut span = toks.peek().unwrap().pos(); + let mut span = toks + .peek() + .ok_or(("Expected identifier.", span_before))? + .pos(); let mut text = String::new(); if toks.peek().unwrap().kind == '-' { toks.next(); diff --git a/src/value/parse.rs b/src/value/parse.rs index eadfff0..e205395 100644 --- a/src/value/parse.rs +++ b/src/value/parse.rs @@ -52,7 +52,7 @@ fn parse_hex>( s.push(tok.kind); } } else { - let i = eat_ident(toks, scope, super_selector)?; + let i = eat_ident(toks, scope, super_selector, span)?; if i.node.chars().all(|c| c.is_ascii_hexdigit()) { s = i.node; span = span.merge(i.span); @@ -524,8 +524,9 @@ impl Value { toks: &mut PeekMoreIterator, scope: &Scope, super_selector: &Selector, + span_before: Span, ) -> SassResult { - let Spanned { node: mut s, span } = eat_ident(toks, scope, super_selector)?; + let Spanned { node: mut s, span } = eat_ident(toks, scope, super_selector, span_before)?; let lower = s.to_ascii_lowercase(); @@ -636,7 +637,7 @@ impl Value { || (!kind.is_ascii() && !kind.is_control()) || (kind == '-' && next_is_hypen(toks)) => { - return Some(Self::ident(toks, scope, super_selector)); + return Some(Self::ident(toks, scope, super_selector, span)); } '0'..='9' | '.' => { let Spanned { @@ -723,9 +724,10 @@ impl Value { }) } '#' => { - if let Some(Token { kind: '{', .. }) = toks.peek_forward(1) { + if let Some(Token { kind: '{', pos }) = toks.peek_forward(1) { + let span_before = *pos; toks.reset_view(); - return Some(Self::ident(toks, scope, super_selector)); + return Some(Self::ident(toks, scope, super_selector, span_before)); } toks.reset_view(); toks.next(); @@ -847,7 +849,7 @@ impl Value { }))); } devour_whitespace(toks); - let v = match eat_ident(toks, scope, super_selector) { + let v = match eat_ident(toks, scope, super_selector, span) { Ok(v) => v, Err(e) => return Some(Err(e)), };