@@ -9,6 +9,33 @@ module ts {
99 return node . end - node . pos ;
1010 }
1111
12+ function hasFlag ( val : number , flag : number ) : boolean {
13+ return ( val & flag ) !== 0 ;
14+ }
15+
16+ // Returns true if this node contains a parse error anywhere underneath it.
17+ export function containsParseError ( node : Node ) : boolean {
18+ if ( ! hasFlag ( node . parserContextFlags , ParserContextFlags . HasPropagatedChildContainsErrorFlag ) ) {
19+ // A node is considered to contain a parse error if:
20+ // a) the parser explicitly marked that it had an error
21+ // b) any of it's children reported that it had an error.
22+ var val = hasFlag ( node . parserContextFlags , ParserContextFlags . ContainsError ) ||
23+ forEachChild ( node , containsParseError ) ;
24+
25+ // If so, mark ourselves accordingly.
26+ if ( val ) {
27+ node . parserContextFlags |= ParserContextFlags . ContainsError ;
28+ }
29+
30+ // Also mark that we've propogated the child information to this node. This way we can
31+ // always consult the bit directly on this node without needing to check its children
32+ // again.
33+ node . parserContextFlags |= ParserContextFlags . HasPropagatedChildContainsErrorFlag ;
34+ }
35+
36+ return hasFlag ( node . parserContextFlags , ParserContextFlags . ContainsError ) ;
37+ }
38+
1239 export function getNodeConstructor ( kind : SyntaxKind ) : new ( ) => Node {
1340 return nodeConstructors [ kind ] || ( nodeConstructors [ kind ] = objectAllocator . getNodeConstructor ( kind ) ) ;
1441 }
@@ -1014,6 +1041,35 @@ module ts {
10141041 // descent parsing and unwinding.
10151042 var contextFlags : ParserContextFlags = 0 ;
10161043
1044+ // Whether or not we've had a parse error since creating the last AST node. If we have
1045+ // encountered an error, it will be stored on the next AST node we create. Parse errors
1046+ // can be broken down into three categories:
1047+ //
1048+ // 1) An error that occurred during scanning. For example, an unterminated literal, or a
1049+ // character that was completely not understood.
1050+ //
1051+ // 2) A token was expected, but was not present. This type of error is commonly produced
1052+ // by the 'parseExpected' function.
1053+ //
1054+ // 3) A token was present that no parsing function was able to consume. This type of error
1055+ // only occurs in the 'abortParsingListOrMoveToNextToken' function when the parser
1056+ // decides to skip the token.
1057+ //
1058+ // In all of these cases, we want to mark the next node as having had an error before it.
1059+ // With this mark, we can know in incremental settings if this node can be reused, or if
1060+ // we have to reparse it. If we don't keep this information around, we may just reuse the
1061+ // node. in that event we would then not produce the same errors as we did before, causing
1062+ // significant confusion problems.
1063+ //
1064+ // Note: it is necessary that this value be saved/restored during speculative/lookahead
1065+ // parsing. During lookahead parsing, we will often create a node. That node will have
1066+ // this value attached, and then this value will be set back to 'false'. If we decide to
1067+ // rewind, we must get back to the same value we had prior to the lookahead.
1068+ //
1069+ // Note: any errors at the end of the file that do not precede a regular node, should get
1070+ // attached to the EOF token.
1071+ var parseErrorBeforeNextFinishedNode = false ;
1072+
10171073 function setContextFlag ( val : Boolean , flag : ParserContextFlags ) {
10181074 if ( val ) {
10191075 contextFlags |= flag ;
@@ -1115,23 +1171,23 @@ module ts {
11151171 return getPositionFromLineAndCharacter ( getLineStarts ( ) , line , character ) ;
11161172 }
11171173
1118- function parseErrorAtCurrentToken ( message : DiagnosticMessage , arg0 ?: any , arg1 ?: any , arg2 ?: any ) : void {
1174+ function parseErrorAtCurrentToken ( message : DiagnosticMessage , arg0 ?: any ) : void {
11191175 var start = scanner . getTokenPos ( ) ;
11201176 var length = scanner . getTextPos ( ) - start ;
11211177
1122- parseErrorAtPosition ( start , length , message , arg0 , arg1 , arg2 ) ;
1178+ parseErrorAtPosition ( start , length , message , arg0 ) ;
11231179 }
11241180
1125- function parseErrorAtPosition ( start : number , length : number , message : DiagnosticMessage , arg0 ?: any , arg1 ?: any , arg2 ?: any ) : void {
1126- var lastErrorPosition = sourceFile . parseDiagnostics . length
1127- ? sourceFile . parseDiagnostics [ sourceFile . parseDiagnostics . length - 1 ] . start
1128- : - 1 ;
1129-
1181+ function parseErrorAtPosition ( start : number , length : number , message : DiagnosticMessage , arg0 ?: any ) : void {
11301182 // Don't report another error if it would just be at the same position as the last error.
1131- if ( start !== lastErrorPosition ) {
1132- var diagnostic = createFileDiagnostic ( sourceFile , start , length , message , arg0 , arg1 , arg2 ) ;
1133- sourceFile . parseDiagnostics . push ( diagnostic ) ;
1183+ var lastError = lastOrUndefined ( sourceFile . parseDiagnostics ) ;
1184+ if ( ! lastError || start !== lastError . start ) {
1185+ sourceFile . parseDiagnostics . push ( createFileDiagnostic ( sourceFile , start , length , message , arg0 ) ) ;
11341186 }
1187+
1188+ // Mark that we've encountered an error. We'll set an appropriate bit on the next
1189+ // node we finish so that it can't be reused incrementally.
1190+ parseErrorBeforeNextFinishedNode = true ;
11351191 }
11361192
11371193 function scanError ( message : DiagnosticMessage ) {
@@ -1172,8 +1228,9 @@ module ts {
11721228 // caller asked us to always reset our state).
11731229 var saveToken = token ;
11741230 var saveParseDiagnosticsLength = sourceFile . parseDiagnostics . length ;
1231+ var saveParseErrorBeforeNextFinishedNode = parseErrorBeforeNextFinishedNode ;
11751232
1176- // Note: it is not actually necessary to save/restore the context falgs here. That's
1233+ // Note: it is not actually necessary to save/restore the context flags here. That's
11771234 // because the saving/restorating of these flags happens naturally through the recursive
11781235 // descent nature of our parser. However, we still store this here just so we can
11791236 // assert that that invariant holds.
@@ -1193,6 +1250,7 @@ module ts {
11931250 if ( ! result || isLookAhead ) {
11941251 token = saveToken ;
11951252 sourceFile . parseDiagnostics . length = saveParseDiagnosticsLength ;
1253+ parseErrorBeforeNextFinishedNode = saveParseErrorBeforeNextFinishedNode ;
11961254 }
11971255
11981256 return result ;
@@ -1301,6 +1359,14 @@ module ts {
13011359 node . parserContextFlags = contextFlags ;
13021360 }
13031361
1362+ // Keep track on the node if we encountered an error while parsing it. If we did, then
1363+ // we cannot reuse the node incrementally. Once we've marked this node, clear out the
1364+ // flag so that we don't mark any subsequent nodes.
1365+ if ( parseErrorBeforeNextFinishedNode ) {
1366+ parseErrorBeforeNextFinishedNode = false ;
1367+ node . parserContextFlags |= ParserContextFlags . ContainsError ;
1368+ }
1369+
13041370 return node ;
13051371 }
13061372
0 commit comments