antlr/antlr4 · error · IllegalArgumentException

replace op boundaries of {rop} overlap with previous {prevRo

Error message

replace op boundaries of {rop} overlap with previous {prevRop}

What it means

During getText()/reduceToSingleOperationPerIndex, TokenStreamRewriter detects two queued ReplaceOps (or a replace plus a delete, i.e. text==null) whose token ranges are not disjoint. Overlapping deletes are merged automatically, but any other overlap is rejected because the output text would be ambiguous. The message prints both operations with their ranges.

Source

Thrown at runtime/Java/src/org/antlr/v4/runtime/TokenStreamRewriter.java:522

				if ( prevRop.index>=rop.index && prevRop.lastIndex <= rop.lastIndex ) {
					// delete replace as it's a no-op.
					rewrites.set(prevRop.instructionIndex, null);
					continue;
				}
				// throw exception unless disjoint or identical
				boolean 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.set(prevRop.instructionIndex, null); // kill first delete
					rop.index = Math.min(prevRop.index, rop.index);
					rop.lastIndex = Math.max(prevRop.lastIndex, rop.lastIndex);
					System.out.println("new rop "+rop);
				}
				else if ( !disjoint ) {
					throw new IllegalArgumentException("replace op boundaries of "+rop+" overlap with previous "+prevRop);
				}
			}
		}

		// WALK INSERTS
		for (int i = 0; i < rewrites.size(); i++) {
			RewriteOperation op = rewrites.get(i);
			if ( op==null ) continue;
			if ( !(op instanceof InsertBeforeOp) ) continue;
			InsertBeforeOp iop = (InsertBeforeOp)rewrites.get(i);
			// combine current insert with prior if any at same index
			List<? extends InsertBeforeOp> prevInserts = getKindOfOps(rewrites, InsertBeforeOp.class, i);
			for (InsertBeforeOp prevIop : prevInserts) {
				if ( prevIop.index==iop.index ) {
					if ( InsertAfterOp.class.isInstance(prevIop) ) {
						iop.text = catOpText(prevIop.text, iop.text);
						rewrites.set(prevIop.instructionIndex, null);
					}

View on GitHub (pinned to 7d5770395b)

Solutions

  1. Make the ranges disjoint: track which token indexes you have already rewritten and skip/split overlapping operations
  2. Merge the two operations yourself into one replace spanning min(from)..max(to) with the combined text
  3. Use separate program names and call getText once per program if the edits are intentionally independent passes

Example fix

// before
rewriter.replace(0, 5, "A");
rewriter.replace(3, 8, "B"); // overlaps 0..5 -> throws at getText()

// after
Set<Integer> claimed = new HashSet<>();
void replaceDisjoint(int from, int to, String text) {
    for (int i = from; i <= to; i++)
        if (!claimed.add(i)) throw new IllegalStateException("overlap at " + i);
    rewriter.replace(from, to, text);
}
Defensive patterns

Strategy: validation

Validate before calling

static boolean overlaps(int aFrom, int aTo, int bFrom, int bTo) {
    return !(aTo < bFrom || bTo < aFrom);
}
// before each replace(from,to):
if (claimedRanges.stream().anyMatch(r -> overlaps(r[0], r[1], from, to))) {
    // merge or skip instead of queuing
}

Try / catch

try { rewriter.getText(); } // validation happens lazily at getText
catch (IllegalArgumentException e) { /* e mentions 'overlap': merge/split the offending ops and rebuild the program */ }

Prevention

When it happens

Trigger: Queuing replace(0,5,...) then replace(3,8,...) on the same program; mixing insert-driven replaces that end up overlapping; accumulating rewrites from multiple rules/visitors that touch adjacent or nested token ranges without coordination.

Common situations: Source-transformation passes where several visitors each rewrite parts of the tree and their token ranges intersect; incremental edits applied on top of an earlier edit program; replacing a statement that another rule already replaced.

Related errors


AI-assisted analysis of antlr/antlr4@7d5770395b (2026-08-14). Data as JSON: /api/errors/5115bf78623a7d45. Report an issue: GitHub.