feat(es/lints): Implement no-restricted-syntax rule (#3607)

This commit is contained in:
Artur 2022-02-19 12:06:10 +03:00 committed by GitHub
parent 144b2c9f9f
commit 72343baf5b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 272 additions and 1 deletions

View File

@ -0,0 +1,17 @@
{
"jsc": {
"lints": {
"noRestrictedSyntax": [
"error",
{
"BinaryExpression": [
{
"message": "no 'in' expession",
"operator": "in"
}
]
}
]
}
}
}

View File

@ -0,0 +1,7 @@
'a' in { a: 10 };
() => {
// nested
if ('a' in { a: 10 }) {}
}

View File

@ -0,0 +1,12 @@
error: no 'in' expession
|
1 | 'a' in { a: 10 };
| ^^^^^^^^^^^^^^^^
error: no 'in' expession
|
6 | if ('a' in { a: 10 }) {}
| ^^^^^^^^^^^^^^^^

View File

@ -0,0 +1,14 @@
{
"jsc": {
"lints": {
"noRestrictedSyntax": [
"error",
{
"ForInExpression": {
"message": "no 'for-in' expession"
}
}
]
}
}
}

View File

@ -0,0 +1 @@
for (var x in {}) {}

View File

@ -0,0 +1,6 @@
error: no 'for-in' expession
|
1 | for (var x in {}) {}
| ^^^^^^^^^^^^^^^^^^^^

View File

@ -0,0 +1,14 @@
{
"jsc": {
"lints": {
"noRestrictedSyntax": [
"error",
{
"ForOfExpression": {
"message": "no 'for-of' expession"
}
}
]
}
}
}

View File

@ -0,0 +1 @@
for (var x of {}) {}

View File

@ -0,0 +1,6 @@
error: no 'for-of' expession
|
1 | for (var x of {}) {}
| ^^^^^^^^^^^^^^^^^^^^

View File

@ -0,0 +1,16 @@
{
"jsc": {
"lints": {
"noRestrictedSyntax": [
"error",
{
"LabelStatement": [
{
"message": "no label statement"
}
]
}
]
}
}
}

View File

@ -0,0 +1,5 @@
loop1:
while (true) {
continue loop1;
}

View File

@ -0,0 +1,10 @@
error: no label statement
|
1 | / loop1:
2 | |
3 | | while (true) {
4 | | continue loop1;
5 | | }
| |_^

View File

@ -5,7 +5,7 @@ use serde::{Deserialize, Serialize};
#[cfg(feature = "non_critical_lints")]
use crate::rules::non_critical_lints::{
dot_notation::DotNotationConfig, eqeqeq::EqeqeqConfig, no_console::NoConsoleConfig,
no_use_before_define::NoUseBeforeDefineConfig,
no_restricted_syntax::NoRestrictedSyntaxConfig, no_use_before_define::NoUseBeforeDefineConfig,
prefer_regex_literals::PreferRegexLiteralsConfig, quotes::QuotesConfig,
};
@ -104,4 +104,8 @@ pub struct LintConfig {
#[cfg(feature = "non_critical_lints")]
#[serde(default)]
pub eqeqeq: RuleConfig<EqeqeqConfig>,
#[cfg(feature = "non_critical_lints")]
#[serde(default, alias = "noRestrictedSyntax")]
pub no_restricted_syntax: RuleConfig<NoRestrictedSyntaxConfig>,
}

View File

@ -21,6 +21,7 @@ pub(crate) mod non_critical_lints {
pub mod no_console;
pub mod no_debugger;
pub mod no_empty_pattern;
pub mod no_restricted_syntax;
pub mod no_use_before_define;
pub mod prefer_regex_literals;
pub mod quotes;
@ -93,6 +94,10 @@ pub fn all(lint_params: LintParams) -> Vec<Box<dyn Rule>> {
));
rules.extend(eqeqeq::eqeqeq(&lint_config.eqeqeq));
rules.extend(no_restricted_syntax::no_restricted_syntax(
&lint_config.no_restricted_syntax,
));
}
rules

View File

@ -0,0 +1,153 @@
use serde::{Deserialize, Serialize};
use swc_common::{errors::HANDLER, Span};
use swc_ecma_ast::*;
use swc_ecma_visit::{noop_visit_type, Visit, VisitWith};
use crate::{
config::{LintRuleReaction, RuleConfig},
rule::{visitor_rule, Rule},
};
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
struct BinaryExpression {
message: String,
operator: BinaryOp,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
struct ForInExpression {
message: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
struct ForOfExpression {
message: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
struct WithStatement {
message: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
struct LabelStatement {
message: String,
}
#[derive(Debug, Default, Clone, Serialize, Deserialize)]
#[serde(rename_all = "PascalCase")]
pub struct NoRestrictedSyntaxConfig {
binary_expression: Option<Vec<BinaryExpression>>,
for_in_expression: Option<ForInExpression>,
for_of_expression: Option<ForOfExpression>,
with_statement: Option<Vec<WithStatement>>,
label_statement: Option<Vec<LabelStatement>>,
}
pub fn no_restricted_syntax(
config: &RuleConfig<NoRestrictedSyntaxConfig>,
) -> Option<Box<dyn Rule>> {
match config.get_rule_reaction() {
LintRuleReaction::Off => None,
_ => Some(visitor_rule(NoRestrictedSyntax::new(config))),
}
}
#[derive(Debug, Default)]
struct NoRestrictedSyntax {
expected_reaction: LintRuleReaction,
binary_expr: Option<Vec<BinaryExpression>>,
for_in_expression: Option<ForInExpression>,
for_of_expression: Option<ForOfExpression>,
with_statement: Option<Vec<WithStatement>>,
label_statement: Option<Vec<LabelStatement>>,
}
impl NoRestrictedSyntax {
fn new(config: &RuleConfig<NoRestrictedSyntaxConfig>) -> Self {
let rule_config = config.get_rule_config();
Self {
expected_reaction: config.get_rule_reaction(),
binary_expr: rule_config.binary_expression.clone(),
for_in_expression: rule_config.for_in_expression.clone(),
for_of_expression: rule_config.for_of_expression.clone(),
with_statement: rule_config.with_statement.clone(),
label_statement: rule_config.label_statement.clone(),
}
}
fn emit_report(&self, span: Span, message: &str) {
HANDLER.with(|handler| match self.expected_reaction {
LintRuleReaction::Error => {
handler.struct_span_err(span, message).emit();
}
LintRuleReaction::Warning => {
handler.struct_span_warn(span, message).emit();
}
_ => {}
});
}
}
impl Visit for NoRestrictedSyntax {
noop_visit_type!();
fn visit_expr(&mut self, expr: &Expr) {
if let Expr::Bin(BinExpr { span, op, .. }) = expr {
let op = *op;
if let Some(binary_expr) = &self.binary_expr {
let rule = binary_expr.iter().find(|rule| rule.operator == op);
if let Some(BinaryExpression { message, .. }) = rule {
self.emit_report(*span, message);
}
}
}
expr.visit_children_with(self);
}
fn visit_for_in_stmt(&mut self, for_in: &ForInStmt) {
if let Some(ForInExpression { message }) = &self.for_in_expression {
self.emit_report(for_in.span, message);
}
for_in.visit_children_with(self);
}
fn visit_for_of_stmt(&mut self, for_of: &ForOfStmt) {
if let Some(ForOfExpression { message }) = &self.for_of_expression {
self.emit_report(for_of.span, message);
}
for_of.visit_children_with(self);
}
fn visit_with_stmt(&mut self, with_stmt: &WithStmt) {
if let Some(rules) = &self.with_statement {
rules.iter().for_each(|rule| {
self.emit_report(with_stmt.span, &rule.message);
});
}
with_stmt.visit_children_with(self);
}
fn visit_labeled_stmt(&mut self, labeled_stmt: &LabeledStmt) {
if let Some(rules) = &self.label_statement {
rules.iter().for_each(|rule| {
self.emit_report(labeled_stmt.span, &rule.message);
});
}
labeled_stmt.visit_children_with(self);
}
}