diff --git a/CHANGELOG.md b/CHANGELOG.md index 6831f9a..3bdc0e2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,7 @@ # 0.9.1 + - 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 diff --git a/src/parse/common.rs b/src/parse/common.rs index d8bbaa5..a34c093 100644 --- a/src/parse/common.rs +++ b/src/parse/common.rs @@ -25,6 +25,14 @@ impl NeverEmptyVec { 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) { self.rest.push(value) } diff --git a/src/parse/function.rs b/src/parse/function.rs index efafff8..a8f26a1 100644 --- a/src/parse/function.rs +++ b/src/parse/function.rs @@ -3,7 +3,6 @@ use std::mem; use codemap::Spanned; use peekmore::PeekMore; -use super::{Parser, Stmt}; use crate::{ args::CallArgs, atrule::Function, @@ -13,6 +12,8 @@ use crate::{ Token, }; +use super::{NeverEmptyVec, Parser, Stmt}; + impl<'a> Parser<'a> { pub(super) fn parse_function(&mut self) -> SassResult<()> { if self.in_mixin { @@ -62,13 +63,12 @@ impl<'a> Parser<'a> { pub fn eval_function(&mut self, mut function: Function, args: CallArgs) -> SassResult { self.eval_fn_args(&mut function, args)?; - self.scopes.push(function.scope); let mut return_value = Parser { toks: &mut function.body.into_iter().peekmore(), map: self.map, path: self.path, - scopes: self.scopes, + scopes: &mut NeverEmptyVec::new(function.scope), global_scope: self.global_scope, super_selectors: self.super_selectors, span_before: self.span_before, @@ -81,8 +81,6 @@ impl<'a> Parser<'a> { } .parse()?; - self.scopes.pop(); - debug_assert!(return_value.len() <= 1); match return_value .pop() diff --git a/src/parse/mixin.rs b/src/parse/mixin.rs index 0a34aec..9a14f9e 100644 --- a/src/parse/mixin.rs +++ b/src/parse/mixin.rs @@ -6,13 +6,12 @@ use crate::{ args::{CallArgs, FuncArgs}, atrule::Mixin, error::SassResult, - scope::Scope, utils::read_until_closing_curly_brace, value::Value, Token, }; -use super::{Parser, Stmt}; +use super::{NeverEmptyVec, Parser, Stmt}; impl<'a> Parser<'a> { 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 // 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 { 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)?; self.eval_mixin_args(&mut mixin, args)?; - self.scopes.push(mixin.scope); - let body = Parser { toks: &mut mixin.body, map: self.map, path: self.path, - scopes: self.scopes, + scopes: &mut NeverEmptyVec::new(mixin.scope), global_scope: self.global_scope, super_selectors: self.super_selectors, span_before: self.span_before, @@ -116,8 +113,6 @@ impl<'a> Parser<'a> { } .parse()?; - self.scopes.pop(); - Ok(body) } diff --git a/src/parse/variable.rs b/src/parse/variable.rs index 2321f91..e95e5a1 100644 --- a/src/parse/variable.rs +++ b/src/parse/variable.rs @@ -81,6 +81,11 @@ impl<'a> Parser<'a> { 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)?; } Ok(()) diff --git a/tests/functions.rs b/tests/functions.rs index aa8d96c..783a040 100644 --- a/tests/functions.rs +++ b/tests/functions.rs @@ -99,3 +99,25 @@ error!( body_missing_closing_curly_brace, "@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" +);