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

feat(rome_analyze): suppress rule via code actions #3572

Merged
merged 12 commits into from
Nov 22, 2022

Conversation

ematipico
Copy link
Contributor

@ematipico ematipico commented Nov 6, 2022

Summary

This PR introduces some infrastructure changes to the analyzer. Before the AnalyzerSignal was returning only one action but now is able to return an iterator of actions.

Doing so allows us to create other actions, and in this PR we create actions to suppress lint rules via LSP. This feature will give us some other perks, like the ability to choose the suppression action when will have commands like rome check --review, where the user can choose different actions.

Note: Another perk will be the ability to create different fixes from the rules, although this is not supported now because it would require some more changes to the Rule trait and the RuleDiagnostic struct, this is something that we have access to.

Suppression action

The suppression of the rules opt-out.

Suppression actions are automatically generated for rules that belong to the category RuleCategory::LINT.

A new API has been introduced, called suppress which returns a small wrapper called SuppressAction.

Heuristic of the suppression comment

The heuristic for suppression comments is languages specific, so I had to create a new generic apply_suppression_comment closure that it's passed down to the specific implementation of the analyzer. The function accepts the token offset of where the diagnostic occurred, and a mutation and string that contains the rome-ignore .. comment.

Test Plan

Run the feature locally, here's a small video:

Screen.Recording.2022-11-04.at.17.09.25.mov

The current tests should stay the same because we should not show suppression actions in the CLI.

I will implement tests for automatically generated actions in another PR.

@ematipico ematipico temporarily deployed to netlify-playground November 6, 2022 13:56 Inactive
@netlify
Copy link

netlify bot commented Nov 6, 2022

Deploy Preview for docs-rometools canceled.

Name Link
🔨 Latest commit 91cb65f
🔍 Latest deploy log https://app.netlify.com/sites/docs-rometools/deploys/637c941f9ea34300082b4cf1

@github-actions
Copy link

github-actions bot commented Nov 6, 2022

@github-actions
Copy link

github-actions bot commented Nov 6, 2022

Parser conformance results on ubuntu-latest

js/262

Test result main count This PR count Difference
Total 45879 45879 0
Passed 44936 44936 0
Failed 943 943 0
Panics 0 0 0
Coverage 97.94% 97.94% 0.00%

jsx/babel

Test result main count This PR count Difference
Total 39 39 0
Passed 36 36 0
Failed 3 3 0
Panics 0 0 0
Coverage 92.31% 92.31% 0.00%

symbols/microsoft

Test result main count This PR count Difference
Total 5946 5946 0
Passed 1757 1757 0
Failed 4189 4189 0
Panics 0 0 0
Coverage 29.55% 29.55% 0.00%

ts/babel

Test result main count This PR count Difference
Total 588 588 0
Passed 519 519 0
Failed 69 69 0
Panics 0 0 0
Coverage 88.27% 88.27% 0.00%

ts/microsoft

Test result main count This PR count Difference
Total 16257 16257 0
Passed 12397 12397 0
Failed 3860 3860 0
Panics 0 0 0
Coverage 76.26% 76.26% 0.00%

@ematipico ematipico force-pushed the feature/rule-suppression branch 3 times, most recently from eef75b3 to 69e2116 Compare November 15, 2022 11:26
@ematipico
Copy link
Contributor Author

!bench_analyzer

@github-actions
Copy link

Analyzer Benchmark Results

group                     main                                   pr
-----                     ----                                   --
analyzer/css.js           1.00      2.1±0.01ms     5.7 MB/sec    1.00      2.1±0.01ms     5.7 MB/sec
analyzer/index.js         1.00      5.8±0.01ms     5.6 MB/sec    1.00      5.8±0.02ms     5.6 MB/sec
analyzer/lint.ts          1.00      2.9±0.01ms    14.1 MB/sec    1.00      2.9±0.01ms    14.1 MB/sec
analyzer/parser.ts        1.00      6.8±0.02ms     7.1 MB/sec    1.00      6.9±0.02ms     7.1 MB/sec
analyzer/router.ts        1.00      4.9±0.02ms    12.6 MB/sec    1.00      4.8±0.01ms    12.6 MB/sec
analyzer/statement.ts     1.00      6.7±0.02ms     5.3 MB/sec    1.00      6.8±0.02ms     5.3 MB/sec
analyzer/typescript.ts    1.00     10.2±0.06ms     5.3 MB/sec    1.00     10.3±0.09ms     5.3 MB/sec

@ematipico ematipico marked this pull request as ready for review November 15, 2022 13:20
@ematipico ematipico requested a review from a team November 15, 2022 13:20
@ematipico ematipico added this to the 11.0.0 milestone Nov 15, 2022
@ematipico ematipico added the A-Linter Area: linter label Nov 15, 2022
@calibre-analytics
Copy link

calibre-analytics bot commented Nov 15, 2022

Comparing feat(rome_analyze): suppress rule via code actions Snapshot #9 to median since last deploy of rome.tools.

LCP? CLS? TBT?
Overall
Median across all pages and test profiles
2.33s
from 265ms
0.0
no change
147ms
no change
Chrome Desktop
Chrome Desktop • Cable
2.33s
from 265ms
0.0
no change
291ms
from 27ms
iPhone, 4G LTE
iPhone 12 • 4G LTE
1.07s
from 238ms
0.0
no change
3ms
no change
Motorola Moto G Power, 3G connection
Motorola Moto G Power • Regular 3G
16.4s
from 1.07s
0.0
no change
147ms
no change

1 page tested

 Home

Browser previews

Chrome Desktop iPhone, 4G LTE Motorola Moto G Power, 3G connection
Chrome Desktop iPhone, 4G LTE Motorola Moto G Power, 3G connection

Most significant changes

Value Budget
Total JavaScript Size in Bytes
Chrome Desktop
5.35 MB
from 86.8 KB
Total JavaScript Size in Bytes
iPhone, 4G LTE
5.35 MB
from 86.8 KB
Total JavaScript Size in Bytes
Motorola Moto G Power, 3G connection
5.35 MB
from 86.8 KB
JS Parse & Compile
Motorola Moto G Power, 3G connection
1.65s
from 29ms
JS Parse & Compile
iPhone, 4G LTE
465ms
from 11ms

27 other significant changes: JS Parse & Compile on Chrome Desktop, First Contentful Paint on Motorola Moto G Power, 3G connection, Largest Contentful Paint on Motorola Moto G Power, 3G connection, Total CSS Size in Bytes on Chrome Desktop, Total CSS Size in Bytes on iPhone, 4G LTE, Total CSS Size in Bytes on Motorola Moto G Power, 3G connection, Total Blocking Time on Chrome Desktop, Time to Interactive on Motorola Moto G Power, 3G connection, Total Page Size in Bytes on Chrome Desktop, Total Page Size in Bytes on iPhone, 4G LTE, Total Page Size in Bytes on Motorola Moto G Power, 3G connection, Time to Interactive on Chrome Desktop, Largest Contentful Paint on Chrome Desktop, First Contentful Paint on Chrome Desktop, Number of Requests on Motorola Moto G Power, 3G connection, Number of Requests on Chrome Desktop, Number of Requests on iPhone, 4G LTE, Speed Index on Motorola Moto G Power, 3G connection, Time to Interactive on iPhone, 4G LTE, First Contentful Paint on iPhone, 4G LTE, Largest Contentful Paint on iPhone, 4G LTE, Speed Index on Chrome Desktop, Total HTML Size in Bytes on Chrome Desktop, Total HTML Size in Bytes on iPhone, 4G LTE, Total HTML Size in Bytes on Motorola Moto G Power, 3G connection, Lighthouse Performance Score on Motorola Moto G Power, 3G connection, Lighthouse Performance Score on Chrome Desktop

Calibre: Site dashboard | View this PR | Edit settings | View documentation

@MichaReiser
Copy link
Contributor

What happens if you add a suppression to a JSX node?

<div>
  <>useless</> <- suppress this
</div>

@ematipico
Copy link
Contributor Author

What happens if you add a suppression to a JSX node?

<div>
  <>useless</> <- suppress this
</div>

Uuuuuuh! Good question! I didn't think about this case! It will break for sure, because the insertion of the comment is hard coded.

Considering the fact that this new feature is exclusively opt-in, I can remove the feature of use_alt_text for now, and fix it later once I improve the testing suite for these kind of actions.

crates/rome_lsp/src/handlers/analysis.rs Outdated Show resolved Hide resolved
crates/rome_analyze/src/signals.rs Outdated Show resolved Hide resolved
crates/rome_analyze/src/rule.rs Outdated Show resolved Hide resolved
@ematipico ematipico marked this pull request as draft November 17, 2022 18:07
@ematipico ematipico changed the title feat(rome_analyze): allow suppress rule via code actions feat(rome_analyze): suppress rule via code actions Nov 18, 2022
@ematipico ematipico force-pushed the feature/rule-suppression branch 2 times, most recently from 3e77f5b to 8371d3c Compare November 18, 2022 11:50
@ematipico ematipico marked this pull request as ready for review November 18, 2022 11:51
@ematipico ematipico requested a review from leops November 18, 2022 11:51
crates/rome_analyze/src/lib.rs Outdated Show resolved Hide resolved
crates/rome_analyze/src/signals.rs Outdated Show resolved Hide resolved
crates/rome_analyze/src/signals.rs Outdated Show resolved Hide resolved
crates/rome_js_analyze/src/lib.rs Outdated Show resolved Hide resolved
TokenAtOffset::Between(token, _) => find_token_with_newline(token),
};
if let Some(current_token) = current_token {
if let Some(element) = current_token.ancestors().find_map(|node| {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure it's safe to visit the ancestors of the node until we find a matching node, specifically I think this logic could fail in a case like this:

<elem
/><elem dangerouslySetInnerHTML={content} />

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The whole function is likely wrong and I plan to change it in the next PR. There are a lot of cases we need to get right and this simple function is not enough. I will push a few changes, but I need to review it in a second pass.

crates/rome_js_analyze/src/lib.rs Outdated Show resolved Hide resolved
@@ -193,6 +201,41 @@ impl JsBatchMutation for BatchMutation<JsLanguage> {
})
.unwrap_or(false)
}

fn add_jsx_element_after_element(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could this function be made generic for every SyntaxList instead of just JsxChildList ? We could then move it to BatchMutation directly

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It will do it in the next PR. The work involved is bigger than I expected

crates/rome_lsp/tests/server.rs Outdated Show resolved Hide resolved
crates/rome_lsp/tests/server.rs Outdated Show resolved Hide resolved
crates/rome_rowan/src/ast/batch.rs Outdated Show resolved Hide resolved
@ematipico ematipico requested a review from leops November 21, 2022 10:35
@ematipico ematipico added the A-Core Area: core label Nov 21, 2022
@ematipico ematipico force-pushed the feature/rule-suppression branch from efbf112 to 4e7bab4 Compare November 21, 2022 16:12
@ematipico ematipico force-pushed the feature/rule-suppression branch from 4e7bab4 to 456c42e Compare November 21, 2022 16:26
crates/rome_analyze/src/lib.rs Outdated Show resolved Hide resolved
crates/rome_analyze/src/matcher.rs Show resolved Hide resolved
crates/rome_analyze/src/rule.rs Outdated Show resolved Hide resolved
crates/rome_analyze/src/signals.rs Outdated Show resolved Hide resolved
crates/rome_analyze/src/signals.rs Outdated Show resolved Hide resolved
crates/rome_js_analyze/src/lib.rs Outdated Show resolved Hide resolved
crates/rome_js_analyze/src/utils/batch.rs Outdated Show resolved Hide resolved
crates/rome_js_analyze/src/utils/batch.rs Outdated Show resolved Hide resolved
crates/rome_js_analyze/src/utils/batch.rs Outdated Show resolved Hide resolved
.pieces()
.chain(prev_token.leading_trivia().pieces())
.map(|piece| TriviaPiece::new(piece.kind(), TextSize::of(piece.text())))
.collect();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could avoid calling collect here when we move the chain_pieces iterator to rowan

fn chain_pieces<F, S>(first: F, second: S) -> ChainTriviaPiecesIterator<F, S>
where
F: Iterator<Item = SyntaxTriviaPiece<JsLanguage>>,
S: Iterator<Item = SyntaxTriviaPiece<JsLanguage>>,
{
ChainTriviaPiecesIterator::new(first, second)
}
/// Chain iterator that chains two iterators over syntax trivia together.
///
/// This is the same as Rust's [Chain] iterator but implements [ExactSizeIterator].
/// Rust doesn't implement [ExactSizeIterator] because adding the sizes of both pieces may overflow.
///
/// Implementing [ExactSizeIterator] in our case is safe because this may only overflow if
/// a source document has more than 2^32 trivia which isn't possible because our source documents are limited to 2^32
/// length.
struct ChainTriviaPiecesIterator<F, S> {
first: Option<F>,
second: S,
}
impl<F, S> ChainTriviaPiecesIterator<F, S> {
fn new(first: F, second: S) -> Self {
Self {
first: Some(first),
second,
}
}
}
impl<F, S> Iterator for ChainTriviaPiecesIterator<F, S>
where
F: Iterator<Item = SyntaxTriviaPiece<JsLanguage>>,
S: Iterator<Item = SyntaxTriviaPiece<JsLanguage>>,
{
type Item = SyntaxTriviaPiece<JsLanguage>;
fn next(&mut self) -> Option<Self::Item> {
match &mut self.first {
Some(first) => match first.next() {
Some(next) => Some(next),
None => {
self.first.take();
self.second.next()
}
},
None => self.second.next(),
}
}
fn size_hint(&self) -> (usize, Option<usize>) {
match &self.first {
Some(first) => {
let (first_lower, first_upper) = first.size_hint();
let (second_lower, second_upper) = self.second.size_hint();
let lower = first_lower.saturating_add(second_lower);
let upper = match (first_upper, second_upper) {
(Some(first), Some(second)) => first.checked_add(second),
_ => None,
};
(lower, upper)
}
None => self.second.size_hint(),
}
}
}
impl<F, S> FusedIterator for ChainTriviaPiecesIterator<F, S>
where
F: Iterator<Item = SyntaxTriviaPiece<JsLanguage>>,
S: Iterator<Item = SyntaxTriviaPiece<JsLanguage>>,
{
}
impl<F, S> ExactSizeIterator for ChainTriviaPiecesIterator<F, S>
where
F: ExactSizeIterator<Item = SyntaxTriviaPiece<JsLanguage>>,
S: ExactSizeIterator<Item = SyntaxTriviaPiece<JsLanguage>>,
{
fn len(&self) -> usize {
match &self.first {
Some(first) => {
let first_len = first.len();
let second_len = self.second.len();
// SAFETY: Should be safe because a program can never contain more than u32 pieces
// because the text ranges are represented as u32 (and each piece must at least contain a single character).
first_len + second_len
}
None => self.second.len(),
}
}
}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We had the function all along and I didn't know that?? Now things will get waaaay easier!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will move this function to my next PR, there's still some other changes I need to do in the batch APIs

@ematipico ematipico force-pushed the feature/rule-suppression branch from 416728e to 91cb65f Compare November 22, 2022 09:19
@ematipico ematipico dismissed MichaReiser’s stale review November 22, 2022 15:41

Feedback addressed

@ematipico ematipico merged commit 689bc0c into main Nov 22, 2022
@ematipico ematipico deleted the feature/rule-suppression branch November 22, 2022 15:41
jeysal added a commit to jeysal/rometools that referenced this pull request Nov 24, 2022
* upstream/main: (73 commits)
  fix(semantic_analyzers): style/noShoutyConstants does not recognize multiple uses of a constant. (rome#3789)
  feat(rome_js_analyze): useDefaultSwitchClauseLast (rome#3791)
  chore: run rustfmt and typo fix (rome#3840)
  feat(rome_js_analyze): use exhaustive deps support properties (rome#3581)
  website(docs): Fix text formatting (rome#3828)
  feat(rome_js_analyze): `noVoidTypeReturn` (rome#3806)
  feat(rome_cli): expose the `--verbose` flag to the CLI (rome#3812)
  fix(rome_diagnostics): allow diagnostic locations to be created without a resource (rome#3834)
  feat(rome_js_analyze): add noExtraNonNullAssertion rule (rome#3797)
  fix(rome_lsp): lsp friendly catch unwind (rome#3740)
  feat(rome_js_semantic): model improvements (rome#3825)
  feat(rome_json_parser): JSON Lexer (rome#3809)
  feat(rome_js_analyze): implement `noDistractingElements` (rome#3820)
  fix(rome_js_formatter): shothanded named import line break with default import (rome#3826)
  feat(rome_js_analyze): `noConstructorReturn` (rome#3805)
  feat(website): change enabledNurseryRules to All/Recommended select (rome#3810)
  feat(rome_js_analyze): noSetterReturn
  feat(rome_js_analyze): noConstructorReturn
  feat(rome_analyze): suppress rule via code actions (rome#3572)
  feat(rome_js_analyze): `noVar` (rome#3765)
  ...
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
A-Core Area: core A-Linter Area: linter
Projects
Status: Done
Development

Successfully merging this pull request may close these issues.

3 participants