feat(linter): react/exhaustive-deps (#7151)

Firstly, a massive thanks to @alisnic for starting this (incredibly complicated) lint rule in https://github.com/oxc-project/oxc/pull/2637 !

still a draft. current state:

3x false positives (all todo with refs)

3x false negatives (TS will catch these
13x false negatvies todo with refs
1x false negative TODO

closes  #2637
relates to #2174
This commit is contained in:
camc314 2024-11-12 11:42:47 +00:00
parent c5f4ee7a82
commit 3dcac1ae0b
7 changed files with 5750 additions and 1 deletions

View file

@ -1,6 +1,6 @@
use oxc_ast::{ast::BindingIdentifier, AstKind};
use oxc_ecmascript::ToBoolean;
use oxc_semantic::{AstNode, IsGlobalReference, NodeId, Semantic, SymbolId};
use oxc_semantic::{AstNode, IsGlobalReference, NodeId, ReferenceId, Semantic, SymbolId};
use oxc_span::{GetSpan, Span};
use oxc_syntax::operator::{AssignmentOperator, BinaryOperator, LogicalOperator, UnaryOperator};
@ -282,6 +282,15 @@ pub fn get_declaration_of_variable<'a, 'b>(
Some(semantic.nodes().get_node(symbol_table.get_declaration(symbol_id)))
}
pub fn get_declaration_from_reference_id<'a, 'b>(
reference_id: ReferenceId,
semantic: &'b Semantic<'a>,
) -> Option<&'b AstNode<'a>> {
let symbol_table = semantic.symbols();
let symbol_id = symbol_table.get_reference(reference_id).symbol_id()?;
Some(semantic.nodes().get_node(symbol_table.get_declaration(symbol_id)))
}
pub fn get_symbol_id_of_variable(
ident: &IdentifierReference,
semantic: &Semantic<'_>,

View file

@ -505,6 +505,10 @@ mod node {
pub mod no_new_require;
}
mod react_hooks {
pub mod exhaustive_deps;
}
oxc_macros::declare_all_lint_rules! {
// import::no_deprecated,
// import::no_unused_modules,
@ -956,4 +960,5 @@ oxc_macros::declare_all_lint_rules! {
vitest::prefer_to_be_object,
vitest::prefer_to_be_truthy,
vitest::require_local_test_context_for_concurrent_snapshots,
react_hooks::exhaustive_deps,
}

File diff suppressed because it is too large Load diff

View file

@ -206,6 +206,7 @@ impl Runtime {
// The semantic model is not built at this stage.
let semantic_builder = SemanticBuilder::new()
.with_cfg(true)
.with_scope_tree_child_ids(true)
.with_build_jsdoc(true)
.with_check_syntax_error(check_syntax_errors)
.build_module_record(path, &ret.program);

File diff suppressed because it is too large Load diff

View file

@ -289,6 +289,21 @@ impl ScopeTree {
&self.child_ids[scope_id]
}
pub fn iter_all_child_ids(&self, scope_id: ScopeId) -> impl Iterator<Item = ScopeId> + '_ {
let mut stack = self.child_ids[scope_id].clone();
let child_ids: &IndexVec<ScopeId, Vec<ScopeId>> = &self.child_ids;
std::iter::from_fn(move || {
if let Some(scope_id) = stack.pop() {
if let Some(children) = child_ids.get(scope_id) {
stack.extend(children.iter().copied());
}
Some(scope_id)
} else {
None
}
})
}
/// Get a mutable reference to a scope's children
#[inline]
pub fn get_child_ids_mut(&mut self, scope_id: ScopeId) -> &mut Vec<ScopeId> {

View file

@ -34,6 +34,7 @@ fn bench_linter(criterion: &mut Criterion) {
let path = Path::new("");
let semantic_ret = SemanticBuilder::new()
.with_build_jsdoc(true)
.with_scope_tree_child_ids(true)
.with_cfg(true)
.build_module_record(path, &ret.program)
.build(&ret.program);