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>> {
|
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()
|
MediaQueryParser::new(toks).parse()
|
||||||
}
|
}
|
||||||
|
@ -204,7 +204,7 @@ pub(crate) struct AstExtendRule {
|
|||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub(crate) struct AstAtRootRule {
|
pub(crate) struct AstAtRootRule {
|
||||||
pub children: Vec<AstStmt>,
|
pub children: Vec<AstStmt>,
|
||||||
pub query: Option<Interpolation>,
|
pub query: Option<Spanned<Interpolation>>,
|
||||||
#[allow(unused)]
|
#[allow(unused)]
|
||||||
pub span: Span,
|
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>> {
|
fn visit_at_root_rule(&mut self, mut at_root_rule: AstAtRootRule) -> SassResult<Option<Value>> {
|
||||||
let query = match at_root_rule.query.clone() {
|
let query = match at_root_rule.query.clone() {
|
||||||
Some(val) => {
|
Some(query) => {
|
||||||
let resolved = self.perform_interpolation(val, true)?;
|
let resolved = self.perform_interpolation(query.node, true)?;
|
||||||
|
|
||||||
|
let span = query.span;
|
||||||
|
|
||||||
let query_toks = Lexer::new(
|
let query_toks = Lexer::new(
|
||||||
resolved
|
resolved.chars().map(|x| Token::new(span, x)).collect(),
|
||||||
.chars()
|
span,
|
||||||
.map(|x| Token::new(self.span_before, x))
|
|
||||||
.collect(),
|
|
||||||
);
|
);
|
||||||
|
|
||||||
AtRootQueryParser::new(query_toks).parse()?
|
AtRootQueryParser::new(query_toks).parse()?
|
||||||
@ -1137,7 +1137,10 @@ impl<'a> Visitor<'a> {
|
|||||||
allows_placeholder: bool,
|
allows_placeholder: bool,
|
||||||
span: Span,
|
span: Span,
|
||||||
) -> SassResult<SelectorList> {
|
) -> 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()
|
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)?;
|
let selector_text = self.interpolation_to_value(ruleset_selector, true, true)?;
|
||||||
|
|
||||||
if self.flags.in_keyframes() {
|
if self.flags.in_keyframes() {
|
||||||
|
let span = ruleset.selector_span;
|
||||||
let sel_toks = Lexer::new(
|
let sel_toks = Lexer::new(
|
||||||
selector_text
|
selector_text.chars().map(|x| Token::new(span, x)).collect(),
|
||||||
.chars()
|
span,
|
||||||
.map(|x| Token::new(self.span_before, x))
|
|
||||||
.collect(),
|
|
||||||
);
|
);
|
||||||
let parsed_selector =
|
let parsed_selector =
|
||||||
KeyframesSelectorParser::new(sel_toks).parse_keyframes_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';
|
const FORM_FEED: char = '\x0C';
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
|
// todo: remove lifetime as Cow is now superfluous
|
||||||
pub(crate) struct Lexer<'a> {
|
pub(crate) struct Lexer<'a> {
|
||||||
buf: Cow<'a, [Token]>,
|
buf: Cow<'a, [Token]>,
|
||||||
|
/// The span to be used in the case that `buf` is empty
|
||||||
|
empty_span: Span,
|
||||||
cursor: usize,
|
cursor: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -37,19 +40,23 @@ impl<'a> Lexer<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn prev_span(&self) -> Span {
|
pub fn prev_span(&self) -> Span {
|
||||||
self.buf
|
match self.buf.get(self.cursor.saturating_sub(1)) {
|
||||||
.get(self.cursor.saturating_sub(1))
|
Some(tok) => tok.pos,
|
||||||
.copied()
|
None => match self.buf.last() {
|
||||||
.unwrap_or_else(|| self.buf.last().copied().unwrap())
|
Some(tok) => tok.pos,
|
||||||
.pos
|
None => self.empty_span,
|
||||||
|
},
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn current_span(&self) -> Span {
|
pub fn current_span(&self) -> Span {
|
||||||
self.buf
|
match self.buf.get(self.cursor) {
|
||||||
.get(self.cursor)
|
Some(tok) => tok.pos,
|
||||||
.copied()
|
None => match self.buf.last() {
|
||||||
.unwrap_or_else(|| self.buf.last().copied().unwrap())
|
Some(tok) => tok.pos,
|
||||||
.pos
|
None => self.empty_span,
|
||||||
|
},
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn peek(&self) -> Option<Token> {
|
pub fn peek(&self) -> Option<Token> {
|
||||||
@ -131,13 +138,14 @@ impl<'a> Lexer<'a> {
|
|||||||
}
|
}
|
||||||
.collect();
|
.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 {
|
Lexer {
|
||||||
buf: Cow::Owned(buf),
|
buf: Cow::Owned(buf),
|
||||||
cursor: 0,
|
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> {
|
fn parse_at_root_rule(&mut self, start: usize) -> SassResult<AstStmt> {
|
||||||
Ok(AstStmt::AtRootRule(if self.toks_mut().next_char_is('(') {
|
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 = self.parse_at_root_query()?;
|
||||||
|
let query_span = self.toks_mut().span_from(query_start);
|
||||||
self.whitespace()?;
|
self.whitespace()?;
|
||||||
let children = self.with_children(Self::parse_statement)?.node;
|
let children = self.with_children(Self::parse_statement)?.node;
|
||||||
|
|
||||||
AstAtRootRule {
|
AstAtRootRule {
|
||||||
query: Some(query),
|
query: Some(Spanned {
|
||||||
|
node: query,
|
||||||
|
span: query_span,
|
||||||
|
}),
|
||||||
children,
|
children,
|
||||||
span: self.toks_mut().span_from(start),
|
span: self.toks_mut().span_from(start),
|
||||||
}
|
}
|
||||||
@ -1521,6 +1526,7 @@ pub(crate) trait StylesheetParser<'a>: BaseParser<'a> + Sized {
|
|||||||
.chars()
|
.chars()
|
||||||
.map(|x| Token::new(self.span_before(), x))
|
.map(|x| Token::new(self.span_before(), x))
|
||||||
.collect(),
|
.collect(),
|
||||||
|
self.span_before(),
|
||||||
);
|
);
|
||||||
|
|
||||||
// if namespace is empty, avoid attempting to parse an identifier from
|
// 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: @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."#
|
r#"Error: Expected digit."#
|
||||||
);
|
);
|
||||||
|
error!(
|
||||||
|
selector_is_empty_after_interpolation_is_resolved,
|
||||||
|
"@keyframes foo {
|
||||||
|
#{null} {}
|
||||||
|
}",
|
||||||
|
r#"Error: Expected number."#
|
||||||
|
);
|
||||||
|
|
||||||
// todo: span for this
|
// todo: span for this
|
||||||
// @keyframes foo {
|
// @keyframes foo {
|
||||||
|
@ -570,3 +570,8 @@ error!(
|
|||||||
}"#,
|
}"#,
|
||||||
"Error: expected no more input."
|
"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