Skip to content

feat(compiler): SYN011 — warn on dynamic import() calls that bypass the module capability model (?bs 0.7+)#156

Merged
marcelofarias merged 2 commits into
mainfrom
botkowski/syn011-v2
Jun 13, 2026
Merged

feat(compiler): SYN011 — warn on dynamic import() calls that bypass the module capability model (?bs 0.7+)#156
marcelofarias merged 2 commits into
mainfrom
botkowski/syn011-v2

Conversation

@marcelofarias

Copy link
Copy Markdown
Owner

Summary

  • Adds SYN011: fires when a fn body calls import(specifier) — the dynamic import form (?bs 0.7+)
  • Dynamic imports load a module at runtime whose capability surface is unbounded: CAP001 only checks stdlib namespace calls, not dynamic module loads
  • Integrated into the single dispatch loop from PR perf(syn-check): consolidate 6 per-check token scans into a single dispatch loop #155 — no extra O(n) per-body scan
  • Suppressed inside unsafe {} blocks and unsafe "reason" fn bodies

Detection logic

import token (kind=ident) not preceded by ./?., followed by ( or ?.(.

Excluded:

  • import.meta property accesses (followed by .)
  • Object/class method shorthands: { import(x) { ... } } (parens followed by {)
  • fn import(...) botscript declarations (checks prev.kind === "keyword" for fn)

Changes

File Change
packages/compiler/src/error-codes.ts Add SYN011 entry (rule, idiom, rewrite, example)
packages/compiler/src/passes/syn-check.ts Add SYN011 as case "import": in the dispatch switch; add SYN011 to header comment
packages/compiler/tests/syn011-check.test.ts 13 tests: call forms, unsafe suppression, import.meta exclusion, method shorthand exclusion, fn declaration exclusion, optional-call form, version gate, severity, multi-call count
packages/compiler/tests/error-codes.test.ts Add SYN011 to exhaustive allowlist
packages/mcp/src/explanations.ts Add SYN011 long-form explanation (no forward references to SYN007/008/009 since those aren't in main yet)
packages/mcp/tests/server.test.ts Add SYN011 to KNOWN_CODES list
AGENTS.md Add SYN011 row to diagnostic table
README.md Add SYN011 to explain tool code list

Test plan

  • pnpm -r build && pnpm test — 1250 tests pass (43 test files)
  • SYN011 fires on bare import(specifier)
  • SYN011 fires on template literal import(`./module`)
  • SYN011 fires on const mod = await import(path)
  • SYN011 fires on optional-call form import?.(path)
  • SYN011 suppressed inside unsafe {} blocks and unsafe fn bodies
  • SYN011 does NOT fire on import.meta.url (property access, not a call)
  • SYN011 does NOT fire on object method shorthands { import(x) {} }
  • SYN011 does NOT fire on fn import(specifier: string) -> any declarations
  • SYN011 does NOT fire below ?bs 0.7

Closes #153 (supersedes the conflicted branch botkowski/syn011-dynamic-import which was incompatible with the #155 refactor).

🤖 Generated with Claude Code

…he module capability model (?bs 0.7+)

Integrates SYN011 into the single dispatch loop introduced in #155 (no extra per-body scan).

Detection: `import` token (kind=ident) not preceded by `.`/`?.`, followed by `(` or `?.(`.
Excluded: `import.meta` property accesses (followed by `.`), object method shorthands,
`fn import(...)` declarations (checks `kind==='keyword'` for the preceding `fn` token).
Suppressed inside `unsafe {}` blocks and `unsafe "reason" fn` bodies.

Closes #153 (replaces the conflicted branch botkowski/syn011-dynamic-import).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a new compiler diagnostic SYN011 to warn when a function body uses dynamic import() (including optional-call import?.()), since runtime module loads bypass the static module capability model; this is integrated into the existing single-pass syn-check dispatch loop and documented/tested across the compiler + MCP surfaces.

Changes:

  • Add SYN011 error-code registry entry plus MCP long-form explanation text.
  • Implement SYN011 detection in passSynCheck (with unsafe suppression, exclusions for import.meta, member calls, and method shorthands).
  • Add comprehensive test coverage and update public docs/lists that enumerate known diagnostic codes.

Reviewed changes

Copilot reviewed 8 out of 8 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
README.md Adds SYN011 to the list of codes supported by the explain tool.
AGENTS.md Adds SYN011 to the diagnostic table.
packages/mcp/tests/server.test.ts Registers SYN011 in the MCP server’s known-codes test list.
packages/mcp/src/explanations.ts Adds a long-form MCP explanation entry for SYN011.
packages/compiler/src/error-codes.ts Adds the canonical compiler registry entry for SYN011 (rule/idiom/rewrite/example).
packages/compiler/src/passes/syn-check.ts Implements token-scan detection and warning emission for SYN011.
packages/compiler/tests/syn011-check.test.ts Adds new test suite covering positive/negative cases, suppression, and version gating for SYN011.
packages/compiler/tests/error-codes.test.ts Updates the exhaustive allowlist to include SYN011.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +541 to +549
if (callTok11.matchedAt !== undefined) {
const afterCloseIdx = nextSignificant(tokens, callTok11.matchedAt + 1);
const afterClose = tokens[afterCloseIdx];
if (afterClose && (
(afterClose.kind === "open" && afterClose.text === "{") ||
afterClose.kind === "fatArrow" ||
(afterClose.kind === "punct" && afterClose.text === ":")
)) continue;
}
… when trailing ':' follows

The ':' exclusion (designed to skip TypeScript method signatures like
`{ import(x): T }`) was incorrectly suppressing SYN011 for `import()`
calls in ternary consequent position: `cond ? import(a) : import(b)`.

Fix: only apply the ':' suppression when the preceding token is not a
ternary '?' (kind==="question"). Adds regression test covering both
import() calls in a ternary expression.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 8 out of 8 changed files in this pull request and generated no new comments.

@marcelofarias marcelofarias merged commit f7a19e8 into main Jun 13, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants