{"record":{"id":"de990c8eb66e68ed","repo":"antlr/antlr4","slug":"can-t-alter-readonly-intervalset","errorCode":null,"errorMessage":"can't alter readonly IntervalSet","messagePattern":"can't alter readonly IntervalSet","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"runtime/Java/src/org/antlr/v4/runtime/misc/IntervalSet.java","lineNumber":83,"sourceCode":"\t}\n\n\t/** Create a set with a single element, el. */\n\n    public static IntervalSet of(int a) {\n\t\tIntervalSet s = new IntervalSet();\n        s.add(a);\n        return s;\n    }\n\n    /** Create a set with all ints within range [a..b] (inclusive) */\n\tpublic static IntervalSet of(int a, int b) {\n\t\tIntervalSet s = new IntervalSet();\n\t\ts.add(a,b);\n\t\treturn s;\n\t}\n\n\tpublic void clear() {\n        if ( readonly ) throw new IllegalStateException(\"can't alter readonly IntervalSet\");\n\t\tintervals.clear();\n\t}\n\n    /** Add a single element to the set.  An isolated element is stored\n     *  as a range el..el.\n     */\n    @Override\n    public void add(int el) {\n        if ( readonly ) throw new IllegalStateException(\"can't alter readonly IntervalSet\");\n        add(el,el);\n    }\n\n    /** Add interval; i.e., add all integers from a to b to set.\n     *  If b&lt;a, do nothing.\n     *  Keep list in sorted order (by left range value).\n     *  If overlap, combine ranges.  For example,\n     *  If this is {1..5, 10..20}, adding 6..7 yields\n     *  {1..5, 6..7, 10..20}.  Adding 4..8 yields {1..8, 10..20}.","sourceCodeStart":65,"sourceCodeEnd":101,"githubUrl":"https://github.com/antlr/antlr4/blob/7d5770395bb7b02eb56e7c62662cb1d7c08f42a3/runtime/Java/src/org/antlr/v4/runtime/misc/IntervalSet.java#L65-L101","documentation":"IntervalSet guards mutation with a readonly flag: once setReadonly(true) is called, clear(), add(int), add(int,int) etc. throw IllegalStateException('can't alter readonly IntervalSet'). The runtime marks shared, immutable sets (notably sets produced during ATN deserialization and grammar-derived label sets) readonly to prevent accidental corruption of shared structures.","triggerScenarios":"Calling add/clear/remove on an IntervalSet obtained from runtime internals: transition.labelSet (SetTransition/NotSetTransition), ATNState or rule definitions from a deserialized ATN, or any set the ANTLR tool marked readonly. Also copying such a set into your own structure and then trying to mutate it without cloning.","commonSituations":"Building a custom error strategy or token filter that extends a label set from the ATN. Reusing IntervalSet.of(...) results vs. readonly ATN sets interchangeably. Tools that aggregate 'all tokens accepted here' by adding into an ATN-supplied set.","solutions":["Clone before mutating: IntervalSet copy = new IntervalSet(readonlySet); copy.add(...); (the copy starts writable)","If you own the set, only call setReadonly(true) after all construction is done","For 'union with existing' logic, always build a fresh writable IntervalSet and addAll from the readonly source"],"exampleFix":"// before\nIntervalSet labels = transition.label(); // readonly set from ATN\nlabels.add(extraToken); // IllegalStateException\n// after\nIntervalSet labels = new IntervalSet(transition.label()); // writable copy\nlabels.add(extraToken);","handlingStrategy":"validation","validationCode":"static IntervalSet writable(IntervalSet s) {\n    return s.isReadonly() ? new IntervalSet(s) : s;\n}\n// then: writable(labels).add(tokenType);","typeGuard":"static boolean isMutable(IntervalSet s) { return !s.isReadonly(); }","tryCatchPattern":"catch (IllegalStateException e) { if (e.getMessage().contains(\"readonly\")) { IntervalSet copy = new IntervalSet(original); copy.add(x); } else throw e; }","preventionTips":["Treat every IntervalSet from ATN transitions as immutable","Clone with new IntervalSet(source) before extending","Call setReadonly(true) last, only on sets you own"],"tags":["antlr","java","intervalset","readonly","immutability"],"backgroundTag":null,"analyzedSha":"7d5770395bb7b02eb56e7c62662cb1d7c08f42a3","analyzedAt":"2026-08-14T14:47:56.354Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}