in src/org/intellij/grammar/parser/GeneratedParserUtilBase.java [552:635]
private static void exit_section_impl_(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;
}
}
}