Skip to content

fix(ego-lint): reduce init-time safety false positives#21

Merged
ZviBaratz merged 2 commits intomainfrom
fix/init-time-safety-false-positives
Mar 4, 2026
Merged

fix(ego-lint): reduce init-time safety false positives#21
ZviBaratz merged 2 commits intomainfrom
fix/init-time-safety-false-positives

Conversation

@ZviBaratz
Copy link
Copy Markdown
Owner

@ZviBaratz ZviBaratz commented Mar 4, 2026

Summary

  • Arrow function definitions exempt: const fn = () => Main.layoutManager.monitors at module scope is lazy — the body isn't executed at load time, so it's not an init-time violation
  • Constructor checks restricted to extension.js: Helper file constructors are runtime-only (instantiated from enable()), matching the existing GObject constructor restriction

Closes #15

Test plan

  • New fixture: tests/fixtures/init-time-safety@test/ with extension.js (true violation + arrow fn) and helper.js (constructor with Shell globals)
  • Assertions verify: FAIL on module-scope Main access, no FP on arrow fn definition, no FP on helper constructor
  • bash tests/run-tests.sh passes (514 assertions)

🤖 Generated with Claude Code

Two changes to check-init.py:

1. Skip arrow function definitions at module scope — the body after =>
   is lazy (not executed at load time), so Shell globals in arrow fn
   bodies are not init-time violations.

2. Restrict constructor checks to extension.js only — helper file
   constructors are runtime-only (instantiated from enable()), matching
   the existing GObject constructor restriction.

Closes #15

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@ZviBaratz ZviBaratz self-assigned this Mar 4, 2026
@ZviBaratz
Copy link
Copy Markdown
Owner Author

Code review

Found 2 issues:

  1. ARROW_FN_DEF regex is missing \s* between = and the parameter list, so the arrow function exemption never triggers. The concatenated pattern requires const fn=()=> (no spaces) but standard JS uses const fn = () =>. The PR's own fixture line const getMonitors = () => Main.layoutManager.monitors is not matched, meaning the exemption is dead code. Fix: change \s*= to \s*=\s* on line 67.

# module scope). Detect `const fn = (...) => expr` patterns.
ARROW_FN_DEF = re.compile(
r'(?:const|let|var)\s+\w+\s*=' # variable assignment
r'(?:\([^)]*\)|\w+)' # parameter list or single param
r'\s*=>' # arrow
)

  1. The two assert_output_not_contains assertions have swapped descriptions and, more critically, the pattern init/shell-modification.*getMonitors is vacuous -- ego-lint output uses the format FAIL|init/shell-modification|extension.js:8: Shell modification outside enable() so the variable name getMonitors never appears in the output. This assertion always passes regardless of whether the exemption works, masking bug AI slop checklist item #4: false positive on _destroyed + _initializing #1 above. The other assertion's description says "arrow function" but its pattern tests helper.js. Suggested fix: swap the descriptions and replace getMonitors with extension.js:8 (or similar) to actually test the arrow function line.

assert_output_contains "fails on module-scope Main access" "\[FAIL\].*init/shell-modification"
assert_output_not_contains "no FP on arrow function definition" "init/shell-modification.*helper.js"
assert_output_not_contains "no FP on helper constructor" "init/shell-modification.*getMonitors"

🤖 Generated with Claude Code

- If this code review was useful, please react with 👍. Otherwise, react with 👎.

The arrow function exemption in check-init.py never fired due to
Python implicit string concatenation producing `=(?:` instead of
`=\s*(?:` — missing whitespace match between assignment and params.

Also fix test assertions: swap descriptions to match their patterns,
and replace vacuous `getMonitors` pattern (source content never
appears in ego-lint output) with `extension.js:8` (actual line number).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@ZviBaratz ZviBaratz merged commit e46e2df into main Mar 4, 2026
3 checks passed
@ZviBaratz ZviBaratz deleted the fix/init-time-safety-false-positives branch March 4, 2026 13:39
@ZviBaratz ZviBaratz added ego-lint Related to ego-lint skill false-positive Rule fires incorrectly labels Mar 6, 2026
ZviBaratz added a commit that referenced this pull request Mar 10, 2026
…129)

## Summary

- Add `filter_signal_callbacks()` to `check-init.py` that excludes
signal callback body lines (`.connect()`, `.connectObject()`) from the
constructor init-time check, while still checking the `.connect()` line
itself (the receiver may be a Shell global accessed at construction
time)
- Add test fixture `init-constructor-signal@test` with both `.connect()`
and `.connectObject()` callbacks containing Shell globals, plus a direct
Shell global access that should still be flagged
- Mark tiling-shell annotation for constructor signal callback FP as
fixed

## Context

Issue #118 identified 8 FPs for `init/shell-modification` across
tiling-shell and v-shell. Research showed that 6 of 8 were already
resolved by prior PRs (#21 scoped constructor checks to extension.js,
#88 added arrow fn exemption). The remaining 2 (tiling-shell
`globalState.js` constructor signal callbacks) are addressed by this PR.

Signal callbacks are deferred — they don't execute during construction,
only when the signal fires at runtime. The `.connect()` line itself is
kept because the receiver object (e.g., `Main.panel.connect(...)`) IS
accessed immediately during construction.

## Test plan

- [x] New fixture `init-constructor-signal@test` verifies:
  - Direct Shell global in constructor is flagged (line 10)
  - `.connect()` callback body is NOT flagged (lines 15-17)
  - `.connectObject()` callback body is NOT flagged (lines 21-24)
- [x] All 653 existing test assertions pass (0 regressions)
- [x] Existing init fixtures unchanged: `init-time-safety`,
`init-helper-constructor`, `init-modification`, `constructor-gobject`

Closes #118

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
ZviBaratz pushed a commit that referenced this pull request Mar 25, 2026
🤖 I have created a release *beep* *boop*
---


##
[0.1.1](v0.1.0...v0.1.1)
(2026-03-25)


### Features

* add automated field test pipeline
([#34](#34))
([6d906d6](6d906d6))
* add parse-review-results.py for structured finding extraction
([#56](#56))
([2083bc7](2083bc7))
* **ego-lint:** add --show and --report flags for output filtering
([#111](#111))
([5fc5a6e](5fc5a6e))
* **ego-lint:** add .destroy without parentheses detection (R-LIFE-23)
([#50](#50))
([f9c035b](f9c035b))
* **ego-lint:** add compat-downgrade for version-aware deprecated API
gating
([#104](#104))
([3e00e6e](3e00e6e))
* **ego-lint:** add D-Bus connectSignal leak detection (R-LIFE-25)
([#59](#59))
([c54d848](c54d848))
* **ego-lint:** add lifecycle leak detection (R-LIFE-21, R-LIFE-22,
R-LIFE-24)
([#48](#48))
([10f6bbd](10f6bbd))
* **ego-lint:** add module-scope mutable state detection (R-LIFE-26)
([#60](#60))
([9c5d2e3](9c5d2e3))
* **ego-lint:** add module-scope prototype mutation detection
(R-LIFE-27)
([#83](#83))
([0d2df8d](0d2df8d))
* **ego-lint:** add notification urgency abuse detection (R-QUAL-36)
([#49](#49))
([546bd10](546bd10))
* **ego-lint:** detect indirect prototype mutation via function calls
([#114](#114))
([bce145b](bce145b))
* **ego-review:** eliminate redundant work by referencing ego-lint
findings
([787261a](787261a))


### Bug Fixes

* **ego-field-test:** fix IFS collapse and commit-hash fetch failures
([f4b480f](f4b480f))
* **ego-field-test:** indent multi-line expressions in json_extract
wrapper
([ae650be](ae650be))
* **ego-field-test:** resolve imports/no-gtk-in-extension false
positives
([#128](#128))
([8e39d98](8e39d98))
* **ego-lint:** add missing CSS shell classes to KNOWN_SHELL_CLASSES
([#54](#54))
([c6c61be](c6c61be))
* **ego-lint:** add replacement-pattern to R-VER rules for
version-compat suppression
([#84](#84))
([d7051cf](d7051cf))
* **ego-lint:** add skip-comments to R-LOG-03 to prevent block comment
FPs
([#132](#132))
([f138a43](f138a43))
* **ego-lint:** add src/ layout license fallback and UUID-dir skip
([#30](#30))
([9f616ec](9f616ec))
* **ego-lint:** add src/ metadata.json fallback for build-system
extensions
([#24](#24))
([145b639](145b639))
* **ego-lint:** add src/schemas/ fallback for non-standard layouts
([#80](#80))
([4d00563](4d00563))
* **ego-lint:** add version-compat suppression and calibrate severities
([#69](#69))
([5442f13](5442f13))
* **ego-lint:** anchor Gdk import pattern to avoid matching GdkPixbuf
([#101](#101))
([0e07043](0e07043))
* **ego-lint:** auto-detect and exclude vendored files from all lint
checks
([#152](#152))
([81d48cd](81d48cd))
* **ego-lint:** avoid false FAIL when enum-id precedes schema-id in
check-schema
([#148](#148))
([8399589](8399589))
* **ego-lint:** correct R-SLOP-01 provenance post-filter matching
([#85](#85))
([ff05efb](ff05efb))
* **ego-lint:** deduplicate prototype-override warnings
([#32](#32))
([431ad18](431ad18))
* **ego-lint:** detect compiled TypeScript and suppress noisy rules
([#52](#52))
([1b1b5d4](1b1b5d4))
* **ego-lint:** downgrade css/shell-class-override from FAIL to WARN
([#100](#100))
([4aa6f2f](4aa6f2f))
* **ego-lint:** downgrade css/shell-class-override from FAIL to WARN
([#112](#112))
([dc83726](dc83726))
* **ego-lint:** downgrade missing LICENSE from FAIL to WARN
([#110](#110))
([fe0eab7](fe0eab7))
* **ego-lint:** enhance GSettings signal leak detection for helper
classes
([#87](#87))
([21f7390](21f7390))
* **ego-lint:** exclude preferences/ dirs from gsettings-signal-leak
([#102](#102))
([60b15d3](60b15d3))
* **ego-lint:** exclude service/ daemon code from extension-specific
checks
([#147](#147))
([2962779](2962779))
* **ego-lint:** exclude service/ dir from R-DEPR-06 and R-DEPR-10
([#99](#99))
([ed40e47](ed40e47))
* **ego-lint:** exempt isLocked guard from impossible-state and
session-modes checks
([#31](#31))
([69c6254](69c6254))
* **ego-lint:** expand CSS shell-class list from GNOME Shell SCSS source
([#57](#57))
([311da35](311da35))
* **ego-lint:** expand init/shell-modification and constructor-resources
exemptions
([#88](#88))
([14d0ca3](14d0ca3))
* **ego-lint:** expand R-SLOP-24 guard for system schema identifiers
([#139](#139))
([1a58b06](1a58b06))
* **ego-lint:** expand R-SLOP-24 guard for system schemas and multi-line
constructors
([#135](#135))
([27787a6](27787a6))
* **ego-lint:** expand R-SLOP-38 guard for domain-specific identifiers
([#141](#141))
([4aaf615](4aaf615))
* **ego-lint:** handle async arrow functions and GObject branch in
check-init.py
([#55](#55))
([9d6654a](9d6654a))
* **ego-lint:** make R-LIFE-25 bare connectSignal always FAIL regardless
of disconnectSignal elsewhere
([#91](#91))
([05523f9](05523f9))
* **ego-lint:** narrow R-LIFE-25 auto-cleanup to D-Bus-specific
disconnectSignal
([#65](#65))
([8263cbc](8263cbc))
* **ego-lint:** propagate strip_comments newline preservation to all
scripts
([#61](#61))
([4178a5a](4178a5a))
* **ego-lint:** recognize alternative cleanup methods in resource
tracking
([#130](#130))
([bbc0f51](bbc0f51))
* **ego-lint:** recognize array-based signal ID storage in
gsettings-signal-leak
([#64](#64))
([99c89c0](99c89c0))
* **ego-lint:** recognize full GPL license text in license check
([#127](#127))
([38cbb59](38cbb59))
* **ego-lint:** reduce false positives for R-SLOP-35, R-SEC-03,
R-SLOP-13
([#123](#123))
([860fc0b](860fc0b))
* **ego-lint:** reduce init-time safety false positives
([#21](#21))
([e46e2df](e46e2df))
* **ego-lint:** reduce Media Controls false positives
([#23](#23))
([7cea626](7cea626))
* **ego-lint:** reduce resource-tracking FPs for blur-my-shell
([#143](#143))
([99c5903](99c5903))
* **ego-lint:** reduce resource-tracking/no-destroy-method false
positives
([#86](#86))
([30bbeac](30bbeac))
* **ego-lint:** reduce signal-balance false positives
([#13](#13))
([900b133](900b133))
* **ego-lint:** reduce Tiling Shell false positives
([#22](#22))
([62c5509](62c5509))
* **ego-lint:** remove Shell.ActionMode.ALL from hallucinated-API list
([#149](#149))
([715519f](715519f))
* **ego-lint:** scope init/shell-modification to Extension class
constructors
([#140](#140))
([5e86aa6](5e86aa6))
* **ego-lint:** skip file-structure checks for compiled TypeScript
extensions
([#103](#103))
([f15ddcb](f15ddcb))
* **ego-lint:** skip JSDoc block comments in R-WEB-06 document.* check
([#150](#150))
([fc10806](fc10806))
* **ego-lint:** skip R-LOG-03 in resources/ and detect Promise-returning
methods in catch-on-sync
([#145](#145))
([7d76ee5](7d76ee5))
* **ego-lint:** skip signal callback bodies in constructor init check
([#129](#129))
([d1fa661](d1fa661))
* **ego-lint:** split R-SLOP-11 to fix false positive on
GLib.source_remove()
([#126](#126))
([a441201](a441201))
* **ego-lint:** suppress R-QUAL-28 for distinct getSettings() schema
arguments
([#81](#81))
([f8e1da7](f8e1da7))
* **ego-lint:** suppress R-VER46-01/02 for ShellVersion-guarded else
branches
([#151](#151))
([614883e](614883e))
* **ego-lint:** tighten R-SLOP-38 to reduce over-long identifier false
positives
([#82](#82))
([fe88f1b](fe88f1b))
* **ego-lint:** verify R-VER48-02 guard matches Config.PACKAGE_VERSION
([#133](#133))
([6dfeca1](6dfeca1))
* **ego-lint:** widen R-VER48-02 guard-window for distant version checks
([#53](#53))
([5ae31e0](5ae31e0))


### Documentation

* add ego-review pipeline efficiency design spec
([56bcdc3](56bcdc3))
* add field test reports for Tiling Shell and Media Controls
([#25](#25))
([f8b55f7](f8b55f7))
* add field-tests/README.md with extension catalog and results
([#67](#67))
([22fc27c](22fc27c))
* add missing skill labels and fix issue template auto-labels
([782c7b4](782c7b4))
* add resource-tracking FP reduction design spec
([a7e0a2c](a7e0a2c))
* **ego-field-test:** add 03-08 regression report, update baselines and
annotations
([c5b823e](c5b823e))
* **ego-field-test:** add version info to README extension catalog and
update to 03-08
([bdb4919](bdb4919))

---
This PR was generated with [Release
Please](https://github.com/googleapis/release-please). See
[documentation](https://github.com/googleapis/release-please#release-please).

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ego-lint Related to ego-lint skill false-positive Rule fires incorrectly

Projects

None yet

Development

Successfully merging this pull request may close these issues.

False positives in check-init.py: arrow function definitions and helper file constructors

1 participant