#[macro_use]
mod macros;

test!(
    return_num,
    "@function a() {\n  @return 1;\n}\n\nb {\ncolor: a();\n}\n",
    "b {\n  color: 1;\n}\n"
);
test!(
    return_spaced_list,
    "@function a() {\n  @return a b;\n}\n\nb {\ncolor: a();\n}\n",
    "b {\n  color: a b;\n}\n"
);
test!(
    single_arg,
    "@function a($c) {\n  @return $c;\n}\n\nb {\ncolor: a(1);\n}\n",
    "b {\n  color: 1;\n}\n"
);
test!(
    return_variable,
    "@function a($a) {\n  @return $a;\n}\n\nb {\ncolor: a(1);\n}\n",
    "b {\n  color: 1;\n}\n"
);
test!(
    function_call_as_arg,
    "@function a($a) {\n  @return $a;\n}\n\nb {\ncolor: a(a(2));\n}\n",
    "b {\n  color: 2;\n}\n"
);
test!(
    function_named_arg_value_variable,
    "$x: red;\n\n@function a($a) {\n  @return $a;\n}\n\nb {\ncolor: a($a: $x);\n}\n",
    "b {\n  color: red;\n}\n"
);
test!(
    function_trailing_comma,
    "@function a($a) {\n  @return $a;\n}\n\nb {\ncolor: a(red,);\n}\n",
    "b {\n  color: red;\n}\n"
);
test!(
    return_no_semicolon,
    "@function a() {\n  @return 1\n}\n\nb {\ncolor: a();\n}\n",
    "b {\n  color: 1;\n}\n"
);
test!(
    two_returns,
    "@function a() {\n  @return 1; @return 2;\n}\n\nb {\ncolor: a();\n}\n",
    "b {\n  color: 1;\n}\n"
);
test!(
    value_after_variable,
    "$x: 0;\na {\n  color: if($x != 0, a, b);\n}\n",
    "a {\n  color: b;\n}\n"
);
test!(
    function_decl_in_ruleset,
    "a {\n  @function foo() {\n    @return 3;\n  }\n  color: foo();\n}\n",
    "a {\n  color: 3;\n}\n"
);
test!(
    function_decl_in_foreign_ruleset,
    "a {\n  @function foo() {\n    @return 3;\n  }\n}\nb {\n  color: foo();\n}\n",
    "b {\n  color: foo();\n}\n"
);
test!(
    global_function_in_scope,
    "@function f() {\n  @return g();\n}\n@function g() {\n  @return false;\n}\na {\n  color: f();\n  color: g();\n}\n",
    "a {\n  color: false;\n  color: false;\n}\n"
);
test!(
    square_bracket_comma_separated,
    "@function foo($a) {\n  @return $a;\n}\n\na {\n  color: foo([a, b]);\n}\n",
    "a {\n  color: [a, b];\n}\n"
);
test!(
    eats_quoted_content,
    "a {\n  color: unquote(\"a, b, c, d\");\n}\n",
    "a {\n  color: a, b, c, d;\n}\n"
);
test!(
    variable_declaration,
    "@function str-replace($string, $search, $replace: \"\") {\n  $index: $string;\n  @return $index;\n}\n\na {\n  color: str-replace(\"a#b#c\", \"#\", \":\");\n}",
    "a {\n  color: \"a#b#c\";\n}\n"
);
error!(
    missing_name,
    "@function() {}", "Error: Expected identifier."
);
error!(
    args_do_not_start_with_var,
    "@function foo(FOO) {}", "Error: expected \")\"."
);
error!(
    double_comma_args,
    "@function foo($a,$b,,) {}", "Error: expected \")\"."
);
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"
);
error!(
    denies_function_declaration_in_control_flow,
    "@if true {\n    @function foo() {}\n}\n",
    "Error: Functions may not be declared in control directives."
);
error!(
    denies_function_declaration_with_reserved_name,
    "@function url() {}", "Error: Invalid function name."
);
test!(
    function_finds_outer_local_variable,
    "a {
        $a: red;
    
        @function foo() {
            @return $a;
        }
    
        color: foo();
    }",
    "a {\n  color: red;\n}\n"
);
test!(
    function_ignores_the_scope_with_which_it_was_defined,
    "a {
        $a: red;
        @function foo() {
            @return $a;
        }
        $a: green;
        color: foo();
    }",
    "a {\n  color: green;\n}\n"
);
test!(
    function_defined_and_called_at_toplevel_can_recognize_inner_variables,
    "@function foo($level) {
        $level: abs($level);
      
        @return $level;
    }
    
    @mixin bar($a) {
        a {
            color: $a;
        }
    }
    
    @include bar(foo(-9));",
    "a {\n  color: 9;\n}\n"
);
test!(
    redeclaration_in_inner_scope,
    "@function foo() {
        @return foo;
    }

    a {
        color: foo();
    
        @function foo() {
            @return bar;
        }

        a {
            @function foo() {
                @return baz;
            }
        }

        color: foo();
    }",
    "a {\n  color: foo;\n  color: bar;\n}\n"
);
error!(
    disallows_unknown_at_rule,
    "@function foo() {
        @foo;
    }
    
    a {
        color: foo();
    }",
    "Error: This at-rule is not allowed here."
);
error!(
    disallows_media_query,
    "@function foo() {
        @media screen {};
    }
    
    a {
        color: foo();
    }",
    "Error: This at-rule is not allowed here."
);
error!(
    disallows_at_root,
    "@function foo() {
        @at-root {};
    }
    
    a {
        color: foo();
    }",
    "Error: This at-rule is not allowed here."
);
error!(
    disallows_charset,
    "@function foo() {
        @charset 'utf-8';
    }
    
    a {
        color: foo();
    }",
    "Error: This at-rule is not allowed here."
);
error!(
    disallows_extend,
    "@function foo() {
        @extend a;
    }
    
    a {
        color: foo();
    }",
    "Error: This at-rule is not allowed here."
);
error!(
    disallows_keyframes,
    "@function foo() {
        @keyframes foo {}
    }
    
    a {
        color: foo();
    }",
    "Error: This at-rule is not allowed here."
);
error!(
    disallows_supports,
    "@function foo() {
        @supports foo {}
    }
    
    a {
        color: foo();
    }",
    "Error: This at-rule is not allowed here."
);
error!(
    disallows_import,
    "@function foo() {
        @import \"foo.css\";
    }
    
    a {
        color: foo();
    }",
    "Error: This at-rule is not allowed here."
);
error!(
    disallows_inner_function_declaration,
    "@function foo() {
        @function bar() {}
    }
    
    a {
        color: foo();
    }",
    "Error: This at-rule is not allowed here."
);
error!(
    disallows_include,
    "@function foo() {
        @include bar;
    }
    
    a {
        color: foo();
    }",
    "Error: This at-rule is not allowed here."
);
error!(
    disallows_selectors,
    "@function foo($a) {
        functiona {
            @return $a;
        }
    }

    a {
        color: foo(nul);
    }",
    "Error: expected \".\"."
);
error!(
    pass_one_arg_to_fn_that_accepts_zero,
    "@function foo() {
      @return red;
    }
    
    a {
      color: foo(1);
    }",
    "Error: Only 0 arguments allowed, but 1 was passed."
);
error!(
    pass_two_args_to_fn_that_accepts_one,
    "@function foo($a) {
      @return red;
    }
    
    a {
      color: foo(1, 2);
    }",
    "Error: Only 1 argument allowed, but 2 were passed."
);
error!(
    declaration_inside_function,
    "@function foo() {
        opacity: 1;
        @return 2;
    }",
    "Error: @function rules may not contain declarations."
);
error!(
    style_rule_inside_function,
    "@function foo() {
        a {}
        @return 2;
    }",
    "Error: @function rules may not contain style rules."
);
test!(
    allows_multiline_comment,
    "@function foo($a) {
        /* foo */
        @return $a;
    }

    a {
        color: foo(nul);
    }",
    "a {\n  color: nul;\n}\n"
);
test!(
    allows_multiline_comment_between_args,
    "@function foo /**/ ()  /**/ {
        @return red;
    }

    a {
        color: foo();
    }",
    "a {\n  color: red;\n}\n"
);
test!(
    return_inside_each,
    "@function foo() {
        @each $i in 0 {
            @return $i;
        }
    }

    a {
        color: foo();
    }",
    "a {\n  color: 0;\n}\n"
);
test!(
    recursive_function_cannot_modify_scope_of_calling_function,
    "@function with-local-variable($recurse) {
        $var: before;

        @if ($recurse) {
                $a: with-local-variable($recurse: false);
        }

        $ret: $var;
        $var: after;
        @return $ret;
    }

    a {
        color: with-local-variable($recurse: true);
    }",
    "a {\n  color: before;\n}\n"
);
test!(
    can_parse_module_variable_declaration,
    "@function foo() {
        foo.$bar: red;
    }",
    ""
);
error!(
    function_no_return,
    "@function foo() {}

    a {
        color: foo();
    }",
    "Error: Function finished without @return."
);

// todo: return inside if, return inside while, return inside for
