mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-02-09 12:15:34 -06:00
Disallow line terminator after arrow function parameters, before =>
Closes #2282
This commit is contained in:
parent
1bb4a62988
commit
84634ac25d
@ -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();
|
||||
|
||||
@ -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.
|
||||
|
||||
@ -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
|
||||
});
|
||||
@ -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; });
|
||||
Loading…
x
Reference in New Issue
Block a user