{"record":{"id":"4e78d0b64a15451e","repo":"antlr/antlr4","slug":"insert-op-iop-within-boundaries-of-previous-rop-4e78d0","errorCode":null,"errorMessage":"insert op {iop} within boundaries of previous {rop}","messagePattern":"insert op (.+?) within boundaries of previous (.+?)","errorType":"validation","errorClass":"ArgumentException","httpStatus":null,"severity":"error","filePath":"runtime/CSharp/src/TokenStreamRewriter.cs","lineNumber":675,"sourceCode":"                        iop.text = CatOpText(iop.text, prevIop.text);\n                        // delete redundant prior insert\n                        rewrites[prevIop.instructionIndex] = null;\n                    }\n                }\n                // look for replaces where iop.index is in range; error\n                IList<TokenStreamRewriter.ReplaceOp> prevReplaces = GetKindOfOps<TokenStreamRewriter.ReplaceOp>(rewrites, i_1);\n                foreach (TokenStreamRewriter.ReplaceOp rop in prevReplaces)\n                {\n                    if (iop.index == rop.index)\n                    {\n                        rop.text = CatOpText(iop.text, rop.text);\n                        rewrites[i_1] = null;\n                        // delete current insert\n                        continue;\n                    }\n                    if (iop.index >= rop.index && iop.index <= rop.lastIndex)\n                    {\n                        throw new ArgumentException(\"insert op \" + iop + \" within boundaries of previous \" + rop);\n                    }\n                }\n            }\n            // System.out.println(\"rewrites after=\"+rewrites);\n            IDictionary<int, TokenStreamRewriter.RewriteOperation> m = new Dictionary<int, TokenStreamRewriter.RewriteOperation>();\n            for (int i_2 = 0; i_2 < rewrites.Count; i_2++)\n            {\n                TokenStreamRewriter.RewriteOperation op = rewrites[i_2];\n                if (op == null)\n                {\n                    continue;\n                }\n                // ignore deleted ops\n                if (m.ContainsKey(op.index))\n                {\n                    throw new InvalidOperationException(\"should only be one op per index\");\n                }\n                m[op.index] = op;","sourceCodeStart":657,"sourceCodeEnd":693,"githubUrl":"https://github.com/antlr/antlr4/blob/7d5770395bb7b02eb56e7c62662cb1d7c08f42a3/runtime/CSharp/src/TokenStreamRewriter.cs#L657-L693","documentation":"Thrown by TokenStreamRewriter when an insert operation's index falls strictly inside the boundaries of a previously issued replace operation (insert at exactly rop.index is legal and is prepended to the replacement text; anything between rop.index+1 and rop.lastIndex-1 is not). The rewriter cannot decide whether the inserted text belongs before, inside, or after the replacement, so it rejects the program.","triggerScenarios":"Calling rewriter.InsertAfter(i, \"x\") or InsertBefore(i, \"x\") where a prior rewriter.Replace(i, j, text) (or delete) exists with i in (rop.index, rop.lastIndex) exclusive of the equal-index merge case. Detected during GetText() when the rewrite program is validated.","commonSituations":"Adding a semicolon or comma after a token that a previous edit already replaced; composing multiple independent fix-up rules (e.g. a formatter plus a refactoring) over the same token stream; off-by-one when computing the insert position relative to a replaced range.","solutions":["Move the insert outside the replaced range: insert before rop.index or after rop.lastIndex instead of inside it.","Fold the inserted text into the replacement itself: rewriter.Replace(rop.index, rop.lastIndex, insertedText + replacementText).","If the insert index equals the replace start, rely on the supported merge by inserting exactly at rop.index (InsertBefore at the replace start is merged automatically).","Reorder edits so replaces are issued first and then validate each insert index against the replace ranges you recorded."],"exampleFix":"// before\nrewriter.Replace(2, 6, \"newExpr\");\nrewriter.InsertAfter(4, \";\"); // 4 is inside 2..6 -> throws\n\n// after\nrewriter.Replace(2, 6, \"newExpr;\"); // fold insertion into the replacement","handlingStrategy":"validation","validationCode":"// Check an insert index is not strictly inside a previously replaced range\nvar replaces = new List<(int from, int to)>();\nvoid SafeInsertBefore(TokenStreamRewriter r, int i, string text)\n{\n    if (replaces.Any(rp => i > rp.from && i < rp.to))\n        throw new InvalidOperationException($\"Insert at {i} falls inside replace {rp.from}..{rp.to}\");\n    r.InsertBefore(i, text);\n}","typeGuard":null,"tryCatchPattern":"try { rewriter.GetText(); } catch (ArgumentException ex) when (ex.Message.Contains(\"within boundaries\")) { /* locate the insert inside a replace and fold its text into the replacement */ }","preventionTips":["Record replace ranges and check every insert index against them before calling InsertBefore/InsertAfter.","Remember insert exactly at the replace start index is legal and auto-merged; only strictly-inside indexes throw.","Fold inserted text into the replacement string when the insertion point is inside a replaced span."],"tags":["antlr","csharp","token-stream-rewriter","insert","rewrite"],"backgroundTag":null,"analyzedSha":"7d5770395bb7b02eb56e7c62662cb1d7c08f42a3","analyzedAt":"2026-08-14T14:47:56.354Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}