Disallow line terminator after arrow function parameters, before =>

Closes #2282
This commit is contained in:
Caitlin Potter 2015-03-09 22:51:23 -04:00
parent 1bb4a62988
commit 84634ac25d
4 changed files with 318 additions and 161 deletions

View File

@ -8,7 +8,7 @@ module ts {
export function getNodeConstructor(kind: SyntaxKind): new () => Node {
return nodeConstructors[kind] || (nodeConstructors[kind] = objectAllocator.getNodeConstructor(kind));
}
export function createNode(kind: SyntaxKind): Node {
return new (getNodeConstructor(kind))();
}
@ -369,7 +369,7 @@ module ts {
function fixupParentReferences(sourceFile: SourceFile) {
// normally parent references are set during binding. However, for clients that only need
// a syntax tree, and no semantic features, then the binding process is an unnecessary
// a syntax tree, and no semantic features, then the binding process is an unnecessary
// overhead. This functions allows us to set all the parents, without all the expense of
// binding.
@ -379,7 +379,7 @@ module ts {
function visitNode(n: Node): void {
// walk down setting parents that differ from the parent we think it should be. This
// allows us to quickly bail out of setting parents for subtrees during incremental
// allows us to quickly bail out of setting parents for subtrees during incremental
// parsing
if (n.parent !== parent) {
n.parent = parent;
@ -417,7 +417,7 @@ module ts {
var text = oldText.substring(node.pos, node.end);
}
// Ditch any existing LS children we may have created. This way we can avoid
// Ditch any existing LS children we may have created. This way we can avoid
// moving them forward.
node._children = undefined;
node.pos += delta;
@ -455,9 +455,9 @@ module ts {
// We may need to update both the 'pos' and the 'end' of the element.
// If the 'pos' is before the start of the change, then we don't need to touch it.
// If it isn't, then the 'pos' must be inside the change. How we update it will
// depend if delta is positive or negative. If delta is positive then we have
// If the 'pos' is before the start of the change, then we don't need to touch it.
// If it isn't, then the 'pos' must be inside the change. How we update it will
// depend if delta is positive or negative. If delta is positive then we have
// something like:
//
// -------------------AAA-----------------
@ -471,7 +471,7 @@ module ts {
// -------------------XXXYYYYYYY-----------------
// -------------------ZZZ-----------------
//
// In this case, any element that started in the 'X' range will keep its position.
// In this case, any element that started in the 'X' range will keep its position.
// However any element htat started after that will have their pos adjusted to be
// at the end of the new range. i.e. any node that started in the 'Y' range will
// be adjusted to have their start at the end of the 'Z' range.
@ -481,7 +481,7 @@ module ts {
element.pos = Math.min(element.pos, changeRangeNewEnd);
// If the 'end' is after the change range, then we always adjust it by the delta
// amount. However, if the end is in the change range, then how we adjust it
// amount. However, if the end is in the change range, then how we adjust it
// will depend on if delta is positive or negative. If delta is positive then we
// have something like:
//
@ -496,7 +496,7 @@ module ts {
// -------------------XXXYYYYYYY-----------------
// -------------------ZZZ-----------------
//
// In this case, any element that ended in the 'X' range will keep its position.
// In this case, any element that ended in the 'X' range will keep its position.
// However any element htat ended after that will have their pos adjusted to be
// at the end of the new range. i.e. any node that ended in the 'Y' range will
// be adjusted to have their end at the end of the 'Z' range.
@ -505,7 +505,7 @@ module ts {
element.end += delta;
}
else {
// Element ends in the change range. The element will keep its position if
// Element ends in the change range. The element will keep its position if
// possible. Or Move backward to the new-end if it's in the 'Y' range.
element.end = Math.min(element.end, changeRangeNewEnd);
}
@ -544,7 +544,7 @@ module ts {
function visitNode(child: IncrementalNode) {
Debug.assert(child.pos <= child.end);
if (child.pos > changeRangeOldEnd) {
// Node is entirely past the change range. We need to move both its pos and
// Node is entirely past the change range. We need to move both its pos and
// end, forward or backward appropriately.
moveElementEntirelyPastChangeRange(child, /*isArray:*/ false, delta, oldText, newText, aggressiveChecks);
return;
@ -607,12 +607,12 @@ module ts {
// If the text changes with an insertion of / just before the semicolon then we end up with:
// void foo() { //; }
//
// If we were to just use the changeRange a is, then we would not rescan the { token
// If we were to just use the changeRange a is, then we would not rescan the { token
// (as it does not intersect the actual original change range). Because an edit may
// change the token touching it, we actually need to look back *at least* one token so
// that the prior token sees that change.
// that the prior token sees that change.
let maxLookahead = 1;
let start = changeRange.span.start;
// the first iteration aligns us with the change start. subsequent iteration move us to
@ -676,7 +676,7 @@ module ts {
return;
}
// If the child intersects this position, then this node is currently the nearest
// If the child intersects this position, then this node is currently the nearest
// node that starts before the position.
if (child.pos <= position) {
if (child.pos >= bestResult.pos) {
@ -687,7 +687,7 @@ module ts {
// Now, the node may overlap the position, or it may end entirely before the
// position. If it overlaps with the position, then either it, or one of its
// children must be the nearest node before the position. So we can just
// children must be the nearest node before the position. So we can just
// recurse into this child to see if we can find something better.
if (position < child.end) {
// The nearest node is either this child, or one of the children inside
@ -703,15 +703,15 @@ module ts {
Debug.assert(child.end <= position);
// The child ends entirely before this position. Say you have the following
// (where $ is the position)
//
// <complex expr 1> ? <complex expr 2> $ : <...> <...>
//
// We would want to find the nearest preceding node in "complex expr 2".
// <complex expr 1> ? <complex expr 2> $ : <...> <...>
//
// We would want to find the nearest preceding node in "complex expr 2".
// To support that, we keep track of this node, and once we're done searching
// for a best node, we recurse down this node to see if we can find a good
// result in it.
//
// This approach allows us to quickly skip over nodes that are entirely
// This approach allows us to quickly skip over nodes that are entirely
// before the position, while still allowing us to find any nodes in the
// last one that might be what we want.
lastNodeEntirelyBeforePosition = child;
@ -744,13 +744,13 @@ module ts {
}
}
// Produces a new SourceFile for the 'newText' provided. The 'textChangeRange' parameter
// Produces a new SourceFile for the 'newText' provided. The 'textChangeRange' parameter
// indicates what changed between the 'text' that this SourceFile has and the 'newText'.
// The SourceFile will be created with the compiler attempting to reuse as many nodes from
// The SourceFile will be created with the compiler attempting to reuse as many nodes from
// this file as possible.
//
// Note: this function mutates nodes from this SourceFile. That means any existing nodes
// from this SourceFile that are being held onto may change as a result (including
// from this SourceFile that are being held onto may change as a result (including
// becoming detached from any SourceFile). It is recommended that this SourceFile not
// be used once 'update' is called on it.
export function updateSourceFile(sourceFile: SourceFile, newText: string, textChangeRange: TextChangeRange, aggressiveChecks?: boolean): SourceFile {
@ -769,7 +769,7 @@ module ts {
}
// Make sure we're not trying to incrementally update a source file more than once. Once
// we do an update the original source file is considered unusbale from that point onwards.
// we do an update the original source file is considered unusbale from that point onwards.
//
// This is because we do incremental parsing in-place. i.e. we take nodes from the old
// tree and give them new positions and parents. From that point on, trusting the old
@ -781,24 +781,24 @@ module ts {
let oldText = sourceFile.text;
let syntaxCursor = createSyntaxCursor(sourceFile);
// Make the actual change larger so that we know to reparse anything whose lookahead
// Make the actual change larger so that we know to reparse anything whose lookahead
// might have intersected the change.
let changeRange = extendToAffectedRange(sourceFile, textChangeRange);
checkChangeRange(sourceFile, newText, changeRange, aggressiveChecks);
// Ensure that extending the affected range only moved the start of the change range
// Ensure that extending the affected range only moved the start of the change range
// earlier in the file.
Debug.assert(changeRange.span.start <= textChangeRange.span.start);
Debug.assert(textSpanEnd(changeRange.span) === textSpanEnd(textChangeRange.span));
Debug.assert(textSpanEnd(textChangeRangeNewSpan(changeRange)) === textSpanEnd(textChangeRangeNewSpan(textChangeRange)));
// The is the amount the nodes after the edit range need to be adjusted. It can be
// The is the amount the nodes after the edit range need to be adjusted. It can be
// positive (if the edit added characters), negative (if the edit deleted characters)
// or zero (if this was a pure overwrite with nothing added/removed).
let delta = textChangeRangeNewSpan(changeRange).length - changeRange.span.length;
// If we added or removed characters during the edit, then we need to go and adjust all
// the nodes after the edit. Those nodes may move forward (if we inserted chars) or they
// the nodes after the edit. Those nodes may move forward (if we inserted chars) or they
// may move backward (if we deleted chars).
//
// Doing this helps us out in two ways. First, it means that any nodes/tokens we want
@ -811,7 +811,7 @@ module ts {
//
// We will also adjust the positions of nodes that intersect the change range as well.
// By doing this, we ensure that all the positions in the old tree are consistent, not
// just the positions of nodes entirely before/after the change range. By being
// just the positions of nodes entirely before/after the change range. By being
// consistent, we can then easily map from positions to nodes in the old tree easily.
//
// Also, mark any syntax elements that intersect the changed span. We know, up front,
@ -822,15 +822,15 @@ module ts {
// Now that we've set up our internal incremental state just proceed and parse the
// source file in the normal fashion. When possible the parser will retrieve and
// reuse nodes from the old tree.
//
//
// Note: passing in 'true' for setNodeParents is very important. When incrementally
// parsing, we will be reusing nodes from the old tree, and placing it into new
// parents. If we don't set the parents now, we'll end up with an observably
// inconsistent tree. Setting the parents on the new tree should be very fast. We
// parents. If we don't set the parents now, we'll end up with an observably
// inconsistent tree. Setting the parents on the new tree should be very fast. We
// will immediately bail out of walking any subtrees when we can see that their parents
// are already correct.
let result = parseSourceFile(sourceFile.fileName, newText, sourceFile.languageVersion, syntaxCursor, /* setParentNode */ true)
let result = parseSourceFile(sourceFile.fileName, newText, sourceFile.languageVersion, syntaxCursor, /* setParentNode */ true)
return result;
}
@ -885,13 +885,13 @@ module ts {
return {
currentNode(position: number) {
// Only compute the current node if the position is different than the last time
// we were asked. The parser commonly asks for the node at the same position
// Only compute the current node if the position is different than the last time
// we were asked. The parser commonly asks for the node at the same position
// twice. Once to know if can read an appropriate list element at a certain point,
// and then to actually read and consume the node.
if (position !== lastQueriedPosition) {
// Much of the time the parser will need the very next node in the array that
// we just returned a node from.So just simply check for that case and move
// Much of the time the parser will need the very next node in the array that
// we just returned a node from.So just simply check for that case and move
// forward in the array instead of searching for the node again.
if (current && current.end === position && currentArrayIndex < (currentArray.length - 1)) {
currentArrayIndex++;
@ -905,7 +905,7 @@ module ts {
}
}
// Cache this query so that we don't do any extra work if the parser calls back
// Cache this query so that we don't do any extra work if the parser calls back
// into us. Note: this is very common as the parser will make pairs of calls like
// 'isListElement -> parseListElement'. If we were unable to find a node when
// called with 'isListElement', we don't want to redo the work when parseListElement
@ -917,7 +917,7 @@ module ts {
return <IncrementalNode>current;
}
};
// Finds the highest element in the tree we can find that starts at the provided position.
// The element must be a direct child of some node list in the tree. This way after we
// return it, we can easily return its next sibling in the list.
@ -960,7 +960,7 @@ module ts {
}
else {
if (child.pos < position && position < child.end) {
// Position in somewhere within this child. Search in it and
// Position in somewhere within this child. Search in it and
// stop searching in this array.
forEachChild(child, visitNode, visitArray);
return true;
@ -1007,10 +1007,10 @@ module ts {
// Whether or not we are in strict parsing mode. All that changes in strict parsing mode is
// that some tokens that would be considered identifiers may be considered keywords.
//
// When adding more parser context flags, consider which is the more common case that the
// When adding more parser context flags, consider which is the more common case that the
// flag will be in. This should be hte 'false' state for that flag. The reason for this is
// that we don't store data in our nodes unless the value is in the *non-default* state. So,
// for example, more often than code 'allows-in' (or doesn't 'disallow-in'). We opt for
// for example, more often than code 'allows-in' (or doesn't 'disallow-in'). We opt for
// 'disallow-in' set to 'false'. Otherwise, if we had 'allowsIn' set to 'true', then almost
// all nodes would need extra state on them to store this info.
//
@ -1030,20 +1030,20 @@ module ts {
// EqualityExpression[?In, ?Yield] === RelationalExpression[?In, ?Yield]
// EqualityExpression[?In, ?Yield] !== RelationalExpression[?In, ?Yield]
//
// Where you have to be careful is then understanding what the points are in the grammar
// Where you have to be careful is then understanding what the points are in the grammar
// where the values are *not* passed along. For example:
//
// SingleNameBinding[Yield,GeneratorParameter]
// [+GeneratorParameter]BindingIdentifier[Yield] Initializer[In]opt
// [~GeneratorParameter]BindingIdentifier[?Yield]Initializer[In, ?Yield]opt
//
// Here this is saying that if the GeneratorParameter context flag is set, that we should
// Here this is saying that if the GeneratorParameter context flag is set, that we should
// explicitly set the 'yield' context flag to false before calling into the BindingIdentifier
// and we should explicitly unset the 'yield' context flag before calling into the Initializer.
// production. Conversely, if the GeneratorParameter context flag is not set, then we
// production. Conversely, if the GeneratorParameter context flag is not set, then we
// should leave the 'yield' context flag alone.
//
// Getting this all correct is tricky and requires careful reading of the grammar to
// Getting this all correct is tricky and requires careful reading of the grammar to
// understand when these values should be changed versus when they should be inherited.
//
// Note: it should not be necessary to save/restore these flags during speculative/lookahead
@ -1051,7 +1051,7 @@ module ts {
// descent parsing and unwinding.
let contextFlags: ParserContextFlags = 0;
// Whether or not we've had a parse error since creating the last AST node. If we have
// Whether or not we've had a parse error since creating the last AST node. If we have
// encountered an error, it will be stored on the next AST node we create. Parse errors
// can be broken down into three categories:
//
@ -1062,7 +1062,7 @@ module ts {
// by the 'parseExpected' function.
//
// 3) A token was present that no parsing function was able to consume. This type of error
// only occurs in the 'abortParsingListOrMoveToNextToken' function when the parser
// only occurs in the 'abortParsingListOrMoveToNextToken' function when the parser
// decides to skip the token.
//
// In all of these cases, we want to mark the next node as having had an error before it.
@ -1071,8 +1071,8 @@ module ts {
// node. in that event we would then not produce the same errors as we did before, causing
// significant confusion problems.
//
// Note: it is necessary that this value be saved/restored during speculative/lookahead
// parsing. During lookahead parsing, we will often create a node. That node will have
// Note: it is necessary that this value be saved/restored during speculative/lookahead
// parsing. During lookahead parsing, we will often create a node. That node will have
// this value attached, and then this value will be set back to 'false'. If we decide to
// rewind, we must get back to the same value we had prior to the lookahead.
//
@ -1135,7 +1135,7 @@ module ts {
setDisallowInContext(true);
return result;
}
// no need to do anything special if 'in' is already allowed.
return func();
}
@ -1206,7 +1206,7 @@ module ts {
sourceFile.parseDiagnostics.push(createFileDiagnostic(sourceFile, start, length, message, arg0));
}
// Mark that we've encountered an error. We'll set an appropriate bit on the next
// Mark that we've encountered an error. We'll set an appropriate bit on the next
// node we finish so that it can't be reused incrementally.
parseErrorBeforeNextFinishedNode = true;
}
@ -1245,7 +1245,7 @@ module ts {
}
function speculationHelper<T>(callback: () => T, isLookAhead: boolean): T {
// Keep track of the state we'll need to rollback to if lookahead fails (or if the
// Keep track of the state we'll need to rollback to if lookahead fails (or if the
// caller asked us to always reset our state).
let saveToken = token;
let saveParseDiagnosticsLength = sourceFile.parseDiagnostics.length;
@ -1253,13 +1253,13 @@ module ts {
// Note: it is not actually necessary to save/restore the context flags here. That's
// because the saving/restorating of these flags happens naturally through the recursive
// descent nature of our parser. However, we still store this here just so we can
// descent nature of our parser. However, we still store this here just so we can
// assert that that invariant holds.
let saveContextFlags = contextFlags;
// If we're only looking ahead, then tell the scanner to only lookahead as well.
// Otherwise, if we're actually speculatively parsing, then tell the scanner to do the
// same.
// Otherwise, if we're actually speculatively parsing, then tell the scanner to do the
// same.
let result = isLookAhead
? scanner.lookAhead(callback)
: scanner.tryScan(callback);
@ -1277,15 +1277,15 @@ module ts {
return result;
}
// Invokes the provided callback then unconditionally restores the parser to the state it
// Invokes the provided callback then unconditionally restores the parser to the state it
// was in immediately prior to invoking the callback. The result of invoking the callback
// is returned from this function.
function lookAhead<T>(callback: () => T): T {
return speculationHelper(callback, /*isLookAhead:*/ true);
}
// Invokes the provided callback. If the callback returns something falsy, then it restores
// the parser to the state it was in immediately prior to invoking the callback. If the
// the parser to the state it was in immediately prior to invoking the callback. If the
// callback returns something truthy, then the parser state is not rolled back. The result
// of invoking the callback is returned from this function.
function tryParse<T>(callback: () => T): T {
@ -1296,8 +1296,8 @@ module ts {
if (token === SyntaxKind.Identifier) {
return true;
}
// If we have a 'yield' keyword, and we're in the [yield] context, then 'yield' is
// If we have a 'yield' keyword, and we're in the [yield] context, then 'yield' is
// considered a keyword and is not an identifier.
if (token === SyntaxKind.YieldKeyword && inYieldContext()) {
return false;
@ -1464,7 +1464,7 @@ module ts {
// LiteralPropertyName
// [+GeneratorParameter] ComputedPropertyName
// [~GeneratorParameter] ComputedPropertyName[?Yield]
//
//
// ComputedPropertyName[Yield] :
// [ AssignmentExpression[In, ?Yield] ]
//
@ -1648,13 +1648,13 @@ module ts {
}
function isVariableDeclaratorListTerminator(): boolean {
// If we can consume a semicolon (either explicitly, or with ASI), then consider us done
// If we can consume a semicolon (either explicitly, or with ASI), then consider us done
// with parsing the list of variable declarators.
if (canParseSemicolon()) {
return true;
}
// in the case where we're parsing the variable declarator of a 'for-in' statement, we
// in the case where we're parsing the variable declarator of a 'for-in' statement, we
// are done if we see an 'in' keyword in front of us. Same with for-of
if (isInOrOfKeyword(token)) {
return true;
@ -1730,7 +1730,7 @@ module ts {
if (node) {
return <T>consumeNode(node);
}
return parseElement();
}
@ -1738,9 +1738,9 @@ module ts {
// If there is an outstanding parse error that we've encountered, but not attached to
// some node, then we cannot get a node from the old source tree. This is because we
// want to mark the next node we encounter as being unusable.
//
//
// Note: This may be too conservative. Perhaps we could reuse hte node and set the bit
// on it (or its leftmost child) as having the error. For now though, being conservative
// on it (or its leftmost child) as having the error. For now though, being conservative
// is nice and likely won't ever affect perf.
if (parseErrorBeforeNextFinishedNode) {
return undefined;
@ -1763,18 +1763,18 @@ module ts {
return undefined;
}
// Can't reuse a node that contains a parse error. This is necessary so that we
// Can't reuse a node that contains a parse error. This is necessary so that we
// produce the same set of errors again.
if (containsParseError(node)) {
return undefined;
}
// We can only reuse a node if it was parsed under the same strict mode that we're
// We can only reuse a node if it was parsed under the same strict mode that we're
// currently in. i.e. if we originally parsed a node in non-strict mode, but then
// the user added 'using strict' at the top of the file, then we can't use that node
// again as the presense of strict mode may cause us to parse the tokens in the file
// differetly.
//
//
// Note: we *can* reuse tokens when the strict mode changes. That's because tokens
// are unaffected by strict mode. It's just the parser will decide what to do with it
// differently depending on what mode it is in.
@ -1828,32 +1828,32 @@ module ts {
case ParsingContext.Parameters:
return isReusableParameter(node);
// Any other lists we do not care about reusing nodes in. But feel free to add if
// Any other lists we do not care about reusing nodes in. But feel free to add if
// you can do so safely. Danger areas involve nodes that may involve speculative
// parsing. If speculative parsing is involved with the node, then the range the
// parser reached while looking ahead might be in the edited range (see the example
// in canReuseVariableDeclaratorNode for a good case of this).
case ParsingContext.HeritageClauses:
// This would probably be safe to reuse. There is no speculative parsing with
// This would probably be safe to reuse. There is no speculative parsing with
// heritage clauses.
case ParsingContext.TypeReferences:
// This would probably be safe to reuse. There is no speculative parsing with
// This would probably be safe to reuse. There is no speculative parsing with
// type names in a heritage clause. There can be generic names in the type
// name list. But because it is a type context, we never use speculative
// name list. But because it is a type context, we never use speculative
// parsing on the type argument list.
case ParsingContext.TypeParameters:
// This would probably be safe to reuse. There is no speculative parsing with
// This would probably be safe to reuse. There is no speculative parsing with
// type parameters. Note that that's because type *parameters* only occur in
// unambiguous *type* contexts. While type *arguments* occur in very ambiguous
// *expression* contexts.
case ParsingContext.TupleElementTypes:
// This would probably be safe to reuse. There is no speculative parsing with
// This would probably be safe to reuse. There is no speculative parsing with
// tuple types.
// Technically, type argument list types are probably safe to reuse. While
// Technically, type argument list types are probably safe to reuse. While
// speculative parsing is involved with them (since type argument lists are only
// produced from speculative parsing a < as a type argument list), we only have
// the types because speculative parsing succeeded. Thus, the lookahead never
@ -1861,12 +1861,12 @@ module ts {
case ParsingContext.TypeArguments:
// Note: these are almost certainly not safe to ever reuse. Expressions commonly
// need a large amount of lookahead, and we should not reuse them as they may
// need a large amount of lookahead, and we should not reuse them as they may
// have actually intersected the edit.
case ParsingContext.ArgumentExpressions:
// This is not safe to reuse for the same reason as the 'AssignmentExpression'
// cases. i.e. a property assignment may end with an expression, and thus might
// cases. i.e. a property assignment may end with an expression, and thus might
// have lookahead far beyond it's old node.
case ParsingContext.ObjectLiteralMembers:
}
@ -1980,12 +1980,12 @@ module ts {
//
// let v = new List < A, B
//
// This is actually legal code. It's a list of variable declarators "v = new List<A"
// This is actually legal code. It's a list of variable declarators "v = new List<A"
// on one side and "B" on the other. If you then change that to:
//
// let v = new List < A, B >()
//
// then we have a problem. "v = new List<A" doesn't intersect the change range, so we
//
// then we have a problem. "v = new List<A" doesn't intersect the change range, so we
// start reparsing at "B" and we completely fail to handle this properly.
//
// In order to prevent this, we do not allow a variable declarator to be reused if it
@ -2039,10 +2039,10 @@ module ts {
// We didn't get a comma, and the list wasn't terminated, explicitly parse
// out a comma so we give a good error message.
parseExpected(SyntaxKind.CommaToken);
// If the token was a semicolon, and the caller allows that, then skip it and
// continue. This ensures we get back on track and don't result in tons of
// parse errors. For example, this can happen when people do things like use
// parse errors. For example, this can happen when people do things like use
// a semicolon to delimit object literal members. Note: we'll have already
// reported an error when we called parseExpected above.
if (considerSemicolonAsDelimeter && token === SyntaxKind.SemicolonToken && !scanner.hasPrecedingLineBreak()) {
@ -2115,22 +2115,22 @@ module ts {
// name.
// keyword identifierNameOrKeyword
//
// Note: the newlines are important here. For example, if that above code
// Note: the newlines are important here. For example, if that above code
// were rewritten into:
//
// name.keyword
// identifierNameOrKeyword
//
// Then we would consider it valid. That's because ASI would take effect and
// the code would be implicitly: "name.keyword; identifierNameOrKeyword".
// the code would be implicitly: "name.keyword; identifierNameOrKeyword".
// In the first case though, ASI will not take effect because there is not a
// line terminator after the keyword.
if (scanner.hasPrecedingLineBreak() && scanner.isReservedWord()) {
let matchesPattern = lookAhead(nextTokenIsIdentifierOrKeywordOnSameLine);
if (matchesPattern) {
// Report that we need an identifier. However, report it right after the dot,
// and not on the next token. This is because the next token might actually
// Report that we need an identifier. However, report it right after the dot,
// and not on the next token. This is because the next token might actually
// be an identifier and the error woudl be quite confusing.
return <Identifier>createMissingNode(SyntaxKind.Identifier, /*reportAtCurrentToken:*/ true, Diagnostics.Identifier_expected);
}
@ -2185,7 +2185,7 @@ module ts {
if (scanner.hasExtendedUnicodeEscape()) {
node.hasExtendedUnicodeEscape = true;
}
if (scanner.isUnterminated()) {
node.isUnterminated = true;
}
@ -2193,7 +2193,7 @@ module ts {
let tokenPos = scanner.getTokenPos();
nextToken();
finishNode(node);
// Octal literals are not allowed in strict mode or ES5
// Note that theoretically the following condition would hold true literals like 009,
// which is not octal.But because of how the scanner separates the tokens, we would
@ -2232,7 +2232,7 @@ module ts {
let node = <TypeParameterDeclaration>createNode(SyntaxKind.TypeParameter);
node.name = parseIdentifier();
if (parseOptional(SyntaxKind.ExtendsKeyword)) {
// It's not uncommon for people to write improper constraints to a generic. If the
// It's not uncommon for people to write improper constraints to a generic. If the
// user writes a constraint that is an expression and not an actual type, then parse
// it out as an expression (so we can recover well), but report that a type is needed
// instead.
@ -2294,7 +2294,7 @@ module ts {
if (getFullWidth(node.name) === 0 && node.flags === 0 && isModifier(token)) {
// in cases like
// 'use strict'
// 'use strict'
// function foo(static)
// isParameter('static') === true, because of isModifier('static')
// however 'static' is not a legal identifier in a strict mode.
@ -2341,7 +2341,7 @@ module ts {
}
}
// Note: after careful analysis of the grammar, it does not appear to be possible to
// Note: after careful analysis of the grammar, it does not appear to be possible to
// have 'Yield' And 'GeneratorParameter' not in sync. i.e. any production calling
// this FormalParameters production either always sets both to true, or always sets
// both to false. As such we only have a single parameter to represent both.
@ -2388,7 +2388,7 @@ module ts {
}
function parseTypeMemberSemicolon() {
// We allow type members to be separated by commas or (possibly ASI) semicolons.
// We allow type members to be separated by commas or (possibly ASI) semicolons.
// First check if it was a comma. If so, we're done with the member.
if (parseOptional(SyntaxKind.CommaToken)) {
return;
@ -2561,9 +2561,9 @@ module ts {
case SyntaxKind.NumericLiteral:
return parsePropertyOrMethodSignature();
default:
// Index declaration as allowed as a type member. But as per the grammar,
// Index declaration as allowed as a type member. But as per the grammar,
// they also allow modifiers. So we have to check for an index declaration
// that might be following modifiers. This ensures that things work properly
// that might be following modifiers. This ensures that things work properly
// when incrementally parsing as the parser will produce the Index declaration
// if it has the same text regardless of whether it is inside a class or an
// object type.
@ -2844,7 +2844,7 @@ module ts {
function parseExpression(): Expression {
// Expression[in]:
// AssignmentExpression[in]
// AssignmentExpression[in]
// Expression[in] , AssignmentExpression[in]
let expr = parseAssignmentExpressionOrHigher();
@ -2860,13 +2860,13 @@ module ts {
// It's not uncommon during typing for the user to miss writing the '=' token. Check if
// there is no newline after the last token and if we're on an expression. If so, parse
// this as an equals-value clause with a missing equals.
// NOTE: There are two places where we allow equals-value clauses. The first is in a
// NOTE: There are two places where we allow equals-value clauses. The first is in a
// variable declarator. The second is with a parameter. For variable declarators
// it's more likely that a { would be a allowed (as an object literal). While this
// is also allowed for parameters, the risk is that we consume the { as an object
// literal when it really will be for the block following the parameter.
if (scanner.hasPrecedingLineBreak() || (inParameter && token === SyntaxKind.OpenBraceToken) || !isStartOfExpression()) {
// preceding line break, open brace in a parameter (likely a function body) or current token is not an expression -
// preceding line break, open brace in a parameter (likely a function body) or current token is not an expression -
// do not try to parse initializer
return undefined;
}
@ -2887,17 +2887,17 @@ module ts {
// 4) ArrowFunctionExpression[?in,?yield]
// 5) [+Yield] YieldExpression[?In]
//
// Note: for ease of implementation we treat productions '2' and '3' as the same thing.
// Note: for ease of implementation we treat productions '2' and '3' as the same thing.
// (i.e. they're both BinaryExpressions with an assignment operator in it).
// First, do the simple check if we have a YieldExpression (production '5').
if (isYieldExpression()) {
return parseYieldExpression();
}
}
// Then, check if we have an arrow function (production '4') that starts with a parenthesized
// parameter list. If we do, we must *not* recurse for productions 1, 2 or 3. An ArrowFunction is
// not a LeftHandSideExpression, nor does it start a ConditionalExpression. So we are done
// not a LeftHandSideExpression, nor does it start a ConditionalExpression. So we are done
// with AssignmentExpression if we see one.
let arrowExpression = tryParseParenthesizedArrowFunctionExpression();
if (arrowExpression) {
@ -2908,9 +2908,9 @@ module ts {
// start with a LogicalOrExpression, while the assignment productions can only start with
// LeftHandSideExpressions.
//
// So, first, we try to just parse out a BinaryExpression. If we get something that is a
// LeftHandSide or higher, then we can try to parse out the assignment expression part.
// Otherwise, we try to parse out the conditional expression bit. We want to allow any
// So, first, we try to just parse out a BinaryExpression. If we get something that is a
// LeftHandSide or higher, then we can try to parse out the assignment expression part.
// Otherwise, we try to parse out the conditional expression bit. We want to allow any
// binary expression here, so we pass in the 'lowest' precedence here so that it matches
// and consumes anything.
let expr = parseBinaryExpressionOrHigher(/*precedence:*/ 0);
@ -2918,12 +2918,12 @@ module ts {
// To avoid a look-ahead, we did not handle the case of an arrow function with a single un-parenthesized
// parameter ('x => ...') above. We handle it here by checking if the parsed expression was a single
// identifier and the current token is an arrow.
if (expr.kind === SyntaxKind.Identifier && token === SyntaxKind.EqualsGreaterThanToken) {
if (expr.kind === SyntaxKind.Identifier && token === SyntaxKind.EqualsGreaterThanToken && !scanner.hasPrecedingLineBreak()) {
return parseSimpleArrowFunctionExpression(<Identifier>expr);
}
// Now see if we might be in cases '2' or '3'.
// If the expression was a LHS expression, and we have an assignment operator, then
// If the expression was a LHS expression, and we have an assignment operator, then
// we're in '2' or '3'. Consume the assignment and return.
//
// Note: we call reScanGreaterToken so that we get an appropriately merged token
@ -2938,7 +2938,7 @@ module ts {
function isYieldExpression(): boolean {
if (token === SyntaxKind.YieldKeyword) {
// If we have a 'yield' keyword, and htis is a context where yield expressions are
// If we have a 'yield' keyword, and htis is a context where yield expressions are
// allowed, then definitely parse out a yield expression.
if (inYieldContext()) {
return true;
@ -2953,12 +2953,12 @@ module ts {
// We're in a context where 'yield expr' is not allowed. However, if we can
// definitely tell that the user was trying to parse a 'yield expr' and not
// just a normal expr that start with a 'yield' identifier, then parse out
// a 'yield expr'. We can then report an error later that they are only
// a 'yield expr'. We can then report an error later that they are only
// allowed in generator expressions.
//
//
// for example, if we see 'yield(foo)', then we'll have to treat that as an
// invocation expression of something called 'yield'. However, if we have
// 'yield foo' then that is not legal as a normal expression, so we can
// 'yield foo' then that is not legal as a normal expression, so we can
// definitely recognize this as a yield expression.
//
// for now we just check if the next token is an identifier. More heuristics
@ -2997,7 +2997,7 @@ module ts {
return finishNode(node);
}
else {
// if the next token is not on the same line as yield. or we don't have an '*' or
// if the next token is not on the same line as yield. or we don't have an '*' or
// the start of an expressin, then this is just a simple "yield" expression.
return finishNode(node);
}
@ -3043,7 +3043,7 @@ module ts {
return undefined;
}
// If we have an arrow, then try to parse the body. Even if not, try to parse if we
// If we have an arrow, then try to parse the body. Even if not, try to parse if we
// have an opening brace, just in case we're in an error state.
if (parseExpected(SyntaxKind.EqualsGreaterThanToken) || token === SyntaxKind.OpenBraceToken) {
arrowFunction.body = parseArrowFunctionExpressionBody();
@ -3146,7 +3146,7 @@ module ts {
// If we're speculatively parsing a signature for a parenthesized arrow function, then
// we have to have a complete parameter list. Otherwise we might see something like
// a => (b => c)
// And think that "(b =>" was actually a parenthesized arrow function with a missing
// And think that "(b =>" was actually a parenthesized arrow function with a missing
// close paren.
fillSignature(SyntaxKind.ColonToken, /*yieldAndGeneratorParameterContext:*/ false, /*requireCompleteParameterList:*/ !allowAmbiguity, node);
@ -3168,6 +3168,11 @@ module ts {
return undefined;
}
// Must be no line terminator before token `=>`.
if (scanner.hasPrecedingLineBreak()) {
return undefined;
}
return node;
}
@ -3179,7 +3184,7 @@ module ts {
if (isStartOfStatement(/*inErrorRecovery:*/ true) && !isStartOfExpressionStatement() && token !== SyntaxKind.FunctionKeyword) {
// Check if we got a plain statement (i.e. no expression-statements, no functions expressions/declarations)
//
// Here we try to recover from a potential error situation in the case where the
// Here we try to recover from a potential error situation in the case where the
// user meant to supply a block. For example, if the user wrote:
//
// a =>
@ -3204,10 +3209,10 @@ module ts {
return leftOperand;
}
// Note: we explicitly 'allowIn' in the whenTrue part of the condition expression, and
// we do not that for the 'whenFalse' part.
// Note: we explicitly 'allowIn' in the whenTrue part of the condition expression, and
// we do not that for the 'whenFalse' part.
let node = <ConditionalExpression>createNode(SyntaxKind.ConditionalExpression, leftOperand.pos);
node.condition = leftOperand;
node.condition = leftOperand;
node.questionToken = questionToken;
node.whenTrue = allowInAnd(parseAssignmentExpressionOrHigher);
node.colonToken = parseExpectedToken(SyntaxKind.ColonToken, /*reportAtCurrentPosition:*/ false,
@ -3227,7 +3232,7 @@ module ts {
function parseBinaryExpressionRest(precedence: number, leftOperand: Expression): Expression {
while (true) {
// We either have a binary operator here, or we're finished. We call
// We either have a binary operator here, or we're finished. We call
// reScanGreaterToken so that we merge token sequences like > and = into >=
reScanGreaterToken();
@ -3374,15 +3379,15 @@ module ts {
function parseLeftHandSideExpressionOrHigher(): LeftHandSideExpression {
// Original Ecma:
// LeftHandSideExpression: See 11.2
// LeftHandSideExpression: See 11.2
// NewExpression
// CallExpression
// CallExpression
//
// Our simplification:
//
// LeftHandSideExpression: See 11.2
// MemberExpression
// CallExpression
// LeftHandSideExpression: See 11.2
// MemberExpression
// CallExpression
//
// See comment in parseMemberExpressionOrHigher on how we replaced NewExpression with
// MemberExpression to make our lives easier.
@ -3391,14 +3396,14 @@ module ts {
// out into its own productions:
//
// CallExpression:
// MemberExpression Arguments
// MemberExpression Arguments
// CallExpression Arguments
// CallExpression[Expression]
// CallExpression.IdentifierName
// super ( ArgumentListopt )
// super.IdentifierName
//
// Because of the recursion in these calls, we need to bottom out first. There are two
// Because of the recursion in these calls, we need to bottom out first. There are two
// bottom out states we can run into. Either we see 'super' which must start either of
// the last two CallExpression productions. Or we have a MemberExpression which either
// completes the LeftHandSideExpression, or starts the beginning of the first four
@ -3407,7 +3412,7 @@ module ts {
? parseSuperExpression()
: parseMemberExpressionOrHigher();
// Now, we *may* be complete. However, we might have consumed the start of a
// Now, we *may* be complete. However, we might have consumed the start of a
// CallExpression. As such, we need to consume the rest of it here to be complete.
return parseCallExpressionRest(expression);
}
@ -3417,39 +3422,39 @@ module ts {
// place ObjectCreationExpression and FunctionExpression into PrimaryExpression.
// like so:
//
// PrimaryExpression : See 11.1
// PrimaryExpression : See 11.1
// this
// Identifier
// Literal
// ArrayLiteral
// ObjectLiteral
// (Expression)
// (Expression)
// FunctionExpression
// new MemberExpression Arguments?
//
// MemberExpression : See 11.2
// PrimaryExpression
// MemberExpression : See 11.2
// PrimaryExpression
// MemberExpression[Expression]
// MemberExpression.IdentifierName
//
// CallExpression : See 11.2
// MemberExpression
// CallExpression : See 11.2
// MemberExpression
// CallExpression Arguments
// CallExpression[Expression]
// CallExpression.IdentifierName
// CallExpression.IdentifierName
//
// Technically this is ambiguous. i.e. CallExpression defines:
//
// CallExpression:
// CallExpression Arguments
//
//
// If you see: "new Foo()"
//
// Then that could be treated as a single ObjectCreationExpression, or it could be
// Then that could be treated as a single ObjectCreationExpression, or it could be
// treated as the invocation of "new Foo". We disambiguate that in code (to match
// the original grammar) by making sure that if we see an ObjectCreationExpression
// we always consume arguments if they are there. So we treat "new Foo()" as an
// object creation only, and not at all as an invocation) Another way to think
// object creation only, and not at all as an invocation) Another way to think
// about this is that for every "new" that we see, we will consume an argument list if
// it is there as part of the *associated* object creation node. Any additional
// argument lists we see, will become invocation expressions.
@ -3539,7 +3544,7 @@ module ts {
if (token === SyntaxKind.LessThanToken) {
// See if this is the start of a generic invocation. If so, consume it and
// keep checking for postfix expressions. Otherwise, it's just a '<' that's
// keep checking for postfix expressions. Otherwise, it's just a '<' that's
// part of an arithmetic expression. Break out so we consume it higher in the
// stack.
let typeArguments = tryParse(parseTypeArgumentsInExpression);
@ -3593,8 +3598,8 @@ module ts {
function canFollowTypeArgumentsInExpression(): boolean {
switch (token) {
case SyntaxKind.OpenParenToken: // foo<x>(
// this case are the only case where this token can legally follow a type argument
case SyntaxKind.OpenParenToken: // foo<x>(
// this case are the only case where this token can legally follow a type argument
// list. So we definitely want to treat this as a type arg list.
case SyntaxKind.DotToken: // foo<x>.
@ -3615,7 +3620,7 @@ module ts {
case SyntaxKind.BarToken: // foo<x> |
case SyntaxKind.CloseBraceToken: // foo<x> }
case SyntaxKind.EndOfFileToken: // foo<x>
// these cases can't legally follow a type arg list. However, they're not legal
// these cases can't legally follow a type arg list. However, they're not legal
// expressions either. The user is probably in the middle of a generic type. So
// treat it as such.
return true;
@ -3836,7 +3841,7 @@ module ts {
parseExpected(SyntaxKind.CloseParenToken);
// From: https://mail.mozilla.org/pipermail/es-discuss/2011-August/016188.html
// 157 min --- All allen at wirfs-brock.com CONF --- "do{;}while(false)false" prohibited in
// 157 min --- All allen at wirfs-brock.com CONF --- "do{;}while(false)false" prohibited in
// spec but allowed in consensus reality. Approved -- this is the de-facto standard whereby
// do;while(0)x will have a semicolon inserted before x.
parseOptional(SyntaxKind.SemicolonToken);
@ -3974,9 +3979,9 @@ module ts {
// ThrowStatement[Yield] :
// throw [no LineTerminator here]Expression[In, ?Yield];
// Because of automatic semicolon insertion, we need to report error if this
// Because of automatic semicolon insertion, we need to report error if this
// throw could be terminated with a semicolon. Note: we can't call 'parseExpression'
// directly as that might consume an expression on the following line.
// directly as that might consume an expression on the following line.
// We just return 'undefined' in that case. The actual error will be reported in the
// grammar walker.
let node = <ThrowStatement>createNode(SyntaxKind.ThrowStatement);
@ -4046,9 +4051,9 @@ module ts {
function isStartOfStatement(inErrorRecovery: boolean): boolean {
// Functions and variable statements are allowed as a statement. But as per the grammar,
// they also allow modifiers. So we have to check for those statements that might be
// following modifiers.This ensures that things work properly when incrementally parsing
// as the parser will produce the same FunctionDeclaraiton or VariableStatement if it has
// they also allow modifiers. So we have to check for those statements that might be
// following modifiers.This ensures that things work properly when incrementally parsing
// as the parser will produce the same FunctionDeclaraiton or VariableStatement if it has
// the same text regardless of whether it is inside a block or not.
if (isModifier(token)) {
let result = lookAhead(parseVariableStatementOrFunctionDeclarationWithModifiers);
@ -4134,7 +4139,7 @@ module ts {
return parseBlock(/*ignoreMissingOpenBrace:*/ false, /*checkForStrictMode:*/ false);
case SyntaxKind.VarKeyword:
case SyntaxKind.ConstKeyword:
// const here should always be parsed as const declaration because of check in 'isStatement'
// const here should always be parsed as const declaration because of check in 'isStatement'
return parseVariableStatement(scanner.getStartPos(), /*modifiers:*/ undefined);
case SyntaxKind.FunctionKeyword:
return parseFunctionDeclaration(scanner.getStartPos(), /*modifiers:*/ undefined);
@ -4174,8 +4179,8 @@ module ts {
}
// Else parse it like identifier - fall through
default:
// Functions and variable statements are allowed as a statement. But as per
// the grammar, they also allow modifiers. So we have to check for those
// Functions and variable statements are allowed as a statement. But as per
// the grammar, they also allow modifiers. So we have to check for those
// statements that might be following modifiers. This ensures that things
// work properly when incrementally parsing as the parser will produce the
// same FunctionDeclaraiton or VariableStatement if it has the same text
@ -4336,7 +4341,7 @@ module ts {
return finishNode(node);
}
function canFollowContextualOfKeyword(): boolean {
return nextTokenIsIdentifier() && nextToken() === SyntaxKind.CloseParenToken;
}
@ -4439,7 +4444,7 @@ module ts {
if (token === SyntaxKind.OpenBracketToken) {
return true;
}
// If we were able to get any potential identifier...
if (idToken !== undefined) {
// If we have a non-keyword identifier, or if we have an accessor, then it's safe to parse.
@ -4718,7 +4723,7 @@ module ts {
// import ImportClause from ModuleSpecifier ;
// import ModuleSpecifier;
if (identifier || // import id
token === SyntaxKind.AsteriskToken || // import *
token === SyntaxKind.AsteriskToken || // import *
token === SyntaxKind.OpenBraceToken) { // import {
importDeclaration.importClause = parseImportClause(identifier, afterImportPos);
parseExpected(SyntaxKind.FromKeyword);
@ -4744,7 +4749,7 @@ module ts {
importClause.name = identifier;
}
// If there was no default import or if there is comma token after default import
// If there was no default import or if there is comma token after default import
// parse namespace or named imports
if (!importClause.name ||
parseOptional(SyntaxKind.CommaToken)) {
@ -4770,12 +4775,12 @@ module ts {
}
function parseModuleSpecifier(): Expression {
// We allow arbitrary expressions here, even though the grammar only allows string
// We allow arbitrary expressions here, even though the grammar only allows string
// literals. We check to ensure that it is only a string literal later in the grammar
// walker.
let result = parseExpression();
// Ensure the string being required is in our 'identifier' table. This will ensure
// that features like 'find refs' will look inside this file when search for its name.
// Ensure the string being required is in our 'identifier' table. This will ensure
// that features like 'find refs' will look inside this file when search for its name.
if (result.kind === SyntaxKind.StringLiteral) {
internIdentifier((<LiteralExpression>result).text);
}
@ -5013,8 +5018,8 @@ module ts {
let amdDependencies: {path: string; name: string}[] = [];
let amdModuleName: string;
// Keep scanning all the leading trivia in the file until we get to something that
// isn't trivia. Any single line comment will be analyzed to see if it is a
// Keep scanning all the leading trivia in the file until we get to something that
// isn't trivia. Any single line comment will be analyzed to see if it is a
// reference comment.
while (true) {
let kind = triviaScanner.scan();

View File

@ -0,0 +1,70 @@
tests/cases/conformance/es6/arrowFunction/disallowLineTerminatorBeforeArrow.ts(2,5): error TS1109: Expression expected.
tests/cases/conformance/es6/arrowFunction/disallowLineTerminatorBeforeArrow.ts(4,7): error TS1109: Expression expected.
tests/cases/conformance/es6/arrowFunction/disallowLineTerminatorBeforeArrow.ts(6,5): error TS1109: Expression expected.
tests/cases/conformance/es6/arrowFunction/disallowLineTerminatorBeforeArrow.ts(8,7): error TS1109: Expression expected.
tests/cases/conformance/es6/arrowFunction/disallowLineTerminatorBeforeArrow.ts(10,5): error TS1109: Expression expected.
tests/cases/conformance/es6/arrowFunction/disallowLineTerminatorBeforeArrow.ts(12,7): error TS1109: Expression expected.
tests/cases/conformance/es6/arrowFunction/disallowLineTerminatorBeforeArrow.ts(14,5): error TS1109: Expression expected.
tests/cases/conformance/es6/arrowFunction/disallowLineTerminatorBeforeArrow.ts(16,7): error TS1109: Expression expected.
tests/cases/conformance/es6/arrowFunction/disallowLineTerminatorBeforeArrow.ts(19,1): error TS2346: Supplied parameters do not match any signature of call target.
tests/cases/conformance/es6/arrowFunction/disallowLineTerminatorBeforeArrow.ts(20,5): error TS1109: Expression expected.
tests/cases/conformance/es6/arrowFunction/disallowLineTerminatorBeforeArrow.ts(21,1): error TS2346: Supplied parameters do not match any signature of call target.
tests/cases/conformance/es6/arrowFunction/disallowLineTerminatorBeforeArrow.ts(22,5): error TS1109: Expression expected.
tests/cases/conformance/es6/arrowFunction/disallowLineTerminatorBeforeArrow.ts(22,17): error TS1005: ':' expected.
tests/cases/conformance/es6/arrowFunction/disallowLineTerminatorBeforeArrow.ts(22,22): error TS1005: ',' expected.
==== tests/cases/conformance/es6/arrowFunction/disallowLineTerminatorBeforeArrow.ts (14 errors) ====
var f1 = ()
=> { }
~~
!!! error TS1109: Expression expected.
var f2 = (x: string, y: string) /*
*/ => { }
~~
!!! error TS1109: Expression expected.
var f3 = (x: string, y: number, ...rest)
=> { }
~~
!!! error TS1109: Expression expected.
var f4 = (x: string, y: number, ...rest) /*
*/ => { }
~~
!!! error TS1109: Expression expected.
var f5 = (...rest)
=> { }
~~
!!! error TS1109: Expression expected.
var f6 = (...rest) /*
*/ => { }
~~
!!! error TS1109: Expression expected.
var f7 = (x: string, y: number, z = 10)
=> { }
~~
!!! error TS1109: Expression expected.
var f8 = (x: string, y: number, z = 10) /*
*/ => { }
~~
!!! error TS1109: Expression expected.
function foo(func: () => boolean) { }
foo(()
~~~~~~
=> true);
~~~~~~~~~~~~
!!! error TS2346: Supplied parameters do not match any signature of call target.
~~
!!! error TS1109: Expression expected.
foo(()
~~~~~~
=> { return false; });
~~~~~~~~~~~~~~~~~~~~~~~~~
!!! error TS2346: Supplied parameters do not match any signature of call target.
~~
!!! error TS1109: Expression expected.
~~~~~
!!! error TS1005: ':' expected.
~
!!! error TS1005: ',' expected.

View File

@ -0,0 +1,60 @@
//// [disallowLineTerminatorBeforeArrow.ts]
var f1 = ()
=> { }
var f2 = (x: string, y: string) /*
*/ => { }
var f3 = (x: string, y: number, ...rest)
=> { }
var f4 = (x: string, y: number, ...rest) /*
*/ => { }
var f5 = (...rest)
=> { }
var f6 = (...rest) /*
*/ => { }
var f7 = (x: string, y: number, z = 10)
=> { }
var f8 = (x: string, y: number, z = 10) /*
*/ => { }
function foo(func: () => boolean) { }
foo(()
=> true);
foo(()
=> { return false; });
//// [disallowLineTerminatorBeforeArrow.js]
var f1 = ;
{
}
var f2 = ; /*
*/
{
}
var f3 = ;
{
}
var f4 = ; /*
*/
{
}
var f5 = ;
{
}
var f6 = ; /*
*/
{
}
var f7 = ;
{
}
var f8 = ; /*
*/
{
}
function foo(func) {
}
foo(, true);
foo(, {
return: false
});

View File

@ -0,0 +1,22 @@
var f1 = ()
=> { }
var f2 = (x: string, y: string) /*
*/ => { }
var f3 = (x: string, y: number, ...rest)
=> { }
var f4 = (x: string, y: number, ...rest) /*
*/ => { }
var f5 = (...rest)
=> { }
var f6 = (...rest) /*
*/ => { }
var f7 = (x: string, y: number, z = 10)
=> { }
var f8 = (x: string, y: number, z = 10) /*
*/ => { }
function foo(func: () => boolean) { }
foo(()
=> true);
foo(()
=> { return false; });