mirror of
https://github.com/jashkenas/coffeescript.git
synced 2022-11-09 12:23:24 -05:00
2006 lines
46 KiB
CoffeeScript
2006 lines
46 KiB
CoffeeScript
# Error Formatting
|
||
# ----------------
|
||
|
||
# Ensure that errors of different kinds (lexer, parser and compiler) are shown
|
||
# in a consistent way.
|
||
|
||
errCallback = (expectedErrorFormat) -> (err) ->
|
||
err.colorful = no
|
||
eq expectedErrorFormat, "#{err}"
|
||
yes
|
||
assertErrorFormatNoAst = (code, expectedErrorFormat) ->
|
||
throws (-> CoffeeScript.run code), errCallback(expectedErrorFormat)
|
||
assertErrorFormat = (code, expectedErrorFormat) ->
|
||
assertErrorFormatNoAst code, expectedErrorFormat
|
||
throws (-> CoffeeScript.compile code, ast: yes), errCallback(expectedErrorFormat)
|
||
|
||
test "lexer errors formatting", ->
|
||
assertErrorFormat '''
|
||
normalObject = {}
|
||
insideOutObject = }{
|
||
''',
|
||
'''
|
||
[stdin]:2:19: error: unmatched }
|
||
insideOutObject = }{
|
||
^
|
||
'''
|
||
|
||
test "parser error formatting", ->
|
||
assertErrorFormat '''
|
||
foo in bar or in baz
|
||
''',
|
||
'''
|
||
[stdin]:1:15: error: unexpected in
|
||
foo in bar or in baz
|
||
^^
|
||
'''
|
||
|
||
test "compiler error formatting", ->
|
||
assertErrorFormat '''
|
||
evil = (foo, eval, bar) ->
|
||
''',
|
||
'''
|
||
[stdin]:1:14: error: 'eval' can't be assigned
|
||
evil = (foo, eval, bar) ->
|
||
^^^^
|
||
'''
|
||
|
||
if require?
|
||
os = require 'os'
|
||
fs = require 'fs'
|
||
path = require 'path'
|
||
|
||
test "patchStackTrace line patching", ->
|
||
err = new Error 'error'
|
||
ok err.stack.match /test[\/\\]error_messages\.coffee:\d+:\d+\b/
|
||
|
||
test "patchStackTrace stack prelude consistent with V8", ->
|
||
err = new Error
|
||
ok err.stack.match /^Error\n/ # Notice no colon when no message.
|
||
|
||
err = new Error 'error'
|
||
ok err.stack.match /^Error: error\n/
|
||
|
||
test "#2849: compilation error in a require()d file", ->
|
||
# Create a temporary file to require().
|
||
tempFile = path.join os.tmpdir(), 'syntax-error.coffee'
|
||
ok not fs.existsSync tempFile
|
||
fs.writeFileSync tempFile, 'foo in bar or in baz'
|
||
|
||
try
|
||
assertErrorFormatNoAst """
|
||
require '#{tempFile.replace /\\/g, '\\\\'}'
|
||
""",
|
||
"""
|
||
#{fs.realpathSync tempFile}:1:15: error: unexpected in
|
||
foo in bar or in baz
|
||
^^
|
||
"""
|
||
finally
|
||
fs.unlinkSync tempFile
|
||
|
||
test "#3890: Error.prepareStackTrace doesn't throw an error if a compiled file is deleted", ->
|
||
# Adapted from https://github.com/atom/coffee-cash/blob/master/spec/coffee-cash-spec.coffee
|
||
filePath = path.join os.tmpdir(), 'PrepareStackTraceTestFile.coffee'
|
||
fs.writeFileSync filePath, "module.exports = -> throw new Error('hello world')"
|
||
throwsAnError = require filePath
|
||
fs.unlinkSync filePath
|
||
|
||
try
|
||
throwsAnError()
|
||
catch error
|
||
|
||
eq error.message, 'hello world'
|
||
doesNotThrow(-> error.stack)
|
||
notEqual error.stack.toString().indexOf(filePath), -1, "Expected " + filePath + "in stack trace: " + error.stack.toString()
|
||
|
||
test "#4418: stack traces for compiled files reference the correct line number", ->
|
||
# The browser is already compiling other anonymous scripts (the tests)
|
||
# which will conflict.
|
||
return if global.testingBrowser
|
||
filePath = path.join os.tmpdir(), 'StackTraceLineNumberTestFile.coffee'
|
||
fileContents = """
|
||
testCompiledFileStackTraceLineNumber = ->
|
||
# `a` on the next line is undefined and should throw a ReferenceError
|
||
console.log a if true
|
||
|
||
do testCompiledFileStackTraceLineNumber
|
||
"""
|
||
fs.writeFileSync filePath, fileContents
|
||
|
||
try
|
||
require filePath
|
||
catch error
|
||
fs.unlinkSync filePath
|
||
|
||
# Make sure the line number reported is line 3 (the original Coffee source)
|
||
# and not line 6 (the generated JavaScript).
|
||
eq /StackTraceLineNumberTestFile.coffee:(\d)/.exec(error.stack.toString())[1], '3'
|
||
|
||
|
||
test "#4418: stack traces for compiled strings reference the correct line number", ->
|
||
# The browser is already compiling other anonymous scripts (the tests)
|
||
# which will conflict.
|
||
return if global.testingBrowser
|
||
try
|
||
CoffeeScript.run '''
|
||
testCompiledStringStackTraceLineNumber = ->
|
||
# `a` on the next line is undefined and should throw a ReferenceError
|
||
console.log a if true
|
||
|
||
do testCompiledStringStackTraceLineNumber
|
||
'''
|
||
catch error
|
||
|
||
# Make sure the line number reported is line 3 (the original Coffee source)
|
||
# and not line 6 (the generated JavaScript).
|
||
eq /testCompiledStringStackTraceLineNumber.*:(\d):/.exec(error.stack.toString())[1], '3'
|
||
|
||
|
||
test "#4558: compiling a string inside a script doesn’t screw up stack trace line number", ->
|
||
# The browser is already compiling other anonymous scripts (the tests)
|
||
# which will conflict.
|
||
return if global.testingBrowser
|
||
try
|
||
CoffeeScript.run '''
|
||
testCompilingInsideAScriptDoesntScrewUpStackTraceLineNumber = ->
|
||
if require?
|
||
CoffeeScript = require './lib/coffeescript'
|
||
CoffeeScript.compile ''
|
||
throw new Error 'Some Error'
|
||
|
||
do testCompilingInsideAScriptDoesntScrewUpStackTraceLineNumber
|
||
'''
|
||
catch error
|
||
|
||
# Make sure the line number reported is line 5 (the original Coffee source)
|
||
# and not line 10 (the generated JavaScript).
|
||
eq /testCompilingInsideAScriptDoesntScrewUpStackTraceLineNumber.*:(\d):/.exec(error.stack.toString())[1], '5'
|
||
|
||
test "#1096: unexpected generated tokens", ->
|
||
# Implicit ends
|
||
assertErrorFormat 'a:, b', '''
|
||
[stdin]:1:3: error: unexpected ,
|
||
a:, b
|
||
^
|
||
'''
|
||
# Explicit ends
|
||
assertErrorFormat '(a:)', '''
|
||
[stdin]:1:4: error: unexpected )
|
||
(a:)
|
||
^
|
||
'''
|
||
# Unexpected end of file
|
||
assertErrorFormat 'a:', '''
|
||
[stdin]:1:3: error: unexpected end of input
|
||
a:
|
||
^
|
||
'''
|
||
assertErrorFormat 'a +', '''
|
||
[stdin]:1:4: error: unexpected end of input
|
||
a +
|
||
^
|
||
'''
|
||
# Unexpected key in implicit object (an implicit object itself is _not_
|
||
# unexpected here)
|
||
assertErrorFormat '''
|
||
for i in [1]:
|
||
1
|
||
''', '''
|
||
[stdin]:2:4: error: unexpected end of input
|
||
1
|
||
^
|
||
'''
|
||
# Unexpected regex
|
||
assertErrorFormat '{/a/i: val}', '''
|
||
[stdin]:1:2: error: unexpected regex
|
||
{/a/i: val}
|
||
^^^^
|
||
'''
|
||
assertErrorFormat '{///a///i: val}', '''
|
||
[stdin]:1:2: error: unexpected regex
|
||
{///a///i: val}
|
||
^^^^^^^^
|
||
'''
|
||
assertErrorFormat '{///#{a}///i: val}', '''
|
||
[stdin]:1:2: error: unexpected regex
|
||
{///#{a}///i: val}
|
||
^^^^^^^^^^^
|
||
'''
|
||
# Unexpected string
|
||
assertErrorFormat 'import foo from "lib-#{version}"', '''
|
||
[stdin]:1:17: error: the name of the module to be imported from must be an uninterpolated string
|
||
import foo from "lib-#{version}"
|
||
^^^^^^^^^^^^^^^^
|
||
'''
|
||
|
||
# Unexpected number
|
||
assertErrorFormat '"a"0x00Af2', '''
|
||
[stdin]:1:4: error: unexpected number
|
||
"a"0x00Af2
|
||
^^^^^^^
|
||
'''
|
||
|
||
test "#1316: unexpected end of interpolation", ->
|
||
assertErrorFormat '''
|
||
"#{+}"
|
||
''', '''
|
||
[stdin]:1:5: error: unexpected end of interpolation
|
||
"#{+}"
|
||
^
|
||
'''
|
||
assertErrorFormat '''
|
||
"#{++}"
|
||
''', '''
|
||
[stdin]:1:6: error: unexpected end of interpolation
|
||
"#{++}"
|
||
^
|
||
'''
|
||
assertErrorFormat '''
|
||
"#{-}"
|
||
''', '''
|
||
[stdin]:1:5: error: unexpected end of interpolation
|
||
"#{-}"
|
||
^
|
||
'''
|
||
assertErrorFormat '''
|
||
"#{--}"
|
||
''', '''
|
||
[stdin]:1:6: error: unexpected end of interpolation
|
||
"#{--}"
|
||
^
|
||
'''
|
||
assertErrorFormat '''
|
||
"#{~}"
|
||
''', '''
|
||
[stdin]:1:5: error: unexpected end of interpolation
|
||
"#{~}"
|
||
^
|
||
'''
|
||
assertErrorFormat '''
|
||
"#{!}"
|
||
''', '''
|
||
[stdin]:1:5: error: unexpected end of interpolation
|
||
"#{!}"
|
||
^
|
||
'''
|
||
assertErrorFormat '''
|
||
"#{not}"
|
||
''', '''
|
||
[stdin]:1:7: error: unexpected end of interpolation
|
||
"#{not}"
|
||
^
|
||
'''
|
||
assertErrorFormat '''
|
||
"#{5) + (4}_"
|
||
''', '''
|
||
[stdin]:1:5: error: unmatched )
|
||
"#{5) + (4}_"
|
||
^
|
||
'''
|
||
# #2918
|
||
assertErrorFormat '''
|
||
"#{foo.}"
|
||
''', '''
|
||
[stdin]:1:8: error: unexpected end of interpolation
|
||
"#{foo.}"
|
||
^
|
||
'''
|
||
|
||
test "#3325: implicit indentation errors", ->
|
||
assertErrorFormat '''
|
||
i for i in a then i
|
||
''', '''
|
||
[stdin]:1:14: error: unexpected then
|
||
i for i in a then i
|
||
^^^^
|
||
'''
|
||
|
||
test "explicit indentation errors", ->
|
||
assertErrorFormat '''
|
||
a = b
|
||
c
|
||
''', '''
|
||
[stdin]:2:1: error: unexpected indentation
|
||
c
|
||
^^
|
||
'''
|
||
|
||
test "unclosed strings", ->
|
||
assertErrorFormat '''
|
||
'
|
||
''', '''
|
||
[stdin]:1:1: error: missing '
|
||
'
|
||
^
|
||
'''
|
||
assertErrorFormat '''
|
||
"
|
||
''', '''
|
||
[stdin]:1:1: error: missing "
|
||
"
|
||
^
|
||
'''
|
||
assertErrorFormat """
|
||
'''
|
||
""", """
|
||
[stdin]:1:1: error: missing '''
|
||
'''
|
||
^^^
|
||
"""
|
||
assertErrorFormat '''
|
||
"""
|
||
''', '''
|
||
[stdin]:1:1: error: missing """
|
||
"""
|
||
^^^
|
||
'''
|
||
assertErrorFormat '''
|
||
"#{"
|
||
''', '''
|
||
[stdin]:1:4: error: missing "
|
||
"#{"
|
||
^
|
||
'''
|
||
assertErrorFormat '''
|
||
"""#{"
|
||
''', '''
|
||
[stdin]:1:6: error: missing "
|
||
"""#{"
|
||
^
|
||
'''
|
||
assertErrorFormat '''
|
||
"#{"""
|
||
''', '''
|
||
[stdin]:1:4: error: missing """
|
||
"#{"""
|
||
^^^
|
||
'''
|
||
assertErrorFormat '''
|
||
"""#{"""
|
||
''', '''
|
||
[stdin]:1:6: error: missing """
|
||
"""#{"""
|
||
^^^
|
||
'''
|
||
assertErrorFormat '''
|
||
///#{"""
|
||
''', '''
|
||
[stdin]:1:6: error: missing """
|
||
///#{"""
|
||
^^^
|
||
'''
|
||
assertErrorFormat '''
|
||
"a
|
||
#{foo """
|
||
bar
|
||
#{ +'12 }
|
||
baz
|
||
"""} b"
|
||
''', '''
|
||
[stdin]:4:11: error: missing '
|
||
#{ +'12 }
|
||
^
|
||
'''
|
||
# https://github.com/jashkenas/coffeescript/issues/3301#issuecomment-31735168
|
||
assertErrorFormat '''
|
||
# Note the double escaping; this would be `"""a\"""` real code.
|
||
"""a\\"""
|
||
''', '''
|
||
[stdin]:2:1: error: missing """
|
||
"""a\\"""
|
||
^^^
|
||
'''
|
||
|
||
test "unclosed heregexes", ->
|
||
assertErrorFormat '''
|
||
///
|
||
''', '''
|
||
[stdin]:1:1: error: missing ///
|
||
///
|
||
^^^
|
||
'''
|
||
# https://github.com/jashkenas/coffeescript/issues/3301#issuecomment-31735168
|
||
assertErrorFormat '''
|
||
# Note the double escaping; this would be `///a\///` real code.
|
||
///a\\///
|
||
''', '''
|
||
[stdin]:2:1: error: missing ///
|
||
///a\\///
|
||
^^^
|
||
'''
|
||
|
||
test "unexpected token after string", ->
|
||
# Parsing error.
|
||
assertErrorFormat '''
|
||
'foo'bar
|
||
''', '''
|
||
[stdin]:1:6: error: unexpected identifier
|
||
'foo'bar
|
||
^^^
|
||
'''
|
||
assertErrorFormat '''
|
||
"foo"bar
|
||
''', '''
|
||
[stdin]:1:6: error: unexpected identifier
|
||
"foo"bar
|
||
^^^
|
||
'''
|
||
# Lexing error.
|
||
assertErrorFormat '''
|
||
'foo'bar'
|
||
''', '''
|
||
[stdin]:1:9: error: missing '
|
||
'foo'bar'
|
||
^
|
||
'''
|
||
assertErrorFormat '''
|
||
"foo"bar"
|
||
''', '''
|
||
[stdin]:1:9: error: missing "
|
||
"foo"bar"
|
||
^
|
||
'''
|
||
|
||
test "#3348: Location data is wrong in interpolations with leading whitespace", ->
|
||
assertErrorFormat '''
|
||
"#{ * }"
|
||
''', '''
|
||
[stdin]:1:5: error: unexpected *
|
||
"#{ * }"
|
||
^
|
||
'''
|
||
|
||
test "octal escapes", ->
|
||
assertErrorFormat '''
|
||
"a\\0\\tb\\\\\\07c"
|
||
''', '''
|
||
[stdin]:1:10: error: octal escape sequences are not allowed \\07
|
||
"a\\0\\tb\\\\\\07c"
|
||
\ \ \ \ ^\^^
|
||
'''
|
||
assertErrorFormat '''
|
||
"a
|
||
#{b} \\1"
|
||
''', '''
|
||
[stdin]:2:8: error: octal escape sequences are not allowed \\1
|
||
#{b} \\1"
|
||
^\^
|
||
'''
|
||
assertErrorFormat '''
|
||
/a\\0\\tb\\\\\\07c/
|
||
''', '''
|
||
[stdin]:1:10: error: octal escape sequences are not allowed \\07
|
||
/a\\0\\tb\\\\\\07c/
|
||
\ \ \ \ ^\^^
|
||
'''
|
||
assertErrorFormat '''
|
||
/a\\1\\tb\\\\\\07c/
|
||
''', '''
|
||
[stdin]:1:10: error: octal escape sequences are not allowed \\07
|
||
/a\\1\\tb\\\\\\07c/
|
||
\ \ \ \ ^\^^
|
||
'''
|
||
assertErrorFormat '''
|
||
///a
|
||
#{b} \\01///
|
||
''', '''
|
||
[stdin]:2:8: error: octal escape sequences are not allowed \\01
|
||
#{b} \\01///
|
||
^\^^
|
||
'''
|
||
# per #5211, also treat \0[8-9] as (disallowed) octal escapes
|
||
assertErrorFormat '''
|
||
"a\\0\\tb\\\\\\09c"
|
||
''', '''
|
||
[stdin]:1:10: error: octal escape sequences are not allowed \\09
|
||
"a\\0\\tb\\\\\\09c"
|
||
\ \ \ \ ^\^^
|
||
'''
|
||
assertErrorFormat '''
|
||
///a
|
||
#{b} \\08///
|
||
''', '''
|
||
[stdin]:2:8: error: octal escape sequences are not allowed \\08
|
||
#{b} \\08///
|
||
^\^^
|
||
'''
|
||
|
||
test "#3795: invalid escapes", ->
|
||
assertErrorFormat '''
|
||
"a\\0\\tb\\\\\\x7g"
|
||
''', '''
|
||
[stdin]:1:10: error: invalid escape sequence \\x7g
|
||
"a\\0\\tb\\\\\\x7g"
|
||
\ \ \ \ ^\^^^
|
||
'''
|
||
assertErrorFormat '''
|
||
"a
|
||
#{b} \\uA02
|
||
c"
|
||
''', '''
|
||
[stdin]:2:8: error: invalid escape sequence \\uA02
|
||
#{b} \\uA02
|
||
^\^^^^
|
||
'''
|
||
assertErrorFormat '''
|
||
/a\\u002space/
|
||
''', '''
|
||
[stdin]:1:3: error: invalid escape sequence \\u002s
|
||
/a\\u002space/
|
||
^\^^^^^
|
||
'''
|
||
assertErrorFormat '''
|
||
///a \\u002 0 space///
|
||
''', '''
|
||
[stdin]:1:6: error: invalid escape sequence \\u002 \n\
|
||
///a \\u002 0 space///
|
||
^\^^^^^
|
||
'''
|
||
assertErrorFormat '''
|
||
///a
|
||
#{b} \\x0
|
||
c///
|
||
''', '''
|
||
[stdin]:2:8: error: invalid escape sequence \\x0
|
||
#{b} \\x0
|
||
^\^^
|
||
'''
|
||
assertErrorFormat '''
|
||
/ab\\u/
|
||
''', '''
|
||
[stdin]:1:4: error: invalid escape sequence \\u
|
||
/ab\\u/
|
||
^\^
|
||
'''
|
||
|
||
test "illegal herecomment", ->
|
||
assertErrorFormat '''
|
||
###
|
||
Regex: /a*/g
|
||
###
|
||
''', '''
|
||
[stdin]:2:12: error: block comments cannot contain */
|
||
Regex: /a*/g
|
||
^^
|
||
'''
|
||
|
||
test "#1724: regular expressions beginning with *", ->
|
||
assertErrorFormat '''
|
||
/* foo/
|
||
''', '''
|
||
[stdin]:1:2: error: regular expressions cannot begin with *
|
||
/* foo/
|
||
^
|
||
'''
|
||
assertErrorFormat '''
|
||
///
|
||
* foo
|
||
///
|
||
''', '''
|
||
[stdin]:2:3: error: regular expressions cannot begin with *
|
||
* foo
|
||
^
|
||
'''
|
||
|
||
test "invalid regex flags", ->
|
||
assertErrorFormat '''
|
||
/a/ii
|
||
''', '''
|
||
[stdin]:1:4: error: invalid regular expression flags ii
|
||
/a/ii
|
||
^^
|
||
'''
|
||
assertErrorFormat '''
|
||
/a/G
|
||
''', '''
|
||
[stdin]:1:4: error: invalid regular expression flags G
|
||
/a/G
|
||
^
|
||
'''
|
||
assertErrorFormat '''
|
||
/a/gimi
|
||
''', '''
|
||
[stdin]:1:4: error: invalid regular expression flags gimi
|
||
/a/gimi
|
||
^^^^
|
||
'''
|
||
assertErrorFormat '''
|
||
/a/g_
|
||
''', '''
|
||
[stdin]:1:4: error: invalid regular expression flags g_
|
||
/a/g_
|
||
^^
|
||
'''
|
||
assertErrorFormat '''
|
||
///a///ii
|
||
''', '''
|
||
[stdin]:1:8: error: invalid regular expression flags ii
|
||
///a///ii
|
||
^^
|
||
'''
|
||
doesNotThrowCompileError '/a/ymgi'
|
||
|
||
test "missing `)`, `}`, `]`", ->
|
||
assertErrorFormat '''
|
||
(
|
||
''', '''
|
||
[stdin]:1:1: error: missing )
|
||
(
|
||
^
|
||
'''
|
||
assertErrorFormat '''
|
||
{
|
||
''', '''
|
||
[stdin]:1:1: error: missing }
|
||
{
|
||
^
|
||
'''
|
||
assertErrorFormat '''
|
||
[
|
||
''', '''
|
||
[stdin]:1:1: error: missing ]
|
||
[
|
||
^
|
||
'''
|
||
assertErrorFormat '''
|
||
obj = {a: [1, (2+
|
||
''', '''
|
||
[stdin]:1:15: error: missing )
|
||
obj = {a: [1, (2+
|
||
^
|
||
'''
|
||
assertErrorFormat '''
|
||
"#{
|
||
''', '''
|
||
[stdin]:1:3: error: missing }
|
||
"#{
|
||
^
|
||
'''
|
||
assertErrorFormat '''
|
||
"""
|
||
foo#{ bar "#{1}"
|
||
''', '''
|
||
[stdin]:2:7: error: missing }
|
||
foo#{ bar "#{1}"
|
||
^
|
||
'''
|
||
|
||
test "unclosed regexes", ->
|
||
assertErrorFormat '''
|
||
/
|
||
''', '''
|
||
[stdin]:1:1: error: missing / (unclosed regex)
|
||
/
|
||
^
|
||
'''
|
||
assertErrorFormat '''
|
||
# Note the double escaping; this would be `/a\/` real code.
|
||
/a\\/
|
||
''', '''
|
||
[stdin]:2:1: error: missing / (unclosed regex)
|
||
/a\\/
|
||
^
|
||
'''
|
||
assertErrorFormat '''
|
||
/// ^
|
||
a #{""" ""#{if /[/].test "|" then 1 else 0}"" """}
|
||
///
|
||
''', '''
|
||
[stdin]:2:18: error: missing / (unclosed regex)
|
||
a #{""" ""#{if /[/].test "|" then 1 else 0}"" """}
|
||
^
|
||
'''
|
||
|
||
test "duplicate function arguments", ->
|
||
assertErrorFormat '''
|
||
(foo, bar, foo) ->
|
||
''', '''
|
||
[stdin]:1:12: error: multiple parameters named 'foo'
|
||
(foo, bar, foo) ->
|
||
^^^
|
||
'''
|
||
assertErrorFormat '''
|
||
(@foo, bar, @foo) ->
|
||
''', '''
|
||
[stdin]:1:13: error: multiple parameters named '@foo'
|
||
(@foo, bar, @foo) ->
|
||
^^^^
|
||
'''
|
||
|
||
test "reserved words", ->
|
||
assertErrorFormat '''
|
||
case
|
||
''', '''
|
||
[stdin]:1:1: error: reserved word 'case'
|
||
case
|
||
^^^^
|
||
'''
|
||
assertErrorFormat '''
|
||
case = 1
|
||
''', '''
|
||
[stdin]:1:1: error: reserved word 'case'
|
||
case = 1
|
||
^^^^
|
||
'''
|
||
assertErrorFormat '''
|
||
for = 1
|
||
''', '''
|
||
[stdin]:1:1: error: keyword 'for' can't be assigned
|
||
for = 1
|
||
^^^
|
||
'''
|
||
assertErrorFormat '''
|
||
unless = 1
|
||
''', '''
|
||
[stdin]:1:1: error: keyword 'unless' can't be assigned
|
||
unless = 1
|
||
^^^^^^
|
||
'''
|
||
assertErrorFormat '''
|
||
for += 1
|
||
''', '''
|
||
[stdin]:1:1: error: keyword 'for' can't be assigned
|
||
for += 1
|
||
^^^
|
||
'''
|
||
assertErrorFormat '''
|
||
for &&= 1
|
||
''', '''
|
||
[stdin]:1:1: error: keyword 'for' can't be assigned
|
||
for &&= 1
|
||
^^^
|
||
'''
|
||
# Make sure token look-behind doesn't go out of range.
|
||
assertErrorFormat '''
|
||
&&= 1
|
||
''', '''
|
||
[stdin]:1:1: error: unexpected &&=
|
||
&&= 1
|
||
^^^
|
||
'''
|
||
# #2306: Show unaliased name in error messages.
|
||
assertErrorFormat '''
|
||
on = 1
|
||
''', '''
|
||
[stdin]:1:1: error: keyword 'on' can't be assigned
|
||
on = 1
|
||
^^
|
||
'''
|
||
|
||
test "strict mode errors", ->
|
||
assertErrorFormat '''
|
||
eval = 1
|
||
''', '''
|
||
[stdin]:1:1: error: 'eval' can't be assigned
|
||
eval = 1
|
||
^^^^
|
||
'''
|
||
assertErrorFormat '''
|
||
class eval
|
||
''', '''
|
||
[stdin]:1:7: error: 'eval' can't be assigned
|
||
class eval
|
||
^^^^
|
||
'''
|
||
assertErrorFormat '''
|
||
arguments++
|
||
''', '''
|
||
[stdin]:1:1: error: 'arguments' can't be assigned
|
||
arguments++
|
||
^^^^^^^^^
|
||
'''
|
||
assertErrorFormat '''
|
||
--arguments
|
||
''', '''
|
||
[stdin]:1:3: error: 'arguments' can't be assigned
|
||
--arguments
|
||
^^^^^^^^^
|
||
'''
|
||
|
||
test "invalid numbers", ->
|
||
assertErrorFormat '''
|
||
0X0
|
||
''', '''
|
||
[stdin]:1:2: error: radix prefix in '0X0' must be lowercase
|
||
0X0
|
||
^
|
||
'''
|
||
assertErrorFormat '''
|
||
10E0
|
||
''', '''
|
||
[stdin]:1:3: error: exponential notation in '10E0' must be indicated with a lowercase 'e'
|
||
10E0
|
||
^
|
||
'''
|
||
assertErrorFormat '''
|
||
018
|
||
''', '''
|
||
[stdin]:1:1: error: decimal literal '018' must not be prefixed with '0'
|
||
018
|
||
^^^
|
||
'''
|
||
assertErrorFormat '''
|
||
010
|
||
''', '''
|
||
[stdin]:1:1: error: octal literal '010' must be prefixed with '0o'
|
||
010
|
||
^^^
|
||
'''
|
||
|
||
|
||
test "unexpected object keys", ->
|
||
assertErrorFormat '''
|
||
{(a + "b")}
|
||
''', '''
|
||
[stdin]:1:11: error: unexpected }
|
||
{(a + "b")}
|
||
^
|
||
'''
|
||
assertErrorFormat '''
|
||
{(a + "b"): 1}
|
||
''', '''
|
||
[stdin]:1:11: error: unexpected :
|
||
{(a + "b"): 1}
|
||
^
|
||
'''
|
||
assertErrorFormat '''
|
||
(a + "b"): 1
|
||
''', '''
|
||
[stdin]:1:10: error: unexpected :
|
||
(a + "b"): 1
|
||
^
|
||
'''
|
||
|
||
test "invalid object keys", ->
|
||
assertErrorFormat '''
|
||
@a: 1
|
||
''', '''
|
||
[stdin]:1:1: error: invalid object key
|
||
@a: 1
|
||
^^
|
||
'''
|
||
assertErrorFormat '''
|
||
f
|
||
@a: 1
|
||
''', '''
|
||
[stdin]:2:3: error: invalid object key
|
||
@a: 1
|
||
^^
|
||
'''
|
||
assertErrorFormat '''
|
||
{a=2}
|
||
''', '''
|
||
[stdin]:1:3: error: unexpected =
|
||
{a=2}
|
||
^
|
||
'''
|
||
assertErrorFormat '''
|
||
@[a]: 1
|
||
''', '''
|
||
[stdin]:1:1: error: invalid object key
|
||
@[a]: 1
|
||
^^^^
|
||
'''
|
||
|
||
test "invalid destructuring default target", ->
|
||
assertErrorFormat '''
|
||
{'a' = 2} = obj
|
||
''', '''
|
||
[stdin]:1:6: error: unexpected =
|
||
{'a' = 2} = obj
|
||
^
|
||
'''
|
||
|
||
test "#4070: lone expansion", ->
|
||
assertErrorFormat '''
|
||
[...] = a
|
||
''', '''
|
||
[stdin]:1:2: error: Destructuring assignment has no target
|
||
[...] = a
|
||
^^^
|
||
'''
|
||
assertErrorFormat '''
|
||
[ ..., ] = a
|
||
''', '''
|
||
[stdin]:1:3: error: Destructuring assignment has no target
|
||
[ ..., ] = a
|
||
^^^
|
||
'''
|
||
|
||
test "#3926: implicit object in parameter list", ->
|
||
assertErrorFormat '''
|
||
(a: b) ->
|
||
''', '''
|
||
[stdin]:1:3: error: unexpected :
|
||
(a: b) ->
|
||
^
|
||
'''
|
||
assertErrorFormat '''
|
||
(one, two, {three, four: five}, key: value) ->
|
||
''', '''
|
||
[stdin]:1:36: error: unexpected :
|
||
(one, two, {three, four: five}, key: value) ->
|
||
^
|
||
'''
|
||
|
||
test "#4130: unassignable in destructured param", ->
|
||
assertErrorFormat '''
|
||
fun = ({
|
||
@param : null
|
||
}) ->
|
||
console.log "Oh hello!"
|
||
''', '''
|
||
[stdin]:2:12: error: keyword 'null' can't be assigned
|
||
@param : null
|
||
^^^^
|
||
'''
|
||
assertErrorFormat '''
|
||
({a: null}) ->
|
||
''', '''
|
||
[stdin]:1:6: error: keyword 'null' can't be assigned
|
||
({a: null}) ->
|
||
^^^^
|
||
'''
|
||
assertErrorFormat '''
|
||
({a: 1}) ->
|
||
''', '''
|
||
[stdin]:1:6: error: '1' can't be assigned
|
||
({a: 1}) ->
|
||
^
|
||
'''
|
||
assertErrorFormat '''
|
||
({1}) ->
|
||
''', '''
|
||
[stdin]:1:3: error: '1' can't be assigned
|
||
({1}) ->
|
||
^
|
||
'''
|
||
assertErrorFormat '''
|
||
({a: true = 1}) ->
|
||
''', '''
|
||
[stdin]:1:6: error: keyword 'true' can't be assigned
|
||
({a: true = 1}) ->
|
||
^^^^
|
||
'''
|
||
|
||
test "`yield` outside of a function", ->
|
||
assertErrorFormat '''
|
||
yield 1
|
||
''', '''
|
||
[stdin]:1:1: error: yield can only occur inside functions
|
||
yield 1
|
||
^^^^^^^
|
||
'''
|
||
assertErrorFormat '''
|
||
yield return
|
||
''', '''
|
||
[stdin]:1:1: error: yield can only occur inside functions
|
||
yield return
|
||
^^^^^^^^^^^^
|
||
'''
|
||
|
||
test "#4097: `yield return` as an expression", ->
|
||
assertErrorFormat '''
|
||
-> (yield return)
|
||
''', '''
|
||
[stdin]:1:5: error: cannot use a pure statement in an expression
|
||
-> (yield return)
|
||
^^^^^^^^^^^^
|
||
'''
|
||
|
||
test "#5013: `await return` as an expression", ->
|
||
assertErrorFormat '''
|
||
-> (await return)
|
||
''', '''
|
||
[stdin]:1:5: error: cannot use a pure statement in an expression
|
||
-> (await return)
|
||
^^^^^^^^^^^^
|
||
'''
|
||
|
||
test "#5013: `return` as an expression", ->
|
||
assertErrorFormat '''
|
||
-> (return)
|
||
''', '''
|
||
[stdin]:1:5: error: cannot use a pure statement in an expression
|
||
-> (return)
|
||
^^^^^^
|
||
'''
|
||
|
||
test "#5013: `break` as an expression", ->
|
||
assertErrorFormat '''
|
||
(b = 1; break) for b in a
|
||
''', '''
|
||
[stdin]:1:9: error: cannot use a pure statement in an expression
|
||
(b = 1; break) for b in a
|
||
^^^^^
|
||
'''
|
||
|
||
test "#5013: `continue` as an expression", ->
|
||
assertErrorFormat '''
|
||
(b = 1; continue) for b in a
|
||
''', '''
|
||
[stdin]:1:9: error: cannot use a pure statement in an expression
|
||
(b = 1; continue) for b in a
|
||
^^^^^^^^
|
||
'''
|
||
|
||
test "`&&=` and `||=` with a space in-between", ->
|
||
assertErrorFormat '''
|
||
a = 0
|
||
a && = 1
|
||
''', '''
|
||
[stdin]:2:6: error: unexpected =
|
||
a && = 1
|
||
^
|
||
'''
|
||
assertErrorFormat '''
|
||
a = 0
|
||
a and = 1
|
||
''', '''
|
||
[stdin]:2:7: error: unexpected =
|
||
a and = 1
|
||
^
|
||
'''
|
||
assertErrorFormat '''
|
||
a = 0
|
||
a || = 1
|
||
''', '''
|
||
[stdin]:2:6: error: unexpected =
|
||
a || = 1
|
||
^
|
||
'''
|
||
assertErrorFormat '''
|
||
a = 0
|
||
a or = 1
|
||
''', '''
|
||
[stdin]:2:6: error: unexpected =
|
||
a or = 1
|
||
^
|
||
'''
|
||
|
||
test "anonymous functions cannot be exported", ->
|
||
assertErrorFormat '''
|
||
export ->
|
||
console.log 'hello, world!'
|
||
''', '''
|
||
[stdin]:1:8: error: unexpected ->
|
||
export ->
|
||
^^
|
||
'''
|
||
|
||
test "anonymous classes cannot be exported", ->
|
||
assertErrorFormat '''
|
||
export class
|
||
constructor: ->
|
||
console.log 'hello, world!'
|
||
''', '''
|
||
[stdin]:1:8: error: anonymous classes cannot be exported
|
||
export class
|
||
^^^^^
|
||
'''
|
||
|
||
test "unless enclosed by curly braces, only * can be aliased", ->
|
||
assertErrorFormat '''
|
||
import foo as bar from 'lib'
|
||
''', '''
|
||
[stdin]:1:12: error: unexpected as
|
||
import foo as bar from 'lib'
|
||
^^
|
||
'''
|
||
|
||
test "unwrapped imports must follow constrained syntax", ->
|
||
assertErrorFormat '''
|
||
import foo, bar from 'lib'
|
||
''', '''
|
||
[stdin]:1:13: error: unexpected identifier
|
||
import foo, bar from 'lib'
|
||
^^^
|
||
'''
|
||
assertErrorFormat '''
|
||
import foo, bar, baz from 'lib'
|
||
''', '''
|
||
[stdin]:1:13: error: unexpected identifier
|
||
import foo, bar, baz from 'lib'
|
||
^^^
|
||
'''
|
||
assertErrorFormat '''
|
||
import foo, bar as baz from 'lib'
|
||
''', '''
|
||
[stdin]:1:13: error: unexpected identifier
|
||
import foo, bar as baz from 'lib'
|
||
^^^
|
||
'''
|
||
|
||
test "cannot export * without a module to export from", ->
|
||
assertErrorFormat '''
|
||
export *
|
||
''', '''
|
||
[stdin]:1:9: error: unexpected end of input
|
||
export *
|
||
^
|
||
'''
|
||
|
||
test "imports and exports must be top-level", ->
|
||
assertErrorFormatNoAst '''
|
||
if foo
|
||
import { bar } from 'lib'
|
||
''', '''
|
||
[stdin]:2:3: error: import statements must be at top-level scope
|
||
import { bar } from 'lib'
|
||
^^^^^^^^^^^^^^^^^^^^^^^^^
|
||
'''
|
||
assertErrorFormatNoAst '''
|
||
foo = ->
|
||
export { bar }
|
||
''', '''
|
||
[stdin]:2:3: error: export statements must be at top-level scope
|
||
export { bar }
|
||
^^^^^^^^^^^^^^
|
||
'''
|
||
|
||
test "cannot import the same member more than once", ->
|
||
assertErrorFormat '''
|
||
import { foo, foo } from 'lib'
|
||
''', '''
|
||
[stdin]:1:15: error: 'foo' has already been declared
|
||
import { foo, foo } from 'lib'
|
||
^^^
|
||
'''
|
||
assertErrorFormat '''
|
||
import { foo, bar, foo } from 'lib'
|
||
''', '''
|
||
[stdin]:1:20: error: 'foo' has already been declared
|
||
import { foo, bar, foo } from 'lib'
|
||
^^^
|
||
'''
|
||
assertErrorFormat '''
|
||
import { foo, bar as foo } from 'lib'
|
||
''', '''
|
||
[stdin]:1:15: error: 'foo' has already been declared
|
||
import { foo, bar as foo } from 'lib'
|
||
^^^^^^^^^^
|
||
'''
|
||
assertErrorFormat '''
|
||
import foo, { foo } from 'lib'
|
||
''', '''
|
||
[stdin]:1:15: error: 'foo' has already been declared
|
||
import foo, { foo } from 'lib'
|
||
^^^
|
||
'''
|
||
assertErrorFormat '''
|
||
import foo, { bar as foo } from 'lib'
|
||
''', '''
|
||
[stdin]:1:15: error: 'foo' has already been declared
|
||
import foo, { bar as foo } from 'lib'
|
||
^^^^^^^^^^
|
||
'''
|
||
assertErrorFormat '''
|
||
import foo from 'libA'
|
||
import foo from 'libB'
|
||
''', '''
|
||
[stdin]:2:8: error: 'foo' has already been declared
|
||
import foo from 'libB'
|
||
^^^
|
||
'''
|
||
assertErrorFormat '''
|
||
import * as foo from 'libA'
|
||
import { foo } from 'libB'
|
||
''', '''
|
||
[stdin]:2:10: error: 'foo' has already been declared
|
||
import { foo } from 'libB'
|
||
^^^
|
||
'''
|
||
|
||
test "imported members cannot be reassigned", ->
|
||
assertErrorFormat '''
|
||
import { foo } from 'lib'
|
||
foo = 'bar'
|
||
''', '''
|
||
[stdin]:2:1: error: 'foo' is read-only
|
||
foo = 'bar'
|
||
^^^
|
||
'''
|
||
assertErrorFormat '''
|
||
import { foo } from 'lib'
|
||
export default foo = 'bar'
|
||
''', '''
|
||
[stdin]:2:16: error: 'foo' is read-only
|
||
export default foo = 'bar'
|
||
^^^
|
||
'''
|
||
assertErrorFormat '''
|
||
import { foo } from 'lib'
|
||
export foo = 'bar'
|
||
''', '''
|
||
[stdin]:2:8: error: 'foo' is read-only
|
||
export foo = 'bar'
|
||
^^^
|
||
'''
|
||
|
||
test "bound functions cannot be generators", ->
|
||
assertErrorFormat 'f = => yield this', '''
|
||
[stdin]:1:8: error: yield cannot occur inside bound (fat arrow) functions
|
||
f = => yield this
|
||
^^^^^^^^^^
|
||
'''
|
||
|
||
test "#4790: bound functions cannot be generators, even when we’re creating IIFEs", ->
|
||
assertErrorFormat '''
|
||
=>
|
||
for x in []
|
||
for y in []
|
||
yield z
|
||
''', '''
|
||
[stdin]:4:7: error: yield cannot occur inside bound (fat arrow) functions
|
||
yield z
|
||
^^^^^^^
|
||
'''
|
||
|
||
test "CoffeeScript keywords cannot be used as unaliased names in import lists", ->
|
||
assertErrorFormat """
|
||
import { unless, baz as bar } from 'lib'
|
||
bar.barMethod()
|
||
""", '''
|
||
[stdin]:1:10: error: unexpected unless
|
||
import { unless, baz as bar } from 'lib'
|
||
^^^^^^
|
||
'''
|
||
|
||
test "CoffeeScript keywords cannot be used as local names in import list aliases", ->
|
||
assertErrorFormat """
|
||
import { bar as unless, baz as bar } from 'lib'
|
||
bar.barMethod()
|
||
""", '''
|
||
[stdin]:1:17: error: unexpected unless
|
||
import { bar as unless, baz as bar } from 'lib'
|
||
^^^^^^
|
||
'''
|
||
|
||
test "cannot have `await return` outside a function", ->
|
||
assertErrorFormat '''
|
||
await return
|
||
''', '''
|
||
[stdin]:1:1: error: await can only occur inside functions
|
||
await return
|
||
^^^^^^^^^^^^
|
||
'''
|
||
|
||
test "indexes are not supported in for-from loops", ->
|
||
assertErrorFormat "x for x, i from [1, 2, 3]", '''
|
||
[stdin]:1:10: error: cannot use index with for-from
|
||
x for x, i from [1, 2, 3]
|
||
^
|
||
'''
|
||
|
||
test "own is not supported in for-from loops", ->
|
||
assertErrorFormat "x for own x from [1, 2, 3]", '''
|
||
[stdin]:1:7: error: cannot use own with for-from
|
||
x for own x from [1, 2, 3]
|
||
^^^
|
||
'''
|
||
|
||
test "tagged template literals must be called by an identifier", ->
|
||
assertErrorFormat "1''", '''
|
||
[stdin]:1:1: error: literal is not a function
|
||
1''
|
||
^
|
||
'''
|
||
assertErrorFormat '1""', '''
|
||
[stdin]:1:1: error: literal is not a function
|
||
1""
|
||
^
|
||
'''
|
||
assertErrorFormat "1'b'", '''
|
||
[stdin]:1:1: error: literal is not a function
|
||
1'b'
|
||
^
|
||
'''
|
||
assertErrorFormat '1"b"', '''
|
||
[stdin]:1:1: error: literal is not a function
|
||
1"b"
|
||
^
|
||
'''
|
||
assertErrorFormat "1'''b'''", """
|
||
[stdin]:1:1: error: literal is not a function
|
||
1'''b'''
|
||
^
|
||
"""
|
||
assertErrorFormat '1"""b"""', '''
|
||
[stdin]:1:1: error: literal is not a function
|
||
1"""b"""
|
||
^
|
||
'''
|
||
assertErrorFormat '1"#{b}"', '''
|
||
[stdin]:1:1: error: literal is not a function
|
||
1"#{b}"
|
||
^
|
||
'''
|
||
assertErrorFormat '1"""#{b}"""', '''
|
||
[stdin]:1:1: error: literal is not a function
|
||
1"""#{b}"""
|
||
^
|
||
'''
|
||
|
||
test "constructor functions can't be async", ->
|
||
assertErrorFormat 'class then constructor: -> await x', '''
|
||
[stdin]:1:12: error: Class constructor may not be async
|
||
class then constructor: -> await x
|
||
^^^^^^^^^^^
|
||
'''
|
||
|
||
test "constructor functions can't be generators", ->
|
||
assertErrorFormat 'class then constructor: -> yield', '''
|
||
[stdin]:1:12: error: Class constructor may not be a generator
|
||
class then constructor: -> yield
|
||
^^^^^^^^^^^
|
||
'''
|
||
|
||
test "non-derived constructors can't call super", ->
|
||
assertErrorFormat 'class then constructor: -> super()', '''
|
||
[stdin]:1:28: error: 'super' is only allowed in derived class constructors
|
||
class then constructor: -> super()
|
||
^^^^^^^
|
||
'''
|
||
|
||
test "derived constructors can't reference `this` before calling super", ->
|
||
assertErrorFormat 'class extends A then constructor: -> @', '''
|
||
[stdin]:1:38: error: Can't reference 'this' before calling super in derived class constructors
|
||
class extends A then constructor: -> @
|
||
^
|
||
'''
|
||
|
||
test "derived constructors can't use @params without calling super", ->
|
||
assertErrorFormat 'class extends A then constructor: (@a) ->', '''
|
||
[stdin]:1:36: error: Can't use @params in derived class constructors without calling super
|
||
class extends A then constructor: (@a) ->
|
||
^^
|
||
'''
|
||
|
||
test "derived constructors can't call super with @params", ->
|
||
assertErrorFormat 'class extends A then constructor: (@a) -> super(@a)', '''
|
||
[stdin]:1:49: error: Can't call super with @params in derived class constructors
|
||
class extends A then constructor: (@a) -> super(@a)
|
||
^^
|
||
'''
|
||
|
||
test "derived constructors can't call super with buried @params", ->
|
||
assertErrorFormat 'class extends A then constructor: (@a) -> super((=> @a)())', '''
|
||
[stdin]:1:53: error: Can't call super with @params in derived class constructors
|
||
class extends A then constructor: (@a) -> super((=> @a)())
|
||
^^
|
||
'''
|
||
|
||
test "'super' is not allowed in constructor parameter defaults", ->
|
||
assertErrorFormat 'class extends A then constructor: (a = super()) ->', '''
|
||
[stdin]:1:40: error: 'super' is not allowed in constructor parameter defaults
|
||
class extends A then constructor: (a = super()) ->
|
||
^^^^^^^
|
||
'''
|
||
|
||
test "can't use pattern matches for loop indices", ->
|
||
assertErrorFormat 'a for b, {c} in d', '''
|
||
[stdin]:1:10: error: index cannot be a pattern matching expression
|
||
a for b, {c} in d
|
||
^^^
|
||
'''
|
||
|
||
test "bare 'super' is no longer allowed", ->
|
||
# TODO Improve this error message (it should at least be 'unexpected super')
|
||
assertErrorFormat 'class extends A then constructor: -> super', '''
|
||
[stdin]:1:35: error: unexpected ->
|
||
class extends A then constructor: -> super
|
||
^^
|
||
'''
|
||
|
||
test "soaked 'super' in constructor", ->
|
||
assertErrorFormat 'class extends A then constructor: -> super?()', '''
|
||
[stdin]:1:38: error: Unsupported reference to 'super'
|
||
class extends A then constructor: -> super?()
|
||
^^^^^
|
||
'''
|
||
|
||
test "new with 'super'", ->
|
||
assertErrorFormat 'class extends A then foo: -> new super()', '''
|
||
[stdin]:1:34: error: Unsupported reference to 'super'
|
||
class extends A then foo: -> new super()
|
||
^^^^^
|
||
'''
|
||
|
||
test "'super' outside method", ->
|
||
assertErrorFormat 'super()', '''
|
||
[stdin]:1:1: error: cannot use super outside of an instance method
|
||
super()
|
||
^^^^^
|
||
'''
|
||
|
||
test "getter keyword in object", ->
|
||
assertErrorFormat '''
|
||
obj =
|
||
get foo: ->
|
||
''', '''
|
||
[stdin]:2:3: error: 'get' cannot be used as a keyword, or as a function call without parentheses
|
||
get foo: ->
|
||
^^^
|
||
'''
|
||
|
||
test "setter keyword in object", ->
|
||
assertErrorFormat '''
|
||
obj =
|
||
set foo: ->
|
||
''', '''
|
||
[stdin]:2:3: error: 'set' cannot be used as a keyword, or as a function call without parentheses
|
||
set foo: ->
|
||
^^^
|
||
'''
|
||
|
||
test "getter keyword in inline implicit object", ->
|
||
assertErrorFormat 'obj = get foo: ->', '''
|
||
[stdin]:1:7: error: 'get' cannot be used as a keyword, or as a function call without parentheses
|
||
obj = get foo: ->
|
||
^^^
|
||
'''
|
||
|
||
test "setter keyword in inline implicit object", ->
|
||
assertErrorFormat 'obj = set foo: ->', '''
|
||
[stdin]:1:7: error: 'set' cannot be used as a keyword, or as a function call without parentheses
|
||
obj = set foo: ->
|
||
^^^
|
||
'''
|
||
|
||
test "getter keyword in inline explicit object", ->
|
||
assertErrorFormat 'obj = {get foo: ->}', '''
|
||
[stdin]:1:8: error: 'get' cannot be used as a keyword, or as a function call without parentheses
|
||
obj = {get foo: ->}
|
||
^^^
|
||
'''
|
||
|
||
test "setter keyword in inline explicit object", ->
|
||
assertErrorFormat 'obj = {set foo: ->}', '''
|
||
[stdin]:1:8: error: 'set' cannot be used as a keyword, or as a function call without parentheses
|
||
obj = {set foo: ->}
|
||
^^^
|
||
'''
|
||
|
||
test "getter keyword in function", ->
|
||
assertErrorFormat '''
|
||
f = ->
|
||
get foo: ->
|
||
''', '''
|
||
[stdin]:2:3: error: 'get' cannot be used as a keyword, or as a function call without parentheses
|
||
get foo: ->
|
||
^^^
|
||
'''
|
||
|
||
test "setter keyword in function", ->
|
||
assertErrorFormat '''
|
||
f = ->
|
||
set foo: ->
|
||
''', '''
|
||
[stdin]:2:3: error: 'set' cannot be used as a keyword, or as a function call without parentheses
|
||
set foo: ->
|
||
^^^
|
||
'''
|
||
|
||
test "getter keyword in inline function", ->
|
||
assertErrorFormat 'f = -> get foo: ->', '''
|
||
[stdin]:1:8: error: 'get' cannot be used as a keyword, or as a function call without parentheses
|
||
f = -> get foo: ->
|
||
^^^
|
||
'''
|
||
|
||
test "setter keyword in inline function", ->
|
||
assertErrorFormat 'f = -> set foo: ->', '''
|
||
[stdin]:1:8: error: 'set' cannot be used as a keyword, or as a function call without parentheses
|
||
f = -> set foo: ->
|
||
^^^
|
||
'''
|
||
|
||
test "getter keyword in class", ->
|
||
assertErrorFormat '''
|
||
class A
|
||
get foo: ->
|
||
''', '''
|
||
[stdin]:2:3: error: 'get' cannot be used as a keyword, or as a function call without parentheses
|
||
get foo: ->
|
||
^^^
|
||
'''
|
||
|
||
test "setter keyword in class", ->
|
||
assertErrorFormat '''
|
||
class A
|
||
set foo: ->
|
||
''', '''
|
||
[stdin]:2:3: error: 'set' cannot be used as a keyword, or as a function call without parentheses
|
||
set foo: ->
|
||
^^^
|
||
'''
|
||
|
||
test "getter keyword in inline class", ->
|
||
assertErrorFormat 'class A then get foo: ->', '''
|
||
[stdin]:1:14: error: 'get' cannot be used as a keyword, or as a function call without parentheses
|
||
class A then get foo: ->
|
||
^^^
|
||
'''
|
||
|
||
test "setter keyword in inline class", ->
|
||
assertErrorFormat 'class A then set foo: ->', '''
|
||
[stdin]:1:14: error: 'set' cannot be used as a keyword, or as a function call without parentheses
|
||
class A then set foo: ->
|
||
^^^
|
||
'''
|
||
|
||
test "getter keyword before static method", ->
|
||
assertErrorFormat '''
|
||
class A
|
||
get @foo = ->
|
||
''', '''
|
||
[stdin]:2:3: error: 'get' cannot be used as a keyword, or as a function call without parentheses
|
||
get @foo = ->
|
||
^^^
|
||
'''
|
||
|
||
test "setter keyword before static method", ->
|
||
assertErrorFormat '''
|
||
class A
|
||
set @foo = ->
|
||
''', '''
|
||
[stdin]:2:3: error: 'set' cannot be used as a keyword, or as a function call without parentheses
|
||
set @foo = ->
|
||
^^^
|
||
'''
|
||
|
||
test "#4248: Unicode code point escapes", ->
|
||
assertErrorFormat '''
|
||
"a
|
||
#{b} \\u{G02}
|
||
c"
|
||
''', '''
|
||
[stdin]:2:8: error: invalid escape sequence \\u{G02}
|
||
#{b} \\u{G02}
|
||
^\^^^^^^
|
||
'''
|
||
assertErrorFormat '''
|
||
/a\\u{}b/
|
||
''', '''
|
||
[stdin]:1:3: error: invalid escape sequence \\u{}
|
||
/a\\u{}b/
|
||
^\^^^
|
||
'''
|
||
assertErrorFormat '''
|
||
///a \\u{01abc///
|
||
''', '''
|
||
[stdin]:1:6: error: invalid escape sequence \\u{01abc
|
||
///a \\u{01abc///
|
||
^\^^^^^^^
|
||
'''
|
||
|
||
assertErrorFormat '''
|
||
/\\u{123} \\u{110000}/
|
||
''', '''
|
||
[stdin]:1:10: error: unicode code point escapes greater than \\u{10ffff} are not allowed
|
||
/\\u{123} \\u{110000}/
|
||
\ ^\^^^^^^^^^
|
||
'''
|
||
|
||
assertErrorFormat '''
|
||
///abc\\\\\\u{123456}///u
|
||
''', '''
|
||
[stdin]:1:9: error: unicode code point escapes greater than \\u{10ffff} are not allowed
|
||
///abc\\\\\\u{123456}///u
|
||
\ \^\^^^^^^^^^
|
||
'''
|
||
|
||
assertErrorFormat '''
|
||
"""
|
||
\\u{123}
|
||
a
|
||
\\u{00110000}
|
||
#{ 'b' }
|
||
"""
|
||
''', '''
|
||
[stdin]:4:5: error: unicode code point escapes greater than \\u{10ffff} are not allowed
|
||
\\u{00110000}
|
||
^\^^^^^^^^^^^
|
||
'''
|
||
|
||
assertErrorFormat '''
|
||
'\\u{a}\\u{1111110000}'
|
||
''', '''
|
||
[stdin]:1:7: error: unicode code point escapes greater than \\u{10ffff} are not allowed
|
||
'\\u{a}\\u{1111110000}'
|
||
\ ^\^^^^^^^^^^^^^
|
||
'''
|
||
|
||
test "JSX error: non-matching tag names", ->
|
||
assertErrorFormat '''
|
||
<div><span></div></span>
|
||
''',
|
||
'''
|
||
[stdin]:1:7: error: expected corresponding JSX closing tag for span
|
||
<div><span></div></span>
|
||
^^^^
|
||
'''
|
||
|
||
test "JSX error: bare expressions not allowed", ->
|
||
assertErrorFormat '''
|
||
<div x=3 />
|
||
''',
|
||
'''
|
||
[stdin]:1:8: error: expected wrapped or quoted JSX attribute
|
||
<div x=3 />
|
||
^
|
||
'''
|
||
|
||
test "JSX error: unescaped opening tag angle bracket disallowed", ->
|
||
assertErrorFormat '''
|
||
<Person><<</Person>
|
||
''',
|
||
'''
|
||
[stdin]:1:9: error: unexpected <<
|
||
<Person><<</Person>
|
||
^^
|
||
'''
|
||
|
||
test "JSX error: ambiguous tag-like expression", ->
|
||
assertErrorFormat '''
|
||
x = a <b > c
|
||
''',
|
||
'''
|
||
[stdin]:1:10: error: missing </
|
||
x = a <b > c
|
||
^
|
||
'''
|
||
|
||
test 'JSX error: invalid attributes', ->
|
||
assertErrorFormat '''
|
||
<div a="b" {props} />
|
||
''', '''
|
||
[stdin]:1:12: error: Unexpected token. Allowed JSX attributes are: id="val", src={source}, {props...} or attribute.
|
||
<div a="b" {props} />
|
||
^^^^^^^
|
||
'''
|
||
assertErrorFormat '''
|
||
<div a={b} {a:{b}} />
|
||
''', '''
|
||
[stdin]:1:12: error: Unexpected token. Allowed JSX attributes are: id="val", src={source}, {props...} or attribute.
|
||
<div a={b} {a:{b}} />
|
||
^^^^^^^
|
||
'''
|
||
assertErrorFormat '''
|
||
<div {"#{a}"} />
|
||
''', '''
|
||
[stdin]:1:6: error: Unexpected token. Allowed JSX attributes are: id="val", src={source}, {props...} or attribute.
|
||
<div {"#{a}"} />
|
||
^^^^^^^^
|
||
'''
|
||
assertErrorFormat '''
|
||
<div props... />
|
||
''', '''
|
||
[stdin]:1:11: error: Unexpected token. Allowed JSX attributes are: id="val", src={source}, {props...} or attribute.
|
||
<div props... />
|
||
^^^
|
||
'''
|
||
assertErrorFormat '''
|
||
<div {a:"b", props..., c:d()} />
|
||
''', '''
|
||
[stdin]:1:6: error: Unexpected token. Allowed JSX attributes are: id="val", src={source}, {props...} or attribute.
|
||
<div {a:"b", props..., c:d()} />
|
||
^^^^^^^^^^^^^^^^^^^^^^^^
|
||
'''
|
||
assertErrorFormat '''
|
||
<div {props..., a, b} />
|
||
''', '''
|
||
[stdin]:1:6: error: Unexpected token. Allowed JSX attributes are: id="val", src={source}, {props...} or attribute.
|
||
<div {props..., a, b} />
|
||
^^^^^^^^^^^^^^^^
|
||
'''
|
||
|
||
test '#5034: JSX error: Adjacent JSX elements must be wrapped in an enclosing tag', ->
|
||
assertErrorFormat '''
|
||
render = -> (
|
||
<Row>a</Row>
|
||
<Row>b</Row>
|
||
)
|
||
''', '''
|
||
[stdin]:3:3: error: Adjacent JSX elements must be wrapped in an enclosing tag
|
||
<Row>b</Row>
|
||
^^^^^^^^^^^^
|
||
'''
|
||
assertErrorFormat '''
|
||
render = -> (
|
||
a = "foo"
|
||
<Row>a</Row>
|
||
<Row>b</Row>
|
||
)
|
||
''', '''
|
||
[stdin]:4:3: error: Adjacent JSX elements must be wrapped in an enclosing tag
|
||
<Row>b</Row>
|
||
^^^^^^^^^^^^
|
||
'''
|
||
test 'Bound method called as callback before binding throws runtime error', ->
|
||
class Base
|
||
constructor: ->
|
||
f = @derivedBound
|
||
try
|
||
f()
|
||
ok no
|
||
catch e
|
||
eq e.message, 'Bound instance method accessed before binding'
|
||
|
||
class Derived extends Base
|
||
derivedBound: =>
|
||
ok no
|
||
d = new Derived
|
||
|
||
test "#3845/#3446: chain after function glyph (but not inline)", ->
|
||
assertErrorFormat '''
|
||
a -> .b
|
||
''',
|
||
'''
|
||
[stdin]:1:6: error: unexpected .
|
||
a -> .b
|
||
^
|
||
'''
|
||
|
||
test "#3906: error for unusual indentation", ->
|
||
assertErrorFormat '''
|
||
a
|
||
c
|
||
.d
|
||
|
||
e(
|
||
f)
|
||
|
||
g
|
||
''', '''
|
||
[stdin]:2:1: error: unexpected indentation
|
||
c
|
||
^^
|
||
'''
|
||
|
||
test "#4283: error message for implicit call", ->
|
||
assertErrorFormat '''
|
||
(a, b c) ->
|
||
''', '''
|
||
[stdin]:1:5: error: unexpected implicit function call
|
||
(a, b c) ->
|
||
^
|
||
'''
|
||
|
||
test "#3199: error message for call indented non-object", ->
|
||
assertErrorFormat '''
|
||
fn = ->
|
||
fn
|
||
1
|
||
''', '''
|
||
[stdin]:3:1: error: unexpected indentation
|
||
1
|
||
^^
|
||
'''
|
||
|
||
test "#3199: error message for call indented comprehension", ->
|
||
assertErrorFormat '''
|
||
fn = ->
|
||
fn
|
||
x for x in [1, 2, 3]
|
||
''', '''
|
||
[stdin]:3:1: error: unexpected indentation
|
||
x for x in [1, 2, 3]
|
||
^^
|
||
'''
|
||
|
||
test "#3199: error message for return indented non-object", ->
|
||
assertErrorFormat '''
|
||
return
|
||
1
|
||
''', '''
|
||
[stdin]:2:3: error: unexpected number
|
||
1
|
||
^
|
||
'''
|
||
|
||
test "#3199: error message for return indented comprehension", ->
|
||
assertErrorFormat '''
|
||
return
|
||
x for x in [1, 2, 3]
|
||
''', '''
|
||
[stdin]:2:3: error: unexpected identifier
|
||
x for x in [1, 2, 3]
|
||
^
|
||
'''
|
||
|
||
test "#3199: error message for throw indented non-object", ->
|
||
assertErrorFormat '''
|
||
throw
|
||
1
|
||
''', '''
|
||
[stdin]:2:3: error: unexpected number
|
||
1
|
||
^
|
||
'''
|
||
|
||
test "#3199: error message for throw indented comprehension", ->
|
||
assertErrorFormat '''
|
||
throw
|
||
x for x in [1, 2, 3]
|
||
''', '''
|
||
[stdin]:2:3: error: unexpected identifier
|
||
x for x in [1, 2, 3]
|
||
^
|
||
'''
|
||
|
||
test "#3199: error message for yield indented non-object", ->
|
||
assertErrorFormat '''
|
||
->
|
||
yield
|
||
1
|
||
''', '''
|
||
[stdin]:3:5: error: unexpected number
|
||
1
|
||
^
|
||
'''
|
||
|
||
test "#3199: error message for yield indented comprehension", ->
|
||
assertErrorFormat '''
|
||
->
|
||
yield
|
||
x for x in [1, 2, 3]
|
||
''', '''
|
||
[stdin]:3:5: error: unexpected identifier
|
||
x for x in [1, 2, 3]
|
||
^
|
||
'''
|
||
|
||
test "#3199: error message for await indented non-object", ->
|
||
assertErrorFormat '''
|
||
->
|
||
await
|
||
1
|
||
''', '''
|
||
[stdin]:3:5: error: unexpected number
|
||
1
|
||
^
|
||
'''
|
||
|
||
test "#3199: error message for await indented comprehension", ->
|
||
assertErrorFormat '''
|
||
->
|
||
await
|
||
x for x in [1, 2, 3]
|
||
''', '''
|
||
[stdin]:3:5: error: unexpected identifier
|
||
x for x in [1, 2, 3]
|
||
^
|
||
'''
|
||
|
||
test "#3098: suppressed newline should be unsuppressed by semicolon", ->
|
||
assertErrorFormat '''
|
||
a = ; 5
|
||
''', '''
|
||
[stdin]:1:5: error: unexpected ;
|
||
a = ; 5
|
||
^
|
||
'''
|
||
|
||
test "#4811: '///' inside a heregex comment does not close the heregex", ->
|
||
assertErrorFormat '''
|
||
/// .* # comment ///
|
||
''', '''
|
||
[stdin]:1:1: error: missing ///
|
||
/// .* # comment ///
|
||
^^^
|
||
'''
|
||
|
||
test "#3933: prevent implicit calls when cotrol flow is missing `THEN`", ->
|
||
assertErrorFormat '''
|
||
for a in b do ->
|
||
''','''
|
||
[stdin]:1:12: error: unexpected do
|
||
for a in b do ->
|
||
^^
|
||
'''
|
||
|
||
assertErrorFormat '''
|
||
for a in b ->
|
||
''','''
|
||
[stdin]:1:12: error: unexpected ->
|
||
for a in b ->
|
||
^^
|
||
'''
|
||
|
||
assertErrorFormat '''
|
||
for a in b do =>
|
||
''','''
|
||
[stdin]:1:12: error: unexpected do
|
||
for a in b do =>
|
||
^^
|
||
'''
|
||
|
||
assertErrorFormat '''
|
||
while a do ->
|
||
''','''
|
||
[stdin]:1:9: error: unexpected do
|
||
while a do ->
|
||
^^
|
||
'''
|
||
|
||
assertErrorFormat '''
|
||
until a do =>
|
||
''','''
|
||
[stdin]:1:9: error: unexpected do
|
||
until a do =>
|
||
^^
|
||
'''
|
||
|
||
assertErrorFormat '''
|
||
switch
|
||
when a ->
|
||
''','''
|
||
[stdin]:2:10: error: unexpected ->
|
||
when a ->
|
||
^^
|
||
'''
|
||
|
||
test "`new.target` outside of a function", ->
|
||
assertErrorFormat '''
|
||
new.target
|
||
''', '''
|
||
[stdin]:1:1: error: new.target can only occur inside functions
|
||
new.target
|
||
^^^^^^^^^^
|
||
'''
|
||
|
||
test "`new.target` is only allowed meta property", ->
|
||
assertErrorFormat '''
|
||
-> new.something
|
||
''', '''
|
||
[stdin]:1:4: error: the only valid meta property for new is new.target
|
||
-> new.something
|
||
^^^^^^^^^^^^^
|
||
'''
|
||
|
||
test "`import.meta` is only allowed meta property", ->
|
||
assertErrorFormat '''
|
||
foo = import.something
|
||
''', '''
|
||
[stdin]:1:7: error: the only valid meta property for import is import.meta
|
||
foo = import.something
|
||
^^^^^^^^^^^^^^^^
|
||
'''
|
||
|
||
test "`new.target` cannot be assigned", ->
|
||
assertErrorFormat '''
|
||
->
|
||
new.target = b
|
||
''', '''
|
||
[stdin]:2:14: error: unexpected =
|
||
new.target = b
|
||
^
|
||
'''
|
||
|
||
test "#4834: dynamic import accepts either one or two arguments", ->
|
||
assertErrorFormat '''
|
||
import()
|
||
''', '''
|
||
[stdin]:1:1: error: import() accepts either one or two arguments
|
||
import()
|
||
^^^^^^^^
|
||
'''
|
||
|
||
assertErrorFormat '''
|
||
import('x', {}, 3)
|
||
''', '''
|
||
[stdin]:1:1: error: import() accepts either one or two arguments
|
||
import('x', {}, 3)
|
||
^^^^^^^^^^^^^^^^^^
|
||
'''
|
||
|
||
test "#4834: dynamic import requires explicit call parentheses", ->
|
||
assertErrorFormat '''
|
||
promise = import 'foo'
|
||
''', '''
|
||
[stdin]:1:23: error: unexpected end of input
|
||
promise = import 'foo'
|
||
^
|
||
'''
|