-
Notifications
You must be signed in to change notification settings - Fork 657
feat(rome_analyze): suppress rule via code actions #3572
Conversation
✅ Deploy Preview for docs-rometools canceled.
|
Parser conformance results on ubuntu-latestjs/262
jsx/babel
symbols/microsoft
ts/babel
ts/microsoft
|
eef75b3
to
69e2116
Compare
!bench_analyzer |
Analyzer Benchmark Results
|
Comparing feat(rome_analyze): suppress rule via code actions Snapshot #9 to median since last deploy of rome.tools.
1 page testedHomeBrowser previews
Most significant changes27 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 |
What happens if you add a suppression to a JSX node?
|
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 |
3e77f5b
to
8371d3c
Compare
crates/rome_js_analyze/src/lib.rs
Outdated
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| { |
There was a problem hiding this comment.
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} />
There was a problem hiding this comment.
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.
@@ -193,6 +201,41 @@ impl JsBatchMutation for BatchMutation<JsLanguage> { | |||
}) | |||
.unwrap_or(false) | |||
} | |||
|
|||
fn add_jsx_element_after_element( |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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
efbf112
to
4e7bab4
Compare
4e7bab4
to
456c42e
Compare
.pieces() | ||
.chain(prev_token.leading_trivia().pieces()) | ||
.map(|piece| TriviaPiece::new(piece.kind(), TextSize::of(piece.text()))) | ||
.collect(); |
There was a problem hiding this comment.
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
tools/crates/rome_js_formatter/src/syntax_rewriter.rs
Lines 433 to 528 in f17ace3
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(), | |
} | |
} | |
} |
There was a problem hiding this comment.
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!
There was a problem hiding this comment.
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
416728e
to
91cb65f
Compare
* 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) ...
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.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 calledSuppressAction
.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 therome-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.