antlr/antlr4 · error · ArgumentException
replace op boundaries of {rop} overlap with previous {prevRo
Error message
replace op boundaries of {rop} overlap with previous {prevRop} What it means
Thrown by TokenStreamRewriter.reduceToSingleOperationPerIndex when two InsertAfter/InsertBefore/Replace operations produce overlapping ReplaceOp instructions that cannot be merged (merging only happens for pure overlapping deletes). ANTLR's rewriter refuses to guess which replacement text should win when two replace regions overlap, so it fails fast instead of silently producing corrupt output.
Source
Thrown at runtime/CSharp/src/TokenStreamRewriter.cs:630
}
// throw exception unless disjoint or identical
bool disjoint = prevRop.lastIndex < rop.index || prevRop.index > rop.lastIndex;
// Delete special case of replace (text==null):
// D.i-j.u D.x-y.v | boundaries overlap combine to max(min)..max(right)
if (prevRop.text == null && rop.text == null && !disjoint)
{
//System.out.println("overlapping deletes: "+prevRop+", "+rop);
rewrites[prevRop.instructionIndex] = null;
// kill first delete
rop.index = Math.Min(prevRop.index, rop.index);
rop.lastIndex = Math.Max(prevRop.lastIndex, rop.lastIndex);
System.Console.Out.WriteLine("new rop " + rop);
}
else
{
if (!disjoint)
{
throw new ArgumentException("replace op boundaries of " + rop + " overlap with previous " + prevRop);
}
}
}
}
// WALK INSERTS
for (int i_1 = 0; i_1 < rewrites.Count; i_1++)
{
TokenStreamRewriter.RewriteOperation op = rewrites[i_1];
if (op == null)
{
continue;
}
if (!(op is TokenStreamRewriter.InsertBeforeOp))
{
continue;
}
TokenStreamRewriter.InsertBeforeOp iop = (TokenStreamRewriter.InsertBeforeOp)rewrites[i_1];
// combine current insert with prior if any at same indexView on GitHub (pinned to 7d5770395b)
Solutions
- Rework the rewrite logic so token ranges passed to Replace/InsertAfter never overlap: track the last processed index and start the next range after it.
- Merge adjacent edits into one Replace call covering the union range with the combined text instead of issuing several overlapping replaces.
- Use separate TokenStreamRewriter instances (or re-parse the output) for each independent rewrite pass rather than stacking overlapping edits on one rewriter.
- Catch ArgumentException in GetText()/GetTextWithDiff() only to surface a clear diagnostic, then fix the offending edit sequence.
Example fix
// before rewriter.Replace(0, 5, "a"); rewriter.Replace(3, 8, "b"); // overlaps 0..5 string out1 = rewriter.GetText(); // after rewriter.Replace(0, 8, "a" + originalText(6, 8) + "b"); // single non-overlapping op string out2 = rewriter.GetText();
Defensive patterns
Strategy: validation
Validate before calling
// Before issuing a Replace, ensure the range does not overlap a previous one
var issued = new List<(int from, int to)>();
void SafeReplace(TokenStreamRewriter r, int from, int to, string text)
{
if (issued.Any(r2 => from <= r2.to && to >= r2.from))
throw new InvalidOperationException($"Replace({from},{to}) overlaps a previous replace");
issued.Add((from, to));
r.Replace(from, to, text);
} Try / catch
try { var text = rewriter.GetText(); } catch (ArgumentException ex) when (ex.Message.Contains("replace op boundaries")) { /* log the edit program and fix the overlapping ranges */ } Prevention
- Track the ranges you have already rewritten and assert non-overlap before each Replace call.
- Prefer one wide Replace with composed text over several overlapping replaces.
- Run independent rewrite passes on separate TokenStreamRewriter instances or re-parse between passes.
When it happens
Trigger: Calling tokens.Replace(from, to, text) or InsertAfter/InsertBefore followed by Replace such that two ReplaceOps share token indexes and at least one is a real replace (not both deletes). Example: rewriter.Replace(0, 5, "a") then rewriter.Replace(3, 8, "b") before calling GetText().
Common situations: Buildng source-to-source translators or instrumentation tools that rewrite several neighboring token ranges in a loop; applying a second rewrite pass over ranges produced by a first pass; using delete via Replace(from,to,null) mixed with replaces over the same span.
Related errors
- replace op boundaries of {rop} overlap with previous {prevRo
- insert op {iop} within boundaries of previous {rop}
- insert op {iop} within boundaries of previous {rop}
- replace: range invalid: {from}..{to}(size={size})
- Invalid state number.
AI-assisted analysis of antlr/antlr4@7d5770395b (2026-08-14).
Data as JSON: /api/errors/e1572d9ba2487a66.
Report an issue: GitHub.