1use crate::context::Context;
4use crate::tags;
5use crate::tags::Tags;
6use crate::Program;
7use crate::ProgramRef;
8use std::cmp::Ordering;
9use std::collections::HashSet;
10
11pub mod adjacent_overload_signatures;
12pub mod ban_ts_comment;
13pub mod ban_types;
14pub mod ban_unknown_rule_code;
15pub mod ban_untagged_ignore;
16pub mod ban_untagged_todo;
17pub mod ban_unused_ignore;
18pub mod camelcase;
19pub mod constructor_super;
20pub mod default_param_last;
21pub mod eqeqeq;
22pub mod explicit_function_return_type;
23pub mod explicit_module_boundary_types;
24pub mod for_direction;
25pub mod fresh_handler_export;
26pub mod fresh_server_event_handlers;
27pub mod getter_return;
28pub mod guard_for_in;
29pub mod jsx_boolean_value;
30pub mod jsx_button_has_type;
31pub mod jsx_curly_braces;
32pub mod jsx_key;
33pub mod jsx_no_children_prop;
34pub mod jsx_no_comment_text_nodes;
35pub mod jsx_no_duplicate_props;
36pub mod jsx_no_unescaped_entities;
37pub mod jsx_no_useless_fragment;
38pub mod jsx_props_no_spread_multi;
39pub mod jsx_void_dom_elements_no_children;
40pub mod no_array_constructor;
41pub mod no_async_promise_executor;
42pub mod no_await_in_loop;
43pub mod no_await_in_sync_fn;
44pub mod no_boolean_literal_for_arguments;
45pub mod no_case_declarations;
46pub mod no_class_assign;
47pub mod no_compare_neg_zero;
48pub mod no_cond_assign;
49pub mod no_console;
50pub mod no_const_assign;
51pub mod no_constant_condition;
52pub mod no_control_regex;
53pub mod no_debugger;
54pub mod no_delete_var;
55pub mod no_deprecated_deno_api;
56pub mod no_dupe_args;
57pub mod no_dupe_class_members;
58pub mod no_dupe_else_if;
59pub mod no_dupe_keys;
60pub mod no_duplicate_case;
61pub mod no_empty;
62pub mod no_empty_character_class;
63pub mod no_empty_enum;
64pub mod no_empty_interface;
65pub mod no_empty_pattern;
66pub mod no_eval;
67pub mod no_ex_assign;
68pub mod no_explicit_any;
69pub mod no_external_imports;
70pub mod no_extra_boolean_cast;
71pub mod no_extra_non_null_assertion;
72pub mod no_fallthrough;
73pub mod no_func_assign;
74pub mod no_global_assign;
75pub mod no_implicit_declare_namespace_export;
76pub mod no_import_assertions;
77pub mod no_import_assign;
78pub mod no_import_prefix;
79pub mod no_inferrable_types;
80pub mod no_inner_declarations;
81pub mod no_invalid_regexp;
82pub mod no_invalid_triple_slash_reference;
83pub mod no_irregular_whitespace;
84pub mod no_misused_new;
85pub mod no_namespace;
86pub mod no_new_symbol;
87pub mod no_node_globals;
88pub mod no_non_null_asserted_optional_chain;
89pub mod no_non_null_assertion;
90pub mod no_obj_calls;
91pub mod no_octal;
92pub mod no_process_global;
93pub mod no_prototype_builtins;
94pub mod no_redeclare;
95pub mod no_regex_spaces;
96pub mod no_self_assign;
97pub mod no_self_compare;
98pub mod no_setter_return;
99pub mod no_shadow_restricted_names;
100pub mod no_sparse_arrays;
101pub mod no_sync_fn_in_async_fn;
102pub mod no_this_alias;
103pub mod no_this_before_super;
104pub mod no_throw_literal;
105pub mod no_top_level_await;
106pub mod no_undef;
107pub mod no_unreachable;
108pub mod no_unsafe_finally;
109pub mod no_unsafe_negation;
110pub mod no_unused_labels;
111pub mod no_unused_vars;
112pub mod no_unversioned_import;
113pub mod no_useless_rename;
114pub mod no_var;
115pub mod no_window;
116pub mod no_window_prefix;
117pub mod no_with;
118pub mod prefer_as_const;
119pub mod prefer_ascii;
120pub mod prefer_const;
121pub mod prefer_namespace_keyword;
122pub mod prefer_primordials;
123pub mod react_no_danger;
124pub mod react_no_danger_with_children;
125pub mod react_rules_of_hooks;
126pub mod require_await;
127pub mod require_yield;
128pub mod single_var_declarator;
129pub mod triple_slash_reference;
130pub mod use_isnan;
131pub mod valid_typeof;
132pub mod verbatim_module_syntax;
133
134pub trait LintRule: std::fmt::Debug + Send + Sync {
135 fn lint_program_with_ast_view<'view>(
138 &self,
139 context: &mut Context<'view>,
140 program: Program<'view>,
141 );
142
143 fn code(&self) -> &'static str;
145
146 fn tags(&self) -> Tags {
148 &[]
149 }
150
151 fn priority(&self) -> u32 {
156 0
157 }
158}
159
160pub fn program_ref(program: Program) -> ProgramRef {
162 match program {
163 Program::Module(m) => ProgramRef::Module(m.inner),
164 Program::Script(s) => ProgramRef::Script(s.inner),
165 }
166}
167
168pub fn get_all_rules() -> Vec<Box<dyn LintRule>> {
169 get_all_rules_raw()
170}
171
172pub fn recommended_rules(
174 all_rules: Vec<Box<dyn LintRule>>,
175) -> Vec<Box<dyn LintRule>> {
176 all_rules
177 .into_iter()
178 .filter(|r| r.tags().contains(&tags::RECOMMENDED))
179 .collect()
180}
181
182pub fn filtered_rules(
198 all_rules: Vec<Box<dyn LintRule>>,
199 maybe_tags: Option<Vec<String>>,
200 maybe_exclude: Option<Vec<String>>,
201 maybe_include: Option<Vec<String>>,
202) -> Vec<Box<dyn LintRule>> {
203 let tags_set =
204 maybe_tags.map(|tags| tags.into_iter().collect::<HashSet<_>>());
205
206 let mut rules = all_rules
207 .into_iter()
208 .filter(|rule| {
209 let mut passes = if let Some(tags_set) = &tags_set {
210 rule
211 .tags()
212 .iter()
213 .any(|t| tags_set.contains(&t.to_string()))
214 } else {
215 true
216 };
217
218 if let Some(includes) = &maybe_include {
219 if includes.contains(&rule.code().to_owned()) {
220 passes |= true;
221 }
222 }
223
224 if let Some(excludes) = &maybe_exclude {
225 if excludes.contains(&rule.code().to_owned()) {
226 passes &= false;
227 }
228 }
229
230 passes
231 })
232 .collect::<Vec<_>>();
233
234 rules.sort_by_key(|r| r.code());
235
236 rules
237}
238
239pub(crate) fn sort_rules_by_priority(rules: &mut [Box<dyn LintRule>]) {
241 rules.sort_by(|rule1, rule2| {
242 let priority_cmp = rule1.priority().cmp(&rule2.priority());
243
244 if priority_cmp == Ordering::Equal {
245 return rule1.code().cmp(rule2.code());
246 }
247
248 priority_cmp
249 });
250}
251
252fn get_all_rules_raw() -> Vec<Box<dyn LintRule>> {
253 vec![
254 Box::new(adjacent_overload_signatures::AdjacentOverloadSignatures),
255 Box::new(ban_ts_comment::BanTsComment),
256 Box::new(ban_types::BanTypes),
257 Box::new(ban_unknown_rule_code::BanUnknownRuleCode),
258 Box::new(ban_untagged_ignore::BanUntaggedIgnore),
259 Box::new(ban_untagged_todo::BanUntaggedTodo),
260 Box::new(ban_unused_ignore::BanUnusedIgnore),
261 Box::new(camelcase::Camelcase),
262 Box::new(constructor_super::ConstructorSuper),
263 Box::new(default_param_last::DefaultParamLast),
264 Box::new(eqeqeq::Eqeqeq),
265 Box::new(explicit_function_return_type::ExplicitFunctionReturnType),
266 Box::new(explicit_module_boundary_types::ExplicitModuleBoundaryTypes),
267 Box::new(for_direction::ForDirection),
268 Box::new(fresh_handler_export::FreshHandlerExport),
269 Box::new(fresh_server_event_handlers::FreshServerEventHandlers),
270 Box::new(getter_return::GetterReturn),
271 Box::new(guard_for_in::GuardForIn),
272 Box::new(jsx_boolean_value::JSXBooleanValue),
273 Box::new(jsx_button_has_type::JSXButtonHasType),
274 Box::new(jsx_curly_braces::JSXCurlyBraces),
275 Box::new(jsx_key::JSXKey),
276 Box::new(jsx_no_children_prop::JSXNoChildrenProp),
277 Box::new(jsx_no_comment_text_nodes::JSXNoCommentTextNodes),
278 Box::new(jsx_no_duplicate_props::JSXNoDuplicateProps),
279 Box::new(jsx_no_unescaped_entities::JSXNoUnescapedEntities),
280 Box::new(jsx_no_useless_fragment::JSXNoUselessFragment),
281 Box::new(jsx_props_no_spread_multi::JSXPropsNoSpreadMulti),
282 Box::new(jsx_void_dom_elements_no_children::JSXVoidDomElementsNoChildren),
283 Box::new(no_array_constructor::NoArrayConstructor),
284 Box::new(no_async_promise_executor::NoAsyncPromiseExecutor),
285 Box::new(no_await_in_loop::NoAwaitInLoop),
286 Box::new(no_await_in_sync_fn::NoAwaitInSyncFn),
287 Box::new(no_boolean_literal_for_arguments::NoBooleanLiteralForArguments),
288 Box::new(no_case_declarations::NoCaseDeclarations),
289 Box::new(no_class_assign::NoClassAssign),
290 Box::new(no_compare_neg_zero::NoCompareNegZero),
291 Box::new(no_cond_assign::NoCondAssign),
292 Box::new(no_console::NoConsole),
293 Box::new(no_const_assign::NoConstAssign),
294 Box::new(no_constant_condition::NoConstantCondition),
295 Box::new(no_control_regex::NoControlRegex),
296 Box::new(no_debugger::NoDebugger),
297 Box::new(no_delete_var::NoDeleteVar),
298 Box::new(no_deprecated_deno_api::NoDeprecatedDenoApi),
299 Box::new(no_dupe_args::NoDupeArgs),
300 Box::new(no_dupe_class_members::NoDupeClassMembers),
301 Box::new(no_dupe_else_if::NoDupeElseIf),
302 Box::new(no_dupe_keys::NoDupeKeys),
303 Box::new(no_duplicate_case::NoDuplicateCase),
304 Box::new(no_empty::NoEmpty),
305 Box::new(no_empty_character_class::NoEmptyCharacterClass),
306 Box::new(no_empty_enum::NoEmptyEnum),
307 Box::new(no_empty_interface::NoEmptyInterface),
308 Box::new(no_empty_pattern::NoEmptyPattern),
309 Box::new(no_eval::NoEval),
310 Box::new(no_ex_assign::NoExAssign),
311 Box::new(no_explicit_any::NoExplicitAny),
312 Box::new(no_external_imports::NoExternalImport),
313 Box::new(no_extra_boolean_cast::NoExtraBooleanCast),
314 Box::new(no_extra_non_null_assertion::NoExtraNonNullAssertion),
315 Box::new(no_fallthrough::NoFallthrough),
316 Box::new(no_func_assign::NoFuncAssign),
317 Box::new(no_global_assign::NoGlobalAssign),
318 Box::new(
319 no_implicit_declare_namespace_export::NoImplicitDeclareNamespaceExport,
320 ),
321 Box::new(no_import_assertions::NoImportAssertions),
322 Box::new(no_import_assign::NoImportAssign),
323 Box::new(no_import_prefix::NoImportPrefix),
324 Box::new(no_inferrable_types::NoInferrableTypes),
325 Box::new(no_inner_declarations::NoInnerDeclarations),
326 Box::new(no_invalid_regexp::NoInvalidRegexp),
327 Box::new(no_invalid_triple_slash_reference::NoInvalidTripleSlashReference),
328 Box::new(no_irregular_whitespace::NoIrregularWhitespace),
329 Box::new(no_misused_new::NoMisusedNew),
330 Box::new(no_namespace::NoNamespace),
331 Box::new(no_new_symbol::NoNewSymbol),
332 Box::new(no_node_globals::NoNodeGlobals),
333 Box::new(
334 no_non_null_asserted_optional_chain::NoNonNullAssertedOptionalChain,
335 ),
336 Box::new(no_non_null_assertion::NoNonNullAssertion),
337 Box::new(no_obj_calls::NoObjCalls),
338 Box::new(no_octal::NoOctal),
339 Box::new(no_process_global::NoProcessGlobal),
340 Box::new(no_prototype_builtins::NoPrototypeBuiltins),
341 Box::new(no_redeclare::NoRedeclare),
342 Box::new(no_regex_spaces::NoRegexSpaces),
343 Box::new(no_self_assign::NoSelfAssign),
344 Box::new(no_self_compare::NoSelfCompare),
345 Box::new(no_setter_return::NoSetterReturn),
346 Box::new(no_shadow_restricted_names::NoShadowRestrictedNames),
347 Box::new(no_sparse_arrays::NoSparseArrays),
348 Box::new(no_sync_fn_in_async_fn::NoSyncFnInAsyncFn),
349 Box::new(no_this_alias::NoThisAlias),
350 Box::new(no_this_before_super::NoThisBeforeSuper),
351 Box::new(no_throw_literal::NoThrowLiteral),
352 Box::new(no_top_level_await::NoTopLevelAwait),
353 Box::new(no_undef::NoUndef),
354 Box::new(no_unreachable::NoUnreachable),
355 Box::new(no_unsafe_finally::NoUnsafeFinally),
356 Box::new(no_unsafe_negation::NoUnsafeNegation),
357 Box::new(no_unused_labels::NoUnusedLabels),
358 Box::new(no_unused_vars::NoUnusedVars),
359 Box::new(no_unversioned_import::NoUnversionedImport),
360 Box::new(no_useless_rename::NoUselessRename),
361 Box::new(no_var::NoVar),
362 Box::new(no_window::NoWindow),
363 Box::new(no_window_prefix::NoWindowPrefix),
364 Box::new(no_with::NoWith),
365 Box::new(prefer_as_const::PreferAsConst),
366 Box::new(prefer_ascii::PreferAscii),
367 Box::new(prefer_const::PreferConst),
368 Box::new(prefer_namespace_keyword::PreferNamespaceKeyword),
369 Box::new(prefer_primordials::PreferPrimordials),
370 Box::new(react_no_danger::ReactNoDanger),
371 Box::new(react_no_danger_with_children::ReactNoDangerWithChildren),
372 Box::new(react_rules_of_hooks::ReactRulesOfHooks),
373 Box::new(require_await::RequireAwait),
374 Box::new(require_yield::RequireYield),
375 Box::new(single_var_declarator::SingleVarDeclarator),
376 Box::new(triple_slash_reference::TripleSlashReference),
377 Box::new(use_isnan::UseIsNaN),
378 Box::new(valid_typeof::ValidTypeof),
379 Box::new(verbatim_module_syntax::VerbatimModuleSyntax),
380 ]
381}
382
383#[cfg(test)]
384mod tests {
385 use std::sync::Arc;
386
387 use crate::tags;
388
389 use super::*;
390
391 #[test]
392 fn recommended_rules_sorted_alphabetically() {
393 let mut sorted_recommended_rules = recommended_rules(get_all_rules());
394 sorted_recommended_rules.sort_by_key(|r| r.code());
395
396 for (sorted, unsorted) in sorted_recommended_rules
397 .iter()
398 .zip(recommended_rules(get_all_rules()).iter())
399 {
400 assert_eq!(sorted.code(), unsorted.code());
401 }
402 }
403
404 #[test]
405 fn all_rules_sorted_alphabetically() {
406 let mut all_rules = get_all_rules_raw();
407 all_rules.sort_by_key(|r| r.code());
408 for (sorted, unsorted) in all_rules.iter().zip(get_all_rules_raw()) {
409 assert_eq!(sorted.code(), unsorted.code());
410 }
411 }
412
413 #[test]
414 fn test_get_filtered_rules() {
415 let rules = filtered_rules(
417 get_all_rules(),
418 Some(vec!["recommended".to_string()]),
419 None,
420 None,
421 );
422 for (r, rr) in rules.iter().zip(recommended_rules(get_all_rules()).iter()) {
423 assert_eq!(r.code(), rr.code());
424 }
425
426 let rules = filtered_rules(
428 get_all_rules(),
429 Some(vec!["recommended".to_string()]),
430 None,
431 Some(vec!["ban-untagged-todo".to_string()]),
432 );
433 assert_eq!(rules.len(), recommended_rules(get_all_rules()).len() + 1);
434
435 let rules = filtered_rules(
437 get_all_rules(),
438 Some(vec!["recommended".to_string()]),
439 Some(vec!["ban-ts-comment".to_string()]),
440 Some(vec!["ban-untagged-todo".to_string()]),
441 );
442 assert_eq!(rules.len(), recommended_rules(get_all_rules()).len());
443
444 let rules = filtered_rules(get_all_rules(), Some(vec![]), None, None);
446 assert!(rules.is_empty());
447
448 let rules = filtered_rules(
450 get_all_rules(),
451 Some(vec![]),
452 None,
453 Some(vec!["ban-untagged-todo".to_string()]),
454 );
455 assert_eq!(rules.len(), 1);
456
457 let rules = filtered_rules(
459 get_all_rules(),
460 Some(vec![]),
461 Some(vec!["ban-untagged-todo".to_string()]),
462 Some(vec!["ban-untagged-todo".to_string()]),
463 );
464 assert_eq!(rules.len(), 0);
465
466 let rules = filtered_rules(
468 get_all_rules(),
469 Some(vec![]),
470 Some(vec![
471 "ban-untagged-todo".to_string(),
472 "ban-ts-comment".to_string(),
473 ]),
474 Some(vec![
475 "ban-untagged-todo".to_string(),
476 "ban-ts-comment".to_string(),
477 "no-const-assign".to_string(),
478 "no-throw-literal".to_string(),
479 ]),
480 );
481 assert_eq!(rules.len(), 2);
482 assert_eq!(rules[0].code(), "no-const-assign");
483 assert_eq!(rules[1].code(), "no-throw-literal");
484 }
485
486 #[test]
487 fn ensure_lint_rules_are_sharable_across_threads() {
488 use std::thread::spawn;
489
490 let rules = Arc::new(recommended_rules(get_all_rules()));
491 let handles = (0..2)
492 .map(|_| {
493 let rules = Arc::clone(&rules);
494 spawn(move || {
495 for rule in rules.iter() {
496 assert!(rule.tags().contains(&tags::RECOMMENDED));
497 }
498 })
499 })
500 .collect::<Vec<_>>();
501
502 for handle in handles {
503 handle.join().unwrap();
504 }
505 }
506
507 #[test]
508 fn sort_by_priority() {
509 let mut rules: Vec<Box<dyn LintRule>> = vec![
510 Box::new(ban_unknown_rule_code::BanUnknownRuleCode),
511 Box::new(ban_unused_ignore::BanUnusedIgnore),
512 Box::new(no_redeclare::NoRedeclare),
513 Box::new(eqeqeq::Eqeqeq),
514 ];
515
516 sort_rules_by_priority(&mut rules);
517
518 assert_eq!(rules[0].code(), "eqeqeq");
519 assert_eq!(rules[1].code(), "no-redeclare");
520 assert_eq!(rules[2].code(), "ban-unknown-rule-code");
521 assert_eq!(rules[3].code(), "ban-unused-ignore");
522 }
523}