Files
coffeescript/test/strict.coffee
Geoffrey Booth b192e215a5 [CS2] Destructuring (#4478)
* Output simple array destructuring assignments to ES2015

* Output simple object destructured assignments to ES2015

* Compile shorthand object properties to ES2015 shorthand properties

This dramatically improves the appearance of destructured imports.

* Compile default values in destructured assignment to ES2015

* Rename `wrapInBraces` to `wrapInParentheses`, and `compilePatternMatch` to `compileDestructuring`, for clarity; style improvements (no `==` or `!=`, etc.)

* Don’t confuse the syntax highlighter

* Comment Assign::compilePatternMatch a bit

* Assignment expressions in conditionals are a bad practice

* Optional check for existence that only checks `!== undefined`, not `!= null`, to follow ES convention that default values only apply when a variable is undefined, not falsy

* Add comments; remove unnecessary array splats in function tests

* The fallback destructuring code should apply default values only if `undefined`, not falsy, to follow ES spec

* Support destructuring in function parameters (first pass); catch destructured reserved words

* Destructured variables in function parameter lists shouldn’t be added to the function body with `var` declarations; treat splat array function parameters the legacy way to avoid rethinking #4005

* Remove redundancy in undefined-only check for existence; fix passing option to check

* Fix undefined redundancy

* Simplify getting the variable name

* Reimplement “check for existence if not undefined” without creating a new operator

* `Obj::isAssignable` should not mutate; pass `lhs` property in from `Assign` or `Code` to child arrays and objects so that those child nodes are set as allowable for destructuring

* Revert changes to tests

* Restore revised test for empty destructuring assignment
2017-04-06 10:06:45 -07:00

183 lines
5.8 KiB
CoffeeScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Strict Early Errors
# -------------------
# The following are prohibited under ES5s `strict` mode
# * `Octal Integer Literals`
# * `Octal Escape Sequences`
# * duplicate property definitions in `Object Literal`s
# * duplicate formal parameter
# * `delete` operand is a variable
# * `delete` operand is a parameter
# * `delete` operand is `undefined`
# * `Future Reserved Word`s as identifiers: implements, interface, let, package, private, protected, public, static, yield
# * `eval` or `arguments` as `catch` identifier
# * `eval` or `arguments` as formal parameter
# * `eval` or `arguments` as function declaration identifier
# * `eval` or `arguments` as LHS of assignment
# * `eval` or `arguments` as the operand of a post/pre-fix inc/dec-rement expression
# helper to assert that code complies with strict prohibitions
strict = (code, msg) ->
throws (-> CoffeeScript.compile code), null, msg ? code
strictOk = (code, msg) ->
doesNotThrow (-> CoffeeScript.compile code), msg ? code
test "octal integer literals prohibited", ->
strict '01'
strict '07777'
# decimals with a leading '0' are also prohibited
strict '09'
strict '079'
strictOk '`01`'
test "octal escape sequences prohibited", ->
strict '"\\1"'
strict '"\\7"'
strict '"\\001"'
strict '"\\777"'
strict '"_\\1"'
strict '"\\1_"'
strict '"_\\1_"'
strict '"\\\\\\1"'
strictOk '"\\0"'
eq "\x00", "\0"
strictOk '"\\08"'
eq "\x008", "\08"
strictOk '"\\0\\8"'
eq "\x008", "\0\8"
strictOk '"\\8"'
eq "8", "\8"
strictOk '"\\\\1"'
eq "\\" + "1", "\\1"
strictOk '"\\\\\\\\1"'
eq "\\\\" + "1", "\\\\1"
strictOk "`'\\1'`"
eq "\\" + "1", `"\\1"`
# Also test other string types.
strict "'\\\\\\1'"
eq "\x008", '\08'
eq "\\\\" + "1", '\\\\1'
strict "'''\\\\\\1'''"
eq "\x008", '''\08'''
eq "\\\\" + "1", '''\\\\1'''
strict '"""\\\\\\1"""'
eq "\x008", """\08"""
eq "\\\\" + "1", """\\\\1"""
test "duplicate formal parameters are prohibited", ->
nonce = {}
# a Param can be an Identifier, ThisProperty( @-param ), Array, or Object
# a Param can also be a splat (...) or an assignment (param=value)
# the following function expressions should throw errors
strict '(_,_)->', 'param, param'
strict '(_,_...)->', 'param, param...'
strict '(_,_ = true)->', 'param, param='
strict '(@_,@_)->', 'two @params'
strict '(@case,@case)->', 'two @reserved'
strict '(_,{_})->', 'param, {param}'
strict '(_,{_=true})->', 'param, {param=}'
strict '({_,_})->', '{param, param}'
strict '({_=true,_})->', '{param=, param}'
strict '(_,[_])->', 'param, [param]'
strict '(_,[_=true])->', 'param, [param=]'
strict '([_,_])->', '[param, param]'
strict '([_=true,_])->', '[param=, param]'
strict '(_,[_]=true)->', 'param, [param]='
strict '(_,[_=true]=true)->', 'param, [param=]='
strict '(_,[@_,{_}])->', 'param, [@param, {param}]'
strict '(_,[_,{@_}])->', 'param, [param, {@param}]'
strict '(_,[_,{@_=true}])->', 'param, [param, {@param=}]'
strict '(_,[_,{_}])->', 'param, [param, {param}]'
strict '(_,[_,{__}])->', 'param, [param, {param2}]'
strict '(_,[__,{_}])->', 'param, [param2, {param}]'
strict '(__,[_,{_}])->', 'param, [param2, {param2}]'
strict '({0:a,1:a})->', '{0:param,1:param}'
strict '(a=b=true,a)->', 'param=assignment, param'
strict '({a=b=true},a)->', '{param=assignment}, param'
# the following function expressions should **not** throw errors
strictOk '(_,@_)->'
strictOk '(@_,_...)->'
strictOk '(_,@_ = true)->'
strictOk '(@_,{_})->'
strictOk '({_,@_})->'
strictOk '({_,@_ = true})->'
strictOk '([_,@_])->'
strictOk '([_,@_ = true])->'
strictOk '({},_arg)->'
strictOk '({},{})->'
strictOk '([]...,_arg)->'
strictOk '({}...,_arg)->'
strictOk '({}...,[],_arg)->'
strictOk '([]...,{},_arg)->'
strictOk '(@case,_case)->'
strictOk '(@case,_case...)->'
strictOk '(@case...,_case)->'
strictOk '(_case,@case)->'
strictOk '(_case,@case...)->'
strictOk '({a:a})->'
strictOk '({a:a,a:b})->'
test "`delete` operand restrictions", ->
strict 'a = 1; delete a'
strictOk 'delete a' #noop
strict '(a) -> delete a'
strict '(a...) -> delete a'
strict '(a = 1) -> delete a'
strict '([a]) -> delete a'
strict '({a}) -> delete a'
test "`Future Reserved Word`s, `eval` and `arguments` restrictions", ->
access = (keyword, check = strict) ->
check "#{keyword}.a = 1"
check "#{keyword}[0] = 1"
assign = (keyword, check = strict) ->
check "#{keyword} = 1"
check "#{keyword} += 1"
check "#{keyword} -= 1"
check "#{keyword} *= 1"
check "#{keyword} /= 1"
check "#{keyword} ?= 1"
check "{keyword}++"
check "++{keyword}"
check "{keyword}--"
check "--{keyword}"
destruct = (keyword, check = strict) ->
check "{#{keyword}}"
check "o = {#{keyword}}"
invoke = (keyword, check = strict) ->
check "#{keyword} yes"
check "do #{keyword}"
fnDecl = (keyword, check = strict) ->
check "class #{keyword}"
param = (keyword, check = strict) ->
check "(#{keyword}) ->"
check "({#{keyword}}) ->"
prop = (keyword, check = strict) ->
check "a.#{keyword} = 1"
tryCatch = (keyword, check = strict) ->
check "try new Error catch #{keyword}"
future = 'implements interface let package private protected public static'.split ' '
for keyword in future
access keyword
assign keyword
destruct keyword
invoke keyword
fnDecl keyword
param keyword
prop keyword, strictOk
tryCatch keyword
for keyword in ['eval', 'arguments']
access keyword, strictOk
assign keyword
destruct keyword, strictOk
invoke keyword, strictOk
fnDecl keyword
param keyword
prop keyword, strictOk
tryCatch keyword