diff --git a/src/atrule/mixin.rs b/src/atrule/mixin.rs index 44bb018..22ca5a0 100644 --- a/src/atrule/mixin.rs +++ b/src/atrule/mixin.rs @@ -1,4 +1,4 @@ -use crate::{args::FuncArgs, scope::Scopes, Token}; +use crate::{args::FuncArgs, Token}; #[derive(Debug, Clone)] pub(crate) struct Mixin { @@ -28,7 +28,6 @@ impl Mixin { pub(crate) struct Content { pub content: Option>, pub content_args: Option, - pub scopes: Scopes, } impl Content { @@ -36,7 +35,6 @@ impl Content { Self { content: None, content_args: None, - scopes: Scopes::new(), } } } diff --git a/src/lib.rs b/src/lib.rs index c312f94..428878d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -157,6 +157,7 @@ pub fn from_path(p: &str) -> Result { at_root: true, at_root_has_selector: false, extender: &mut Extender::new(empty_span), + content_scopes: &mut Scopes::new(), } .parse() .map_err(|e| raw_to_parse_error(&map, *e))?; @@ -199,6 +200,7 @@ pub fn from_string(p: String) -> Result { at_root: true, at_root_has_selector: false, extender: &mut Extender::new(empty_span), + content_scopes: &mut Scopes::new(), } .parse() .map_err(|e| raw_to_parse_error(&map, *e))?; @@ -232,6 +234,7 @@ pub fn from_string(p: String) -> std::result::Result { at_root: true, at_root_has_selector: false, extender: &mut Extender::new(empty_span), + content_scopes: &mut Scopes::new(), } .parse() .map_err(|e| raw_to_parse_error(&map, *e).to_string())?; diff --git a/src/parse/args.rs b/src/parse/args.rs index 30fae5b..1bda379 100644 --- a/src/parse/args.rs +++ b/src/parse/args.rs @@ -357,6 +357,10 @@ impl<'a> Parser<'a> { mut args: CallArgs, ) -> SassResult { let mut scope = Scope::new(); + if fn_args.0.is_empty() { + args.max_args(0)?; + return Ok(scope); + } self.scopes.enter_new_scope(); for (idx, arg) in fn_args.0.iter_mut().enumerate() { if arg.is_variadic { diff --git a/src/parse/control_flow.rs b/src/parse/control_flow.rs index 79fc7ab..3bd85a0 100644 --- a/src/parse/control_flow.rs +++ b/src/parse/control_flow.rs @@ -51,6 +51,7 @@ impl<'a> Parser<'a> { at_root: self.at_root, at_root_has_selector: self.at_root_has_selector, extender: self.extender, + content_scopes: self.content_scopes, } .parse_stmt()?; } else { @@ -108,6 +109,7 @@ impl<'a> Parser<'a> { at_root: self.at_root, at_root_has_selector: self.at_root_has_selector, extender: self.extender, + content_scopes: self.content_scopes, } .parse_stmt()?; } else { @@ -134,6 +136,7 @@ impl<'a> Parser<'a> { at_root: self.at_root, at_root_has_selector: self.at_root_has_selector, extender: self.extender, + content_scopes: self.content_scopes, } .parse_stmt(); } @@ -312,6 +315,7 @@ impl<'a> Parser<'a> { at_root: self.at_root, at_root_has_selector: self.at_root_has_selector, extender: self.extender, + content_scopes: self.content_scopes, } .parse()?; if !these_stmts.is_empty() { @@ -332,6 +336,7 @@ impl<'a> Parser<'a> { at_root: self.at_root, at_root_has_selector: self.at_root_has_selector, extender: self.extender, + content_scopes: self.content_scopes, } .parse()?, ); @@ -380,6 +385,7 @@ impl<'a> Parser<'a> { at_root: self.at_root, at_root_has_selector: self.at_root_has_selector, extender: self.extender, + content_scopes: self.content_scopes, } .parse()?; if !these_stmts.is_empty() { @@ -400,6 +406,7 @@ impl<'a> Parser<'a> { at_root: self.at_root, at_root_has_selector: self.at_root_has_selector, extender: self.extender, + content_scopes: self.content_scopes, } .parse()?, ); @@ -496,6 +503,7 @@ impl<'a> Parser<'a> { at_root: self.at_root, at_root_has_selector: self.at_root_has_selector, extender: self.extender, + content_scopes: self.content_scopes, } .parse()?; if !these_stmts.is_empty() { @@ -516,6 +524,7 @@ impl<'a> Parser<'a> { at_root: self.at_root, at_root_has_selector: self.at_root_has_selector, extender: self.extender, + content_scopes: self.content_scopes, } .parse()?, ); diff --git a/src/parse/function.rs b/src/parse/function.rs index 187d0bc..2dc5296 100644 --- a/src/parse/function.rs +++ b/src/parse/function.rs @@ -106,6 +106,7 @@ impl<'a> Parser<'a> { at_root: false, at_root_has_selector: self.at_root_has_selector, extender: self.extender, + content_scopes: self.content_scopes, } .parse()?; diff --git a/src/parse/import.rs b/src/parse/import.rs index 5ae3699..eb89660 100644 --- a/src/parse/import.rs +++ b/src/parse/import.rs @@ -90,6 +90,7 @@ impl<'a> Parser<'a> { at_root: self.at_root, at_root_has_selector: self.at_root_has_selector, extender: self.extender, + content_scopes: self.content_scopes, } .parse(); } diff --git a/src/parse/keyframes.rs b/src/parse/keyframes.rs index b5c15af..67519b9 100644 --- a/src/parse/keyframes.rs +++ b/src/parse/keyframes.rs @@ -164,6 +164,7 @@ impl<'a> Parser<'a> { at_root: self.at_root, at_root_has_selector: self.at_root_has_selector, extender: self.extender, + content_scopes: self.content_scopes, }) .parse_keyframes_selector()?; @@ -193,6 +194,7 @@ impl<'a> Parser<'a> { at_root: false, at_root_has_selector: self.at_root_has_selector, extender: self.extender, + content_scopes: self.content_scopes, } .parse_stmt()?; diff --git a/src/parse/mixin.rs b/src/parse/mixin.rs index 087ebde..e737ac0 100644 --- a/src/parse/mixin.rs +++ b/src/parse/mixin.rs @@ -1,3 +1,5 @@ +use std::mem; + use codemap::Spanned; use peekmore::PeekMore; @@ -6,7 +8,6 @@ use crate::{ args::{CallArgs, FuncArgs}, atrule::{Content, Mixin}, error::SassResult, - scope::Scopes, utils::read_until_closing_curly_brace, Token, }; @@ -121,30 +122,32 @@ impl<'a> Parser<'a> { let scope = self.eval_args(fn_args, args)?; - let mut new_scope = Scopes::new(); - let mut entered_scope = false; - if declared_at_root { - new_scope.enter_scope(scope); - } else { - entered_scope = true; - self.scopes.enter_scope(scope); - }; + let mut new_scope = std::mem::take(self.content_scopes); self.content.push(Content { content, content_args, - scopes: self.scopes.clone(), }); + let mut scopes = if declared_at_root { + mem::take(&mut new_scope) + } else { + mem::take(self.scopes) + }; + + let mut content_scopes = if declared_at_root { + mem::take(self.scopes) + } else { + mem::take(&mut new_scope) + }; + + scopes.enter_scope(scope); + let body = Parser { toks: &mut body.into_iter().peekmore(), map: self.map, path: self.path, - scopes: if declared_at_root { - &mut new_scope - } else { - self.scopes - }, + scopes: &mut scopes, global_scope: self.global_scope, super_selectors: self.super_selectors, span_before: self.span_before, @@ -153,12 +156,18 @@ impl<'a> Parser<'a> { at_root: false, at_root_has_selector: self.at_root_has_selector, extender: self.extender, + content_scopes: &mut content_scopes, } .parse()?; self.content.pop(); - if entered_scope { - self.scopes.exit_scope(); + if declared_at_root { + *self.scopes = content_scopes; + *self.content_scopes = scopes; + } else { + scopes.exit_scope(); + *self.scopes = scopes; + *self.content_scopes = content_scopes; } Ok(body) @@ -173,19 +182,14 @@ impl<'a> Parser<'a> { .into()); } - let mut scope = self - .content - .last() - .cloned() - .unwrap_or_else(Content::new) - .scopes; if let Some(Token { kind: '(', .. }) = self.toks.peek() { self.toks.next(); let args = self.parse_call_args()?; if let Some(Some(content_args)) = self.content.last().map(|v| v.content_args.clone()) { args.max_args(content_args.len())?; - scope.merge(self.eval_args(content_args, args)?); + let scope = self.eval_args(content_args, args)?; + self.content_scopes.merge(scope); } else { args.max_args(0)?; } @@ -197,7 +201,7 @@ impl<'a> Parser<'a> { toks: &mut body.into_iter().peekmore(), map: self.map, path: self.path, - scopes: &mut scope, + scopes: self.content_scopes, global_scope: self.global_scope, super_selectors: self.super_selectors, span_before: self.span_before, @@ -206,6 +210,7 @@ impl<'a> Parser<'a> { at_root: self.at_root, at_root_has_selector: self.at_root_has_selector, extender: self.extender, + content_scopes: self.scopes, } .parse()? } else { diff --git a/src/parse/mod.rs b/src/parse/mod.rs index d015af4..5377fa1 100644 --- a/src/parse/mod.rs +++ b/src/parse/mod.rs @@ -70,6 +70,7 @@ pub(crate) struct Parser<'a> { pub path: &'a Path, pub global_scope: &'a mut Scope, pub scopes: &'a mut Scopes, + pub content_scopes: &'a mut Scopes, pub super_selectors: &'a mut NeverEmptyVec, pub span_before: Span, pub content: &'a mut Vec, @@ -357,6 +358,7 @@ impl<'a> Parser<'a> { at_root: self.at_root, at_root_has_selector: self.at_root_has_selector, extender: self.extender, + content_scopes: self.content_scopes, }, allows_parent, true, @@ -583,6 +585,7 @@ impl<'a> Parser<'a> { at_root: false, at_root_has_selector: self.at_root_has_selector, extender: self.extender, + content_scopes: self.content_scopes, } .parse_stmt()?; @@ -649,6 +652,7 @@ impl<'a> Parser<'a> { at_root: true, at_root_has_selector, extender: self.extender, + content_scopes: self.content_scopes, } .parse()? .into_iter() @@ -688,6 +692,7 @@ impl<'a> Parser<'a> { at_root: self.at_root, at_root_has_selector: self.at_root_has_selector, extender: self.extender, + content_scopes: self.content_scopes, } .parse_selector(false, true, String::new())?; @@ -764,6 +769,7 @@ impl<'a> Parser<'a> { at_root: false, at_root_has_selector: self.at_root_has_selector, extender: self.extender, + content_scopes: self.content_scopes, } .parse()?; diff --git a/src/parse/value/parse.rs b/src/parse/value/parse.rs index df349e1..51d4cdb 100644 --- a/src/parse/value/parse.rs +++ b/src/parse/value/parse.rs @@ -195,6 +195,7 @@ impl<'a> Parser<'a> { at_root: self.at_root, at_root_has_selector: self.at_root_has_selector, extender: self.extender, + content_scopes: self.content_scopes, } .parse_value() } diff --git a/src/value/mod.rs b/src/value/mod.rs index 3e73c73..c05474c 100644 --- a/src/value/mod.rs +++ b/src/value/mod.rs @@ -444,6 +444,7 @@ impl Value { at_root: parser.at_root, at_root_has_selector: parser.at_root_has_selector, extender: parser.extender, + content_scopes: parser.content_scopes, } .parse_selector(allows_parent, true, String::new()) } diff --git a/tests/mixins.rs b/tests/mixins.rs index 76e62c4..99bdf5a 100644 --- a/tests/mixins.rs +++ b/tests/mixins.rs @@ -399,3 +399,30 @@ test!( }", "a {\n color: foo;\n color: bar;\n}\n" ); +test!( + three_depth_of_content, + "@mixin foo($arg) { + @include bar { + color: $arg; + } + } + + @mixin bar { + @include baz { + @content; + } + } + + @mixin baz { + @content; + } + + @mixin font-size($value) { + @include foo($value); + } + + a { + @include font-size(1rem); + }", + "a {\n color: 1rem;\n}\n" +);