Skip to content

Commit

Permalink
feat(linter): implement noOctalEscape
Browse files Browse the repository at this point in the history
  • Loading branch information
fireairforce committed Sep 19, 2024
1 parent 00760d4 commit 880e6e8
Show file tree
Hide file tree
Showing 11 changed files with 1,546 additions and 27 deletions.
59 changes: 34 additions & 25 deletions crates/biome_configuration/src/analyzer/linter/rules.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3305,6 +3305,9 @@ pub struct Nursery {
#[serde(skip_serializing_if = "Option::is_none")]
pub no_missing_var_function:
Option<RuleConfiguration<biome_css_analyze::options::NoMissingVarFunction>>,
#[doc = "Disallow octal escape sequences in string literals."]
#[serde(skip_serializing_if = "Option::is_none")]
pub no_octal_escape: Option<RuleConfiguration<biome_js_analyze::options::NoOctalEscape>>,
#[doc = "Disallow the use of process.env."]
#[serde(skip_serializing_if = "Option::is_none")]
pub no_process_env: Option<RuleConfiguration<biome_js_analyze::options::NoProcessEnv>>,
Expand Down Expand Up @@ -3411,6 +3414,7 @@ impl Nursery {
"noExportedImports",
"noIrregularWhitespace",
"noMissingVarFunction",
"noOctalEscape",
"noProcessEnv",
"noRestrictedImports",
"noRestrictedTypes",
Expand Down Expand Up @@ -3451,10 +3455,11 @@ impl Nursery {
RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[2]),
RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[3]),
RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[8]),
RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[15]),
RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[16]),
RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[17]),
RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[20]),
RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[18]),
RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[21]),
RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[23]),
RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[24]),
RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[27]),
Expand Down Expand Up @@ -3551,62 +3556,62 @@ impl Nursery {
index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[8]));
}
}
if let Some(rule) = self.no_process_env.as_ref() {
if let Some(rule) = self.no_octal_escape.as_ref() {
if rule.is_enabled() {
index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[9]));
}
}
if let Some(rule) = self.no_restricted_imports.as_ref() {
if let Some(rule) = self.no_process_env.as_ref() {
if rule.is_enabled() {
index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[10]));
}
}
if let Some(rule) = self.no_restricted_types.as_ref() {
if let Some(rule) = self.no_restricted_imports.as_ref() {
if rule.is_enabled() {
index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[11]));
}
}
if let Some(rule) = self.no_secrets.as_ref() {
if let Some(rule) = self.no_restricted_types.as_ref() {
if rule.is_enabled() {
index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[12]));
}
}
if let Some(rule) = self.no_static_element_interactions.as_ref() {
if let Some(rule) = self.no_secrets.as_ref() {
if rule.is_enabled() {
index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[13]));
}
}
if let Some(rule) = self.no_substr.as_ref() {
if let Some(rule) = self.no_static_element_interactions.as_ref() {
if rule.is_enabled() {
index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[14]));
}
}
if let Some(rule) = self.no_unknown_pseudo_class.as_ref() {
if let Some(rule) = self.no_substr.as_ref() {
if rule.is_enabled() {
index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[15]));
}
}
if let Some(rule) = self.no_unknown_pseudo_element.as_ref() {
if let Some(rule) = self.no_unknown_pseudo_class.as_ref() {
if rule.is_enabled() {
index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[16]));
}
}
if let Some(rule) = self.no_useless_escape_in_regex.as_ref() {
if let Some(rule) = self.no_unknown_pseudo_element.as_ref() {
if rule.is_enabled() {
index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[17]));
}
}
if let Some(rule) = self.no_value_at_rule.as_ref() {
if let Some(rule) = self.no_useless_escape_in_regex.as_ref() {
if rule.is_enabled() {
index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[18]));
}
}
if let Some(rule) = self.use_adjacent_overload_signatures.as_ref() {
if let Some(rule) = self.no_value_at_rule.as_ref() {
if rule.is_enabled() {
index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[19]));
}
}
if let Some(rule) = self.use_aria_props_supported_by_role.as_ref() {
if let Some(rule) = self.use_adjacent_overload_signatures.as_ref() {
if rule.is_enabled() {
index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[20]));
}
Expand Down Expand Up @@ -3705,62 +3710,62 @@ impl Nursery {
index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[8]));
}
}
if let Some(rule) = self.no_process_env.as_ref() {
if let Some(rule) = self.no_octal_escape.as_ref() {
if rule.is_disabled() {
index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[9]));
}
}
if let Some(rule) = self.no_restricted_imports.as_ref() {
if let Some(rule) = self.no_process_env.as_ref() {
if rule.is_disabled() {
index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[10]));
}
}
if let Some(rule) = self.no_restricted_types.as_ref() {
if let Some(rule) = self.no_restricted_imports.as_ref() {
if rule.is_disabled() {
index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[11]));
}
}
if let Some(rule) = self.no_secrets.as_ref() {
if let Some(rule) = self.no_restricted_types.as_ref() {
if rule.is_disabled() {
index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[12]));
}
}
if let Some(rule) = self.no_static_element_interactions.as_ref() {
if let Some(rule) = self.no_secrets.as_ref() {
if rule.is_disabled() {
index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[13]));
}
}
if let Some(rule) = self.no_substr.as_ref() {
if let Some(rule) = self.no_static_element_interactions.as_ref() {
if rule.is_disabled() {
index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[14]));
}
}
if let Some(rule) = self.no_unknown_pseudo_class.as_ref() {
if let Some(rule) = self.no_substr.as_ref() {
if rule.is_disabled() {
index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[15]));
}
}
if let Some(rule) = self.no_unknown_pseudo_element.as_ref() {
if let Some(rule) = self.no_unknown_pseudo_class.as_ref() {
if rule.is_disabled() {
index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[16]));
}
}
if let Some(rule) = self.no_useless_escape_in_regex.as_ref() {
if let Some(rule) = self.no_unknown_pseudo_element.as_ref() {
if rule.is_disabled() {
index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[17]));
}
}
if let Some(rule) = self.no_value_at_rule.as_ref() {
if let Some(rule) = self.no_useless_escape_in_regex.as_ref() {
if rule.is_disabled() {
index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[18]));
}
}
if let Some(rule) = self.use_adjacent_overload_signatures.as_ref() {
if let Some(rule) = self.no_value_at_rule.as_ref() {
if rule.is_disabled() {
index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[19]));
}
}
if let Some(rule) = self.use_aria_props_supported_by_role.as_ref() {
if let Some(rule) = self.use_adjacent_overload_signatures.as_ref() {
if rule.is_disabled() {
index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[20]));
}
Expand Down Expand Up @@ -3882,6 +3887,10 @@ impl Nursery {
.no_missing_var_function
.as_ref()
.map(|conf| (conf.level(), conf.get_options())),
"noOctalEscape" => self
.no_octal_escape
.as_ref()
.map(|conf| (conf.level(), conf.get_options())),
"noProcessEnv" => self
.no_process_env
.as_ref()
Expand Down
3 changes: 2 additions & 1 deletion crates/biome_diagnostics_categories/src/categories.rs
Original file line number Diff line number Diff line change
Expand Up @@ -150,9 +150,10 @@ define_categories! {
"lint/nursery/noInvalidPositionAtImportRule": "https://biomejs.dev/linter/rules/no-invalid-position-at-import-rule",
"lint/nursery/noIrregularWhitespace": "https://biomejs.dev/linter/rules/no-irregular-whitespace",
"lint/nursery/noMissingGenericFamilyKeyword": "https://biomejs.dev/linter/rules/no-missing-generic-family-keyword",
"lint/nursery/noMissingVarFunction": "https://biomejs.dev/linter/rules/no-missing-var-function",
"lint/nursery/noOctalEscape": "https://biomejs.dev/linter/rules/no-octal-escape",
"lint/nursery/noProcessEnv": "https://biomejs.dev/linter/rules/no-process-env",
"lint/nursery/noReactSpecificProps": "https://biomejs.dev/linter/rules/no-react-specific-props",
"lint/nursery/noMissingVarFunction": "https://biomejs.dev/linter/rules/no-missing-var-function",
"lint/nursery/noRestrictedImports": "https://biomejs.dev/linter/rules/no-restricted-imports",
"lint/nursery/noRestrictedTypes": "https://biomejs.dev/linter/rules/no-restricted-types",
"lint/nursery/noSecrets": "https://biomejs.dev/linter/rules/no-secrets",
Expand Down
2 changes: 2 additions & 0 deletions crates/biome_js_analyze/src/lint/nursery.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ pub mod no_dynamic_namespace_import_access;
pub mod no_enum;
pub mod no_exported_imports;
pub mod no_irregular_whitespace;
pub mod no_octal_escape;
pub mod no_process_env;
pub mod no_restricted_imports;
pub mod no_restricted_types;
Expand Down Expand Up @@ -36,6 +37,7 @@ declare_lint_group! {
self :: no_enum :: NoEnum ,
self :: no_exported_imports :: NoExportedImports ,
self :: no_irregular_whitespace :: NoIrregularWhitespace ,
self :: no_octal_escape :: NoOctalEscape ,
self :: no_process_env :: NoProcessEnv ,
self :: no_restricted_imports :: NoRestrictedImports ,
self :: no_restricted_types :: NoRestrictedTypes ,
Expand Down
84 changes: 84 additions & 0 deletions crates/biome_js_analyze/src/lint/nursery/no_octal_escape.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
use biome_analyze::{
context::RuleContext, declare_lint_rule, Ast, Rule, RuleDiagnostic, RuleSource,
};
use biome_console::markup;
use biome_js_syntax::JsStringLiteralExpression;
use biome_rowan::AstNode;

declare_lint_rule! {
/// Disallow octal escape sequences in string literals
///
/// As of the ECMAScript 5 specification, octal escape sequences in string literals are deprecated and should not be used.
/// Unicode escape sequences should be used instead.
///
/// ### Examples
///
/// ### Invalid
///
/// ```js,expect_diagnostic
/// var foo = "Copyright \251";
/// ```
///
/// ### Valid
///
/// ```js
/// var foo = "Copyright \u00A9"; // unicode
///
/// var foo = "Copyright \xA9"; // hexadecimal
/// ```
///
pub NoOctalEscape {
version: "next",
name: "noOctalEscape",
language: "js",
sources: &[RuleSource::Eslint("no-octal-escape")],
recommended: false,
}
}

impl Rule for NoOctalEscape {
type Query = Ast<JsStringLiteralExpression>;
type State = ();
type Signals = Option<Self::State>;
type Options = ();

fn run(ctx: &RuleContext<Self>) -> Self::Signals {
let node = ctx.query();
let token = node.value_token().ok()?;
let text = token.text();

let bytes = text.as_bytes();
let mut byte_it = bytes.iter();
while let Some(&byte) = byte_it.next() {
if byte == b'\\' {
if let Some(&next_byte) = byte_it.next() {
if (b'0'..=b'7').contains(&next_byte) {
return Some(());
}
}
}
}
None
}

fn diagnostic(ctx: &RuleContext<Self>, _state: &Self::State) -> Option<RuleDiagnostic> {
let node = ctx.query();
let token = node.value_token().ok()?;
let text = token.text();
Some(
RuleDiagnostic::new(
rule_category!(),
node.range(),
markup! {
"Don't use "<Emphasis>"octal"</Emphasis>
},
)
.note(markup! {
"Don't use octal: " {text}
})
.note(markup! {
"Use '\\u...' instead."
}),
)
}
}
2 changes: 2 additions & 0 deletions crates/biome_js_analyze/src/options.rs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
var foo = "foo \01 bar";
var foo = "foo \000 bar";
var foo = "foo \377 bar";
var foo = "foo \378 bar";
var foo = "foo \37a bar";
var foo = "foo \381 bar";
var foo = "foo \3a1 bar";
var foo = "foo \251 bar";
var foo = "foo \258 bar";
var foo = "foo \25a bar";
var foo = "\3s51";
var foo = "\77";
var foo = "\78";
var foo = "\5a";
var foo = "\751";
var foo = "foo \400 bar";
var foo = "foo \400 bar";
var foo = "\t\1";
var foo = "\\\751";
'\0\1'
'\0 \1'
'\0\01'
'\0 \01'
'\0a\1'
'\0a\01'
'\0\08'
'\1'
'\2'
'\7'
'\00'
'\01'
'\02'
'\07'
'\08'
'\09'
'\10'
'\12'
' \1'
'\1 '
'a\1'
'\1a'
'a\1a'
' \01'
'\01 '
'a\01'
'\01a'
'a\01a'
'a\08a'
'\n\1'
'\n\01'
'\n\08'
'\\\1'
'\\\01'
'\\\08'
'\\n\1'
'\01\02'
'\02\01'
'\01\2'
'\2\01'
'\08\1'
'foo \1 bar \2'



Loading

0 comments on commit 880e6e8

Please sign in to comment.