@@ -5,7 +5,7 @@ use std::{fmt, hash::Hash};
55use schemars:: { JsonSchema , SchemaGenerator , schema:: Schema } ;
66use serde:: { Deserialize , Serialize } ;
77
8- use oxc_semantic:: SymbolId ;
8+ use oxc_semantic:: { AstTypesBitset , SymbolId } ;
99
1010use crate :: {
1111 AstNode , FixKind ,
@@ -66,6 +66,16 @@ pub trait Rule: Sized + Default + fmt::Debug {
6666 }
6767}
6868
69+ pub trait RuleRunner : Rule {
70+ /// `AstType`s that rule acts on
71+ const NODE_TYPES : & AstTypesBitset ;
72+ /// `true` if codegen can't figure out what node types rule acts on
73+ const ANY_NODE_TYPE : bool ;
74+
75+ fn types_info ( & self ) -> ( & ' static AstTypesBitset , bool ) {
76+ ( Self :: NODE_TYPES , Self :: ANY_NODE_TYPE )
77+ }
78+ }
6979pub trait RuleMeta {
7080 const NAME : & ' static str ;
7181
@@ -279,6 +289,8 @@ impl From<RuleFixMeta> for FixKind {
279289
280290#[ cfg( test) ]
281291mod test {
292+ use crate :: { RuleMeta , RuleRunner } ;
293+
282294 use super :: RuleCategory ;
283295
284296 #[ test]
@@ -322,4 +334,83 @@ mod test {
322334 assert_eq ! ( de, RuleCategory :: try_from( input) . unwrap( ) , "{input}" ) ;
323335 }
324336 }
337+
338+ #[ test]
339+ fn test_rule_runner_impls ( ) {
340+ use crate :: rules:: * ;
341+ use oxc_ast:: AstType :: * ;
342+
343+ // The RuleRunner code is automatically generated by the `oxc_linter_codegen` crate.
344+ // This is set of manually verified test cases to ensure that the generated code
345+ // is working as expected and is not skipping rules for nodes that actually should be linted.
346+ assert_rule_runs_on_node_types ( & eslint:: no_debugger:: NoDebugger , & [ DebuggerStatement ] ) ;
347+ assert_rule_runs_on_node_types ( & eslint:: no_with:: NoWith , & [ WithStatement ] ) ;
348+ assert_rule_runs_on_node_types (
349+ & eslint:: curly:: Curly :: default ( ) ,
350+ & [
351+ IfStatement ,
352+ ForStatement ,
353+ ForInStatement ,
354+ ForOfStatement ,
355+ WhileStatement ,
356+ DoWhileStatement ,
357+ ] ,
358+ ) ;
359+ assert_rule_runs_on_node_types (
360+ & eslint:: default_case:: DefaultCase :: default ( ) ,
361+ & [ SwitchStatement ] ,
362+ ) ;
363+ assert_rule_runs_on_node_types (
364+ & import:: no_mutable_exports:: NoMutableExports ,
365+ & [ ExportNamedDeclaration , ExportDefaultDeclaration ] ,
366+ ) ;
367+ assert_rule_runs_on_node_types (
368+ & jest:: prefer_jest_mocked:: PreferJestMocked ,
369+ & [ TSAsExpression , TSTypeAssertion ] ,
370+ ) ;
371+ assert_rule_runs_on_node_types (
372+ & jsx_a11y:: anchor_is_valid:: AnchorIsValid :: default ( ) ,
373+ & [ JSXElement ] ,
374+ ) ;
375+ assert_rule_runs_on_node_types (
376+ & nextjs:: no_head_element:: NoHeadElement ,
377+ & [ JSXOpeningElement ] ,
378+ ) ;
379+ assert_rule_runs_on_node_types (
380+ & oxc:: bad_bitwise_operator:: BadBitwiseOperator ,
381+ & [ BinaryExpression , AssignmentExpression ] ,
382+ ) ;
383+ assert_rule_runs_on_node_types (
384+ & promise:: prefer_await_to_callbacks:: PreferAwaitToCallbacks ,
385+ & [ CallExpression , Function , ArrowFunctionExpression ] ,
386+ ) ;
387+ assert_rule_runs_on_node_types (
388+ & react:: button_has_type:: ButtonHasType :: default ( ) ,
389+ & [ JSXOpeningElement , CallExpression ] ,
390+ ) ;
391+ assert_rule_runs_on_node_types (
392+ & typescript:: ban_types:: BanTypes ,
393+ & [ TSTypeReference , TSTypeLiteral ] ,
394+ ) ;
395+ assert_rule_runs_on_node_types (
396+ & unicorn:: explicit_length_check:: ExplicitLengthCheck :: default ( ) ,
397+ & [ StaticMemberExpression ] ,
398+ ) ;
399+ }
400+
401+ fn assert_rule_runs_on_node_types < R : RuleMeta + RuleRunner > (
402+ rule : & R ,
403+ node_types : & [ oxc_ast:: AstType ] ,
404+ ) {
405+ let ( types, _) = rule. types_info ( ) ;
406+ assert ! ( !R :: ANY_NODE_TYPE , "{} should not have ANY_NODE_TYPE set to true" , R :: NAME ) ;
407+ for node_type in node_types {
408+ assert ! (
409+ types. has( * node_type) ,
410+ "{}: missing {:?} in its NODE_TYPES (this means it will incorrectly skip nodes it needs to lint)" ,
411+ R :: NAME ,
412+ node_type
413+ ) ;
414+ }
415+ }
325416}
0 commit comments