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

Commit

Permalink
chore: support class expressions and declaration
Browse files Browse the repository at this point in the history
  • Loading branch information
kaioduarte committed Nov 27, 2022
1 parent 6d1eec1 commit fd8f47c
Show file tree
Hide file tree
Showing 5 changed files with 264 additions and 12 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,10 @@ use rome_analyze::{declare_rule, ActionCategory, Ast, Rule, RuleDiagnostic};
use rome_console::markup;
use rome_diagnostics::Applicability;
use rome_js_factory::{make, syntax::T};
use rome_js_syntax::{JsAnyExpression, JsBinaryOperator, JsCallExpression, OperatorPrecedence};
use rome_js_syntax::{
JsAnyExpression, JsBinaryOperator, JsCallExpression, JsClassDeclaration, JsClassExpression,
JsExtendsClause, OperatorPrecedence,
};
use rome_rowan::{AstNode, AstSeparatedList, BatchMutationExt};

declare_rule! {
Expand Down Expand Up @@ -176,12 +179,15 @@ impl Rule for UseExponentiationOperator {
math_pow_call.get_exponent()?,
);

if let Some(parent) = does_parent_expression_need_parens(node) {
if let Some((needs_parens, parent)) = does_exponentiation_expression_need_parens(node) {
if needs_parens && parent.is_some() {
mutation.replace_node(parent.clone()?, parenthesize_js_any_expression(&parent?));
}

mutation.replace_node(
JsAnyExpression::from(node.clone()),
parenthesize_js_any_expression(&JsAnyExpression::from(new_node)),
);
mutation.replace_node(parent.clone(), parenthesize_js_any_expression(&parent));
} else {
mutation.replace_node(
JsAnyExpression::from(node.clone()),
Expand Down Expand Up @@ -228,10 +234,37 @@ fn parenthesize_js_any_expression(expr: &JsAnyExpression) -> JsAnyExpression {
}

/// Determines whether the given parent node needs parens if used as the exponent in an exponentiation binary expression.
fn does_parent_expression_need_parens(node: &JsCallExpression) -> Option<JsAnyExpression> {
let parent = node.parent::<JsAnyExpression>()?;
fn does_exponentiation_expression_need_parens(
node: &JsCallExpression,
) -> Option<(bool, Option<JsAnyExpression>)> {
if let Some(parent) = node.parent::<JsAnyExpression>() {
if does_expression_need_parens(node, &parent)? {
return Some((true, Some(parent)));
}
}

if let Some(extends_clause) = node.parent::<JsExtendsClause>() {
if extends_clause.parent::<JsClassDeclaration>().is_some() {
return Some((true, None));
}

if let Some(class_expr) = extends_clause.parent::<JsClassExpression>() {
let class_expr = JsAnyExpression::from(class_expr);
if does_expression_need_parens(node, &class_expr)? {
return Some((true, Some(class_expr)));
}
}
}

None
}

let needs_parentheses = match &parent {
/// Determines whether the given expression needs parens when used in an exponentiation binary expression.
fn does_expression_need_parens(
node: &JsCallExpression,
expression: &JsAnyExpression,
) -> Option<bool> {
let needs_parentheses = match &expression {
// Skips already parenthesized expressions
JsAnyExpression::JsParenthesizedExpression(_) => return None,
JsAnyExpression::JsBinaryExpression(bin_expr) => {
Expand Down Expand Up @@ -266,15 +299,12 @@ fn does_parent_expression_need_parens(node: &JsCallExpression) -> Option<JsAnyEx
JsAnyExpression::JsComputedMemberExpression(member_expr) => {
member_expr.member().ok()?.as_js_call_expression()? != node
}
JsAnyExpression::JsStaticMemberExpression(_)
JsAnyExpression::JsClassExpression(_)
| JsAnyExpression::JsStaticMemberExpression(_)
| JsAnyExpression::JsUnaryExpression(_)
| JsAnyExpression::JsTemplate(_) => true,
_ => false,
};

if needs_parentheses && parent.precedence().ok()? >= OperatorPrecedence::Exponential {
return Some(parent);
}

None
Some(needs_parentheses && expression.precedence().ok()? >= OperatorPrecedence::Exponential)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
class C extends Math.pow(a, b) {}
(class extends Math.pow(a, b) {})
(class extends (Math.pow(a, b)) {})
class C extends (Math.pow(a, b)) {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
---
source: crates/rome_js_analyze/tests/spec_tests.rs
assertion_line: 73
expression: invalidClass.js
---
# Input
```js
class C extends Math.pow(a, b) {}
(class extends Math.pow(a, b) {})
(class extends (Math.pow(a, b)) {})
class C extends (Math.pow(a, b)) {}

```

# Diagnostics
```
invalidClass.js:1:17 lint/nursery/useExponentiationOperator FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
! Use the '**' operator instead of 'Math.pow'.
> 1 │ class C extends Math.pow(a, b) {}
│ ^^^^^^^^^^^^^^
2 │ (class extends Math.pow(a, b) {})
3 │ (class extends (Math.pow(a, b)) {})
i Suggested fix: Use the '**' operator instead of 'Math.pow'.
1 │ - class·C·extends·Math.pow(a,·b)·{}
1 │ + class·C·extends·(a**b)·{}
2 2 │ (class extends Math.pow(a, b) {})
3 3 │ (class extends (Math.pow(a, b)) {})
```

```
invalidClass.js:2:16 lint/nursery/useExponentiationOperator FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
! Use the '**' operator instead of 'Math.pow'.
1 │ class C extends Math.pow(a, b) {}
> 2 │ (class extends Math.pow(a, b) {})
│ ^^^^^^^^^^^^^^
3 │ (class extends (Math.pow(a, b)) {})
4 │ class C extends (Math.pow(a, b)) {}
i Suggested fix: Use the '**' operator instead of 'Math.pow'.
1 1 │ class C extends Math.pow(a, b) {}
2 │ - (class·extends·Math.pow(a,·b)·{})
2 │ + (class·extends·(a**b)·{})
3 3 │ (class extends (Math.pow(a, b)) {})
4 4 │ class C extends (Math.pow(a, b)) {}
```

```
invalidClass.js:3:17 lint/nursery/useExponentiationOperator FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
! Use the '**' operator instead of 'Math.pow'.
1 │ class C extends Math.pow(a, b) {}
2 │ (class extends Math.pow(a, b) {})
> 3 │ (class extends (Math.pow(a, b)) {})
│ ^^^^^^^^^^^^^^
4 │ class C extends (Math.pow(a, b)) {}
5 │
i Suggested fix: Use the '**' operator instead of 'Math.pow'.
1 1 │ class C extends Math.pow(a, b) {}
2 2 │ (class extends Math.pow(a, b) {})
3 │ - (class·extends·(Math.pow(a,·b))·{})
3 │ + (class·extends·(a**b)·{})
4 4 │ class C extends (Math.pow(a, b)) {}
5 5 │
```

```
invalidClass.js:4:18 lint/nursery/useExponentiationOperator FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
! Use the '**' operator instead of 'Math.pow'.
2 │ (class extends Math.pow(a, b) {})
3 │ (class extends (Math.pow(a, b)) {})
> 4 │ class C extends (Math.pow(a, b)) {}
│ ^^^^^^^^^^^^^^
5 │
i Suggested fix: Use the '**' operator instead of 'Math.pow'.
2 2 │ (class extends Math.pow(a, b) {})
3 3 │ (class extends (Math.pow(a, b)) {})
4 │ - class·C·extends·(Math.pow(a,·b))·{}
4 │ + class·C·extends·(a**b)·{}
5 5 │
```


Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
// @ts-nocheck
class C<T> extends Math.pow(a, b) implements Foo<T> {}
(class A extends Math.pow(a, b) implements Foo {})
(class A<T> extends (Math.pow(a, b)) {})
class C<T> extends (Math.pow(a, b)) implements Foo, Bar<T> {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
---
source: crates/rome_js_analyze/tests/spec_tests.rs
assertion_line: 73
expression: invalidClass.ts
---
# Input
```js
// @ts-nocheck
class C<T> extends Math.pow(a, b) implements Foo<T> {}
(class A extends Math.pow(a, b) implements Foo {})
(class A<T> extends (Math.pow(a, b)) {})
class C<T> extends (Math.pow(a, b)) implements Foo, Bar<T> {}

```

# Diagnostics
```
invalidClass.ts:2:20 lint/nursery/useExponentiationOperator FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
! Use the '**' operator instead of 'Math.pow'.
1 │ // @ts-nocheck
> 2 │ class C<T> extends Math.pow(a, b) implements Foo<T> {}
│ ^^^^^^^^^^^^^^
3 │ (class A extends Math.pow(a, b) implements Foo {})
4 │ (class A<T> extends (Math.pow(a, b)) {})
i Suggested fix: Use the '**' operator instead of 'Math.pow'.
1 1 │ // @ts-nocheck
2 │ - class·C<T>·extends·Math.pow(a,·b)·implements·Foo<T>·{}
2 │ + class·C<T>·extends·(a**b)·implements·Foo<T>·{}
3 3 │ (class A extends Math.pow(a, b) implements Foo {})
4 4 │ (class A<T> extends (Math.pow(a, b)) {})
```

```
invalidClass.ts:3:18 lint/nursery/useExponentiationOperator FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
! Use the '**' operator instead of 'Math.pow'.
1 │ // @ts-nocheck
2 │ class C<T> extends Math.pow(a, b) implements Foo<T> {}
> 3 │ (class A extends Math.pow(a, b) implements Foo {})
│ ^^^^^^^^^^^^^^
4 │ (class A<T> extends (Math.pow(a, b)) {})
5 │ class C<T> extends (Math.pow(a, b)) implements Foo, Bar<T> {}
i Suggested fix: Use the '**' operator instead of 'Math.pow'.
1 1 │ // @ts-nocheck
2 2 │ class C<T> extends Math.pow(a, b) implements Foo<T> {}
3 │ - (class·A·extends·Math.pow(a,·b)·implements·Foo·{})
3 │ + (class·A·extends·(a**b)·implements·Foo·{})
4 4 │ (class A<T> extends (Math.pow(a, b)) {})
5 5 │ class C<T> extends (Math.pow(a, b)) implements Foo, Bar<T> {}
```

```
invalidClass.ts:4:22 lint/nursery/useExponentiationOperator FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
! Use the '**' operator instead of 'Math.pow'.
2 │ class C<T> extends Math.pow(a, b) implements Foo<T> {}
3 │ (class A extends Math.pow(a, b) implements Foo {})
> 4 │ (class A<T> extends (Math.pow(a, b)) {})
│ ^^^^^^^^^^^^^^
5 │ class C<T> extends (Math.pow(a, b)) implements Foo, Bar<T> {}
6 │
i Suggested fix: Use the '**' operator instead of 'Math.pow'.
2 2 │ class C<T> extends Math.pow(a, b) implements Foo<T> {}
3 3 │ (class A extends Math.pow(a, b) implements Foo {})
4 │ - (class·A<T>·extends·(Math.pow(a,·b))·{})
4 │ + (class·A<T>·extends·(a**b)·{})
5 5 │ class C<T> extends (Math.pow(a, b)) implements Foo, Bar<T> {}
6 6 │
```

```
invalidClass.ts:5:21 lint/nursery/useExponentiationOperator FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
! Use the '**' operator instead of 'Math.pow'.
3 │ (class A extends Math.pow(a, b) implements Foo {})
4 │ (class A<T> extends (Math.pow(a, b)) {})
> 5 │ class C<T> extends (Math.pow(a, b)) implements Foo, Bar<T> {}
│ ^^^^^^^^^^^^^^
6 │
i Suggested fix: Use the '**' operator instead of 'Math.pow'.
3 3 │ (class A extends Math.pow(a, b) implements Foo {})
4 4 │ (class A<T> extends (Math.pow(a, b)) {})
5 │ - class·C<T>·extends·(Math.pow(a,·b))·implements·Foo,·Bar<T>·{}
5 │ + class·C<T>·extends·(a**b)·implements·Foo,·Bar<T>·{}
6 6 │
```


0 comments on commit fd8f47c

Please sign in to comment.