mirror of
https://github.com/swc-project/swc.git
synced 2024-12-23 13:51:19 +03:00
feat(es/lints): Implement no-throw-literal
(#4477)
This commit is contained in:
parent
8611b1bf23
commit
3a8cade209
@ -0,0 +1,9 @@
|
||||
{
|
||||
"jsc": {
|
||||
"lints": {
|
||||
"no-throw-literal": [
|
||||
"error"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,43 @@
|
||||
throw 'error';
|
||||
throw 0;
|
||||
throw false;
|
||||
throw null;
|
||||
throw {};
|
||||
throw undefined;
|
||||
throw 'a' + 'b';
|
||||
var b = new Error(); throw 'a' + b;
|
||||
throw foo = 'error';
|
||||
throw foo += new Error();
|
||||
throw foo &= new Error();
|
||||
throw foo &&= 'literal'
|
||||
throw new Error(), 1, 2, 3;
|
||||
throw 'literal' && 'not an Error';
|
||||
throw foo && 'literal'
|
||||
throw foo ? 'not an Error' : 'literal';
|
||||
throw `${err}`;
|
||||
|
||||
// valid
|
||||
throw new Error();
|
||||
throw new Error('error');
|
||||
throw Error('error');
|
||||
var e = new Error(); throw e;
|
||||
try { throw new Error(); } catch (e) { throw e; };
|
||||
throw a;
|
||||
throw foo();
|
||||
throw new foo();
|
||||
throw foo.bar;
|
||||
throw foo[bar];
|
||||
class C { #field; foo() { throw foo.#field; } }
|
||||
throw foo = new Error();
|
||||
throw foo.bar ||= 'literal'
|
||||
throw foo[bar] ??= 'literal'
|
||||
throw 1, 2, new Error();
|
||||
throw 'literal' && new Error();
|
||||
throw new Error() || 'literal';
|
||||
throw foo ? new Error() : 'literal';
|
||||
throw foo ? 'literal' : new Error();
|
||||
throw tag`${foo}`;
|
||||
function* foo() { var index = 0; throw yield index++; }
|
||||
async function foo() { throw await bar; }
|
||||
throw obj?.foo;
|
||||
throw obj?.foo();
|
@ -0,0 +1,96 @@
|
||||
|
||||
x Expected an error object to be thrown
|
||||
,----
|
||||
1 | throw 'error';
|
||||
: ^^^^^^^^^^^^^^
|
||||
`----
|
||||
|
||||
x Expected an error object to be thrown
|
||||
,----
|
||||
2 | throw 0;
|
||||
: ^^^^^^^^
|
||||
`----
|
||||
|
||||
x Expected an error object to be thrown
|
||||
,----
|
||||
3 | throw false;
|
||||
: ^^^^^^^^^^^^
|
||||
`----
|
||||
|
||||
x Expected an error object to be thrown
|
||||
,----
|
||||
4 | throw null;
|
||||
: ^^^^^^^^^^^
|
||||
`----
|
||||
|
||||
x Expected an error object to be thrown
|
||||
,----
|
||||
5 | throw {};
|
||||
: ^^^^^^^^^
|
||||
`----
|
||||
|
||||
x Do not throw undefined
|
||||
,----
|
||||
6 | throw undefined;
|
||||
: ^^^^^^^^^^^^^^^^
|
||||
`----
|
||||
|
||||
x Expected an error object to be thrown
|
||||
,----
|
||||
7 | throw 'a' + 'b';
|
||||
: ^^^^^^^^^^^^^^^^
|
||||
`----
|
||||
|
||||
x Expected an error object to be thrown
|
||||
,----
|
||||
9 | throw foo = 'error';
|
||||
: ^^^^^^^^^^^^^^^^^^^^
|
||||
`----
|
||||
|
||||
x Expected an error object to be thrown
|
||||
,----
|
||||
10 | throw foo += new Error();
|
||||
: ^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
`----
|
||||
|
||||
x Expected an error object to be thrown
|
||||
,----
|
||||
11 | throw foo &= new Error();
|
||||
: ^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
`----
|
||||
|
||||
x Expected an error object to be thrown
|
||||
,----
|
||||
12 | throw foo &&= 'literal'
|
||||
: ^^^^^^^^^^^^^^^^^^^^^^^
|
||||
`----
|
||||
|
||||
x Expected an error object to be thrown
|
||||
,----
|
||||
13 | throw new Error(), 1, 2, 3;
|
||||
: ^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
`----
|
||||
|
||||
x Expected an error object to be thrown
|
||||
,----
|
||||
14 | throw 'literal' && 'not an Error';
|
||||
: ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
`----
|
||||
|
||||
x Expected an error object to be thrown
|
||||
,----
|
||||
15 | throw foo && 'literal'
|
||||
: ^^^^^^^^^^^^^^^^^^^^^^
|
||||
`----
|
||||
|
||||
x Expected an error object to be thrown
|
||||
,----
|
||||
16 | throw foo ? 'not an Error' : 'literal';
|
||||
: ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
`----
|
||||
|
||||
x Expected an error object to be thrown
|
||||
,----
|
||||
17 | throw `${err}`;
|
||||
: ^^^^^^^^^^^^^^^
|
||||
`----
|
@ -164,4 +164,8 @@ pub struct LintConfig {
|
||||
#[cfg(feature = "non_critical_lints")]
|
||||
#[serde(default, alias = "noObjCalls")]
|
||||
pub no_obj_calls: RuleConfig<()>,
|
||||
|
||||
#[cfg(feature = "non_critical_lints")]
|
||||
#[serde(default, alias = "noThrowLiteral")]
|
||||
pub no_throw_literal: RuleConfig<()>,
|
||||
}
|
||||
|
@ -30,6 +30,7 @@ pub(crate) mod non_critical_lints {
|
||||
pub mod no_obj_calls;
|
||||
pub mod no_param_reassign;
|
||||
pub mod no_restricted_syntax;
|
||||
pub mod no_throw_literal;
|
||||
pub mod no_use_before_define;
|
||||
pub mod prefer_regex_literals;
|
||||
pub mod quotes;
|
||||
@ -155,6 +156,10 @@ pub fn all(lint_params: LintParams) -> Vec<Box<dyn Rule>> {
|
||||
unresolved_ctxt,
|
||||
&lint_config.no_obj_calls,
|
||||
));
|
||||
|
||||
rules.extend(no_throw_literal::no_throw_literal(
|
||||
&lint_config.no_throw_literal,
|
||||
))
|
||||
}
|
||||
|
||||
rules
|
||||
|
111
crates/swc_ecma_lints/src/rules/no_throw_literal.rs
Normal file
111
crates/swc_ecma_lints/src/rules/no_throw_literal.rs
Normal file
@ -0,0 +1,111 @@
|
||||
use swc_common::{errors::HANDLER, Span};
|
||||
use swc_ecma_ast::*;
|
||||
use swc_ecma_visit::{Visit, VisitWith};
|
||||
|
||||
use crate::{
|
||||
config::{LintRuleReaction, RuleConfig},
|
||||
rule::{visitor_rule, Rule},
|
||||
rules::utils::unwrap_seqs_and_parens,
|
||||
};
|
||||
|
||||
const EXPECTED_AN_ERROR_OBJECT: &str = "Expected an error object to be thrown";
|
||||
const NO_THROW_UNDEFINED: &str = "Do not throw undefined";
|
||||
|
||||
pub fn no_throw_literal(config: &RuleConfig<()>) -> Option<Box<dyn Rule>> {
|
||||
let rule_reaction = config.get_rule_reaction();
|
||||
|
||||
match rule_reaction {
|
||||
LintRuleReaction::Off => None,
|
||||
_ => Some(visitor_rule(NoThrowLiteral::new(rule_reaction))),
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
struct NoThrowLiteral {
|
||||
expected_reaction: LintRuleReaction,
|
||||
}
|
||||
|
||||
impl NoThrowLiteral {
|
||||
fn new(expected_reaction: LintRuleReaction) -> Self {
|
||||
Self { expected_reaction }
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
_ => {}
|
||||
});
|
||||
}
|
||||
|
||||
fn could_be_error(&self, expr: &Expr) -> bool {
|
||||
match unwrap_seqs_and_parens(expr) {
|
||||
Expr::Ident(_)
|
||||
| Expr::New(_)
|
||||
| Expr::Call(_)
|
||||
| Expr::Member(_)
|
||||
| Expr::TaggedTpl(_)
|
||||
| Expr::Yield(_)
|
||||
| Expr::Await(_)
|
||||
| Expr::OptChain(_) => true,
|
||||
|
||||
Expr::Assign(AssignExpr {
|
||||
op, left, right, ..
|
||||
}) => match op {
|
||||
op!("=") | op!("&&=") => self.could_be_error(right.as_ref()),
|
||||
op!("||=") | op!("??=") => {
|
||||
if let PatOrExpr::Expr(left) = left {
|
||||
self.could_be_error(left.as_ref()) || self.could_be_error(right.as_ref())
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
_ => false,
|
||||
},
|
||||
|
||||
Expr::Bin(BinExpr {
|
||||
op, left, right, ..
|
||||
}) => {
|
||||
if let op!("&&") = op {
|
||||
self.could_be_error(right.as_ref())
|
||||
} else {
|
||||
self.could_be_error(left.as_ref()) || self.could_be_error(right.as_ref())
|
||||
}
|
||||
}
|
||||
|
||||
Expr::Cond(CondExpr { cons, alt, .. }) => {
|
||||
self.could_be_error(cons.as_ref()) || self.could_be_error(alt.as_ref())
|
||||
}
|
||||
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
fn check(&self, throw_stmt: &ThrowStmt) {
|
||||
let arg = unwrap_seqs_and_parens(throw_stmt.arg.as_ref());
|
||||
|
||||
if !self.could_be_error(arg) {
|
||||
self.emit_report(throw_stmt.span, EXPECTED_AN_ERROR_OBJECT);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if let Expr::Ident(Ident { sym, .. }) = arg {
|
||||
if &*sym == "undefined" {
|
||||
self.emit_report(throw_stmt.span, NO_THROW_UNDEFINED);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Visit for NoThrowLiteral {
|
||||
fn visit_throw_stmt(&mut self, throw_stmt: &ThrowStmt) {
|
||||
self.check(throw_stmt);
|
||||
|
||||
throw_stmt.visit_children_with(self);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user