-
-
Notifications
You must be signed in to change notification settings - Fork 481
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: add
noIrregularWhitespace
rule (#3333)
- Loading branch information
1 parent
6163e58
commit 4284b19
Showing
16 changed files
with
1,456 additions
and
117 deletions.
There are no files selected for viewing
10 changes: 10 additions & 0 deletions
10
crates/biome_cli/src/execute/migrate/eslint_any_rule_to_biome.rs
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
112 changes: 112 additions & 0 deletions
112
crates/biome_js_analyze/src/lint/nursery/no_irregular_whitespace.rs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,112 @@ | ||
use biome_analyze::{context::RuleContext, declare_lint_rule, Ast, Rule, RuleDiagnostic}; | ||
use biome_analyze::{RuleSource, RuleSourceKind}; | ||
use biome_console::markup; | ||
use biome_js_syntax::{JsLanguage, JsModule}; | ||
use biome_rowan::{AstNode, Direction, SyntaxTriviaPiece, TextRange}; | ||
|
||
const IRREGULAR_WHITESPACES: &[char; 22] = &[ | ||
'\u{c}', '\u{b}', '\u{85}', '\u{feff}', '\u{a0}', '\u{1680}', '\u{180e}', '\u{2000}', | ||
'\u{2001}', '\u{2002}', '\u{2003}', '\u{2004}', '\u{2005}', '\u{2006}', '\u{2007}', '\u{2008}', | ||
'\u{2009}', '\u{200a}', '\u{200b}', '\u{202f}', '\u{205f}', '\u{3000}', | ||
]; | ||
|
||
declare_lint_rule! { | ||
/// Disallows the use of irregular whitespace characters. | ||
/// | ||
/// Invalid or irregular whitespace causes issues with ECMAScript 5 parsers and also makes code harder to debug. | ||
/// | ||
/// ## Examples | ||
/// | ||
/// ### Invalid | ||
/// | ||
/// ```js,expect_diagnostic | ||
/// constcount=1; | ||
/// ``` | ||
/// | ||
/// ```js,expect_diagnostic | ||
/// const foo = 'thing'; | ||
/// ``` | ||
/// | ||
/// ### Valid | ||
/// | ||
/// ```js | ||
/// const count = 1; | ||
/// ``` | ||
/// | ||
/// ```js | ||
/// const foo = ''; | ||
/// ``` | ||
/// | ||
pub NoIrregularWhitespace { | ||
version: "next", | ||
name: "noIrregularWhitespace", | ||
language: "js", | ||
recommended: false, | ||
sources: &[RuleSource::Eslint("no-irregular-whitespace")], | ||
source_kind: RuleSourceKind::SameLogic, | ||
} | ||
} | ||
|
||
impl Rule for NoIrregularWhitespace { | ||
type Query = Ast<JsModule>; | ||
type State = TextRange; | ||
type Signals = Vec<Self::State>; | ||
type Options = (); | ||
|
||
fn run(ctx: &RuleContext<Self>) -> Self::Signals { | ||
let node = ctx.query(); | ||
get_irregular_whitespace(node) | ||
} | ||
|
||
fn diagnostic(_ctx: &RuleContext<Self>, range: &Self::State) -> Option<RuleDiagnostic> { | ||
Some( | ||
RuleDiagnostic::new( | ||
rule_category!(), | ||
range, | ||
markup! { | ||
"Irregular whitespaces found." | ||
}, | ||
) | ||
.note(markup! { | ||
"Replace the irregular whitespaces with normal whitespaces or tabs." | ||
}), | ||
) | ||
} | ||
} | ||
|
||
fn get_irregular_whitespace(node: &JsModule) -> Vec<TextRange> { | ||
let syntax = node.syntax(); | ||
let mut all_whitespaces_trivia: Vec<SyntaxTriviaPiece<JsLanguage>> = vec![]; | ||
let is_whitespace = |trivia: &SyntaxTriviaPiece<JsLanguage>| { | ||
trivia.is_whitespace() && !trivia.text().replace(' ', "").is_empty() | ||
}; | ||
|
||
for token in syntax.descendants_tokens(Direction::Next) { | ||
let leading_trivia_pieces = token.leading_trivia().pieces(); | ||
let trailing_trivia_pieces = token.trailing_trivia().pieces(); | ||
|
||
for trivia in leading_trivia_pieces { | ||
if is_whitespace(&trivia) { | ||
all_whitespaces_trivia.push(trivia); | ||
} | ||
} | ||
|
||
for trivia in trailing_trivia_pieces { | ||
if is_whitespace(&trivia) { | ||
all_whitespaces_trivia.push(trivia); | ||
} | ||
} | ||
} | ||
|
||
all_whitespaces_trivia | ||
.iter() | ||
.filter_map(|trivia| { | ||
let has_irregular_whitespace = trivia.text().chars().any(|char| { | ||
IRREGULAR_WHITESPACES | ||
.iter() | ||
.any(|irregular_whitespace| &char == irregular_whitespace) | ||
}); | ||
has_irregular_whitespace.then(|| trivia.text_range()) | ||
}) | ||
.collect::<Vec<TextRange>>() | ||
} |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
20 changes: 20 additions & 0 deletions
20
crates/biome_js_analyze/tests/specs/nursery/noIrregularWhitespace/invalid.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
/* \u{b} */ constfoo='thing'; | ||
/* \u{c} */ constfoo='thing'; | ||
/* \u{feff} */ constfoo='thing'; | ||
/* \u{a0} */ const foo = 'thing'; | ||
/* \u{1680} */ const foo = 'thing'; | ||
/* \u{2000} */ const foo = 'thing'; | ||
/* \u{2001} */ const foo = 'thing'; | ||
/* \u{2002} */ const foo = 'thing'; | ||
/* \u{2003} */ const foo = 'thing'; | ||
/* \u{2004} */ const foo = 'thing'; | ||
/* \u{2005} */ const foo = 'thing'; | ||
/* \u{2006} */ const foo = 'thing'; | ||
/* \u{2007} */ const foo = 'thing'; | ||
/* \u{2008} */ const foo = 'thing'; | ||
/* \u{2009} */ const foo = 'thing'; | ||
/* \u{200a} */ const foo = 'thing'; | ||
/* \u{200b} */ constfoo='thing'; | ||
/* \u{202f} */ const foo = 'thing'; | ||
/* \u{205f} */ const foo = 'thing'; | ||
/* \u{3000} */ const foo = 'thing'; |
Oops, something went wrong.