resolve regression in @mixin
scoping
This commit is contained in:
parent
947b4a3e15
commit
1a5301d0fa
@ -1,6 +1,7 @@
|
|||||||
# 0.9.1
|
# 0.9.1
|
||||||
|
|
||||||
- fix regression in which `@at-root` would panic when placed after a ruleset
|
- fix regression in which `@at-root` would panic when placed after a ruleset
|
||||||
- fix regression related to `@mixin` scoping and outer, local variables
|
- fix regression related to `@mixin` and `@function` scoping when combined with outer, local variables
|
||||||
|
|
||||||
# 0.9.0
|
# 0.9.0
|
||||||
|
|
||||||
|
@ -25,6 +25,14 @@ impl<T> NeverEmptyVec<T> {
|
|||||||
self.rest.last_mut().unwrap_or(&mut self.first)
|
self.rest.last_mut().unwrap_or(&mut self.first)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn first(&mut self) -> &T {
|
||||||
|
&self.first
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn first_mut(&mut self) -> &mut T {
|
||||||
|
&mut self.first
|
||||||
|
}
|
||||||
|
|
||||||
pub fn push(&mut self, value: T) {
|
pub fn push(&mut self, value: T) {
|
||||||
self.rest.push(value)
|
self.rest.push(value)
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,6 @@ use std::mem;
|
|||||||
use codemap::Spanned;
|
use codemap::Spanned;
|
||||||
use peekmore::PeekMore;
|
use peekmore::PeekMore;
|
||||||
|
|
||||||
use super::{Parser, Stmt};
|
|
||||||
use crate::{
|
use crate::{
|
||||||
args::CallArgs,
|
args::CallArgs,
|
||||||
atrule::Function,
|
atrule::Function,
|
||||||
@ -13,6 +12,8 @@ use crate::{
|
|||||||
Token,
|
Token,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use super::{NeverEmptyVec, Parser, Stmt};
|
||||||
|
|
||||||
impl<'a> Parser<'a> {
|
impl<'a> Parser<'a> {
|
||||||
pub(super) fn parse_function(&mut self) -> SassResult<()> {
|
pub(super) fn parse_function(&mut self) -> SassResult<()> {
|
||||||
if self.in_mixin {
|
if self.in_mixin {
|
||||||
@ -62,13 +63,12 @@ impl<'a> Parser<'a> {
|
|||||||
|
|
||||||
pub fn eval_function(&mut self, mut function: Function, args: CallArgs) -> SassResult<Value> {
|
pub fn eval_function(&mut self, mut function: Function, args: CallArgs) -> SassResult<Value> {
|
||||||
self.eval_fn_args(&mut function, args)?;
|
self.eval_fn_args(&mut function, args)?;
|
||||||
self.scopes.push(function.scope);
|
|
||||||
|
|
||||||
let mut return_value = Parser {
|
let mut return_value = Parser {
|
||||||
toks: &mut function.body.into_iter().peekmore(),
|
toks: &mut function.body.into_iter().peekmore(),
|
||||||
map: self.map,
|
map: self.map,
|
||||||
path: self.path,
|
path: self.path,
|
||||||
scopes: self.scopes,
|
scopes: &mut NeverEmptyVec::new(function.scope),
|
||||||
global_scope: self.global_scope,
|
global_scope: self.global_scope,
|
||||||
super_selectors: self.super_selectors,
|
super_selectors: self.super_selectors,
|
||||||
span_before: self.span_before,
|
span_before: self.span_before,
|
||||||
@ -81,8 +81,6 @@ impl<'a> Parser<'a> {
|
|||||||
}
|
}
|
||||||
.parse()?;
|
.parse()?;
|
||||||
|
|
||||||
self.scopes.pop();
|
|
||||||
|
|
||||||
debug_assert!(return_value.len() <= 1);
|
debug_assert!(return_value.len() <= 1);
|
||||||
match return_value
|
match return_value
|
||||||
.pop()
|
.pop()
|
||||||
|
@ -6,13 +6,12 @@ use crate::{
|
|||||||
args::{CallArgs, FuncArgs},
|
args::{CallArgs, FuncArgs},
|
||||||
atrule::Mixin,
|
atrule::Mixin,
|
||||||
error::SassResult,
|
error::SassResult,
|
||||||
scope::Scope,
|
|
||||||
utils::read_until_closing_curly_brace,
|
utils::read_until_closing_curly_brace,
|
||||||
value::Value,
|
value::Value,
|
||||||
Token,
|
Token,
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::{Parser, Stmt};
|
use super::{NeverEmptyVec, Parser, Stmt};
|
||||||
|
|
||||||
impl<'a> Parser<'a> {
|
impl<'a> Parser<'a> {
|
||||||
pub(super) fn parse_mixin(&mut self) -> SassResult<()> {
|
pub(super) fn parse_mixin(&mut self) -> SassResult<()> {
|
||||||
@ -41,7 +40,7 @@ impl<'a> Parser<'a> {
|
|||||||
// this is blocked on figuring out just how to check for this. presumably we could have a check
|
// this is blocked on figuring out just how to check for this. presumably we could have a check
|
||||||
// not when parsing initially, but rather when `@include`ing to see if an `@content` was found.
|
// not when parsing initially, but rather when `@include`ing to see if an `@content` was found.
|
||||||
|
|
||||||
let mixin = Mixin::new(Scope::new(), args, body, false);
|
let mixin = Mixin::new(self.scopes.last().clone(), args, body, false);
|
||||||
|
|
||||||
if self.at_root {
|
if self.at_root {
|
||||||
self.global_scope.insert_mixin(name, mixin);
|
self.global_scope.insert_mixin(name, mixin);
|
||||||
@ -97,13 +96,11 @@ impl<'a> Parser<'a> {
|
|||||||
let mut mixin = self.scopes.last().get_mixin(name, self.global_scope)?;
|
let mut mixin = self.scopes.last().get_mixin(name, self.global_scope)?;
|
||||||
self.eval_mixin_args(&mut mixin, args)?;
|
self.eval_mixin_args(&mut mixin, args)?;
|
||||||
|
|
||||||
self.scopes.push(mixin.scope);
|
|
||||||
|
|
||||||
let body = Parser {
|
let body = Parser {
|
||||||
toks: &mut mixin.body,
|
toks: &mut mixin.body,
|
||||||
map: self.map,
|
map: self.map,
|
||||||
path: self.path,
|
path: self.path,
|
||||||
scopes: self.scopes,
|
scopes: &mut NeverEmptyVec::new(mixin.scope),
|
||||||
global_scope: self.global_scope,
|
global_scope: self.global_scope,
|
||||||
super_selectors: self.super_selectors,
|
super_selectors: self.super_selectors,
|
||||||
span_before: self.span_before,
|
span_before: self.span_before,
|
||||||
@ -116,8 +113,6 @@ impl<'a> Parser<'a> {
|
|||||||
}
|
}
|
||||||
.parse()?;
|
.parse()?;
|
||||||
|
|
||||||
self.scopes.pop();
|
|
||||||
|
|
||||||
Ok(body)
|
Ok(body)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -81,6 +81,11 @@ impl<'a> Parser<'a> {
|
|||||||
scope.insert_var(ident.clone(), value.value.clone())?;
|
scope.insert_var(ident.clone(), value.value.clone())?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if self.scopes.first().var_exists_no_global(&ident) {
|
||||||
|
self.scopes
|
||||||
|
.first_mut()
|
||||||
|
.insert_var(ident.clone(), value.value.clone())?;
|
||||||
|
}
|
||||||
self.scopes.last_mut().insert_var(ident, value.value)?;
|
self.scopes.last_mut().insert_var(ident, value.value)?;
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -99,3 +99,25 @@ error!(
|
|||||||
body_missing_closing_curly_brace,
|
body_missing_closing_curly_brace,
|
||||||
"@function foo() {", "Error: expected \"}\"."
|
"@function foo() {", "Error: expected \"}\"."
|
||||||
);
|
);
|
||||||
|
test!(
|
||||||
|
does_not_modify_local_variables,
|
||||||
|
"@function bar($color-name) {
|
||||||
|
@if $color-name==bar {
|
||||||
|
@error bar;
|
||||||
|
}
|
||||||
|
|
||||||
|
$color: bar;
|
||||||
|
@return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@function foo($a, $b) {
|
||||||
|
@return \"success!\";
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
$color: foo;
|
||||||
|
|
||||||
|
color: foo(bar($color), bar($color));
|
||||||
|
}",
|
||||||
|
"a {\n color: \"success!\";\n}\n"
|
||||||
|
);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user