feat(compiler): SYN010 — warn on setTimeout/setInterval/queueMicrotask calls that defer side effects (?bs 0.7+)#151
Merged
Conversation
There was a problem hiding this comment.
Pull request overview
Adds a new compiler warning (SYN010) to flag setTimeout(...), setInterval(...), and queueMicrotask(...) calls in function bodies at ?bs 0.7+, since deferred callbacks can perform side effects outside the caller-visible capability surface. The PR also wires the new code through the error-code registry, MCP explain output, docs, and test suites.
Changes:
- Implement SYN010 detection in
syn-checkwithunsafe {}/unsafe fnsuppression and member-call exclusion. - Register SYN010 in the compiler error-code registry and MCP explanations, and expose it via docs/tooling lists.
- Add a dedicated SYN010 test suite and update “known codes”/allowlists across packages.
Reviewed changes
Copilot reviewed 8 out of 8 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| README.md | Adds SYN010 to the MCP explain tool’s supported diagnostic code list. |
| packages/mcp/tests/server.test.ts | Adds SYN010 to the MCP server test’s KNOWN_CODES list. |
| packages/mcp/src/explanations.ts | Adds a long-form SYN010 explanation plus fails/passes examples. |
| packages/compiler/tests/syn010-check.test.ts | Introduces SYN010 behavior tests (version gating, unsafe suppression, member-call exclusion, etc.). |
| packages/compiler/tests/error-codes.test.ts | Adds SYN010 to the exhaustive error-code allowlist. |
| packages/compiler/src/passes/syn-check.ts | Implements SYN010 token-based detection and updates module header comments. |
| packages/compiler/src/error-codes.ts | Registers SYN010 (rule/idiom/rewrite/example) in the compiler’s error-code registry. |
| AGENTS.md | Adds SYN010 to the diagnostic table for agent/tooling reference. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
7 tasks
marcelofarias
pushed a commit
that referenced
this pull request
Jun 11, 2026
…he module capability model (?bs 0.7+)
- Adds SYN011: fires when a fn body calls import(specifier) — the dynamic import form
- Dynamic imports load a module at runtime whose capability surface is unbounded:
CAP001 cannot see what the dynamically loaded module does; the fn header cannot
declare capabilities it doesn't know about at compile time
- import.meta (followed by `.`) is excluded — it's a property, not a call
- Suppressed inside `unsafe "reason" { }` blocks and `unsafe "reason" fn` bodies
- 10 tests: bare/template-literal/awaited forms, unsafe suppression, import.meta
exclusion, version gate, severity, multi-call count, bare reference non-fire
- Should be merged after SYN010 (PR #151) lands — SYN011 is the next available code
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…k calls in fn bodies (?bs 0.7+)
- Adds **SYN010**: a new warning that fires when a fn body calls `setTimeout(...)`,
`setInterval(...)`, or `queueMicrotask(...)` at `?bs 0.7+`
- These globals schedule callbacks that run after the fn returns — any effects inside
those callbacks are invisible to callers: no capability declaration, no `writes {}`
label, and no `throws {}` entry can cover them
- Same bypass class as `fetch` (SYN007) and `WebSocket` (SYN008): real side effects
that sidestep the declared capability surface, but deferred rather than immediate
- Suppressed inside `unsafe "reason" { }` blocks and `unsafe "reason" fn` bodies
- Member calls (`obj.setTimeout(...)`) and bare references (without `(`) are excluded
- Note: should be merged after PR #150 (SYN009) lands; SYN010 is the next available code
Changes:
- error-codes.ts: add SYN010 entry with rule/idiom/rewrite/example
- syn-check.ts: add SYN010 detection; add SYN006 + SYN010 to module header comment;
fix outdated unsafe-fn skip comment
- tests/syn010-check.test.ts: 12 tests covering all three globals, optional-call form,
severity, version gating, unsafe suppression, member-call exclusion, bare reference
- tests/error-codes.test.ts: add SYN010 to exhaustive allowlist
- mcp/src/explanations.ts: add SYN010 long-form explanation with fails/passes examples
- mcp/tests/server.test.ts: add SYN010 to KNOWN_CODES list
- AGENTS.md: add SYN010 row to diagnostic table
- README.md: add SYN010 to explain tool code list
…name and reason string - Hoist TIMER_GLOBALS to module scope (was re-created per fn, wasteful in hot pass) - Rename `fn` parameter to `callback` in unsafe-fn test (fn is a reserved keyword) - Fix unsafe reason string: "suspends execution" → "schedules deferred effect" (consistent with idiom) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…eclarations and explanation codes
- syn-check: exclude function declarations named setTimeout/setInterval/queueMicrotask
(prev significant token is `function`) — these are definitions, not calls
- syn-check: exclude object/class method shorthands where after closing `)` is `{` or `:`
- tests: add two tests covering function-declaration and object-method-shorthand exclusions
- explanations: remove SYN007/SYN008 code references in SYN010 body; reference by name instead
to avoid dangling code cross-references until those PRs land
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…yword declarations
- syn-check.ts: also skip `fn setTimeout(...) -> void {}` botscript-style nested
function declarations (prev token is `keyword`/`fn`), not just JS `function`
declarations — prevents false positives on nested fn declarations inside fn bodies
- syn010-check.test.ts: add test asserting SYN010 does NOT fire on
`fn setTimeout(delay: number) -> void { }` inside a fn body
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
9234bd2 to
6847365
Compare
marcelofarias
pushed a commit
that referenced
this pull request
Jun 12, 2026
…he module capability model (?bs 0.7+)
- Adds SYN011: fires when a fn body calls import(specifier) — the dynamic import form
- Dynamic imports load a module at runtime whose capability surface is unbounded:
CAP001 cannot see what the dynamically loaded module does; the fn header cannot
declare capabilities it doesn't know about at compile time
- import.meta (followed by `.`) is excluded — it's a property, not a call
- Suppressed inside `unsafe "reason" { }` blocks and `unsafe "reason" fn` bodies
- 10 tests: bare/template-literal/awaited forms, unsafe suppression, import.meta
exclusion, version gate, severity, multi-call count, bare reference non-fire
- Should be merged after SYN010 (PR #151) lands — SYN011 is the next available code
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
setTimeout(...),setInterval(...), orqueueMicrotask(...)at?bs 0.7+writes {}label, and nothrows {}entry can cover themfetch(SYN007) andWebSocket(SYN008): real side effects that sidestep the declared capability surface, but deferred rather than immediateunsafe "reason" { }blocks andunsafe "reason" fnbodiesChanges
packages/compiler/src/error-codes.tspackages/compiler/src/passes/syn-check.tspackages/compiler/tests/syn010-check.test.tspackages/compiler/tests/error-codes.test.tspackages/mcp/src/explanations.tspackages/mcp/tests/server.test.tsAGENTS.mdREADME.mdexplaintool code listTest plan
pnpm -r build && pnpm test— 1161 tests passsetTimeout(),setInterval(),queueMicrotask()setTimeout?.()unsafe {}blocks andunsafe fnbodiesobj.setTimeout()(member call on a local)setTimeoutreference (not called)?bs 0.7🤖 Generated with Claude Code