Documentation
¶
Overview ¶
Package exhaustive_deps implements the rslint port of upstream `react-hooks/exhaustive-deps`.
The upstream rule (facebook/react/packages/eslint-plugin-react-hooks) leans heavily on ESLint's scope manager: every Identifier reference is preresolved to a Variable, references inside a callback can be enumerated by walking the callback's Scope.references, and references across the entire file get a `from` Scope and `resolved` Variable. rslint has no scope manager. Instead we resolve identifiers via the TypeChecker (`GetSymbolAtLocation`), then look at the symbol's declaration to infer scope membership. When the TypeChecker is unavailable (gap files, parse errors), we fall back to a name-based best-effort: scan the call's enclosing function for declarations matching the identifier text, and treat anything else as external.
The diagnostics intentionally mirror upstream's wording so consumers of the JS rule can switch over without rewriting message assertions. The autofix and suggestion shapes also mirror upstream — replacing the deps-array text wholesale, or inserting a deps array after the callback when none was provided.
Index ¶
Constants ¶
This section is empty.
Variables ¶
var ExhaustiveDepsRule = rule.Rule{ Name: "react-hooks/exhaustive-deps", Run: func(ctx rule.RuleContext, options any) rule.RuleListeners { opts := parseOptions(options, ctx.Settings) sf := ctx.SourceFile tc := ctx.TypeChecker caches := &runCaches{ extraWrite: map[*ast.Symbol]bool{}, currentAssign: map[*ast.Symbol]bool{}, usedOutside: map[*ast.Node]bool{}, } report := func(node *ast.Node, msg string) { ctx.ReportNode(node, rule.RuleMessage{Description: msg}) } reportWithSuggestions := func(node *ast.Node, msg string, sugs ...rule.RuleSuggestion) { if opts.EnableDangerousAutofixThisMayCauseInfiniteLoops && len(sugs) > 0 && len(sugs[0].FixesArr) > 0 { firstFix := []rule.RuleFix{sugs[0].FixesArr[0]} if ctx.ReportNodeWithFixesAndSuggestions != nil { ctx.ReportNodeWithFixesAndSuggestions(node, rule.RuleMessage{Description: msg}, firstFix, sugs) return } ctx.ReportNodeWithFixes(node, rule.RuleMessage{Description: msg}, firstFix...) return } if len(sugs) == 0 { ctx.ReportNode(node, rule.RuleMessage{Description: msg}) return } ctx.ReportNodeWithSuggestions(node, rule.RuleMessage{Description: msg}, sugs...) } return rule.RuleListeners{ ast.KindCallExpression: func(node *ast.Node) { visitCall(ctx, sf, tc, opts, caches, node, report, reportWithSuggestions) }, } }, }
ExhaustiveDepsRule is the rslint port of upstream `react-hooks/exhaustive-deps`.
Architectural difference from upstream: instead of relying on ESLint's scope manager to enumerate `scope.references`, we walk the callback body and resolve each Identifier reference via the TypeChecker. The "pure scope" check (does this identifier resolve to a binding declared outside the callback but inside the component?) is encoded as `containsNode(componentBody, declSite) && !containsNode(callback, declSite)`. When the TypeChecker is unavailable, the same check degrades to a best-effort name walk over the component body.
Diagnostics intentionally mirror upstream wording for switchover parity.
Functions ¶
This section is empty.
Types ¶
type Options ¶
type Options struct {
AdditionalHooks *regexp.Regexp
EnableDangerousAutofixThisMayCauseInfiniteLoops bool
RequireExplicitEffectDeps bool
// AutoDepsHooks: experimental_autoDependenciesHooks. When the hook's
// bare name is in this list, missing deps are inferred ("auto deps")
// rather than being flagged. Mirrors upstream's same-named option.
AutoDepsHooks map[string]bool
}
Options holds the parsed rule options.