antlr/antlr4 · error · IllegalArgumentException
insert op {iop} within boundaries of previous {rop}
Error message
insert op {iop} within boundaries of previous {rop} What it means
TokenStreamRewriter rejects an InsertBeforeOp whose token index falls strictly inside a previously queued ReplaceOp's range (index > rop.index && index <= rop.lastIndex). Inserting text into the middle of a region that will be wholly replaced has no well-defined meaning, so the rewriter fails fast. Note insert-at-index == replace-index is legal and is folded into the replace text.
Source
Thrown at runtime/Java/src/org/antlr/v4/runtime/TokenStreamRewriter.java:559
else if ( InsertBeforeOp.class.isInstance(prevIop) ) { // combine objects
// convert to strings...we're in process of toString'ing
// whole token buffer so no lazy eval issue with any templates
iop.text = catOpText(iop.text, prevIop.text);
// delete redundant prior insert
rewrites.set(prevIop.instructionIndex, null);
}
}
}
// look for replaces where iop.index is in range; error
List<? extends ReplaceOp> prevReplaces = getKindOfOps(rewrites, ReplaceOp.class, i);
for (ReplaceOp rop : prevReplaces) {
if ( iop.index == rop.index ) {
rop.text = catOpText(iop.text,rop.text);
rewrites.set(i, null); // delete current insert
continue;
}
if ( iop.index >= rop.index && iop.index <= rop.lastIndex ) {
throw new IllegalArgumentException("insert op "+iop+" within boundaries of previous "+rop);
}
}
}
// System.out.println("rewrites after="+rewrites);
Map<Integer, RewriteOperation> m = new HashMap<Integer, RewriteOperation>();
for (int i = 0; i < rewrites.size(); i++) {
RewriteOperation op = rewrites.get(i);
if ( op==null ) continue; // ignore deleted ops
if ( m.get(op.index)!=null ) {
throw new Error("should only be one op per index");
}
m.put(op.index, op);
}
//System.out.println("index to op: "+m);
return m;
}
protected String catOpText(Object a, Object b) {View on GitHub (pinned to 7d5770395b)
Solutions
- Move the inserted text outside the replaced range, or prepend/append it to the replace text yourself
- Check queued replaces first: only insert when the index is not inside any (from, to] replace range
- Restructure the pass so one visitor owns a token range: either it replaces it or it inserts into it, never both
Example fix
// before rewriter.replace(5, 10, "newExpr"); rewriter.insertAfter(tokens.get(7), ";"); // index 8 inside (5,10] -> throws // after rewriter.replace(5, 10, "newExpr;"); // fold the insertion into the replacement
Defensive patterns
Strategy: validation
Validate before calling
static boolean insertSafe(int idx, List<int[]> replaces) {
for (int[] r : replaces)
if (idx > r[0] && idx <= r[1]) return false; // strictly inside a replaced range
return true;
}
if (insertSafe(t.getTokenIndex(), claimedReplaces)) rewriter.insertBefore(t, text); Try / catch
try { rewriter.getText(); }
catch (IllegalArgumentException e) { /* fold the insert text into the covering replace op and re-run */ } Prevention
- Prefer folding insertions into the replace text of the owning rule
- Remember insertAfter(n) becomes insertBefore(n+1) and can land inside a replace range
- Insert at index == replace.from is legal (prepended), use it as the escape hatch
When it happens
Trigger: rewriter.insertBefore(t, text) or insertAfter(t, text) where t's index is within (from, to] of an earlier replace(from, to, ...); insertAfter a replaced token (index from+..to) since insertAfter maps to insertBefore of index+1.
Common situations: Transformation pipelines that replace an expression but also try to insert a token inside it; visitors adding separators or annotations into ranges another rule already rewrote.
Related errors
- replace op boundaries of {rop} overlap with previous {prevRo
- replace op boundaries of {rop} overlap with previous {prevRo
- insert op {iop} within boundaries of previous {rop}
- replace: range invalid: {from}..{to}(size={size})
- FileStream is only available when running in Node!
AI-assisted analysis of antlr/antlr4@7d5770395b (2026-08-14).
Data as JSON: /api/errors/0ac091f48dbb74f2.
Report an issue: GitHub.