void (ErrorState state, Frame frame, PsiBuilder builder, @Nullable IElementType elementType, boolean result, boolean pinned, @Nullable Parser eatMore) { int initialPos = builder.rawTokenIndex(); replace_variants_with_name_(state, frame, builder, elementType, result, pinned); int lastErrorPos = frame.lastVariantAt < 0 ? initialPos : frame.lastVariantAt; if (!state.suppressErrors && eatMore != null) { state.suppressErrors = true; final boolean eatMoreFlagOnce = !builder.eof() && eatMore.parse(builder, frame.level + 1); boolean eatMoreFlag = eatMoreFlagOnce || !result && frame.position == initialPos && lastErrorPos > frame.position; PsiBuilderImpl.ProductionMarker latestDoneMarker = (pinned || result) && (state.altMode || elementType != null) && eatMoreFlagOnce ? getLatestExtensibleDoneMarker(builder) : null; // advance to the last error pos // skip tokens until lastErrorPos. parseAsTree might look better here... int parenCount = 0; while ((eatMoreFlag || parenCount > 0) && builder.rawTokenIndex() < lastErrorPos) { IElementType tokenType = builder.getTokenType(); if (state.braces != null) { if (tokenType == state.braces[0].getLeftBraceType()) parenCount ++; else if (tokenType == state.braces[0].getRightBraceType()) parenCount --; } if (!(builder.rawTokenIndex() < lastErrorPos)) break; state.tokenAdvancer.parse(builder, frame.level + 1); eatMoreFlag = eatMore.parse(builder, frame.level + 1); } boolean errorReported = frame.errorReportedAt == initialPos || !result && frame.errorReportedAt >= frame.position; if (errorReported || eatMoreFlag) { if (!errorReported) { errorReported = reportError(builder, state, frame, false, true, true); } else if (eatMoreFlag) { state.tokenAdvancer.parse(builder, frame.level + 1); } if (eatMore.parse(builder, frame.level + 1)) { parseAsTree(state, builder, frame.level + 1, DUMMY_BLOCK, true, state.tokenAdvancer, eatMore); } } else if (eatMoreFlagOnce || !result && frame.position != builder.rawTokenIndex() || frame.errorReportedAt > initialPos) { errorReported = reportError(builder, state, frame, false, true, false); } else if (!result && pinned && frame.errorReportedAt < 0) { errorReported = reportError(builder, state, frame, elementType != null, false, false); } // whitespace prefix makes the very first frame offset bigger than marker start offset which is always 0 if (latestDoneMarker != null && frame.position >= latestDoneMarker.getStartIndex() && frame.position <= latestDoneMarker.getEndIndex()) { extend_marker_impl((PsiBuilder.Marker)latestDoneMarker); } state.suppressErrors = false; if (errorReported || result) { state.clearVariants(true, 0); state.clearVariants(false, 0); frame.lastVariantAt = -1; for (Frame f = frame; f != null && f.variantCount > 0; f = f.parentFrame) f.variantCount = 0; } } else if (!result && pinned && frame.errorReportedAt < 0) { // do not report if there are errors beyond current position if (lastErrorPos == initialPos) { // do not force, inner recoverRoot might have skipped some tokens reportError(builder, state, frame, elementType != null && (frame.modifiers & _UPPER_) == 0, false, false); } else if (lastErrorPos > initialPos) { // set error pos here as if it is reported for future reference frame.errorReportedAt = lastErrorPos; } } // propagate errorReportedAt up the stack to avoid duplicate reporting if (state.currentFrame != null) { if (state.currentFrame.errorReportedAt < frame.errorReportedAt) { state.currentFrame.errorReportedAt = frame.errorReportedAt; } if (state.currentFrame.lastVariantAt < frame.lastVariantAt) { state.currentFrame.lastVariantAt = frame.lastVariantAt; } } }