Skip to content

diffEditorViewModel breaks when previous _unchangedRegions is non-empty #198516

@OfekShilon

Description

@OfekShilon

The direct symptom is in the site www.compiler-explorer.com, but the investigation went through monaco-editor (which the site uses) and ends up here.

Steps to Reproduce:

  1. Navigate to https://godbolt.org/z/dsav96z6P
    This shows the "LLVM Opt Pipeline Viewer" (1) which is a diff pane created by monaco's createDiffEditor (2). It's contents change based on the user selection in from the 'passes' list (3).
    image

  2. To see normal operation in action: click in order the passes "SROAPass" (first one) and "Greedy Register Allocator". Diffs are shown properly:
    image

  3. To reproduce the bug, click in order the passes "SROAPass", then "Debug Variable Analysis" (one above Greedy) and finally "Greedy Register Allocator". First, text diffs are not being decorated despite being present:

image

and second, a console shows the error:

lineRange.js:94  Uncaught (in promise) TypeError: Cannot read properties of undefined (reading 'endLineNumberExclusive')
    at nl.intersectsStrict (lineRange.js:94:45)
    at a (diffEditorViewModel.js:98:50)
    at diffEditorViewModel.js:180:17
    at BV (base.js:76:9)
    at BU.<anonymous> (diffEditorViewModel.js:179:13)
    at Generator.next (<anonymous>)
    at r (diffEditorViewModel.js:17:58)

Preliminary investigation directs attention to these lines in DiffEditorViewModel's updateUnchangedRegions:

        const updateUnchangedRegions = (result, tx, reader) => {
            const newUnchangedRegions = UnchangedRegion.fromDiffs(result.changes, model.original.getLineCount(), model.modified.getLineCount(), this._options.hideUnchangedRegionsMinimumLineCount.read(reader), this._options.hideUnchangedRegionsContextLineCount.read(reader));
            // Transfer state from cur state
            const lastUnchangedRegions = this._unchangedRegions.get();
            const lastUnchangedRegionsOrigRanges = lastUnchangedRegions.originalDecorationIds
                .map(id => model.original.getDecorationRange(id))
                .filter(r => !!r)
                .map(r => LineRange.fromRange(r));

...
            for (const r of newUnchangedRegions) {   
                for (let i = 0; i < lastUnchangedRegions.regions.length; i++) {
                    if (r.originalUnchangedRange.intersectsStrict(lastUnchangedRegionsOrigRanges[i])   <--- attempt to access an empty array
...
                    }
                }
            }

In the (not so rare) problematic scenario, newUnchangedRegions and lastUnchangedRegions.regions are non-empty but lastUnchangedRegionsOrigRanges is, so the code enters the loop and crashes immediately.

I suspect something broke in @hediet PR #191977, but not sure.

Metadata

Metadata

Assignees

Labels

insiders-releasedPatch has been released in VS Code Insiders

Type

No type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions