Commit graph

1015 commits

Author SHA1 Message Date
Boshen
313fb839b5
fix(linter/default): ignore unsupported files (e.g. .vue)
relates #3157
2024-05-12 13:33:29 +08:00
Boshen
dbde5b3a04
refactor(diagnostics): remove export of miette 2024-05-12 11:46:48 +08:00
Boshen
551632a348
refactor(diagnostics): remove thiserror 2024-05-12 11:34:14 +08:00
Boshen
f7a37739c8
refactor(linter): clean up diagnostics 2024-05-12 11:26:51 +08:00
Dunqing
93030e8af9
chore(linter): change the category of all react-perf rules to perf (#3243)
close: #3242
2024-05-12 10:55:48 +08:00
Boshen
5671714adb
refactor(linter): clean up diagnostics in fixer 2024-05-12 10:45:22 +08:00
Boshen
312f74bb63
refactor(diagnostics): s/OxcDiagnostic::new/OxcDiagnostic::error 2024-05-12 01:08:54 +08:00
Boshen
6e90f67c47
refactor(linter): remove unnecessary usages of CompactStr 2024-05-12 00:24:57 +08:00
Boshen
f6f7adc07f
refactor(linter,diagnostic): one diagnostic struct to eliminate monomorphization of generic types (#3235)
part of #3213
2024-05-12 00:10:47 +08:00
Boshen
2064ae9e0a refactor(parser,diagnostic): one diagnostic struct to eliminate monomorphization of generic types (#3214)
part of #3213

We should only have one diagnostic struct instead 353 copies of them, so we don't end up choking LLVM with 50k lines of the same code due to monomorphization.

If the proposed approach is good, then I'll start writing a codemod to turn all the existing structs to plain functions.

---

Background:

Using `--timings`, we see `oxc_linter` is slow on codegen (the purple part).

![image](https://github.com/zkat/miette/assets/1430279/c1df4f7d-90ef-4c0f-9956-2ec3194db7ca)

The crate currently contains 353 miette errors. [cargo-llvm-lines](https://github.com/dtolnay/cargo-llvm-lines) displays

```
cargo llvm-lines -p oxc_linter --lib --release

  Lines                 Copies               Function name
  -----                 ------               -------------
  830350                33438                (TOTAL)
   29252 (3.5%,  3.5%)    808 (2.4%,  2.4%)  <alloc::boxed::Box<T,A> as core::ops::drop::Drop>::drop
   23298 (2.8%,  6.3%)    353 (1.1%,  3.5%)  miette::eyreish::error::object_downcast
   19062 (2.3%,  8.6%)    706 (2.1%,  5.6%)  core::error::Error::type_id
   12610 (1.5%, 10.1%)     65 (0.2%,  5.8%)  alloc::raw_vec::RawVec<T,A>::grow_amortized
   12002 (1.4%, 11.6%)    706 (2.1%,  7.9%)  miette::eyreish::ptr::Own<T>::boxed
    9215 (1.1%, 12.7%)    115 (0.3%,  8.2%)  core::iter::traits::iterator::Iterator::try_fold
    9150 (1.1%, 13.8%)      1 (0.0%,  8.2%)  oxc_linter::rules::RuleEnum::read_json
    8825 (1.1%, 14.9%)    353 (1.1%,  9.3%)  <miette::eyreish::error::ErrorImpl<E> as core::error::Error>::source
    8822 (1.1%, 15.9%)    353 (1.1%, 10.3%)  miette::eyreish::error::<impl miette::eyreish::Report>::construct
    8119 (1.0%, 16.9%)    353 (1.1%, 11.4%)  miette::eyreish::error::object_ref
    8119 (1.0%, 17.9%)    353 (1.1%, 12.5%)  miette::eyreish::error::object_ref_stderr
    7413 (0.9%, 18.8%)    353 (1.1%, 13.5%)  <miette::eyreish::error::ErrorImpl<E> as core::fmt::Display>::fmt
    7413 (0.9%, 19.7%)    353 (1.1%, 14.6%)  miette::eyreish::ptr::Own<T>::new
    6669 (0.8%, 20.5%)     39 (0.1%, 14.7%)  alloc::raw_vec::RawVec<T,A>::try_allocate_in
    6173 (0.7%, 21.2%)    353 (1.1%, 15.7%)  miette::eyreish::error::<impl miette::eyreish::Report>::from_std
    6027 (0.7%, 21.9%)     70 (0.2%, 16.0%)  <alloc::vec::Vec<T> as alloc::vec::spec_from_iter_nested::SpecFromIterNested<T,I>>::from_iter
    6001 (0.7%, 22.7%)    353 (1.1%, 17.0%)  miette::eyreish::error::object_drop
    6001 (0.7%, 23.4%)    353 (1.1%, 18.1%)  miette::eyreish::error::object_drop_front
    5648 (0.7%, 24.1%)    353 (1.1%, 19.1%)  <miette::eyreish::error::ErrorImpl<E> as core::fmt::Debug>::fmt
```

It's totalling more than 50k llvm lines, and is putting pressure on rustc codegen (the purple part on `oxc_linter` in the image above.

---

It's pretty obvious by looking at https://github.com/zkat/miette/blob/main/src/eyreish/error.rs, the generics can expand out to lots of code.
2024-05-11 04:56:22 +00:00
Ali Rezvani
20f643063e
improvement(semantic/cfg): add explicit break block element. (#3223)
Works similar to how `throw` is working but is done for `break` statements.
2024-05-10 22:22:30 +08:00
Dunqing
0ba7778e5e
fix(parser): correctly parse cls.fn<C> = x (#3208)
close: #3206
2024-05-09 10:23:45 +08:00
Kuba Jastrzębski
7113e850b4
feat(linter): add radix rule (#3167) 2024-05-08 23:44:12 +08:00
Boshen
15f275f572
refactor(linter): reduce llvm lines generated by RuleEnum::read_json (#3207)
Previous: 

```
  Lines                 Copies               Function name
  -----                 ------               -------------
    9150 (1.1%, 13.8%)      1 (0.0%,  8.2%)  oxc_linter::rules::RuleEnum::read_json
```

After:

```
    2285 (0.3%, 36.2%)      1 (0.0%, 40.3%)  oxc_linter::rules::RuleEnum::read_json
```
2024-05-08 21:53:06 +08:00
Wang Wenzhe
cba1e7f463
chore(linter): shorten eslint/eqeqeq rule error message's span (#3193) 2024-05-08 14:32:57 +08:00
Dunqing
fa0093b222
feat(linter): eslint-plugin-next/no-page-custom-font (#3185) 2024-05-07 09:53:43 +00:00
Boshen
4defe37f12
feat(linter): remove deprecated eslint v9 rules no-return-await and no-mixed-operators (#3188) 2024-05-07 17:17:31 +08:00
Boshen
ca9f13f4f2
feat(linter): eslint/no-new-native-nonconstructor (#3187)
feat(linter): eslint/no-new-native-nonconstructor

closes #3179

remove no new symbol
2024-05-07 17:10:17 +08:00
Jelle van der Waa
5081652bc1
feat(linter/eslint): Implement no-empty-function rule (#3181)
Rule Detail:
[link](https://eslint.org/docs/latest/rules/no-empty-function)

---------

Co-authored-by: Boshen <boshenc@gmail.com>
2024-05-07 02:56:13 +00:00
Dunqing
5514936f51
feat(linter): eslint-plugin-next/no-styled-jsx-in-document (#3184) 2024-05-07 10:35:28 +08:00
Wang Wenzhe
c0abbbd204
feat(linter/tree-shaking): add isPureFunction (#3175) 2024-05-07 00:03:04 +08:00
Cameron
07076d9765
feat(linter) improve prefer-string-starts-ends-with rule (#3176)
basically:
`^#/i.test(hex)` is the same as `/^#/.test(hex)`
so, in this case the `i` flag does nothing and we can safely ignore it


inspired by https://x.com/the_moisrex/status/1787444601571221892


---

This could potentially lead a new `oxc` rule called
`no_useless_case_insensitive_regex_flag` that reports if you have a
regex with the `i` flag with no ascii alphabetic chars.
2024-05-06 23:52:19 +08:00
Boshen
cb2e651eea
feat(linter): eslint-plugin-next/no-duplicate-head (#3174)
closes #2438
2024-05-06 19:43:38 +08:00
Boshen
a84454cf87
refactor(linter): clean up prefer_node_protocol and move to restriction (#3171)
closes #3161
2024-05-06 12:43:24 +08:00
Dunqing
f88f330426
feat(linter/import): improve multiple exports error message (#3160)
https://github.com/oxc-project/oxc/issues/3157#issuecomment-2094113518
2024-05-04 22:14:26 +08:00
Boshen
d78edb1eb5
chore(linter): move more import plugin rules out of nursery 2024-05-04 19:07:25 +08:00
Dunqing
6f5df113af
feat(linter/import): move some rules out of nursery (#2841)
### Correctness
- named
- export
- default
- namespace
- no-unresolved

The above rules will prevent bugs or broken, so I changed these to
correctness

### Suspicious
- no-duplicates
- no-named-as-default-member
- no-named-as-default

I'm still not sure whether the `no-unresolved` rule should be changed to
`correctness`. Because I found that the rule reported a lot in the
AFFiNE. But in fact, they are all correct reports

---------

Co-authored-by: Boshen <boshenc@gmail.com>
2024-05-04 18:51:26 +08:00
Boshen
fde7d65992
fix(linter): handle named export default in import-plugin/named (#3158) 2024-05-04 17:40:56 +08:00
Boshen
b1bddacd09
fix(linter): fix hang if a file fails to parse while using --import-plugin 2024-05-04 17:16:43 +08:00
Boshen
222030c4c5
refactor(linter): render --rules in a table 2024-05-04 15:31:20 +08:00
Wang Wenzhe
32df6d779c
feat(linter/tree-shaking): support While/Switch/Yield Statement (#3155)
All test cases passed now. There are something remain. I'll change the category when it's ready.

- [ ] Port [`isPureFuncion`](96f0d1c825/src/utils/helpers.ts (L53-L67)), [pure-function](198432ecc9/src/utils/pure-functions.ts) 
- [ ] Support [options](463fa1f0be/src/rules/no-side-effects-in-initialization.ts (L1130-L1138))
- [ ] Clean TODO
- [ ] Add more support of operator computation. [The original version](463fa1f0be/src/rules/no-side-effects-in-initialization.ts (L139-L210)) is straightforward benefit of JS. We only [support Number](f91a063616/crates/oxc_linter/src/utils/tree_shaking.rs (L194)) now.
2024-05-03 23:21:28 +08:00
Wang Wenzhe
8290421e72
feat(linter/tree-shaking): support SequenceExpression (#3154) 2024-05-03 23:19:36 +08:00
Wang Wenzhe
5c21b7f843
feat(linter/tree-shaking): support UnaryExpression (#3153) 2024-05-03 23:17:15 +08:00
renovate[bot]
56a0db8621
chore(deps): update dependency rust to v1.78.0 (#3151) 2024-05-03 00:01:42 +08:00
Yuji Sugiura
5a1d63afed
feat(linter/jsdoc): Implement require-yields rule (#3150)
Part of #1170 

>
https://github.com/gajus/eslint-plugin-jsdoc/blob/main/docs/rules/require-yields.md

With:

- Fix typo of JSDocPluginSettings
- Update utils/jsdoc
2024-05-02 22:49:19 +08:00
Yuji Sugiura
d7a8345e4c
feat(linter/jsdoc): Support settings.ignore(Private|Internal) (#3147)
>
https://github.com/gajus/eslint-plugin-jsdoc/blob/main/docs/settings.md#user-content-settings-allow-tags-private-or-internal-to-disable-rules-for-that-comment-block

...and fixed the issue that intended initial settings values are not
used in some cases.
2024-05-01 19:48:28 +08:00
Wang Wenzhe
8cdd5b0fd8
feat(linter/tree_shaking): support LogicExpression and MemberExpression (#3148)
MemberExpression's message is not accurate, I will update later.
2024-05-01 19:46:40 +08:00
kaykdm
80cf0b2b2e
feat(linter): @typescript-eslint/prefer-literal-enum-member (#3134)
Related issue: https://github.com/oxc-project/oxc/issues/2180

original implementation

- code: https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/eslint-plugin/src/rules/prefer-literal-enum-member.ts
- test: https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/eslint-plugin/tests/rules/prefer-literal-enum-member.test.ts
- doc: https://typescript-eslint.io/rules/prefer-literal-enum-member/
2024-05-01 11:17:46 +08:00
Wang Wenzhe
733361822e
feat(linter/tree-shaking): support JSX (#3139) 2024-04-30 11:25:39 +08:00
Wang Wenzhe
16a31e95b8
feat(linter/tree-shaking): support import statement (#3138) 2024-04-30 11:23:23 +08:00
Boshen
a8af5de8f5
refactor(syntax): move number related functions to number module (#3130) 2024-04-29 18:54:35 +08:00
Boshen
ae6561308c
refactor(syntax): use FxHashMap for ModuleRecord::request_modules (#3124)
closes #3121

The ordering is not important here.
2024-04-29 03:37:34 +00:00
Wang Wenzhe
8f31ad618e
chore(linter/tree-shaking): uncomment function and identifier test cases (#3119) 2024-04-28 22:05:33 +08:00
overlookmotel
7e1fe36c68
refactor(ast): squash nested enums (#3115)
OK, this is a big one...

I have done this as part of work on Traversable AST, but I believe it
has wider benefits, so thought better to spin it off into its own PR.

## What this PR does

This PR squashes all nested AST enum types (#2685).

e.g.: Previously:

```rs
pub enum Statement<'a> {
    BlockStatement(Box<'a, BlockStatement<'a>>),
    /* ...other Statement variants... */
    Declaration(Declaration<'a>),
}

pub enum Declaration<'a> {
    VariableDeclaration(Box<'a, VariableDeclaration<'a>>),
    /* ...other Declaration variants... */
}
```

After this PR:

```rs
#[repr(C, u8)]
pub enum Statement<'a> {
    BlockStatement(Box<'a, BlockStatement<'a>>) = 0,
    /* ...other Statement variants... */

    VariableDeclaration(Box<'a, VariableDeclaration<'a>>) = 32,
    /* ...other Declaration variants... */
}

#[repr(C, u8)]
pub enum Declaration<'a> {
    VariableDeclaration(Box<'a, VariableDeclaration<'a>>) = 32,
    /* ...other Declaration variants... */
}
```

All `Declaration`'s variants are combined into `Statement`, but
`Declaration` type still exists.

As both types are `#[repr(C, u8)]`, and the discriminants are aligned, a
`Declaration` can be transmuted to a `Statement` at zero cost.

This is the same thing as #2847, but here applied to *all* nested enums
in the AST, and with improved helper methods.

No enums increase in size, and a few get smaller. Indirection is reduced
for some types (this removes multiple levels of boxing).

## Why?

1. It is a prerequisite for Traversable AST (#2987).
2. It would help a lot with AST Transfer (#2409) - it solves the only
remaining blocker for this.
3. It is a step closer to making the whole AST `#[repr(C)]`.

## Why is it a good thing for the AST to be `#[repr(C)]`?

Oxc's direction appears to be increasingly to build up control over the
fundamental primitives we use, in order to unlock performance and
features. We have our own allocator, our own custom implementations for
`Box` and `Vec`, our own `IndexVec` (TBC). The AST is the central
building block of Oxc, and taking control of its memory layout feels
like a step in this same direction.

Oxc has a major advantage over other similar libraries in that it keeps
all the AST data in an arena. This opens the door to treating the AST
either as Rust types or as *pure data* (just bytes). That data can be
moved around and manipulated beyond what Rust natively allows.

However, to enable that, the types need to be well-specified, with
completely stable layouts. `#[repr(C)]` is the only tool Rust provides
to do this.

Once the types are `#[repr(C)]`, various features become possible:

1. Cheap transfer of the AST across boundaries without ser/deser - the
property used by AST Transfer.
2. Having multiple versions of the AST (standard, read-only,
traversable), and these AST representations can be converted to one
other at zero cost via transmute - the property used by Traversable AST
scheme.
3. Caching AST data on disk (#3079) or transferring across network.
4. Stuff we haven't thought of yet!

Allowing the AST to be treated as pure data will likely unlock other
"next level" features further down the track (caching for "edge
bundling" comes to mind).

## The problem with `#[repr(C)]`

It's not *required* to squash nested enums to make the AST `#[repr(C)]`.

But the problem with `#[repr(C)]` is that it disables some compiler
optimizations. Without `#[repr(C)]`, the compiler squashes enums itself
in some cases (which is how `Statement` is currently 16 bytes). But
making the types `#[repr(C)]` as they are currently disables this
optimization.

So this PR essentially makes explicit what the compiler is already doing
- and in fact goes a bit further with the optimization than the compiler
is able to, in squashing 3 or 4 layers of nested enums (the compiler
only does up to 2 layers).

## Implementation

One enum "inheriting" variants from another is implemented with
`inherit_variants!` macro.

```rs
inherit_variants! {
#[repr(C, u8)]
pub enum Statement<'a> {
    BlockStatement(Box<'a, BlockStatement<'a>>),
    /* ...other Statement variants... */
    
    // `Declaration` variants added here by `inherit_variants!` macro
    @inherit Declaration
    // `ModuleDeclaration` variants added here by `inherit_variants!` macro
    @inherit ModuleDeclaration
}
}
```

The macro is *fairly* lightweight, and I think the above is quite easy
to understand. No proc macros.

The macro also implements utility methods for converting between enums
e.g. `Statement::as_declaration`. These methods are all zero-cost
(essentially transmutes).

New patterns for dealing with nested enums are introduced:

Creation:

```rs
// Old
let stmt = Statement::Declaration(Declaration::VariableDeclaration(var_decl));

// New
let stmt = Statement::VariableDeclaration(var_decl);
```

Conversion:

```rs
// Old
let stmt = Statement::Declaration(decl);

// New
let stmt = Statement::from(decl);
```

Testing:

```rs
// Old
if matches!(stmt, Statement::Declaration(_)) { }
if matches!(stmt, Statement::ModuleDeclaration(m) if m.is_import()) { }

// New
if stmt.is_declaration() { }
if matches!(stmt, Statement::ImportDeclaration(_)) { }
```

Branching:

```rs
// Old
if let Statement::Declaration(decl) = &stmt { decl.do_stuff() };

// New
if let Some(decl) = stmt.as_declaration() { decl.do_stuff() };
```

Matching:

```rs
// Old
match stmt {
    Statement::Declaration(decl) => visitor.visit(decl),
}

// New (exhaustive match)
match stmt {
    match_declaration!(Statement) => visitor.visit(stmt.to_declaration()),
}

// New (alternative)
match stmt {
    _ if stmt.is_declaration() => visitor.visit(stmt.to_declaration()),
}
```

New syntax has pluses and minuses vs the old. `match` syntax is worse,
but when working with a deeply nested enum, the code is much nicer -
it's shorter and easier to read.

This PR removes 200 lines from the linter with changes like this:


https://github.com/oxc-project/oxc/pull/3115/files#diff-dc417ff57352da6727a760ec6dee22de6816f8231fb69dbef1bf05d478699103L92-R95

```diff
- let AssignmentTarget::SimpleAssignmentTarget(simple_assignment_target) =
-     &assignment_expr.left
- else {
-     return;
- };
- let SimpleAssignmentTarget::AssignmentTargetIdentifier(ident) =
-     simple_assignment_target
+ let AssignmentTarget::AssignmentTargetIdentifier(ident) = &assignment_expr.left
else {
    return;
};
```
2024-04-28 20:40:37 +08:00
Yuji Sugiura
1f12aee149
refactor(linter/jsdoc): Misc improvements (#3109)
- [x] Always use resolved_tag_name
- [x] Nits syntax, naming fixes
- [x] Split up utils
2024-04-27 21:47:33 +08:00
谭光志
cd600fade4
feat(linter): add more "ban-ts-comment" test cases. (#3107)
https://github.com/oxc-project/oxc/issues/2934

| name           | passed | failed |
| -------------- | ------ | ------ |
| ts-expect-error| 14     | 20     |
| ts-ignore      | 15     | 20     |
| ts-nocheck     | 11     | 9      |
| ts-check       | 11     | 8      |
| total          | 51     | 57
2024-04-26 22:54:05 +08:00
cinchen
bef8a71b9d
feat(linter): eslint-plugin-jest/require-hook (#3110)
part of https://github.com/oxc-project/oxc/issues/492

Rule Detail: [link](https://github.com/jest-community/eslint-plugin-jest/blob/main/src/rules/require-hook.ts)
2024-04-26 21:26:28 +08:00
Todor Andonov
388ee5135a
feat(linter): typescript-eslint/prefer-enum-initializers (#3097) 2024-04-26 21:09:05 +08:00
Yuji Sugiura
5866086d17
feat(linter/jsdoc): Implement no-defaults rule (#3098)
Part of #1170

>
https://github.com/gajus/eslint-plugin-jsdoc/blob/main/docs/rules/no-defaults.md#repos-sticky-header
2024-04-26 00:03:21 +08:00
Ali Rezvani
ac72d08592
chore: cleanup the dependencies on static_assertions and oxc_index. (#3095)
We used to export `static_assertions` as part of the `oxc_index`. It
would've made sense back when it was only a vessel for exporting other
crates - although even then it wouldn't make much sense other than being
convenient - Now with it turning into a port of `index_vec` and
potentially getting bigger as the result of specific needs of the
project; It makes much more sense to stop exporting it from `oxc_index`
and use the crate directly in places that used to use what `oxc_index`
were exporting.


PS: we may want to follow up this with an `oxc_asset` crate containing
our own set of assertion tools which would also export
`static_assertions`.
2024-04-25 16:56:23 +08:00