Repository navigation
AI Code Scan Bugfixes
mjcmatrix edited this page Oct 8, 2026
·
4 revisions
Bugs found by an AI-assisted code scan of the fork on 2026-09-28, and the fix bundles raised for them. Unlike the other pages, nothing here is a new feature: these are correctness fixes, nearly all in code inherited from upstream.
-
Scope:
qmlui(the QLC+ 5 UI) andengine. The QtWidgets v4 UI (ui/), plugins and webaccess were not scanned. - Method: targeted, not exhaustive. Pattern searches (lookups used without a null check, unchecked casts, signed-byte sign extension, index bounds, divisions by values that can be zero, lock and lifetime issues), then close reading of the riskiest files: the Function Manager tree, undo/redo, VC Cue List and Frame, Simple Desk, ShowRunner, ChaserRunner, GenericFader/FadeChannel, Universe, Scene, Collection, EFX, RGB Matrix, Doc and MasterTimer.
-
Verification: every fixed bug was first reproduced on unmodified
upstream/master, either in the running app (headless session driven byxdotool) or with a regression unit test that fails on upstream and passes with the fix. Where neither was practical, the finding is marked code review. -
Tests: the full engine unit test suite passes with all bundles applied, on both
upstream/masterand the fork'smaster. The exceptions areqlcfileandrgbtext, which fail the same way on unmodified upstream (test resources and fonts in the test environment).
Column meanings:
- Confidence: how sure the scan was that the defect is real before verifying it.
- Patch risk: how likely the fix is to disturb something else.
- Verified: how the bug was confirmed on upstream.
-
Origin: where the buggy code came from, per
git blame.
| # | Where | Bug | Origin | Confidence | Patch risk | Verified | Fork PR |
|---|---|---|---|---|---|---|---|
| 1 | treemodel.cpp |
Deleting, moving or renaming a Function removed a folder with the same name (and every Function in it) from the tree; the deleted Function's row stayed behind with no icon | Upstream, 2018 TreeModel
|
High | Medium | App + model harness | #80 |
| 2 |
functionmanager.cpp addFunctiontoDoc()
|
A new Function was added to the existing selection, so Delete also removed the Functions selected before | Upstream, 2018. The fork's container-from-selection inherits it | High | Low | App | #80 |
| 3 | tardis.cpp |
After undoing everything, a new action didn't discard the undone ones, so further undos replayed them | Upstream, 2017 Tardis | High | Low | App (log) | #81 |
| 3b |
tardis.cpp redoAction()
|
One redo also applied the first action of the next batch | Upstream, 2017 Tardis | High | Low | App (log) | #81 |
| 4 | tardis.cpp |
Undo history changed on two threads without a lock | Upstream, 2017 Tardis | High (race exists); low (how often) | Medium | Code review | #81 |
| 5 | vccuelist.cpp |
Previous while stopped (or Next on a backward Chaser) went to step -2: "Run next" started with no step shown, "Select" lost the selection | Upstream, 2018 (the v4 UI handles it) | High | Low | App | #82 |
| 6 | simpledesk.cpp |
Unpatched channel values above 127 shown as ~4 billion after a universe switch | Upstream, 2020 | High | Very low | Code review | #83 |
| 7 |
functionmanager.cpp createFolder()
|
Duplicate check used the wrong path separator and a bare prefix match: nested duplicates allowed, "Foo" refused next to "Foobar" | Upstream, 2018 | High | Low | App | #80 |
| 8 |
functionmanager.cpp deleteSelectedFolders()
|
Deleting an empty folder left its empty subfolders registered; recreating them was refused | Upstream: the 2026-07 fix for #2058 only covered folders with Functions | Medium-high | Low | App | #80 |
| 9 | contextmanager.cpp |
Fixture distribution sorted positions as unsigned integers (negative/fractional positions misordered) | Upstream, 2018 | High | Very low | Code review | #83 |
| 10 | listmodel.cpp |
Role bound check let one role past the end through; negative rows not rejected | Upstream, 2016 | High (defect); low (impact) | Very low | Code review | #83 |
| # | Where | Bug | Origin | Confidence | Patch risk | Verified | Fork PR |
|---|---|---|---|---|---|---|---|
| A |
treemodel.cpp addItem()
|
Items added to a newly created empty folder didn't appear until the tree was rebuilt | Upstream | High | Low | App | #80 |
| B |
contextmanager.cpp setFixturesDistribution()
|
Vertical distribution in Front/Side views (and horizontal in Right side) wrote positions without undoing the 2D axis flip: fixtures landed mirrored or off the grid | Upstream, 2018 | High | Low | App | #83 |
| # | Where | Bug | Origin | Confidence | Patch risk | Verified | Fork PR |
|---|---|---|---|---|---|---|---|
| E1 | showrunner.cpp |
Two Show items using the same Function (back-to-back, e.g. after Split, or overlapping on two tracks): the second item never played | Upstream, 2014–2016 | High | Medium | Unit test + app | #84 |
| E2 | rgbmatrix.cpp |
Deleting the fixture group of a running (or previewed) RGB Matrix left it using freed memory | Upstream | High | Low | Unit test | #85 |
| E3 |
doc.cpp + qmlui moveFixture()
|
Moving a fixture left its HTP/LTP types and defaults at the old address (none at the new one); deleting one left them set up and its last LTP value on the output | Upstream, old | High | Medium | Unit test | #86 |
| E4 | chaserrunner.cpp |
Random order could report a step that doesn't exist (single step, or going backward) | Upstream | High | Low | Unit test | #87 |
| E5 | chaserrunner.cpp |
Random order not rebuilt when steps were added or removed while running | Upstream | Medium-high | Low | Unit test | #87 |
| E6 | collection.cpp |
A Collection's duration was the sum of its members, though they run in parallel | Upstream, 2015 | High | Low | Unit test | #87 |
| E7 |
collection.cpp adjustAttribute()
|
Intensity overrides looked up by position in the current member list; out of range if members change while running | Upstream, 2015/2017 | Medium | Low | Code review | Not yet fixed |
| E8 | efx.cpp |
Only the first head of a deleted multi-head fixture was removed; EFX fixture entries could be deleted while the MasterTimer thread used them | Upstream, 2012/2013 | High (heads); medium (race) | Low (heads); medium (lock) | Unit test (heads), code review (race) | #85 |
| E9 | showrunner.cpp |
Starting a Show mid-way compared beat-based items with a time in ms: below 60 BPM items still to come were skipped | Upstream, 2012 condition reused for beats | Medium-high | Low | Unit test | #84 |
| E10 |
efx.cpp loopDuration()
|
A fade in not shorter than the duration wrapped the loop to ~49 days, freezing the EFX (reachable from the EFX editor) | Upstream | High | Low | Unit test | #87 |
| E11 |
rgbmatrix.cpp setTotalDuration()
|
Integer division by zero (crash) with a 0-step algorithm. Only the v4 Show Manager calls it | Upstream | High | Very low | Unit test (SIGFPE on upstream) | #87 |
| Q1 |
chasereditor.cpp setTempoType()
|
Switching a Chaser's tempo type overflowed the duration of infinite-hold steps, so cues no longer held | Upstream, 2016 | High | Very low | Code review | #87 |
| Q2 | vcframe.cpp |
Lowering a VC Frame's pages number left widgets hidden on removed pages, still active and saved with the project | Upstream | Medium-high | Medium | App | #88 |
| Q2b | vcframe.cpp |
The frame's page selector jumped back to "Page 1" when the pages changed, while showing another page (found while testing Q2) | Upstream | High | Very low | App | #88 |
- E7 above.
-
FunctionEditor::setFadeInSpeed()changes the fade in without keeping the hold. This is the root cause of E10 being easy to reach; changing it affects every editor that uses it. - EFX ignores the Beats tempo type (not reachable in qmlui: the EFX editor has no tempo control).
- The qmlui EFX editor and undo remove EFX fixture entries without deleting them (small leak).
-
Chaser::setTotalDuration()per-step path can divide by zero (v4 UI only). -
Scene::slotFixtureRemoved()edits the Scene values without the lockwrite()holds. -
Universe::requestFaderPriority()orders equal-priority faders inconsistently. - ChaserRunner's start offset uses per-step durations for the "Default" duration mode, while playback uses the Chaser's duration.
Each bundle is a fork PR for review first. All are branched from upstream/master, with a regression test or an app test for every fix, so they can go upstream as they are.
| Fork PR | Upstream branch | Title | Fixes | Notes |
|---|---|---|---|---|
| #80 | fix-functiontree-name-clash |
qmlui: fix Function Manager tree when folder and Function share a name | 1, 2, 7, 8, A | One-line conflict with mcallegari/qlcplus#2175, now merged upstream, (both add return on the same line). Fork PR uses fork/fix-functiontree-name-clash, adapted to the fork's Function Manager |
| #81 | fix-tardis-history |
qmlui: fix Tardis undo/redo history handling | 3, 3b, 4 | |
| #82 | fix-cuelist-prev-when-stopped |
qmlui: fix Cue List previous/next step when not playing | 5 | |
| #83 | fix-qmlui-value-bounds |
qmlui: fix 2D fixture distribution, Simple Desk sign extension and ListModel bounds | 6, 9, 10, B | |
| #84 | fix-showrunner-shared-functions |
engine: fix Show items sharing a Function and beat items on mid-way start | E1, E9 | Fork PR uses fork/fix-showrunner-shared-functions, adapted to the fork's tempo map and output hold. The second half of a split Chaser restarts from step 1 (as a separate item does) |
| #85 | fix-running-function-stale-refs |
engine: fix running Functions using deleted groups and fixtures | E2, E8 | |
| #86 | fix-universe-fixture-patching |
engine: keep universe channel setup in step with fixture patching | E3 | Changes output behaviour after moving or deleting fixtures, towards what a freshly loaded project does |
| #87 | fix-small-engine-qmlui |
engine/qmlui: fix random Chasers, Collection/EFX durations and a crash | E4, E5, E6, E10, E11, Q1 | Fork PR uses fork/fix-small-engine-qmlui, merged with the fork's tempo map ChaserRunner |
| #88 | fix-vcframe-page-reduction |
qmlui: don't lose VC Frame widgets when reducing the pages number | Q2, Q2b | Widgets are never deleted: the pages can't go below the highest page with widgets |
➖ Upstream: none sent yet, pending review on the fork