Conversation
When adding a new connection tab, the ID spinbox was always populated from the first adapter default (id=1) instead of reflecting the next tab index. Check for an integer "id" field in the item schema and override it with _nextItemTabIndex so the value matches the tab name. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
parseConditional() now falls back to inferring the trigger key from the single property in if.properties when if.required is absent, matching the real adapter schema format that omits the required array. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
SchemaFormWidget now emits fieldChanged(key, value) when any string field changes. AdapterSettings uses the name field value as the tab label on load and add, and connects fieldChanged to keep the label in sync as the user types. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
WalkthroughAdds optional Changes
Sequence DiagramssequenceDiagram
actor User
participant DeviceConfigTab
participant AdapterDeviceSettings
participant TabBar
User->>DeviceConfigTab: Edit name field
DeviceConfigTab->>DeviceConfigTab: _pNameEdit::textChanged
DeviceConfigTab->>AdapterDeviceSettings: nameChanged(name)
AdapterDeviceSettings->>TabBar: setTabText(index, name)
sequenceDiagram
actor User
participant SchemaFormWidget
participant AdapterSettings
participant TabBar
User->>SchemaFormWidget: Edit name QLineEdit
SchemaFormWidget->>SchemaFormWidget: wireFieldChanged()
SchemaFormWidget->>AdapterSettings: fieldChanged("name", value)
AdapterSettings->>TabBar: setTabText(index, value)
Note over AdapterSettings: Falls back to formatTabName() if empty
Possibly related PRs
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 3
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/dialogs/adaptersettings.cpp (1)
82-90:⚠️ Potential issue | 🟡 MinorAvoid naive singularisation of every trailing
s.
statusbecomesStatu 1,busbecomesBu 1, and a one-character key likescan produce an empty label beforelabel[0]. Prefer preserving the key unless there is an explicit known singular label.🐛 Proposed fix
- QString label = _propertyKey.endsWith('s') ? _propertyKey.chopped(1) : _propertyKey; + QString label = _propertyKey; + if (label == QStringLiteral("connections")) + { + label.chop(1); + } return QString("%1 %2").arg(label[0].toUpper() + label.mid(1)).arg(index);🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/dialogs/adaptersettings.cpp` around lines 82 - 90, The current AdapterSettings::formatTabName naively chops a trailing 's' from _propertyKey causing wrong labels (e.g., "status" -> "Statu") and potential empty-label access; change the logic to only singularize when _propertyKey.length() > 1 and it actually looks like a plural you intend to handle (e.g., not one-character keys and not known exceptions), and ensure label is non-empty before accessing label[0]; update the code in formatTabName to compute label = _propertyKey (or singularized only under the guarded conditions), then capitalize safely (check label.isEmpty() first) and format the tab name.
🧹 Nitpick comments (4)
src/customwidgets/schemaformwidget.h (1)
47-49: Add\briefto the new signal comment.The new signal is useful, but the comment should match the repository’s Qt Doxygen convention.
Proposed Doxygen style fix
signals: - //! Emitted when a string field's value changes; \a key is the property name. + //! \brief Emitted when a string field's value changes; \a key is the property name. void fieldChanged(const QString& key, const QString& value);As per coding guidelines, “Use Qt Doxygen style comments: /*! */ for multi-line blocks, //! for single-line; use \brief, \param, \return with backslash prefix (not @)”.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/customwidgets/schemaformwidget.h` around lines 47 - 49, The signal comment for fieldChanged lacks a \brief tag and should follow the Qt Doxygen convention; update the comment immediately above the void fieldChanged(const QString& key, const QString& value); declaration to a single-line Qt Doxygen comment using //! \brief ... and include a brief description (and optional \param entries for key and value if desired) so it matches the repository's Qt Doxygen style.src/customwidgets/deviceconfigtab.h (1)
49-51: Add\briefto the new signal comment.The signal itself looks fine, but the new Doxygen comment should follow the project’s
//! \brief ...style.Proposed Doxygen style fix
signals: - //! Emitted when the device name field changes. + //! \brief Emitted when the device name field changes. void nameChanged(const QString& name);As per coding guidelines, “Use Qt Doxygen style comments: /*! */ for multi-line blocks, //! for single-line; use \brief, \param, \return with backslash prefix (not @)”.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/customwidgets/deviceconfigtab.h` around lines 49 - 51, Update the single-line Doxygen comment for the signal `nameChanged(const QString& name)` to use the project Qt Doxygen style by replacing the current comment with a single-line `//! \brief ...` form (e.g., `//! \brief Emitted when the device name field changes.`) so it follows the `//! \brief`, backslash-escaped commands convention used in `deviceconfigtab.h`.src/dialogs/adaptersettings.cpp (1)
92-103: Move the tab-name update body out of the lambda.The current lambda has multiple statements and nested control flow. Keep the connection lambda as a one-line dispatcher and put the logic in a named helper. As per coding guidelines, Avoid lambda expressions with more than 2 captures or multiple statements; use named functions instead for clarity.
♻️ Proposed refactor
void AdapterSettings::connectTabNameTracking(SchemaFormWidget* form) { - connect(form, &SchemaFormWidget::fieldChanged, this, [this, form](const QString& key, const QString& value) { - if (key == "name") - { - const int tabIndex = _pItemTabs->indexOf(form); - if (tabIndex >= 0) - { - _pItemTabs->setTabName(tabIndex, value.isEmpty() ? formatTabName(tabIndex + 1) : value); - } - } - }); + connect(form, &SchemaFormWidget::fieldChanged, this, + [this, form](const QString& key, const QString& value) { updateTabNameFromField(form, key, value); }); +} + +void AdapterSettings::updateTabNameFromField(SchemaFormWidget* form, const QString& key, const QString& value) +{ + if (key == "name") + { + const int tabIndex = _pItemTabs->indexOf(form); + if (tabIndex >= 0) + { + _pItemTabs->setTabName(tabIndex, value.isEmpty() ? formatTabName(tabIndex + 1) : value); + } + } }Also add the private helper declaration to
adaptersettings.h.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/dialogs/adaptersettings.cpp` around lines 92 - 103, The lambda passed in AdapterSettings::connectTabNameTracking contains multi-statement logic updating tab names; extract that body into a new private helper method (e.g., AdapterSettings::onSchemaFieldNameChanged) and leave the connect call as a one-line dispatcher calling that method. Specifically, create a helper that takes (SchemaFormWidget* form, const QString& key, const QString& value), performs the existing key == "name" check, computes tabIndex via _pItemTabs->indexOf(form), and calls _pItemTabs->setTabName(tabIndex, value.isEmpty() ? formatTabName(tabIndex + 1) : value) when tabIndex >= 0; update the connect invocation in connectTabNameTracking to call this helper from the lambda, and add the helper declaration to adaptersettings.h as a private method.tests/customwidgets/tst_schemaformwidget.cpp (1)
512-525: UseQSignalSpyinstead of a multi-statement capture lambda.This makes the signal assertion clearer and avoids the guideline violation in the current lambda body. As per coding guidelines, Avoid lambda expressions with more than 2 captures or multiple statements; use named functions instead for clarity.
♻️ Proposed refactor
+#include <QSignalSpy> `#include` <QSpinBox> `#include` <QTest>- QString emittedKey; - QString emittedValue; - connect(&w, &SchemaFormWidget::fieldChanged, - [&emittedKey, &emittedValue](const QString& key, const QString& value) { - emittedKey = key; - emittedValue = value; - }); + QSignalSpy spy(&w, &SchemaFormWidget::fieldChanged); auto* edit = w.findChild<QLineEdit*>(); QVERIFY(edit != nullptr); edit->setText("updated"); - QCOMPARE(emittedKey, QStringLiteral("host")); - QCOMPARE(emittedValue, QStringLiteral("updated")); + QCOMPARE(spy.count(), 1); + const QList<QVariant> arguments = spy.takeFirst(); + QCOMPARE(arguments.at(0).toString(), QStringLiteral("host")); + QCOMPARE(arguments.at(1).toString(), QStringLiteral("updated"));🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@tests/customwidgets/tst_schemaformwidget.cpp` around lines 512 - 525, Replace the multi-statement capture lambda used to connect to SchemaFormWidget::fieldChanged with a QSignalSpy on the widget instance (w) to capture the signal emission; create a QSignalSpy listening for SchemaFormWidget::fieldChanged, trigger the change by calling QLineEdit::setText on the found edit, then assert the spy recorded exactly one invocation and compare the spy's first call arguments to "host" and "updated". Use the spy's recorded arguments rather than captured variables (remove emittedKey/emittedValue and the lambda connect) and keep the remaining assertions using QCOMPARE on the spy results.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@adapters/describe.json`:
- Line 268: The "version" field currently contains an undocumented suffix
("0.0.1-master:ac7eec7"); update the value of the "version" property in
adapters/describe.json to follow the JSON-RPC spec debug format
"<semver>-<git-branch>" (e.g., "0.0.1-master") by removing the ":<hash>" suffix
so consumers that parse or compare the "version" key will match the documented
format.
In `@src/dialogs/adapterdevicesettings.cpp`:
- Around line 110-114: Update AdapterDeviceSettings::connectTabNameTracking so
the nameChanged lambda uses a fallback when the provided name is empty: compute
a label = name.isEmpty() ? constructTabName(tab) : name and pass that label to
_pDeviceTabs->setTabName(_pDeviceTabs->indexOf(tab), label). Also add the
private helper declaration for constructTabName(...) to adapterdevicesettings.h
so the method is visible to the implementation.
In `@src/dialogs/adaptersettings.cpp`:
- Around line 116-133: The code currently sets new item ids using
_nextItemTabIndex which can collide when tabs were deleted or configs imported;
change the id generation in the block that builds defaultValues (where
_itemSchema, defaultValues, _nextItemTabIndex and formatTabName are used) to
scan existing items/forms for the highest current "id" and set
defaultValues["id"] = maxId + 1 instead of using _nextItemTabIndex; keep
existing logic for default name (formatTabName) and calls to
form->setSchema(...) and connectTabNameTracking(...). Also add a regression test
that creates non-contiguous existing ids (e.g. [1,3]) and asserts the new id
becomes 4.
---
Outside diff comments:
In `@src/dialogs/adaptersettings.cpp`:
- Around line 82-90: The current AdapterSettings::formatTabName naively chops a
trailing 's' from _propertyKey causing wrong labels (e.g., "status" -> "Statu")
and potential empty-label access; change the logic to only singularize when
_propertyKey.length() > 1 and it actually looks like a plural you intend to
handle (e.g., not one-character keys and not known exceptions), and ensure label
is non-empty before accessing label[0]; update the code in formatTabName to
compute label = _propertyKey (or singularized only under the guarded
conditions), then capitalize safely (check label.isEmpty() first) and format the
tab name.
---
Nitpick comments:
In `@src/customwidgets/deviceconfigtab.h`:
- Around line 49-51: Update the single-line Doxygen comment for the signal
`nameChanged(const QString& name)` to use the project Qt Doxygen style by
replacing the current comment with a single-line `//! \brief ...` form (e.g.,
`//! \brief Emitted when the device name field changes.`) so it follows the `//!
\brief`, backslash-escaped commands convention used in `deviceconfigtab.h`.
In `@src/customwidgets/schemaformwidget.h`:
- Around line 47-49: The signal comment for fieldChanged lacks a \brief tag and
should follow the Qt Doxygen convention; update the comment immediately above
the void fieldChanged(const QString& key, const QString& value); declaration to
a single-line Qt Doxygen comment using //! \brief ... and include a brief
description (and optional \param entries for key and value if desired) so it
matches the repository's Qt Doxygen style.
In `@src/dialogs/adaptersettings.cpp`:
- Around line 92-103: The lambda passed in
AdapterSettings::connectTabNameTracking contains multi-statement logic updating
tab names; extract that body into a new private helper method (e.g.,
AdapterSettings::onSchemaFieldNameChanged) and leave the connect call as a
one-line dispatcher calling that method. Specifically, create a helper that
takes (SchemaFormWidget* form, const QString& key, const QString& value),
performs the existing key == "name" check, computes tabIndex via
_pItemTabs->indexOf(form), and calls _pItemTabs->setTabName(tabIndex,
value.isEmpty() ? formatTabName(tabIndex + 1) : value) when tabIndex >= 0;
update the connect invocation in connectTabNameTracking to call this helper from
the lambda, and add the helper declaration to adaptersettings.h as a private
method.
In `@tests/customwidgets/tst_schemaformwidget.cpp`:
- Around line 512-525: Replace the multi-statement capture lambda used to
connect to SchemaFormWidget::fieldChanged with a QSignalSpy on the widget
instance (w) to capture the signal emission; create a QSignalSpy listening for
SchemaFormWidget::fieldChanged, trigger the change by calling QLineEdit::setText
on the found edit, then assert the spy recorded exactly one invocation and
compare the spy's first call arguments to "host" and "updated". Use the spy's
recorded arguments rather than captured variables (remove
emittedKey/emittedValue and the lambda connect) and keep the remaining
assertions using QCOMPARE on the spy results.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: d7307832-08f7-4e8d-848b-50c33579b3b5
⛔ Files ignored due to path filters (2)
adapters/dummymodbusadapter.exeis excluded by!**/*.exeadapters/modbusadapter.exeis excluded by!**/*.exe
📒 Files selected for processing (16)
adapters/describe.jsonadapters/dummymodbusadapteradapters/json-rpc-spec.mdadapters/modbusadaptersrc/customwidgets/deviceconfigtab.cppsrc/customwidgets/deviceconfigtab.hsrc/customwidgets/schemaformwidget.cppsrc/customwidgets/schemaformwidget.hsrc/dialogs/adapterdevicesettings.cppsrc/dialogs/adapterdevicesettings.hsrc/dialogs/adaptersettings.cppsrc/dialogs/adaptersettings.htests/customwidgets/tst_schemaformwidget.cpptests/customwidgets/tst_schemaformwidget.htests/dialogs/tst_adaptersettings.cpptests/dialogs/tst_adaptersettings.h
| "type": "object" | ||
| }, | ||
| "version": "0.0.1-master" | ||
| "version": "0.0.1-master:ac7eec7" |
There was a problem hiding this comment.
Keep the version format aligned with the JSON-RPC spec.
Line 268 now advertises 0.0.1-master:ac7eec7, but the spec documents debug versions as "<semver>-<git-branch>". The extra :<hash> suffix is undocumented and can break consumers that parse or compare the version using the published format.
Proposed format alignment
- "version": "0.0.1-master:ac7eec7"
+ "version": "0.0.1-master"🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@adapters/describe.json` at line 268, The "version" field currently contains
an undocumented suffix ("0.0.1-master:ac7eec7"); update the value of the
"version" property in adapters/describe.json to follow the JSON-RPC spec debug
format "<semver>-<git-branch>" (e.g., "0.0.1-master") by removing the ":<hash>"
suffix so consumers that parse or compare the "version" key will match the
documented format.
Only singularize _propertyKey when its length is greater than 1, preventing a single-character key (e.g. "s") from being chopped to an empty string and crashing on label[0] access; add an explicit isEmpty() guard as a backstop. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add a constructTabName(DeviceConfigTab*) overload and use it in connectTabNameTracking so clearing the name field restores the generated fallback (e.g. "Device 1") instead of leaving the tab blank. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Replace the _nextItemTabIndex-based id assignment in addItemTab() with a scan of current form widgets for the highest id, using maxId+1 instead; this prevents duplicates when items have been deleted or configs imported with non-sequential ids. Add regression test for non-contiguous ids [1,3]. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Move the multi-statement lambda body in connectTabNameTracking into a named private method to comply with the style guide's single-statement lambda rule; the connect call becomes a one-line dispatcher. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Remove the two captured QString variables and the connect lambda in fieldChangedEmittedOnStringEdit; use QSignalSpy instead, which is the idiomatic Qt approach and avoids a multi-capture/multi-statement lambda. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Expose the internal _deviceId so close handlers can identify the device without parsing form JSON. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
tabClosed previously emitted an int index, forcing DeviceSettings to use Qt::DirectConnection so tabContent(index) was still valid during the slot. Emitting the widget pointer instead makes the slot independent of when removeTab is called; the pointer stays valid until deleteLater fires. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
AdapterDeviceSettings was adding devices to SettingsModel on tab add but never removing them on tab close. Wire tabClosed and call removeDevice so the model stays in sync with the UI. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Extract onNameChanged slot to replace multi-statement lambda in DeviceForm - Rename constructor parameter _deviceId to deviceId to remove shadowing - Use deleteLater() instead of delete in rebuildSchemaForm to avoid deleting a widget during event dispatch Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
acceptValues() guarded setName() with deviceList().contains(), which silently dropped the name for any device whose ID was in the adapter config but not yet registered in SettingsModel (e.g. after a project reload that omitted the device from the top-level devices section). On every subsequent dialog open the name field stayed empty because the same guard also blocked loading from SettingsModel. Replace the guard with addDevice() so the device is registered if missing before saving its adapter ID and name. Adds a regression test that verifies the name is persisted across an accept/reopen cycle for a device absent from the model. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
addItemTab() computed the new ID as maxId+1 but derived the default name from _nextItemTabIndex, which is initialised as tab-count+1. When existing IDs have gaps (e.g. 1 and 3 present), the count (2) and the max ID (3) diverge, so the name was "Connection 3" while the ID was 4. Sync _nextItemTabIndex to maxId+1 immediately after assigning the new ID so that the default name and tab title always match the assigned ID. Adds a regression test covering the non-contiguous-ID case. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…tItemTabIndex Introduces a local nameIndex variable derived from maxId+1 (id case) or _nextItemTabIndex (no-id case), so _nextItemTabIndex remains a plain sequential counter and is no longer re-synced inside the id block. Adds a consecutive-add regression test for non-contiguous starting IDs. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Remove redundant local deviceId variable in DeviceConfigTab ctor; use _deviceId directly and hasDevice() instead of deviceList().contains() - Initialize _pLayout in member initialiser list instead of body - Use tab as connection context in connectTabNameTracking so the connection is automatically severed when the tab is destroyed Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
With devices 1 and 3 present, addNewDevice() returned 2, creating a confusing [Device 1][Device 3][Device 2] tab order where the new device appeared numerically lower than an existing one. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
There was a problem hiding this comment.
Actionable comments posted: 3
🧹 Nitpick comments (2)
tests/dialogs/tst_adapterdevicesettings.cpp (1)
596-633: Consider also asserting the leaked device is reaped on reopen.The comment on line 614 notes that "leaked device 2 remains in model" after cancel, and the test then relies on the second dialog open trimming the model back to the adapter config so that the next add gets id 2. It would be worth adding an explicit
QVERIFY(!model.hasDevice(2))immediately after constructingw2to pin down the "orphan removal on reopen" contract — that way a regression in the constructor's sync logic would fail here with a clearer message than the finalQCOMPARE(assignedId, 2).🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@tests/dialogs/tst_adapterdevicesettings.cpp` around lines 596 - 633, Add an explicit assertion that the leaked device was removed from the SettingsModel when reopening the dialog: after constructing AdapterDeviceSettings w2(&model) and obtaining tabs, call model.hasDevice(2) and QVERIFY that it returns false (e.g., QVERIFY(!model.hasDevice(2))). This should be placed just before emit tabs->addTabRequested() in TestAdapterDeviceSettings::cancelAndReopenDoesNotLeakDeviceIds so the test fails clearly if the constructor/sync logic does not reap the orphaned device id.src/dialogs/adapterdevicesettings.cpp (1)
181-183: Minor: use the tab widget as receiver context, not the tab content.Using
tab(aDeviceConfigTab*) as the context object works, but the signal-receiver pattern is more conventional withthisas context and a guarded lookup. Current code is functionally correct because the connection is auto-disconnected whentabis destroyed; feel free to leave as-is.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/dialogs/adapterdevicesettings.cpp` around lines 181 - 183, Change the connection to use this as the receiver/context instead of tab: connect(tab, &DeviceConfigTab::nameChanged, this, [this, tab]() { int idx = _pDeviceTabs->indexOf(tab); if (idx != -1) _pDeviceTabs->setTabName(idx, constructTabName(tab)); }); This keeps the guarded lookup via _pDeviceTabs->indexOf(tab) before calling setTabName and ensures the slot is tied to the adapter object rather than the tab widget.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/customwidgets/deviceconfigtab.cpp`:
- Around line 106-113: The issue is that _deviceId is captured once and becomes
stale causing onNameChanged, constructTabName and
AdapterDeviceSettings::handleCloseTab to operate on the wrong device; fix by
keeping the tab's id in sync with the schema form: either subscribe to
SchemaFormWidget::fieldChanged for the "id" field (update the tab's _deviceId
whenever that field changes) or, alternatively, stop using the stored _deviceId
in onNameChanged, constructTabName and where handleCloseTab reads
tab->deviceId() and instead read the current id from
_pSchemaForm->values().value("id") (cast to deviceId_t) immediately before any
model operations (also ensure acceptValues() still writes the new id to the
adapter config).
In `@src/dialogs/adaptersettings.cpp`:
- Around line 88-93: The fallback label logic that sets QString label = (...)
overly aggressively chops a trailing 's' from _propertyKey (producing "Bu 1"
from "bus"); update the condition used to derive label so you only remove the
final 's' for likely plurals (e.g., require _propertyKey.length() > 3 and
exclude common non-plural endings like "ss", "us", "is" or "es" that form
singulars differently), or alternatively prefer an explicit display name when
available; modify the expression that assigns label (and related branch that
returns QString("%1 %2").arg(...).arg(index)) to use that safer heuristic so
short or special-case keys are not incorrectly truncated.
In `@src/models/settingsmodel.cpp`:
- Line 55: The device ID allocation in settingsmodel.cpp uses deviceId_t newId =
_devices.isEmpty() ? Device::cFirstDeviceId :
static_cast<deviceId_t>(_devices.lastKey() + 1) and lacks a guard against
overflow; add a runtime check against the maximum representable deviceId_t (e.g.
compare _devices.lastKey() to std::numeric_limits<deviceId_t>::max() or
UINT32_MAX) and handle the exhaustion case explicitly (reject the allocation by
returning an error/optional, throw an exception, or log and abort) instead of
allowing wrap-around that would let _devices[newId] overwrite an existing entry;
update the allocation code path that uses deviceId_t, _devices,
Device::cFirstDeviceId and lastKey() to perform this check and choose the
failure behavior consistent with the surrounding API.
---
Nitpick comments:
In `@src/dialogs/adapterdevicesettings.cpp`:
- Around line 181-183: Change the connection to use this as the receiver/context
instead of tab: connect(tab, &DeviceConfigTab::nameChanged, this, [this, tab]()
{ int idx = _pDeviceTabs->indexOf(tab); if (idx != -1)
_pDeviceTabs->setTabName(idx, constructTabName(tab)); }); This keeps the guarded
lookup via _pDeviceTabs->indexOf(tab) before calling setTabName and ensures the
slot is tied to the adapter object rather than the tab widget.
In `@tests/dialogs/tst_adapterdevicesettings.cpp`:
- Around line 596-633: Add an explicit assertion that the leaked device was
removed from the SettingsModel when reopening the dialog: after constructing
AdapterDeviceSettings w2(&model) and obtaining tabs, call model.hasDevice(2) and
QVERIFY that it returns false (e.g., QVERIFY(!model.hasDevice(2))). This should
be placed just before emit tabs->addTabRequested() in
TestAdapterDeviceSettings::cancelAndReopenDoesNotLeakDeviceIds so the test fails
clearly if the constructor/sync logic does not reap the orphaned device id.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 7c4b9322-a02e-47d7-bfd4-cc1fd0bc690b
📒 Files selected for processing (18)
src/customwidgets/addabletabwidget.cppsrc/customwidgets/addabletabwidget.hsrc/customwidgets/deviceconfigtab.cppsrc/customwidgets/deviceconfigtab.hsrc/customwidgets/deviceform.cppsrc/customwidgets/deviceform.hsrc/dialogs/adapterdevicesettings.cppsrc/dialogs/adapterdevicesettings.hsrc/dialogs/adaptersettings.cppsrc/dialogs/adaptersettings.hsrc/dialogs/devicesettings.cppsrc/dialogs/devicesettings.hsrc/models/settingsmodel.cpptests/customwidgets/tst_schemaformwidget.cpptests/dialogs/tst_adapterdevicesettings.cpptests/dialogs/tst_adapterdevicesettings.htests/dialogs/tst_adaptersettings.cpptests/dialogs/tst_adaptersettings.h
🚧 Files skipped from review as they are similar to previous changes (3)
- tests/customwidgets/tst_schemaformwidget.cpp
- src/dialogs/adapterdevicesettings.h
- tests/dialogs/tst_adaptersettings.cpp
| void DeviceConfigTab::onNameChanged(const QString& name) | ||
| { | ||
| if (_deviceId >= 0 && _pSettingsModel->hasDevice(static_cast<deviceId_t>(_deviceId))) | ||
| { | ||
| _pSettingsModel->deviceSettings(static_cast<deviceId_t>(_deviceId))->setName(name); | ||
| } | ||
| emit nameChanged(name); | ||
| } |
There was a problem hiding this comment.
Name edits and tab close write to a stale _deviceId after the user changes the id field.
_deviceId is captured once in the constructor from deviceValues["id"] and never updated, but the schema form exposes id as an editable spinbox (test addTabAfterIdEditDoesNotDuplicate exercises that path). Once the user edits the id:
onNameChangedwritessetName(name)to the model entry keyed by the old_deviceIdrather than the currently displayed id.AdapterDeviceSettings::handleCloseTabcallsremoveDevice(tab->deviceId()), so closing the tab removes the wrong (old) device from the model whileacceptValues()persists the new id into adapter config.- The tab title from
constructTabNameis also resolved against the stale id.
Consider keeping _deviceId in sync with the schema form (e.g. listen to SchemaFormWidget::fieldChanged for the id field, or always re-read _pSchemaForm->values().value("id") before using it), or disable editing of the id field in the UI so the two sources of truth cannot diverge.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/customwidgets/deviceconfigtab.cpp` around lines 106 - 113, The issue is
that _deviceId is captured once and becomes stale causing onNameChanged,
constructTabName and AdapterDeviceSettings::handleCloseTab to operate on the
wrong device; fix by keeping the tab's id in sync with the schema form: either
subscribe to SchemaFormWidget::fieldChanged for the "id" field (update the tab's
_deviceId whenever that field changes) or, alternatively, stop using the stored
_deviceId in onNameChanged, constructTabName and where handleCloseTab reads
tab->deviceId() and instead read the current id from
_pSchemaForm->values().value("id") (cast to deviceId_t) immediately before any
model operations (also ensure acceptValues() still writes the new id to the
adapter config).
Summary by CodeRabbit
New Features
Behaviour Changes
Tests & Documentation