disallow standalone @else

This commit is contained in:
ConnorSkees 2020-05-24 12:47:04 -04:00
parent 3c1c55038f
commit 3051cec45a
4 changed files with 30 additions and 18 deletions

View File

@ -1,3 +1,9 @@
use std::convert::TryFrom;
use codemap::Spanned;
use crate::error::SassError;
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum AtRuleKind {
// SASS specific @rules
@ -57,9 +63,10 @@ pub enum AtRuleKind {
Unknown(String),
}
impl From<&str> for AtRuleKind {
fn from(c: &str) -> Self {
match c.to_ascii_lowercase().as_str() {
impl TryFrom<&Spanned<String>> for AtRuleKind {
type Error = SassError;
fn try_from(c: &Spanned<String>) -> Result<Self, SassError> {
Ok(match c.node.to_ascii_lowercase().as_str() {
"use" => Self::Use,
"forward" => Self::Forward,
"import" => Self::Import,
@ -81,7 +88,9 @@ impl From<&str> for AtRuleKind {
"keyframes" => Self::Keyframes,
"content" => Self::Content,
"media" => Self::Media,
"else" => return Err(("This at-rule is not allowed here.", c.span).into()),
"" => return Err(("Expected identifier.", c.span).into()),
s => Self::Unknown(s.to_owned()),
}
})
}
}

View File

@ -82,6 +82,7 @@ grass input.scss
#![cfg_attr(feature = "nightly", feature(track_caller))]
#![cfg_attr(feature = "profiling", inline(never))]
use std::convert::TryFrom;
use std::iter::Iterator;
use codemap::{Span, Spanned};
@ -337,10 +338,10 @@ pub(crate) fn eat_expr<I: Iterator<Item = Token>>(
}
'@' => {
let span = toks.next().unwrap().pos();
let Spanned { node: ident, span } = eat_ident(toks, scope, super_selector, span)?;
let rule = eat_ident(toks, scope, super_selector, span)?;
devour_whitespace(toks);
let rule = AtRule::from_tokens(
AtRuleKind::from(ident.as_str()),
AtRuleKind::try_from(&rule)?,
span,
toks,
scope,

View File

@ -1,3 +1,4 @@
use std::convert::TryFrom;
use std::fs;
use std::iter::Iterator;
use std::path::Path;
@ -226,20 +227,14 @@ impl<'a> StyleSheetParser<'a> {
}
'@' => {
let span_before = self.lexer.next().unwrap().pos();
let Spanned {
node: at_rule_kind,
span,
} = eat_ident(self.lexer, &Scope::new(), &Selector::new(), span_before)?;
if at_rule_kind.is_empty() {
return Err(("Expected identifier.", span).into());
}
match AtRuleKind::from(at_rule_kind.as_str()) {
let rule = eat_ident(self.lexer, &Scope::new(), &Selector::new(), span_before)?;
match AtRuleKind::try_from(&rule)? {
AtRuleKind::Include => rules.extend(eat_include(
self.lexer,
&Scope::new(),
&Selector::new(),
None,
span,
rule.span,
)?),
AtRuleKind::Import => {
devour_whitespace(self.lexer);
@ -259,7 +254,7 @@ impl<'a> StyleSheetParser<'a> {
)?
.node
.unquote()
.to_css_string(span)?,
.to_css_string(rule.span)?,
);
}
_ => return Err(("Expected string.", next.pos()).into()),
@ -282,7 +277,7 @@ impl<'a> StyleSheetParser<'a> {
v => {
let rule = AtRule::from_tokens(
v,
span,
rule.span,
self.lexer,
&mut Scope::new(),
&Selector::new(),

View File

@ -200,4 +200,11 @@ error!(value_after_style, "a {}a", "Error: expected \"{\".");
test!(whitespace_after_style, "a {}\t\n ", "");
test!(toplevel_semicolon, ";", "");
test!(toplevel_semicolon_after_style, "a {};", "");
error!(nothing_after_hash_in_interpolated_ident_body, "a {color: foo#", "Error: Expected identifier.");
error!(
nothing_after_hash_in_interpolated_ident_body,
"a {color: foo#", "Error: Expected identifier."
);
error!(
at_else_alone,
"@else {}", "Error: This at-rule is not allowed here."
);