forked from oxc-project/oxc
-
Notifications
You must be signed in to change notification settings - Fork 0
/
default_param_last.rs
104 lines (94 loc) · 3.19 KB
/
default_param_last.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
use oxc_ast::ast::FormalParameter;
use oxc_ast::AstKind;
use oxc_diagnostics::{
miette::{self, Diagnostic},
thiserror::Error,
};
use oxc_macros::declare_oxc_lint;
use oxc_span::Span;
use crate::{context::LintContext, rule::Rule, AstNode};
#[derive(Debug, Error, Diagnostic)]
#[error("eslint(default-param-last): Default parameters should be last")]
#[diagnostic(severity(warning), help("Enforce default parameters to be last."))]
struct DefaultParamLastDiagnostic(#[label] pub Span);
#[derive(Debug, Default, Clone)]
pub struct DefaultParamLast;
declare_oxc_lint!(
/// ### What it does
/// Enforce default parameters to be last
///
/// ### Why is this bad?
/// Putting default parameter at last allows function calls to omit optional tail arguments.
///
/// ### Example
/// ```javascript
/// // Correct: optional argument can be omitted
/// function createUser(id, isAdmin = false) {}
/// createUser("tabby")
///
/// // Incorrect: optional argument can **not** be omitted
/// function createUser(isAdmin = false, id) {}
/// createUser(undefined, "tabby")
/// ```
DefaultParamLast,
style
);
impl Rule for DefaultParamLast {
fn run<'a>(&self, node: &AstNode<'a>, ctx: &LintContext<'a>) {
match node.kind() {
AstKind::Function(function) => {
if !function.is_declaration() & !function.is_expression() {
return;
}
check_params(&function.params.items, ctx);
}
AstKind::ArrowFunctionExpression(function) => check_params(&function.params.items, ctx),
_ => {}
}
}
}
fn check_params<'a>(items: &'a [FormalParameter<'a>], ctx: &LintContext<'a>) {
let mut has_seen_plain_param = false;
for param in items.iter().rev() {
if !param.pattern.kind.is_assignment_pattern() {
has_seen_plain_param = true;
continue;
}
if has_seen_plain_param && param.pattern.kind.is_assignment_pattern() {
ctx.diagnostic(DefaultParamLastDiagnostic(param.span));
}
}
}
#[test]
fn test() {
use crate::tester::Tester;
let pass = vec![
"function f() {}",
"function f(a) {}",
"function f(a = 5) {}",
"function f(a, b) {}",
"function f(a, b = 5) {}",
"function f(a, b = 5, c = 5) {}",
"function f(a, b = 5, ...c) {}",
"const f = () => {}",
"const f = (a) => {}",
"const f = (a = 5) => {}",
"const f = function f() {}",
"const f = function f(a) {}",
"const f = function f(a = 5) {}",
];
let fail = vec![
"function f(a = 5, b) {}",
"function f(a = 5, b = 6, c) {}",
"function f (a = 5, b, c = 6, d) {}",
"function f(a = 5, b, c = 5) {}",
"const f = (a = 5, b, ...c) => {}",
"const f = function f (a, b = 5, c) {}",
"const f = (a = 5, { b }) => {}",
"const f = ({ a } = {}, b) => {}",
"const f = ({ a, b } = { a: 1, b: 2 }, c) => {}",
"const f = ([a] = [], b) => {}",
"const f = ([a, b] = [1, 2], c) => {}",
];
Tester::new(DefaultParamLast::NAME, pass, fail).test_and_snapshot();
}