Skip to content
This repository has been archived by the owner on Aug 31, 2023. It is now read-only.

Commit

Permalink
refactor(rome_js_analyze): enhance useDefaultParameterLast
Browse files Browse the repository at this point in the history
  • Loading branch information
Conaclos committed Jun 16, 2023
1 parent aea9efc commit d2cce51
Show file tree
Hide file tree
Showing 10 changed files with 125 additions and 77 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use rome_rowan::{declare_node_union, AstNode, BatchMutationExt, Direction};
use crate::JsRuleAction;

declare_rule! {
/// Enforce default function parameters and optional parameters to be last.
/// Enforce default function parameters and optional function parameters to be last.
///
/// Default and optional parameters that precede a required parameter cannot be omitted at call site.
///
Expand Down Expand Up @@ -60,8 +60,8 @@ declare_node_union! {
}

impl AnyFormalParameter {
pub(crate) fn is_optional(&self) -> bool {
self.question_mark_token().is_some() || self.initializer().is_some()
pub(crate) fn is_required(&self) -> bool {
self.question_mark_token().is_none() && self.initializer().is_none()
}

pub(crate) fn initializer(&self) -> Option<JsInitializerClause> {
Expand Down Expand Up @@ -95,19 +95,22 @@ impl Rule for UseDefaultParameterLast {

fn run(ctx: &RuleContext<Self>) -> Self::Signals {
let formal_param = ctx.query();
if formal_param.is_optional() {
let next_required_param = formal_param
.syntax()
.siblings(Direction::Next)
.filter_map(AnyFormalParameter::cast)
.find(|x| !x.is_optional());
next_required_param
} else {
None
if formal_param.is_required() {
return None;
}
let last_required_param = formal_param
.syntax()
.siblings(Direction::Next)
.filter_map(AnyFormalParameter::cast)
.filter(|x| x.is_required())
.last();
last_required_param
}

fn diagnostic(ctx: &RuleContext<Self>, required_param: &Self::State) -> Option<RuleDiagnostic> {
fn diagnostic(
ctx: &RuleContext<Self>,
last_required_param: &Self::State,
) -> Option<RuleDiagnostic> {
let formal_param = ctx.query();
let param_kind = if formal_param.question_mark_token().is_some() {
"optional"
Expand All @@ -118,12 +121,12 @@ impl Rule for UseDefaultParameterLast {
rule_category!(),
formal_param.range(),
markup! {
"The "<Emphasis>{param_kind}" parameter"</Emphasis>" should follow the "<Emphasis>"required parameter"</Emphasis>" or should be a "<Emphasis>"required parameter"</Emphasis>"."
"This "<Emphasis>{param_kind}" parameter"</Emphasis>" should follow the last "<Emphasis>"required parameter"</Emphasis>" or should be a "<Emphasis>"required parameter"</Emphasis>"."
},
).detail(
required_param.range(),
last_required_param.range(),
markup! {
"The "<Emphasis>"required parameter"</Emphasis>" is here:"
"The last "<Emphasis>"required parameter"</Emphasis>" is here:"
},
).note(
markup! {
Expand All @@ -150,18 +153,27 @@ impl Rule for UseDefaultParameterLast {
mutation.remove_token(question_mark);
} else {
let initializer = opt_param.initializer()?;
let first_token = initializer.syntax().first_token()?;
let last_token = initializer.syntax().last_token()?;
let prev_token = first_token.prev_token()?;
let new_prev_token = prev_token.with_trailing_trivia_pieces(
prev_token
let first_initializer_token = initializer.syntax().first_token()?;
let last_initializer_token = initializer.syntax().last_token()?;
let prev_initializer_token = first_initializer_token.prev_token()?;
let trailing_trivia_count = prev_initializer_token.trailing_trivia().pieces().count();
let last_trailing_non_space = prev_initializer_token
.trailing_trivia()
.pieces()
.rev()
.position(|p| !p.is_newline() && !p.is_whitespace())
.unwrap_or(trailing_trivia_count);
let new_prev_initializer_token = prev_initializer_token.with_trailing_trivia_pieces(
prev_initializer_token
.trailing_trivia()
.pieces()
.chain(first_token.leading_trivia().pieces())
.chain(last_token.trailing_trivia().pieces())
.take(trailing_trivia_count - last_trailing_non_space)
.chain(first_initializer_token.leading_trivia().pieces())
.chain(last_initializer_token.trailing_trivia().pieces())
.collect::<Vec<_>>(),
);
mutation.replace_token_discard_trivia(prev_token, new_prev_token);
mutation
.replace_token_discard_trivia(prev_initializer_token, new_prev_initializer_token);
mutation.remove_node(initializer);
}
Some(JsRuleAction {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,6 @@ export function g(a, b = 0, c) {}

export function g(a, b /* before */ = /* mid */ 0/* after */) {}

export function g(a, b /* before */ = /* mid */ 0 /* after */ /* after comma */, c) {}
export function g(a, b /* before */ = /* mid */ 0 /* after */ /* after comma */, c) {}

export function u(a=5, b, c) {}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
---
source: crates/rome_js_analyze/tests/spec_tests.rs
assertion_line: 100
expression: invalid.js
---
# Input
Expand All @@ -11,20 +12,23 @@ export function g(a, b = 0, c) {}
export function g(a, b /* before */ = /* mid */ 0/* after */) {}

export function g(a, b /* before */ = /* mid */ 0 /* after */ /* after comma */, c) {}

export function u(a=5, b, c) {}

```

# Diagnostics
```
invalid.js:1:19 lint/style/useDefaultParameterLast FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
! The default parameter should follow the required parameter or should be a required parameter.
! This default parameter should follow the last required parameter or should be a required parameter.
> 1 │ export function f(a = 0, b) {}
│ ^^^^^
2 │
3 │ export function g(a, b = 0, c) {}
i The required parameter is here:
i The last required parameter is here:
> 1 │ export function f(a = 0, b) {}
│ ^
Expand All @@ -36,14 +40,14 @@ invalid.js:1:19 lint/style/useDefaultParameterLast FIXABLE ━━━━━━
i Suggested fix: Turn the parameter into a required parameter.
1 │ export·function·f(a·=·0,·b)·{}
---
----
```

```
invalid.js:3:22 lint/style/useDefaultParameterLast FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
! The default parameter should follow the required parameter or should be a required parameter.
! This default parameter should follow the last required parameter or should be a required parameter.
1 │ export function f(a = 0, b) {}
2 │
Expand All @@ -52,7 +56,7 @@ invalid.js:3:22 lint/style/useDefaultParameterLast FIXABLE ━━━━━━
4 │
5 │ export function g(a, b /* before */ = /* mid */ 0/* after */) {}
i The required parameter is here:
i The last required parameter is here:
1 │ export function f(a = 0, b) {}
2 │
Expand All @@ -66,36 +70,65 @@ invalid.js:3:22 lint/style/useDefaultParameterLast FIXABLE ━━━━━━
i Suggested fix: Turn the parameter into a required parameter.
3 │ export·function·g(a,·b·=·0,·c)·{}
---
----
```

```
invalid.js:7:22 lint/style/useDefaultParameterLast FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
! The default parameter should follow the required parameter or should be a required parameter.
! This default parameter should follow the last required parameter or should be a required parameter.
5 │ export function g(a, b /* before */ = /* mid */ 0/* after */) {}
6 │
> 7 │ export function g(a, b /* before */ = /* mid */ 0 /* after */ /* after comma */, c) {}
│ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
8 │
9 │ export function u(a=5, b, c) {}
i The required parameter is here:
i The last required parameter is here:
5 │ export function g(a, b /* before */ = /* mid */ 0/* after */) {}
6 │
> 7 │ export function g(a, b /* before */ = /* mid */ 0 /* after */ /* after comma */, c) {}
│ ^
8 │
9 │ export function u(a=5, b, c) {}
i A default parameter that precedes a required parameter cannot be omitted at call site.
i Suggested fix: Turn the parameter into a required parameter.
5 5 │ export function g(a, b /* before */ = /* mid */ 0/* after */) {}
6 6 │
7 │ - export·function·g(a,·b·/*·before·*/·=·/*·mid·*/·0·/*·after·*/·/*·after·comma·*/,·c)·{}
7 │ + export·function·g(a,·b·/*·before·*/··/*·after·*/·/*·after·comma·*/,·c)·{}
7 │ export·function·g(a,·b·/*·before·*/·=·/*·mid·*/·0·/*·after·*/·/*·after·comma·*/,·c)·{}
│ --------------
```

```
invalid.js:9:19 lint/style/useDefaultParameterLast FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
! This default parameter should follow the last required parameter or should be a required parameter.
7 │ export function g(a, b /* before */ = /* mid */ 0 /* after */ /* after comma */, c) {}
8 │
> 9 │ export function u(a=5, b, c) {}
│ ^^^
10 │
i The last required parameter is here:
7 │ export function g(a, b /* before */ = /* mid */ 0 /* after */ /* after comma */, c) {}
8 │
> 9 │ export function u(a=5, b, c) {}
│ ^
10 │
i A default parameter that precedes a required parameter cannot be omitted at call site.
i Suggested fix: Turn the parameter into a required parameter.
9 │ export·function·u(a=5,·b,·c)·{}
│ --
```

Expand Down
Loading

0 comments on commit d2cce51

Please sign in to comment.