Unify, simplify and fixup assignment errors
- Show the same type of error message for compound assignment as for `=` assignment when the LHS is invalid. - Show the same type of error message when trying to assign to a CoffeeScript keyword as when trying to assign to a JavaScript keyword. - Now longer treat `&& =` as `&&=`. The same goes for `and=`, `||=` and `or=`. - Unify the error message to: `<optional type> '<value>' can't be assigned`.
This commit is contained in:
parent
585932cf5b
commit
4d8cd03298
|
@ -1,7 +1,8 @@
|
|||
// Generated by CoffeeScript 1.10.0
|
||||
(function() {
|
||||
var BOM, BOOL, CALLABLE, CODE, COFFEE_ALIASES, COFFEE_ALIAS_MAP, COFFEE_KEYWORDS, COMMENT, COMPARE, COMPOUND_ASSIGN, HERECOMMENT_ILLEGAL, HEREDOC_DOUBLE, HEREDOC_INDENT, HEREDOC_SINGLE, HEREGEX, HEREGEX_OMIT, IDENTIFIER, INDENTABLE_CLOSERS, INDEXABLE, INVALID_ESCAPE, INVERSES, JSTOKEN, JS_FORBIDDEN, JS_KEYWORDS, LEADING_BLANK_LINE, LINE_BREAK, LINE_CONTINUER, LOGIC, Lexer, MATH, MULTI_DENT, NOT_REGEX, NUMBER, OPERATOR, POSSIBLY_DIVISION, REGEX, REGEX_FLAGS, REGEX_ILLEGAL, RELATION, RESERVED, Rewriter, SHIFT, SIMPLE_STRING_OMIT, STRICT_PROSCRIBED, STRING_DOUBLE, STRING_OMIT, STRING_SINGLE, STRING_START, TRAILING_BLANK_LINE, TRAILING_SPACES, UNARY, UNARY_MATH, VALID_FLAGS, WHITESPACE, compact, count, invertLiterate, key, locationDataToString, ref, ref1, repeat, starts, throwSyntaxError,
|
||||
indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; };
|
||||
var BOM, BOOL, CALLABLE, CODE, COFFEE_ALIASES, COFFEE_ALIAS_MAP, COFFEE_KEYWORDS, COMMENT, COMPARE, COMPOUND_ASSIGN, HERECOMMENT_ILLEGAL, HEREDOC_DOUBLE, HEREDOC_INDENT, HEREDOC_SINGLE, HEREGEX, HEREGEX_OMIT, IDENTIFIER, INDENTABLE_CLOSERS, INDEXABLE, INVALID_ESCAPE, INVERSES, JSTOKEN, JS_FORBIDDEN, JS_KEYWORDS, LEADING_BLANK_LINE, LINE_BREAK, LINE_CONTINUER, LOGIC, Lexer, MATH, MULTI_DENT, NOT_REGEX, NUMBER, OPERATOR, POSSIBLY_DIVISION, REGEX, REGEX_FLAGS, REGEX_ILLEGAL, RELATION, RESERVED, Rewriter, SHIFT, SIMPLE_STRING_OMIT, STRICT_PROSCRIBED, STRING_DOUBLE, STRING_OMIT, STRING_SINGLE, STRING_START, TRAILING_BLANK_LINE, TRAILING_SPACES, UNARY, UNARY_MATH, VALID_FLAGS, WHITESPACE, compact, count, invertLiterate, isUnassignable, key, locationDataToString, ref, ref1, repeat, starts, throwSyntaxError,
|
||||
indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; },
|
||||
slice = [].slice;
|
||||
|
||||
ref = require('./rewriter'), Rewriter = ref.Rewriter, INVERSES = ref.INVERSES;
|
||||
|
||||
|
@ -495,7 +496,7 @@
|
|||
};
|
||||
|
||||
Lexer.prototype.literalToken = function() {
|
||||
var match, prev, ref2, ref3, ref4, ref5, ref6, tag, token, value;
|
||||
var match, message, origin, prev, ref2, ref3, ref4, ref5, ref6, skipToken, tag, token, value;
|
||||
if (match = OPERATOR.exec(this.chunk)) {
|
||||
value = match[0];
|
||||
if (CODE.test(value)) {
|
||||
|
@ -506,16 +507,22 @@
|
|||
}
|
||||
tag = value;
|
||||
ref2 = this.tokens, prev = ref2[ref2.length - 1];
|
||||
if (value === '=' && prev) {
|
||||
if (prev.variable && (ref3 = prev[1], indexOf.call(JS_FORBIDDEN, ref3) >= 0)) {
|
||||
if (prev.origin) {
|
||||
prev = prev.origin;
|
||||
}
|
||||
this.error("reserved word '" + prev[1] + "' can't be assigned", prev[2]);
|
||||
}
|
||||
if ((ref4 = prev[1]) === '||' || ref4 === '&&') {
|
||||
if (prev && indexOf.call(['='].concat(slice.call(COMPOUND_ASSIGN)), value) >= 0) {
|
||||
skipToken = false;
|
||||
if (value === '=' && ((ref3 = prev[1]) === '||' || ref3 === '&&') && !prev.spaced) {
|
||||
prev[0] = 'COMPOUND_ASSIGN';
|
||||
prev[1] += '=';
|
||||
prev = this.tokens[this.tokens.length - 2];
|
||||
skipToken = true;
|
||||
}
|
||||
if (prev && prev.variable) {
|
||||
origin = (ref4 = prev.origin) != null ? ref4 : prev;
|
||||
message = isUnassignable(prev[1], origin[1]);
|
||||
if (message) {
|
||||
this.error(message, origin[2]);
|
||||
}
|
||||
}
|
||||
if (skipToken) {
|
||||
return value.length;
|
||||
}
|
||||
}
|
||||
|
@ -886,6 +893,24 @@
|
|||
|
||||
})();
|
||||
|
||||
isUnassignable = function(name, displayName) {
|
||||
if (displayName == null) {
|
||||
displayName = name;
|
||||
}
|
||||
switch (false) {
|
||||
case indexOf.call(slice.call(JS_KEYWORDS).concat(slice.call(COFFEE_KEYWORDS)), name) < 0:
|
||||
return "keyword '" + displayName + "' can't be assigned";
|
||||
case indexOf.call(STRICT_PROSCRIBED, name) < 0:
|
||||
return "'" + displayName + "' can't be assigned";
|
||||
case indexOf.call(RESERVED, name) < 0:
|
||||
return "reserved word '" + displayName + "' can't be assigned";
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
exports.isUnassignable = isUnassignable;
|
||||
|
||||
JS_KEYWORDS = ['true', 'false', 'null', 'this', 'new', 'delete', 'typeof', 'in', 'instanceof', 'return', 'throw', 'break', 'continue', 'debugger', 'yield', 'if', 'else', 'switch', 'for', 'while', 'do', 'try', 'catch', 'finally', 'class', 'extends', 'super'];
|
||||
|
||||
COFFEE_KEYWORDS = ['undefined', 'then', 'unless', 'until', 'loop', 'of', 'by', 'when'];
|
||||
|
@ -915,15 +940,11 @@
|
|||
|
||||
RESERVED = ['case', 'default', 'function', 'var', 'void', 'with', 'const', 'let', 'enum', 'export', 'import', 'native', 'implements', 'interface', 'package', 'private', 'protected', 'public', 'static'];
|
||||
|
||||
STRICT_PROSCRIBED = ['arguments', 'eval', 'yield*'];
|
||||
STRICT_PROSCRIBED = ['arguments', 'eval'];
|
||||
|
||||
JS_FORBIDDEN = JS_KEYWORDS.concat(RESERVED).concat(STRICT_PROSCRIBED);
|
||||
|
||||
exports.RESERVED = RESERVED.concat(JS_KEYWORDS).concat(COFFEE_KEYWORDS).concat(STRICT_PROSCRIBED);
|
||||
|
||||
exports.STRICT_PROSCRIBED = STRICT_PROSCRIBED;
|
||||
|
||||
exports.JS_FORBIDDEN = JS_FORBIDDEN;
|
||||
exports.JS_FORBIDDEN = JS_KEYWORDS.concat(RESERVED).concat(STRICT_PROSCRIBED);
|
||||
|
||||
BOM = 65279;
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
// Generated by CoffeeScript 1.10.0
|
||||
(function() {
|
||||
var Access, Arr, Assign, Base, Block, BooleanLiteral, Call, Class, Code, CodeFragment, Comment, Existence, Expansion, Extends, For, IdentifierLiteral, If, In, Index, InfinityLiteral, JS_FORBIDDEN, LEVEL_ACCESS, LEVEL_COND, LEVEL_LIST, LEVEL_OP, LEVEL_PAREN, LEVEL_TOP, Literal, NEGATE, NO, NullLiteral, NumberLiteral, Obj, Op, Param, Parens, PassthroughLiteral, RESERVED, Range, RegexLiteral, RegexWithInterpolations, Return, SIMPLENUM, STRICT_PROSCRIBED, Scope, Slice, Splat, StatementLiteral, StringLiteral, StringWithInterpolations, SuperCall, Switch, TAB, THIS, ThisLiteral, Throw, Try, UTILITIES, UndefinedLiteral, Value, While, YES, YieldReturn, addLocationDataFn, compact, del, ends, extend, flatten, fragmentsToText, isComplexOrAssignable, isLiteralArguments, isLiteralThis, locationDataToString, merge, multident, ref1, ref2, some, starts, throwSyntaxError, unfoldSoak, utility,
|
||||
var Access, Arr, Assign, Base, Block, BooleanLiteral, Call, Class, Code, CodeFragment, Comment, Existence, Expansion, Extends, For, IdentifierLiteral, If, In, Index, InfinityLiteral, JS_FORBIDDEN, LEVEL_ACCESS, LEVEL_COND, LEVEL_LIST, LEVEL_OP, LEVEL_PAREN, LEVEL_TOP, Literal, NEGATE, NO, NullLiteral, NumberLiteral, Obj, Op, Param, Parens, PassthroughLiteral, Range, RegexLiteral, RegexWithInterpolations, Return, SIMPLENUM, Scope, Slice, Splat, StatementLiteral, StringLiteral, StringWithInterpolations, SuperCall, Switch, TAB, THIS, ThisLiteral, Throw, Try, UTILITIES, UndefinedLiteral, Value, While, YES, YieldReturn, addLocationDataFn, compact, del, ends, extend, flatten, fragmentsToText, isComplexOrAssignable, isLiteralArguments, isLiteralThis, isUnassignable, locationDataToString, merge, multident, ref1, ref2, some, starts, throwSyntaxError, unfoldSoak, utility,
|
||||
extend1 = function(child, parent) { for (var key in parent) { if (hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; },
|
||||
hasProp = {}.hasOwnProperty,
|
||||
indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; },
|
||||
|
@ -10,7 +10,7 @@
|
|||
|
||||
Scope = require('./scope').Scope;
|
||||
|
||||
ref1 = require('./lexer'), RESERVED = ref1.RESERVED, STRICT_PROSCRIBED = ref1.STRICT_PROSCRIBED, JS_FORBIDDEN = ref1.JS_FORBIDDEN;
|
||||
ref1 = require('./lexer'), isUnassignable = ref1.isUnassignable, JS_FORBIDDEN = ref1.JS_FORBIDDEN;
|
||||
|
||||
ref2 = require('./helpers'), compact = ref2.compact, flatten = ref2.flatten, extend = ref2.extend, merge = ref2.merge, del = ref2.del, starts = ref2.starts, ends = ref2.ends, some = ref2.some, addLocationDataFn = ref2.addLocationDataFn, locationDataToString = ref2.locationDataToString, throwSyntaxError = ref2.throwSyntaxError;
|
||||
|
||||
|
@ -608,10 +608,7 @@
|
|||
return IdentifierLiteral.__super__.constructor.apply(this, arguments);
|
||||
}
|
||||
|
||||
IdentifierLiteral.prototype.isAssignable = function() {
|
||||
var ref3;
|
||||
return ref3 = this.value, indexOf.call(RESERVED, ref3) < 0;
|
||||
};
|
||||
IdentifierLiteral.prototype.isAssignable = YES;
|
||||
|
||||
return IdentifierLiteral;
|
||||
|
||||
|
@ -1580,7 +1577,7 @@
|
|||
Class.prototype.defaultClassVariableName = '_Class';
|
||||
|
||||
Class.prototype.determineName = function() {
|
||||
var name, node, ref3, tail;
|
||||
var message, name, node, ref3, tail;
|
||||
if (!this.variable) {
|
||||
return this.defaultClassVariableName;
|
||||
}
|
||||
|
@ -1590,6 +1587,12 @@
|
|||
return this.defaultClassVariableName;
|
||||
}
|
||||
name = node.value;
|
||||
if (!tail) {
|
||||
message = isUnassignable(name);
|
||||
if (message) {
|
||||
this.variable.error(message);
|
||||
}
|
||||
}
|
||||
if (indexOf.call(JS_FORBIDDEN, name) >= 0) {
|
||||
return "_" + name;
|
||||
} else {
|
||||
|
@ -1762,7 +1765,6 @@
|
|||
extend1(Assign, superClass1);
|
||||
|
||||
function Assign(variable1, value1, context, options) {
|
||||
var forbidden, name, ref3;
|
||||
this.variable = variable1;
|
||||
this.value = value1;
|
||||
this.context = context;
|
||||
|
@ -1770,10 +1772,6 @@
|
|||
options = {};
|
||||
}
|
||||
this.param = options.param, this.subpattern = options.subpattern, this.operatorToken = options.operatorToken;
|
||||
forbidden = (ref3 = (name = this.variable.unwrapAll().value), indexOf.call(STRICT_PROSCRIBED, ref3) >= 0);
|
||||
if (forbidden && this.context !== 'object') {
|
||||
this.variable.error("variable name may not be \"" + name + "\"");
|
||||
}
|
||||
}
|
||||
|
||||
Assign.prototype.children = ['variable', 'value'];
|
||||
|
@ -1823,7 +1821,7 @@
|
|||
if (!this.context) {
|
||||
varBase = this.variable.unwrapAll();
|
||||
if (!varBase.isAssignable()) {
|
||||
this.variable.error("\"" + (this.variable.compile(o)) + "\" cannot be assigned");
|
||||
this.variable.error("'" + (this.variable.compile(o)) + "' can't be assigned");
|
||||
}
|
||||
if (!(typeof varBase.hasProperties === "function" ? varBase.hasProperties() : void 0)) {
|
||||
if (this.param) {
|
||||
|
@ -1854,7 +1852,7 @@
|
|||
};
|
||||
|
||||
Assign.prototype.compilePatternMatch = function(o) {
|
||||
var acc, assigns, code, defaultValue, expandedIdx, fragments, i, idx, isObject, ivar, j, len1, name, obj, objects, olen, ref, ref3, ref4, ref5, ref6, ref7, rest, top, val, value, vvar, vvarText;
|
||||
var acc, assigns, code, defaultValue, expandedIdx, fragments, i, idx, isObject, ivar, j, len1, message, name, obj, objects, olen, ref, ref3, ref4, ref5, ref6, rest, top, val, value, vvar, vvarText;
|
||||
top = o.level === LEVEL_TOP;
|
||||
value = this.value;
|
||||
objects = this.variable.base.objects;
|
||||
|
@ -1889,8 +1887,9 @@
|
|||
acc = idx.unwrap() instanceof IdentifierLiteral;
|
||||
value = new Value(value);
|
||||
value.properties.push(new (acc ? Access : Index)(idx));
|
||||
if (ref5 = obj.unwrap().value, indexOf.call(RESERVED, ref5) >= 0) {
|
||||
obj.error("assignment to a reserved word: " + (obj.compile(o)));
|
||||
message = isUnassignable(obj.unwrap().value);
|
||||
if (message) {
|
||||
obj.error(message);
|
||||
}
|
||||
if (defaultValue) {
|
||||
value = new Op('?', value, defaultValue);
|
||||
|
@ -1945,7 +1944,7 @@
|
|||
}
|
||||
defaultValue = null;
|
||||
if (obj instanceof Assign && obj.context === 'object') {
|
||||
ref6 = obj, (ref7 = ref6.variable, idx = ref7.base), obj = ref6.value;
|
||||
ref5 = obj, (ref6 = ref5.variable, idx = ref6.base), obj = ref5.value;
|
||||
if (obj instanceof Assign) {
|
||||
defaultValue = obj.value;
|
||||
obj = obj.variable;
|
||||
|
@ -1964,8 +1963,11 @@
|
|||
val = new Op('?', val, defaultValue);
|
||||
}
|
||||
}
|
||||
if ((name != null) && indexOf.call(RESERVED, name) >= 0) {
|
||||
obj.error("assignment to a reserved word: " + (obj.compile(o)));
|
||||
if (name != null) {
|
||||
message = isUnassignable(name);
|
||||
if (message) {
|
||||
obj.error(message);
|
||||
}
|
||||
}
|
||||
assigns.push(new Assign(obj, val, null, {
|
||||
param: this.param,
|
||||
|
@ -2223,12 +2225,13 @@
|
|||
extend1(Param, superClass1);
|
||||
|
||||
function Param(name1, value1, splat) {
|
||||
var name, ref3, token;
|
||||
var message, token;
|
||||
this.name = name1;
|
||||
this.value = value1;
|
||||
this.splat = splat;
|
||||
if (ref3 = (name = this.name.unwrapAll().value), indexOf.call(STRICT_PROSCRIBED, ref3) >= 0) {
|
||||
this.name.error("parameter name \"" + name + "\" is not allowed");
|
||||
message = isUnassignable(this.name.unwrapAll().value);
|
||||
if (message) {
|
||||
this.name.error(message);
|
||||
}
|
||||
if (this.name instanceof Obj && this.name.generated) {
|
||||
token = this.name.objects[0].operatorToken;
|
||||
|
@ -2611,7 +2614,7 @@
|
|||
};
|
||||
|
||||
Op.prototype.compileNode = function(o) {
|
||||
var answer, isChain, lhs, ref3, ref4, rhs;
|
||||
var answer, isChain, lhs, message, ref3, rhs;
|
||||
isChain = this.isChainable() && this.first.isChainable();
|
||||
if (!isChain) {
|
||||
this.first.front = this.front;
|
||||
|
@ -2619,8 +2622,11 @@
|
|||
if (this.operator === 'delete' && o.scope.check(this.first.unwrapAll().value)) {
|
||||
this.error('delete operand may not be argument or var');
|
||||
}
|
||||
if (((ref3 = this.operator) === '--' || ref3 === '++') && (ref4 = this.first.unwrapAll().value, indexOf.call(STRICT_PROSCRIBED, ref4) >= 0)) {
|
||||
this.error("cannot increment/decrement \"" + (this.first.unwrapAll().value) + "\"");
|
||||
if ((ref3 = this.operator) === '--' || ref3 === '++') {
|
||||
message = isUnassignable(this.first.unwrapAll().value);
|
||||
if (message) {
|
||||
this.first.error(message);
|
||||
}
|
||||
}
|
||||
if (this.isYield()) {
|
||||
return this.compileYield(o);
|
||||
|
@ -2858,12 +2864,12 @@
|
|||
};
|
||||
|
||||
Try.prototype.compileNode = function(o) {
|
||||
var catchPart, ensurePart, generatedErrorVariableName, placeholder, tryPart;
|
||||
var catchPart, ensurePart, generatedErrorVariableName, message, placeholder, tryPart;
|
||||
o.indent += TAB;
|
||||
tryPart = this.attempt.compileToFragments(o, LEVEL_TOP);
|
||||
catchPart = this.recovery ? (generatedErrorVariableName = o.scope.freeVariable('error', {
|
||||
reserve: false
|
||||
}), placeholder = new IdentifierLiteral(generatedErrorVariableName), this.errorVariable ? this.recovery.unshift(new Assign(this.errorVariable, placeholder)) : void 0, [].concat(this.makeCode(" catch ("), placeholder.compileToFragments(o), this.makeCode(") {\n"), this.recovery.compileToFragments(o, LEVEL_TOP), this.makeCode("\n" + this.tab + "}"))) : !(this.ensure || this.recovery) ? (generatedErrorVariableName = o.scope.freeVariable('error', {
|
||||
}), placeholder = new IdentifierLiteral(generatedErrorVariableName), this.errorVariable ? (message = isUnassignable(this.errorVariable.unwrapAll().value), message ? this.errorVariable.error(message) : void 0, this.recovery.unshift(new Assign(this.errorVariable, placeholder))) : void 0, [].concat(this.makeCode(" catch ("), placeholder.compileToFragments(o), this.makeCode(") {\n"), this.recovery.compileToFragments(o, LEVEL_TOP), this.makeCode("\n" + this.tab + "}"))) : !(this.ensure || this.recovery) ? (generatedErrorVariableName = o.scope.freeVariable('error', {
|
||||
reserve: false
|
||||
}), [this.makeCode(" catch (" + generatedErrorVariableName + ") {}")]) : [];
|
||||
ensurePart = this.ensure ? [].concat(this.makeCode(" finally {\n"), this.ensure.compileToFragments(o, LEVEL_TOP), this.makeCode("\n" + this.tab + "}")) : [];
|
||||
|
|
|
@ -411,14 +411,20 @@ exports.Lexer = class Lexer
|
|||
value = @chunk.charAt 0
|
||||
tag = value
|
||||
[..., prev] = @tokens
|
||||
if value is '=' and prev
|
||||
if prev.variable and prev[1] in JS_FORBIDDEN
|
||||
prev = prev.origin if prev.origin
|
||||
@error "reserved word '#{prev[1]}' can't be assigned", prev[2]
|
||||
if prev[1] in ['||', '&&']
|
||||
|
||||
if prev and value in ['=', COMPOUND_ASSIGN...]
|
||||
skipToken = false
|
||||
if value is '=' and prev[1] in ['||', '&&'] and not prev.spaced
|
||||
prev[0] = 'COMPOUND_ASSIGN'
|
||||
prev[1] += '='
|
||||
return value.length
|
||||
prev = @tokens[@tokens.length - 2]
|
||||
skipToken = true
|
||||
if prev and prev.variable
|
||||
origin = prev.origin ? prev
|
||||
message = isUnassignable prev[1], origin[1]
|
||||
@error message, origin[2] if message
|
||||
return value.length if skipToken
|
||||
|
||||
if value is ';'
|
||||
@seenFor = no
|
||||
tag = 'TERMINATOR'
|
||||
|
@ -744,6 +750,21 @@ exports.Lexer = class Lexer
|
|||
{first_line, first_column, last_column: first_column + (options.length ? 1) - 1}
|
||||
throwSyntaxError message, location
|
||||
|
||||
# Helper functions
|
||||
# ----------------
|
||||
|
||||
isUnassignable = (name, displayName = name) -> switch
|
||||
when name in [JS_KEYWORDS..., COFFEE_KEYWORDS...]
|
||||
"keyword '#{displayName}' can't be assigned"
|
||||
when name in STRICT_PROSCRIBED
|
||||
"'#{displayName}' can't be assigned"
|
||||
when name in RESERVED
|
||||
"reserved word '#{displayName}' can't be assigned"
|
||||
else
|
||||
false
|
||||
|
||||
exports.isUnassignable = isUnassignable
|
||||
|
||||
# Constants
|
||||
# ---------
|
||||
|
||||
|
@ -782,15 +803,12 @@ RESERVED = [
|
|||
'protected', 'public', 'static'
|
||||
]
|
||||
|
||||
STRICT_PROSCRIBED = ['arguments', 'eval', 'yield*']
|
||||
STRICT_PROSCRIBED = ['arguments', 'eval']
|
||||
|
||||
# The superset of both JavaScript keywords and reserved words, none of which may
|
||||
# be used as identifiers or properties.
|
||||
JS_FORBIDDEN = JS_KEYWORDS.concat(RESERVED).concat(STRICT_PROSCRIBED)
|
||||
|
||||
exports.RESERVED = RESERVED.concat(JS_KEYWORDS).concat(COFFEE_KEYWORDS).concat(STRICT_PROSCRIBED)
|
||||
exports.STRICT_PROSCRIBED = STRICT_PROSCRIBED
|
||||
exports.JS_FORBIDDEN = JS_FORBIDDEN
|
||||
exports.JS_FORBIDDEN = JS_KEYWORDS.concat(RESERVED).concat(STRICT_PROSCRIBED)
|
||||
|
||||
# The character code of the nasty Microsoft madness otherwise known as the BOM.
|
||||
BOM = 65279
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
Error.stackTraceLimit = Infinity
|
||||
|
||||
{Scope} = require './scope'
|
||||
{RESERVED, STRICT_PROSCRIBED, JS_FORBIDDEN} = require './lexer'
|
||||
{isUnassignable, JS_FORBIDDEN} = require './lexer'
|
||||
|
||||
# Import the helpers we plan to use.
|
||||
{compact, flatten, extend, merge, del, starts, ends, some,
|
||||
|
@ -411,7 +411,7 @@ exports.RegexLiteral = class RegexLiteral extends Literal
|
|||
exports.PassthroughLiteral = class PassthroughLiteral extends Literal
|
||||
|
||||
exports.IdentifierLiteral = class IdentifierLiteral extends Literal
|
||||
isAssignable: -> @value not in RESERVED
|
||||
isAssignable: YES
|
||||
|
||||
exports.StatementLiteral = class StatementLiteral extends Literal
|
||||
isStatement: YES
|
||||
|
@ -1081,6 +1081,9 @@ exports.Class = class Class extends Base
|
|||
@variable.base
|
||||
return @defaultClassVariableName unless node instanceof IdentifierLiteral
|
||||
name = node.value
|
||||
unless tail
|
||||
message = isUnassignable name
|
||||
@variable.error message if message
|
||||
if name in JS_FORBIDDEN then "_#{name}" else name
|
||||
|
||||
# For all `this`-references and bound functions in the class definition,
|
||||
|
@ -1215,9 +1218,6 @@ exports.Class = class Class extends Base
|
|||
exports.Assign = class Assign extends Base
|
||||
constructor: (@variable, @value, @context, options = {}) ->
|
||||
{@param, @subpattern, @operatorToken} = options
|
||||
forbidden = (name = @variable.unwrapAll().value) in STRICT_PROSCRIBED
|
||||
if forbidden and @context isnt 'object'
|
||||
@variable.error "variable name may not be \"#{name}\""
|
||||
|
||||
children: ['variable', 'value']
|
||||
|
||||
|
@ -1254,7 +1254,7 @@ exports.Assign = class Assign extends Base
|
|||
unless @context
|
||||
varBase = @variable.unwrapAll()
|
||||
unless varBase.isAssignable()
|
||||
@variable.error "\"#{@variable.compile o}\" cannot be assigned"
|
||||
@variable.error "'#{@variable.compile o}' can't be assigned"
|
||||
unless varBase.hasProperties?()
|
||||
if @param
|
||||
o.scope.add varBase.value, 'var'
|
||||
|
@ -1309,8 +1309,8 @@ exports.Assign = class Assign extends Base
|
|||
acc = idx.unwrap() instanceof IdentifierLiteral
|
||||
value = new Value value
|
||||
value.properties.push new (if acc then Access else Index) idx
|
||||
if obj.unwrap().value in RESERVED
|
||||
obj.error "assignment to a reserved word: #{obj.compile o}"
|
||||
message = isUnassignable obj.unwrap().value
|
||||
obj.error message if message
|
||||
value = new Op '?', value, defaultValue if defaultValue
|
||||
return new Assign(obj, value, null, param: @param).compileToFragments o, LEVEL_TOP
|
||||
vvar = value.compileToFragments o, LEVEL_LIST
|
||||
|
@ -1369,8 +1369,9 @@ exports.Assign = class Assign extends Base
|
|||
acc = idx.unwrap() instanceof IdentifierLiteral
|
||||
val = new Value new Literal(vvarText), [new (if acc then Access else Index) idx]
|
||||
val = new Op '?', val, defaultValue if defaultValue
|
||||
if name? and name in RESERVED
|
||||
obj.error "assignment to a reserved word: #{obj.compile o}"
|
||||
if name?
|
||||
message = isUnassignable name
|
||||
obj.error message if message
|
||||
assigns.push new Assign(obj, val, null, param: @param, subpattern: yes).compileToFragments o, LEVEL_LIST
|
||||
assigns.push vvar unless top or @subpattern
|
||||
fragments = @joinFragmentArrays assigns, ', '
|
||||
|
@ -1527,8 +1528,8 @@ exports.Code = class Code extends Base
|
|||
# as well as be a splat, gathering up a group of parameters into an array.
|
||||
exports.Param = class Param extends Base
|
||||
constructor: (@name, @value, @splat) ->
|
||||
if (name = @name.unwrapAll().value) in STRICT_PROSCRIBED
|
||||
@name.error "parameter name \"#{name}\" is not allowed"
|
||||
message = isUnassignable @name.unwrapAll().value
|
||||
@name.error message if message
|
||||
if @name instanceof Obj and @name.generated
|
||||
token = @name.objects[0].operatorToken
|
||||
token.error "unexpected #{token.value}"
|
||||
|
@ -1817,8 +1818,9 @@ exports.Op = class Op extends Base
|
|||
@first.front = @front unless isChain
|
||||
if @operator is 'delete' and o.scope.check(@first.unwrapAll().value)
|
||||
@error 'delete operand may not be argument or var'
|
||||
if @operator in ['--', '++'] and @first.unwrapAll().value in STRICT_PROSCRIBED
|
||||
@error "cannot increment/decrement \"#{@first.unwrapAll().value}\""
|
||||
if @operator in ['--', '++']
|
||||
message = isUnassignable @first.unwrapAll().value
|
||||
@first.error message if message
|
||||
return @compileYield o if @isYield()
|
||||
return @compileUnary o if @isUnary()
|
||||
return @compileChain o if isChain
|
||||
|
@ -1969,7 +1971,10 @@ exports.Try = class Try extends Base
|
|||
catchPart = if @recovery
|
||||
generatedErrorVariableName = o.scope.freeVariable 'error', reserve: no
|
||||
placeholder = new IdentifierLiteral generatedErrorVariableName
|
||||
@recovery.unshift new Assign @errorVariable, placeholder if @errorVariable
|
||||
if @errorVariable
|
||||
message = isUnassignable @errorVariable.unwrapAll().value
|
||||
@errorVariable.error message if message
|
||||
@recovery.unshift new Assign @errorVariable, placeholder
|
||||
[].concat @makeCode(" catch ("), placeholder.compileToFragments(o), @makeCode(") {\n"),
|
||||
@recovery.compileToFragments(o, LEVEL_TOP), @makeCode("\n#{@tab}}")
|
||||
else unless @ensure or @recovery
|
||||
|
|
|
@ -36,7 +36,7 @@ test "compiler error formatting", ->
|
|||
evil = (foo, eval, bar) ->
|
||||
''',
|
||||
'''
|
||||
[stdin]:1:14: error: parameter name "eval" is not allowed
|
||||
[stdin]:1:14: error: 'eval' can't be assigned
|
||||
evil = (foo, eval, bar) ->
|
||||
^^^^
|
||||
'''
|
||||
|
@ -656,22 +656,88 @@ test "reserved words", ->
|
|||
case
|
||||
^^^^
|
||||
'''
|
||||
assertErrorFormat '''
|
||||
case = 1
|
||||
''', '''
|
||||
[stdin]:1:1: error: reserved word 'case'
|
||||
case = 1
|
||||
^^^^
|
||||
'''
|
||||
assertErrorFormat '''
|
||||
for = 1
|
||||
''', '''
|
||||
[stdin]:1:1: error: reserved word 'for' can't be assigned
|
||||
[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: reserved word 'on' can't be assigned
|
||||
[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
|
||||
|
@ -832,35 +898,35 @@ test "#4130: unassignable in destructured param", ->
|
|||
}) ->
|
||||
console.log "Oh hello!"
|
||||
''', '''
|
||||
[stdin]:2:12: error: assignment to a reserved word: null
|
||||
[stdin]:2:12: error: keyword 'null' can't be assigned
|
||||
@param : null
|
||||
^^^^
|
||||
'''
|
||||
assertErrorFormat '''
|
||||
({a: null}) ->
|
||||
''', '''
|
||||
[stdin]:1:6: error: assignment to a reserved word: null
|
||||
[stdin]:1:6: error: keyword 'null' can't be assigned
|
||||
({a: null}) ->
|
||||
^^^^
|
||||
'''
|
||||
assertErrorFormat '''
|
||||
({a: 1}) ->
|
||||
''', '''
|
||||
[stdin]:1:6: error: "1" cannot be assigned
|
||||
[stdin]:1:6: error: '1' can't be assigned
|
||||
({a: 1}) ->
|
||||
^
|
||||
'''
|
||||
assertErrorFormat '''
|
||||
({1}) ->
|
||||
''', '''
|
||||
[stdin]:1:3: error: "1" cannot be assigned
|
||||
[stdin]:1:3: error: '1' can't be assigned
|
||||
({1}) ->
|
||||
^
|
||||
'''
|
||||
assertErrorFormat '''
|
||||
({a: true = 1}) ->
|
||||
''', '''
|
||||
[stdin]:1:6: error: reserved word 'true' can't be assigned
|
||||
[stdin]:1:6: error: keyword 'true' can't be assigned
|
||||
({a: true = 1}) ->
|
||||
^^^^
|
||||
'''
|
||||
|
@ -889,3 +955,37 @@ test "#4097: `yield return` as an expression", ->
|
|||
-> (yield return)
|
||||
^^^^^^^^^^^^
|
||||
'''
|
||||
|
||||
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
|
||||
^
|
||||
'''
|
||||
|
|
Loading…
Reference in New Issue