diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index 3e98b6cbdbc86..f976f77ea985a 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -1717,6 +1717,8 @@ namespace ts { } function currentNode(parsingContext: ParsingContext): Node | undefined { + // If we don't have a cursor or the parsing context isn't reusable, there's nothing to reuse. + // // 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. @@ -1724,30 +1726,17 @@ namespace ts { // Note: This may be too conservative. Perhaps we could reuse the node and set the bit // 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; - } - - if (!syntaxCursor) { - // if we don't have a cursor, we could never return a node from the old tree. + if (!syntaxCursor || !isReusableParsingContext(parsingContext) || parseErrorBeforeNextFinishedNode) { return undefined; } const node = syntaxCursor.currentNode(scanner.getStartPos()); // Can't reuse a missing node. - if (nodeIsMissing(node)) { - return undefined; - } - // Can't reuse a node that intersected the change range. - if (node.intersectsChange) { - return undefined; - } - // 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)) { + if (nodeIsMissing(node) || node.intersectsChange || containsParseError(node)) { return undefined; } @@ -1788,6 +1777,23 @@ namespace ts { return node; } + function isReusableParsingContext(parsingContext: ParsingContext): boolean { + switch (parsingContext) { + case ParsingContext.ClassMembers: + case ParsingContext.SwitchClauses: + case ParsingContext.SourceElements: + case ParsingContext.BlockStatements: + case ParsingContext.SwitchClauseStatements: + case ParsingContext.EnumMembers: + case ParsingContext.TypeMembers: + case ParsingContext.VariableDeclarations: + case ParsingContext.JSDocParameters: + case ParsingContext.Parameters: + return true; + } + return false; + } + function canReuseNode(node: Node, parsingContext: ParsingContext): boolean { switch (parsingContext) { case ParsingContext.ClassMembers: @@ -1814,25 +1820,23 @@ namespace ts { case ParsingContext.Parameters: return isReusableParameter(node); - case ParsingContext.RestProperties: - return false; - // 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: + + // case ParsingContext.HeritageClauses: // This would probably be safe to reuse. There is no speculative parsing with // heritage clauses. - case ParsingContext.TypeParameters: + // case ParsingContext.TypeParameters: // 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: + // case ParsingContext.TupleElementTypes: // This would probably be safe to reuse. There is no speculative parsing with // tuple types. @@ -1841,28 +1845,28 @@ namespace ts { // produced from speculative parsing a < as a type argument list), we only have // the types because speculative parsing succeeded. Thus, the lookahead never // went past the end of the list and rewound. - case ParsingContext.TypeArguments: + // 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 // have actually intersected the edit. - case ParsingContext.ArgumentExpressions: + // 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 // have lookahead far beyond it's old node. - case ParsingContext.ObjectLiteralMembers: + // case ParsingContext.ObjectLiteralMembers: // This is probably not safe to reuse. There can be speculative parsing with // type names in a heritage clause. There can be generic names in the type // name list, and there can be left hand side expressions (which can have type // arguments.) - case ParsingContext.HeritageClauseElement: + // case ParsingContext.HeritageClauseElement: // Perhaps safe to reuse, but it's unlikely we'd see more than a dozen attributes // on any given element. Same for children. - case ParsingContext.JsxAttributes: - case ParsingContext.JsxChildren: + // case ParsingContext.JsxAttributes: + // case ParsingContext.JsxChildren: }