Yes. ESLint's no-restricted-syntax rule accepts AST selectors and an optional custom message, so you can ban a function call or code pattern with a few lines of configuration and no plugin. Use it first, and write a custom rule only when selectors cannot express what you need.
What the rule does
ESLint's documentation describes no-restricted-syntax as a way to disallow user-defined syntax without creating a separate rule for every feature. For JavaScript, elements are identified by ESTree node types, and the rule also accepts AST selectors for more precise control. It can be used with any language you lint using ESLint, and the docs point to Code Explorer and the typescript-eslint Playground for viewing node types.
Options are a list. Each item is either a selector string, or an object with a selector and a message. When a message is given, ESLint uses it when reporting.
Example
// eslint.config.js
export default [
{
rules: {
"no-restricted-syntax": [
"error",
{
selector: "CallExpression[callee.name='runShell']",
message: "Do not call runShell directly. Use safeExec from the process helpers."
}
]
}
}
];
The selector reads as: a call expression whose callee name is runShell. The attribute syntax is the same family used in the documentation's own examples, such as selecting a call where the callee name is setTimeout and the argument count is not two.
Related built in option for imports
For modules, the no-restricted-imports rule lets you disallow named modules, with a custom message per module that is appended to the default error message. The docs note it applies to static imports only, not dynamic ones, which is a gap you should be aware of.
Config or custom rule
| Need | Use |
|---|---|
| Ban a function or syntax form | no-restricted-syntax |
| Ban an import | no-restricted-imports |
| Ban a call only when an argument is not a literal | A selector may do it, check, or a custom rule |
| Logic across several nodes, or checking scope | Custom rule |
| An autofix | Custom rule |
| Several options and per option documentation | Custom rule |
Steps
- Write the pattern in one sentence.
- Open Code Explorer and find the node type and fields.
- Write a selector for it.
- Add a message with the replacement.
- Test on a file that should fail and one that should not. You can still do this with ESLint's own test tooling, or with a small fixture directory and a CI check.
- Roll out as warn, then error.
Limits to remember
- A selector on a name does not follow aliases. Importing the function under another name evades it.
- Dynamic imports are not covered by the import rule.
- A long list of selectors in one config is hard to document and own. If you pass a handful, move them into a dedicated custom rule with its own README.
- Messages are only as useful as you make them, so name the replacement.
Quick check
Do I need to publish a plugin to use selectors?
No. The rule is part of ESLint and is configured in your config file.
How do I find the right selector?
Paste a sample of the code into Code Explorer, read the node types and field values, and build a selector from them.
Safeguard's scanning covers dependencies and SBOMs, and its reachability analysis helps show which findings your code can actually call. You can run a free scan on a repository.
Sources
Last reviewed: 19 October 2026.