Skip to content

Guard splice(indexOf()) against -1 to prevent silent removal of wrong element#314635

Open
SebTardif wants to merge 1 commit intomicrosoft:mainfrom
SebTardif:fix/guard-splice-indexof-v2
Open

Guard splice(indexOf()) against -1 to prevent silent removal of wrong element#314635
SebTardif wants to merge 1 commit intomicrosoft:mainfrom
SebTardif:fix/guard-splice-indexof-v2

Conversation

@SebTardif
Copy link
Copy Markdown

Problem

When Array.indexOf() returns -1 (element not found), splice(-1, 1) silently removes the last element of the array instead of being a no-op. Several locations in the codebase use this unguarded pattern, which can cause incorrect behavior:

  • keyboardLayoutService.ts: removeKeyboardLayout could be called with a stale reference, removing the wrong layout from both _mru and _keymapInfos
  • activityService.ts: The dispose callback returned from showViewContainerActivity/showActivity could fire twice (double dispose), removing the wrong activity badge
  • progressService.ts: If the promise resolved before the 150ms delay, the task was never added to the stack, but the finally block still attempts to splice it
  • settingsTree.ts: TOCTOU between the this.checked guard (set from a config listener) and the fresh getValue() call; another window/process could have removed the setting between the check and the splice

Fix

Add index !== -1 guards before each splice call. This is a standard defensive pattern that costs nothing and prevents a real class of silent data corruption.

… element

When Array.indexOf() returns -1 (element not found), splice(-1, 1) silently
removes the last element of the array instead of being a no-op. This can
cause incorrect behavior such as removing the wrong keyboard layout, activity
badge, progress indicator, or settings profile entry.

Add index !== -1 guards before splice calls in:
- keyboardLayoutService: removeKeyboardLayout could be called with stale ref
- activityService: dispose callback could fire twice (double dispose)
- progressService: task may not be in stack if promise resolved before delay
- settingsTree: setting may have been removed between checked and splice
Copilot AI review requested due to automatic review settings May 6, 2026 04:26
@vs-code-engineering
Copy link
Copy Markdown
Contributor

📬 CODENOTIFY

The following users are being notified based on files changed in this PR:

@rzhao271

Matched files:

  • src/vs/workbench/contrib/preferences/browser/settingsTree.ts

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR hardens several array-removal sites across the workbench against the splice(-1, 1) footgun by guarding indexOf() results, preventing accidental removal of the last element when the target is not present.

Changes:

  • Guard removal from the window progress task stack in ProgressService.
  • Guard removal from MRU and keymap layout arrays in BrowserKeyboardMapperFactoryBase.
  • Guard removal from activity badge arrays in ActivityService disposables.
  • Guard removal of a setting key from the “apply to all profiles” list in settings UI logic.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 1 comment.

File Description
src/vs/workbench/services/progress/browser/progressService.ts Adds idx !== -1 guard before removing a window-progress task from the stack.
src/vs/workbench/services/keybinding/browser/keyboardLayoutService.ts Prevents unintended removal when a keymap layout is missing from _mru / _keymapInfos.
src/vs/workbench/services/activity/browser/activityService.ts Makes activity disposal robust when the activity entry is no longer present in the array.
src/vs/workbench/contrib/preferences/browser/settingsTree.ts Avoids splice(-1, 1) when removing a setting key from the apply-to-all-profiles list.

Comment thread src/vs/workbench/contrib/preferences/browser/settingsTree.ts
@rzhao271 rzhao271 modified the milestones: 1.121.0, 1.120.0 May 6, 2026
@aiday-mar aiday-mar removed their assignment May 7, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants