more robust handling of empty input after resolving interpolation
This commit is contained in:
parent
bba405392c
commit
bb937ae84f
@ -54,7 +54,7 @@ impl MediaQuery {
|
||||
}
|
||||
|
||||
pub fn parse_list(list: &str, span: Span) -> SassResult<Vec<Self>> {
|
||||
let toks = Lexer::new(list.chars().map(|x| Token::new(span, x)).collect());
|
||||
let toks = Lexer::new(list.chars().map(|x| Token::new(span, x)).collect(), span);
|
||||
|
||||
MediaQueryParser::new(toks).parse()
|
||||
}
|
||||
|
@ -204,7 +204,7 @@ pub(crate) struct AstExtendRule {
|
||||
#[derive(Debug, Clone)]
|
||||
pub(crate) struct AstAtRootRule {
|
||||
pub children: Vec<AstStmt>,
|
||||
pub query: Option<Interpolation>,
|
||||
pub query: Option<Spanned<Interpolation>>,
|
||||
#[allow(unused)]
|
||||
pub span: Span,
|
||||
}
|
||||
|
@ -969,14 +969,14 @@ impl<'a> Visitor<'a> {
|
||||
|
||||
fn visit_at_root_rule(&mut self, mut at_root_rule: AstAtRootRule) -> SassResult<Option<Value>> {
|
||||
let query = match at_root_rule.query.clone() {
|
||||
Some(val) => {
|
||||
let resolved = self.perform_interpolation(val, true)?;
|
||||
Some(query) => {
|
||||
let resolved = self.perform_interpolation(query.node, true)?;
|
||||
|
||||
let span = query.span;
|
||||
|
||||
let query_toks = Lexer::new(
|
||||
resolved
|
||||
.chars()
|
||||
.map(|x| Token::new(self.span_before, x))
|
||||
.collect(),
|
||||
resolved.chars().map(|x| Token::new(span, x)).collect(),
|
||||
span,
|
||||
);
|
||||
|
||||
AtRootQueryParser::new(query_toks).parse()?
|
||||
@ -1137,7 +1137,10 @@ impl<'a> Visitor<'a> {
|
||||
allows_placeholder: bool,
|
||||
span: Span,
|
||||
) -> SassResult<SelectorList> {
|
||||
let sel_toks = Lexer::new(selector_text.chars().map(|x| Token::new(span, x)).collect());
|
||||
let sel_toks = Lexer::new(
|
||||
selector_text.chars().map(|x| Token::new(span, x)).collect(),
|
||||
span,
|
||||
);
|
||||
|
||||
SelectorParser::new(sel_toks, allows_parent, allows_placeholder, span).parse()
|
||||
}
|
||||
@ -2742,11 +2745,10 @@ impl<'a> Visitor<'a> {
|
||||
let selector_text = self.interpolation_to_value(ruleset_selector, true, true)?;
|
||||
|
||||
if self.flags.in_keyframes() {
|
||||
let span = ruleset.selector_span;
|
||||
let sel_toks = Lexer::new(
|
||||
selector_text
|
||||
.chars()
|
||||
.map(|x| Token::new(self.span_before, x))
|
||||
.collect(),
|
||||
selector_text.chars().map(|x| Token::new(span, x)).collect(),
|
||||
span,
|
||||
);
|
||||
let parsed_selector =
|
||||
KeyframesSelectorParser::new(sel_toks).parse_keyframes_selector()?;
|
||||
|
32
src/lexer.rs
32
src/lexer.rs
@ -7,8 +7,11 @@ use crate::Token;
|
||||
const FORM_FEED: char = '\x0C';
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
// todo: remove lifetime as Cow is now superfluous
|
||||
pub(crate) struct Lexer<'a> {
|
||||
buf: Cow<'a, [Token]>,
|
||||
/// The span to be used in the case that `buf` is empty
|
||||
empty_span: Span,
|
||||
cursor: usize,
|
||||
}
|
||||
|
||||
@ -37,19 +40,23 @@ impl<'a> Lexer<'a> {
|
||||
}
|
||||
|
||||
pub fn prev_span(&self) -> Span {
|
||||
self.buf
|
||||
.get(self.cursor.saturating_sub(1))
|
||||
.copied()
|
||||
.unwrap_or_else(|| self.buf.last().copied().unwrap())
|
||||
.pos
|
||||
match self.buf.get(self.cursor.saturating_sub(1)) {
|
||||
Some(tok) => tok.pos,
|
||||
None => match self.buf.last() {
|
||||
Some(tok) => tok.pos,
|
||||
None => self.empty_span,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn current_span(&self) -> Span {
|
||||
self.buf
|
||||
.get(self.cursor)
|
||||
.copied()
|
||||
.unwrap_or_else(|| self.buf.last().copied().unwrap())
|
||||
.pos
|
||||
match self.buf.get(self.cursor) {
|
||||
Some(tok) => tok.pos,
|
||||
None => match self.buf.last() {
|
||||
Some(tok) => tok.pos,
|
||||
None => self.empty_span,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn peek(&self) -> Option<Token> {
|
||||
@ -131,13 +138,14 @@ impl<'a> Lexer<'a> {
|
||||
}
|
||||
.collect();
|
||||
|
||||
Self::new(buf)
|
||||
Self::new(buf, file.span.subspan(0, 0))
|
||||
}
|
||||
|
||||
pub fn new(buf: Vec<Token>) -> Self {
|
||||
pub fn new(buf: Vec<Token>, empty_span: Span) -> Self {
|
||||
Lexer {
|
||||
buf: Cow::Owned(buf),
|
||||
cursor: 0,
|
||||
empty_span,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -325,12 +325,17 @@ pub(crate) trait StylesheetParser<'a>: BaseParser<'a> + Sized {
|
||||
|
||||
fn parse_at_root_rule(&mut self, start: usize) -> SassResult<AstStmt> {
|
||||
Ok(AstStmt::AtRootRule(if self.toks_mut().next_char_is('(') {
|
||||
let query_start = self.toks().cursor();
|
||||
let query = self.parse_at_root_query()?;
|
||||
let query_span = self.toks_mut().span_from(query_start);
|
||||
self.whitespace()?;
|
||||
let children = self.with_children(Self::parse_statement)?.node;
|
||||
|
||||
AstAtRootRule {
|
||||
query: Some(query),
|
||||
query: Some(Spanned {
|
||||
node: query,
|
||||
span: query_span,
|
||||
}),
|
||||
children,
|
||||
span: self.toks_mut().span_from(start),
|
||||
}
|
||||
@ -1521,6 +1526,7 @@ pub(crate) trait StylesheetParser<'a>: BaseParser<'a> + Sized {
|
||||
.chars()
|
||||
.map(|x| Token::new(self.span_before(), x))
|
||||
.collect(),
|
||||
self.span_before(),
|
||||
);
|
||||
|
||||
// if namespace is empty, avoid attempting to parse an identifier from
|
||||
|
@ -279,3 +279,12 @@ error!(
|
||||
}",
|
||||
"Error: @extend may only be used within style rules."
|
||||
);
|
||||
error!(
|
||||
selector_is_empty_after_interpolation_is_resolved,
|
||||
"@at-root #{null} {}", "Error: expected selector."
|
||||
);
|
||||
error!(
|
||||
// todo: dart-sass gives error r#"Error: Expected "with" or "without"."#
|
||||
query_is_empty_parens_after_interpolation_is_resolved,
|
||||
"@at-root (#{null}) {}", r#"Error: Expected "without"."#
|
||||
);
|
||||
|
@ -327,6 +327,13 @@ error!(
|
||||
}",
|
||||
r#"Error: Expected digit."#
|
||||
);
|
||||
error!(
|
||||
selector_is_empty_after_interpolation_is_resolved,
|
||||
"@keyframes foo {
|
||||
#{null} {}
|
||||
}",
|
||||
r#"Error: Expected number."#
|
||||
);
|
||||
|
||||
// todo: span for this
|
||||
// @keyframes foo {
|
||||
|
@ -570,3 +570,8 @@ error!(
|
||||
}"#,
|
||||
"Error: expected no more input."
|
||||
);
|
||||
error!(
|
||||
empty_query_after_resolving_interpolation,
|
||||
"@media #{null} {}",
|
||||
"Error: expected no more input."
|
||||
);
|
||||
|
Loading…
x
Reference in New Issue
Block a user