Skip to content

Fix SubList.get() accepting an index equal to its own size - #12656

Open
renechoi wants to merge 1 commit into
apache:maven-4.0.xfrom
renechoi:fix/12595-sublist-get-bounds
Open

Fix SubList.get() accepting an index equal to its own size#12656
renechoi wants to merge 1 commit into
apache:maven-4.0.xfrom
renechoi:fix/12595-sublist-get-bounds

Conversation

@renechoi

@renechoi renechoi commented Aug 2, 2026

Copy link
Copy Markdown

SubList.get() checked index > size(), so an index equal to the sub list's size passed and reached AbstractImmutableList.this.get(fromIndex + index).

The issue describes this as surfacing an IndexOutOfBoundsException from the parent instead of a clear one from the sub list. That is what happens when the sub list reaches the end of its parent, and the exception is an ArrayIndexOutOfBoundsException from the backing array. When the sub list stops earlier there is no exception at all: the parent resolves the index and returns an element the sub list does not contain.

List<String> list = ImmutableCollections.copy(List.of("a", "b", "c"));
list.subList(0, 1).get(1);   // "b"   before
list.subList(0, 1).get(1);   // IndexOutOfBoundsException: Index: 1, Size: 1   after
list.subList(2, 3).get(1);   // ArrayIndexOutOfBoundsException: Index 3 out of bounds for length 3   before

ListN.get() indexes the array directly and has no bounds check of its own, which is why a read past the sub list is silent.

The same class is checked in three times: the mdo template plus the two copies generated from it under api/maven-api-xml and impl/maven-xml. All three are fixed, since changing only the copies would let the next regeneration bring it back.

listIterator(int) a few lines above keeps index > size(). That bound is correct there because List.listIterator(int) accepts size() as a cursor position, so one of the added tests pins it to prevent the two being aligned later by mistake.

Tests: four cases in a new ImmutableCollectionsTest in impl/maven-xml, covering the equal-to-size index, the read past a sub list that ends early, negative indices, and the listIterator bound. Two of them fail on maven-4.0.x and pass with this change; the module suite is green at 75 tests with checkstyle and spotless enabled.

Fixes #12595

The bounds check used `index > size()`, so `get(size())` fell through to
`AbstractImmutableList.this.get(fromIndex + index)`. When the sub list stops
before the end of the backing list that resolves to a real element outside the
sub list and is returned without any error: `copy(List.of("a","b","c")).subList(0, 1).get(1)`
answers "b". Only a sub list reaching the end of its parent fails, and then with
an ArrayIndexOutOfBoundsException from the backing array rather than the
IndexOutOfBoundsException the class builds for this case.

Applied to the mdo template and to both generated copies, otherwise the next
regeneration reintroduces it.

listIterator(int) keeps `index > size()` a few lines above. That bound is
correct there, since List.listIterator(int) accepts size() as a cursor
position, and a test pins it so the two are not aligned by mistake later.

Fixes apache#12595

@gnodet gnodet left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Clean, correct off-by-one bug fix in SubList.get() bounds check. The fix properly changes index > size() to index >= size() to align with the java.util.List contract, which specifies that get(size()) must throw IndexOutOfBoundsException.

Key observations:

  • All three copies of the file are fixed (template + both generated copies), preventing reintroduction on code generation
  • The listIterator(int index) method correctly retains index > size() since List.listIterator(size()) is a valid cursor position
  • The four new test cases are well-chosen: they reproduce both bug variants, verify normal iteration, and pin the listIterator contract
  • This is the first test class for ImmutableCollections, adding coverage to a previously untested area

This review was generated by an AI agent and may contain inaccuracies. Please verify all suggestions before applying.

Claude Code on behalf of gnodet

@elharo elharo added the bug Something isn't working label Aug 2, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants