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)] #[derive(Clone, Debug, Eq, PartialEq)]
pub enum AtRuleKind { pub enum AtRuleKind {
// SASS specific @rules // SASS specific @rules
@ -57,9 +63,10 @@ pub enum AtRuleKind {
Unknown(String), Unknown(String),
} }
impl From<&str> for AtRuleKind { impl TryFrom<&Spanned<String>> for AtRuleKind {
fn from(c: &str) -> Self { type Error = SassError;
match c.to_ascii_lowercase().as_str() { fn try_from(c: &Spanned<String>) -> Result<Self, SassError> {
Ok(match c.node.to_ascii_lowercase().as_str() {
"use" => Self::Use, "use" => Self::Use,
"forward" => Self::Forward, "forward" => Self::Forward,
"import" => Self::Import, "import" => Self::Import,
@ -81,7 +88,9 @@ impl From<&str> for AtRuleKind {
"keyframes" => Self::Keyframes, "keyframes" => Self::Keyframes,
"content" => Self::Content, "content" => Self::Content,
"media" => Self::Media, "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()), 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 = "nightly", feature(track_caller))]
#![cfg_attr(feature = "profiling", inline(never))] #![cfg_attr(feature = "profiling", inline(never))]
use std::convert::TryFrom;
use std::iter::Iterator; use std::iter::Iterator;
use codemap::{Span, Spanned}; use codemap::{Span, Spanned};
@ -337,10 +338,10 @@ pub(crate) fn eat_expr<I: Iterator<Item = Token>>(
} }
'@' => { '@' => {
let span = toks.next().unwrap().pos(); 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); devour_whitespace(toks);
let rule = AtRule::from_tokens( let rule = AtRule::from_tokens(
AtRuleKind::from(ident.as_str()), AtRuleKind::try_from(&rule)?,
span, span,
toks, toks,
scope, scope,

View File

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