mirror of
https://github.com/danbulant/oxc
synced 2026-05-19 12:19:15 +00:00
feat(linter): add allowReject option to no-useless-promise-resolve-reject (#7274)
followup to #7232.
This commit is contained in:
parent
84038ee434
commit
01ddf37843
1 changed files with 337 additions and 146 deletions
|
|
@ -27,7 +27,12 @@ fn reject(span: Span, preferred: &str) -> OxcDiagnostic {
|
|||
}
|
||||
|
||||
#[derive(Debug, Default, Clone)]
|
||||
pub struct NoUselessPromiseResolveReject;
|
||||
pub struct NoUselessPromiseResolveReject(Box<NoUselessPromiseResolveRejectOptions>);
|
||||
|
||||
#[derive(Debug, Default, Clone)]
|
||||
pub struct NoUselessPromiseResolveRejectOptions {
|
||||
pub allow_reject: bool,
|
||||
}
|
||||
|
||||
declare_oxc_lint!(
|
||||
/// ### What it does
|
||||
|
|
@ -55,6 +60,17 @@ declare_oxc_lint!(
|
|||
);
|
||||
|
||||
impl Rule for NoUselessPromiseResolveReject {
|
||||
fn from_configuration(value: serde_json::Value) -> Self {
|
||||
let config = value.get(0);
|
||||
|
||||
let allow_reject = config
|
||||
.and_then(|c| c.get("allowReject"))
|
||||
.and_then(serde_json::Value::as_bool)
|
||||
.unwrap_or_default();
|
||||
|
||||
Self(Box::new(NoUselessPromiseResolveRejectOptions { allow_reject }))
|
||||
}
|
||||
|
||||
fn run<'a>(&self, node: &AstNode<'a>, ctx: &LintContext<'a>) {
|
||||
let AstKind::CallExpression(call_expr) = node.kind() else {
|
||||
return;
|
||||
|
|
@ -130,6 +146,9 @@ impl Rule for NoUselessPromiseResolveReject {
|
|||
);
|
||||
}
|
||||
"reject" => {
|
||||
if self.0.allow_reject {
|
||||
return;
|
||||
}
|
||||
ctx.diagnostic_with_fix(
|
||||
reject(node.kind().span(), if is_yield { "yield" } else { "return" }),
|
||||
|fixer| {
|
||||
|
|
@ -380,159 +399,227 @@ fn test() {
|
|||
|
||||
let pass = vec => Promise.resolve(foo))",
|
||||
(r"promise.then(() => foo).catch(() => bar).finally(() => baz)", None),
|
||||
(r"promise.then(() => foo, () => bar).finally(() => baz)", None),
|
||||
(r"promise.then(x, y, () => Promise.resolve(foo))", None),
|
||||
(r"promise.catch(x, () => Promise.resolve(foo))", None),
|
||||
(r"promise.finally(x, () => Promise.resolve(foo))", None),
|
||||
(r"promise[then](() => Promise.resolve(foo))", None),
|
||||
// additional cases:
|
||||
r"(async () => { Promise.resolve().then(() => console.log('foo')); })();",
|
||||
(r"(async () => { Promise.resolve().then(() => console.log('foo')); })();", None),
|
||||
// TODO: enhance to report this case?
|
||||
r#"fs.promises.readFile("foo", 'utf8').then(undefined, err => err.code === 'ENOENT' ? Promise.resolve('{}') : Promise.reject(err))"#,
|
||||
r"Promise.resolve(4).then(function(x) { return x })",
|
||||
r"Promise.reject(4).then(function(x) { return x })",
|
||||
r"Promise.resolve(4).then(function() {})",
|
||||
r"Promise.reject(4).then(function() {})",
|
||||
r"doThing().then(function() { return 4 })",
|
||||
r"doThing().then(function() { throw 4 })",
|
||||
r"doThing().then(null, function() { return 4 })",
|
||||
r"doThing().then(null, function() { throw 4 })",
|
||||
r"doThing().catch(null, function() { return 4 })",
|
||||
r"doThing().catch(null, function() { throw 4 })",
|
||||
r"doThing().then(function() { return Promise.all([a,b,c]) })",
|
||||
r"doThing().then(() => 4)",
|
||||
r"doThing().then(() => { throw 4 })",
|
||||
r"doThing().then(()=>{}, () => 4)",
|
||||
r"doThing().then(()=>{}, () => { throw 4 })",
|
||||
r"doThing().catch(() => 4)",
|
||||
r"doThing().catch(() => { throw 4 })",
|
||||
r"var x = function() { return Promise.resolve(4) }",
|
||||
r"function y() { return Promise.resolve(4) }",
|
||||
r"function then() { return Promise.reject() }",
|
||||
r"doThing(function(x) { return Promise.reject(x) })",
|
||||
r"doThing().then(function() { return })",
|
||||
// TODO: support `allow_reject` option
|
||||
// "doThing().then(function() { return Promise.reject(4) })",
|
||||
r"doThing().then((function() { return Promise.resolve(4) }).toString())",
|
||||
// TODO: support `allow_reject` option
|
||||
// "doThing().then(() => Promise.reject(4))",
|
||||
r"doThing().then(function() { return a() })",
|
||||
r"doThing().then(function() { return Promise.a() })",
|
||||
r"doThing().then(() => { return a() })",
|
||||
r"doThing().then(() => { return Promise.a() })",
|
||||
r"doThing().then(() => a())",
|
||||
r"doThing().then(() => Promise.a())",
|
||||
(
|
||||
r#"fs.promises.readFile("foo", 'utf8').then(undefined, err => err.code === 'ENOENT' ? Promise.resolve('{}') : Promise.reject(err))"#,
|
||||
None,
|
||||
),
|
||||
(r"Promise.resolve(4).then(function(x) { return x })", None),
|
||||
(r"Promise.reject(4).then(function(x) { return x })", None),
|
||||
(r"Promise.resolve(4).then(function() {})", None),
|
||||
(r"Promise.reject(4).then(function() {})", None),
|
||||
(r"doThing().then(function() { return 4 })", None),
|
||||
(r"doThing().then(function() { throw 4 })", None),
|
||||
(r"doThing().then(null, function() { return 4 })", None),
|
||||
(r"doThing().then(null, function() { throw 4 })", None),
|
||||
(r"doThing().catch(null, function() { return 4 })", None),
|
||||
(r"doThing().catch(null, function() { throw 4 })", None),
|
||||
(r"doThing().then(function() { return Promise.all([a,b,c]) })", None),
|
||||
(r"doThing().then(() => 4)", None),
|
||||
(r"doThing().then(() => { throw 4 })", None),
|
||||
(r"doThing().then(()=>{}, () => 4)", None),
|
||||
(r"doThing().then(()=>{}, () => { throw 4 })", None),
|
||||
(r"doThing().catch(() => 4)", None),
|
||||
(r"doThing().catch(() => { throw 4 })", None),
|
||||
(r"var x = function() { return Promise.resolve(4) }", None),
|
||||
(r"function y() { return Promise.resolve(4) }", None),
|
||||
(r"function then() { return Promise.reject() }", None),
|
||||
(r"doThing(function(x) { return Promise.reject(x) })", None),
|
||||
(r"doThing().then(function() { return })", None),
|
||||
(
|
||||
"doThing().then(function() { return Promise.reject(4) })",
|
||||
Some(serde_json::json!([{ "allowReject": true }])),
|
||||
),
|
||||
(r"doThing().then((function() { return Promise.resolve(4) }).toString())", None),
|
||||
(
|
||||
"doThing().then(() => Promise.reject(4))",
|
||||
Some(serde_json::json!([{ "allowReject": true }])),
|
||||
),
|
||||
(r"doThing().then(function() { return a() })", None),
|
||||
(r"doThing().then(function() { return Promise.a() })", None),
|
||||
(r"doThing().then(() => { return a() })", None),
|
||||
(r"doThing().then(() => { return Promise.a() })", None),
|
||||
(r"doThing().then(() => a())", None),
|
||||
(r"doThing().then(() => Promise.a())", None),
|
||||
];
|
||||
|
||||
let fail = vec![
|
||||
r"
|
||||
(
|
||||
r"
|
||||
const main = async foo => {
|
||||
if (foo > 4) {
|
||||
return Promise.reject(new Error('🤪'));
|
||||
|
|
@ -540,196 +627,286 @@ fn test() {
|
|||
return Promise.resolve(result);
|
||||
};
|
||||
",
|
||||
None,
|
||||
),
|
||||
// Async function returning Promise.resolve
|
||||
r"async () => Promise.resolve(bar);",
|
||||
r"
|
||||
(r"async () => Promise.resolve(bar);", None),
|
||||
(
|
||||
r"
|
||||
async () => {
|
||||
return Promise.resolve(bar);
|
||||
};
|
||||
",
|
||||
r"
|
||||
None,
|
||||
),
|
||||
(
|
||||
r"
|
||||
async function foo() {
|
||||
return Promise.resolve(bar);
|
||||
}
|
||||
",
|
||||
r"
|
||||
None,
|
||||
),
|
||||
(
|
||||
r"
|
||||
(async function() {
|
||||
return Promise.resolve(bar);
|
||||
});
|
||||
",
|
||||
r"
|
||||
None,
|
||||
),
|
||||
(
|
||||
r"
|
||||
async function * foo() {
|
||||
return Promise.resolve(bar);
|
||||
}
|
||||
",
|
||||
r"
|
||||
None,
|
||||
),
|
||||
(
|
||||
r"
|
||||
(async function*() {
|
||||
return Promise.resolve(bar);
|
||||
});
|
||||
",
|
||||
None,
|
||||
),
|
||||
// Async function returning Promise.reject
|
||||
r"async () => Promise.reject(bar);",
|
||||
r"
|
||||
(r"async () => Promise.reject(bar);", None),
|
||||
(
|
||||
r"
|
||||
async () => {
|
||||
return Promise.reject(bar);
|
||||
};
|
||||
",
|
||||
r"
|
||||
None,
|
||||
),
|
||||
(
|
||||
r"
|
||||
async function foo() {
|
||||
return Promise.reject(bar);
|
||||
}
|
||||
",
|
||||
r"
|
||||
None,
|
||||
),
|
||||
(
|
||||
r"
|
||||
(async function() {
|
||||
return Promise.reject(bar);
|
||||
});
|
||||
",
|
||||
r"
|
||||
None,
|
||||
),
|
||||
(
|
||||
r"
|
||||
async function * foo() {
|
||||
return Promise.reject(bar);
|
||||
}
|
||||
",
|
||||
r"
|
||||
None,
|
||||
),
|
||||
(
|
||||
r"
|
||||
(async function*() {
|
||||
return Promise.reject(bar);
|
||||
});
|
||||
",
|
||||
None,
|
||||
),
|
||||
// Async generator yielding Promise.resolve
|
||||
r"
|
||||
(
|
||||
r"
|
||||
async function * foo() {
|
||||
yield Promise.resolve(bar);
|
||||
}
|
||||
",
|
||||
r"
|
||||
None,
|
||||
),
|
||||
(
|
||||
r"
|
||||
(async function * () {
|
||||
yield Promise.resolve(bar);
|
||||
});
|
||||
",
|
||||
// Async generator yielding Promise.reject
|
||||
r"
|
||||
None,
|
||||
),
|
||||
(
|
||||
// Async generator yielding Promise.reject
|
||||
r"
|
||||
async function * foo() {
|
||||
yield Promise.reject(bar);
|
||||
}
|
||||
",
|
||||
r"
|
||||
None,
|
||||
),
|
||||
(
|
||||
r"
|
||||
(async function * () {
|
||||
yield Promise.reject(bar);
|
||||
});
|
||||
",
|
||||
r"async () => Promise.resolve();",
|
||||
r"
|
||||
None,
|
||||
),
|
||||
(r"async () => Promise.resolve();", None),
|
||||
(
|
||||
r"
|
||||
async function foo() {
|
||||
return Promise.resolve();
|
||||
}
|
||||
",
|
||||
r"async () => Promise.reject();",
|
||||
r"
|
||||
None,
|
||||
),
|
||||
(r"async () => Promise.reject();", None),
|
||||
(
|
||||
r"
|
||||
async function foo() {
|
||||
return Promise.reject();
|
||||
}
|
||||
",
|
||||
r"
|
||||
None,
|
||||
),
|
||||
(
|
||||
r"
|
||||
async function * foo() {
|
||||
yield Promise.resolve();
|
||||
}
|
||||
",
|
||||
None,
|
||||
),
|
||||
// Multiple arguments
|
||||
r"async () => Promise.resolve(bar, baz);",
|
||||
r"async () => Promise.reject(bar, baz);",
|
||||
(r"async () => Promise.resolve(bar, baz);", None),
|
||||
(r"async () => Promise.reject(bar, baz);", None),
|
||||
// Sequence expressions
|
||||
r"async
|
||||
(
|
||||
r"async
|
||||
async function * foo() {
|
||||
yield Promise.resolve((bar, baz));
|
||||
}
|
||||
",
|
||||
r"async () => Promise.resolve((bar, baz))",
|
||||
None,
|
||||
),
|
||||
(r"async () => Promise.resolve((bar, baz))", None),
|
||||
// Arrow function returning an object
|
||||
r"async () => Promise.resolve({})",
|
||||
(r"async () => Promise.resolve({})", None),
|
||||
// Try statements
|
||||
r"
|
||||
(
|
||||
r"
|
||||
async function foo() {
|
||||
try {
|
||||
return Promise.resolve(1);
|
||||
} catch {}
|
||||
}
|
||||
",
|
||||
r"
|
||||
None,
|
||||
),
|
||||
(
|
||||
r"
|
||||
async function foo() {
|
||||
try {
|
||||
return Promise.reject(1);
|
||||
} catch {}
|
||||
}
|
||||
",
|
||||
None,
|
||||
),
|
||||
// Spread arguments
|
||||
r"async () => Promise.resolve(...bar);",
|
||||
r"async () => Promise.reject(...bar);",
|
||||
(r"async () => Promise.resolve(...bar);", None),
|
||||
(r"async () => Promise.reject(...bar);", None),
|
||||
// Yield not in an ExpressionStatement
|
||||
r"#
|
||||
(
|
||||
r"#
|
||||
async function * foo() {
|
||||
const baz = yield Promise.resolve(bar);
|
||||
}
|
||||
#",
|
||||
r"
|
||||
None,
|
||||
),
|
||||
(
|
||||
r"
|
||||
async function * foo() {
|
||||
const baz = yield Promise.reject(bar);
|
||||
}
|
||||
",
|
||||
None,
|
||||
),
|
||||
// Parenthesized Promise.resolve/reject
|
||||
r"async () => (Promise.resolve(bar));",
|
||||
r"async () => (Promise.reject(bar));",
|
||||
r"async () => ((Promise.reject(bar)));",
|
||||
r"
|
||||
(r"async () => (Promise.resolve(bar));", None),
|
||||
(r"async () => (Promise.reject(bar));", None),
|
||||
(r"async () => ((Promise.reject(bar)));", None),
|
||||
(
|
||||
r"
|
||||
async function * foo() {
|
||||
(yield Promise.reject(bar));
|
||||
}
|
||||
",
|
||||
r"
|
||||
None,
|
||||
),
|
||||
(
|
||||
r"
|
||||
async function * foo() {
|
||||
((yield Promise.reject(bar)));
|
||||
}
|
||||
",
|
||||
r"promise.then(() => Promise.resolve(bar))",
|
||||
r"promise.then(() => { return Promise.resolve(bar); })",
|
||||
r"promise.then(async () => Promise.reject(bar))",
|
||||
r"promise.then(async () => { return Promise.reject(bar); })",
|
||||
r"promise.catch(() => Promise.resolve(bar))",
|
||||
r"promise.catch(() => { return Promise.resolve(bar); })",
|
||||
r"promise.catch(async () => Promise.reject(bar))",
|
||||
r"promise.catch(async () => { return Promise.reject(bar); })",
|
||||
r"promise.finally(() => Promise.resolve(bar))",
|
||||
r"promise.finally(() => { return Promise.resolve(bar); })",
|
||||
r"promise.finally(async () => Promise.reject(bar))",
|
||||
r"promise.finally(async () => { return Promise.reject(bar); })",
|
||||
r"promise.then(() => {}, () => Promise.resolve(bar))",
|
||||
r"promise.then(() => Promise.resolve(bar), () => Promise.resolve(baz))",
|
||||
r"promise.then(() => {}, () => { return Promise.resolve(bar); })",
|
||||
r"promise.then(() => {}, async () => Promise.reject(bar))",
|
||||
r"promise.then(() => {}, async () => { return Promise.reject(bar); })",
|
||||
r"doThing().then(function() { return Promise.resolve(4) })",
|
||||
r"doThing().then(null, function() { return Promise.resolve(4) })",
|
||||
r"doThing().catch(function() { return Promise.resolve(4) })",
|
||||
r"doThing().then(function() { return Promise.reject(4) })",
|
||||
r"doThing().then(null, function() { return Promise.reject(4) })",
|
||||
r"doThing().catch(function() { return Promise.reject(4) })",
|
||||
r#"doThing().then(function(x) { if (x>1) { return Promise.resolve(4) } else { throw "bad" } })"#,
|
||||
r"doThing().then(function(x) { if (x>1) { return Promise.reject(4) } })",
|
||||
r"doThing().then(null, function() { if (true && false) { return Promise.resolve() } })",
|
||||
r"doThing().catch(function(x) {if (x) { return Promise.resolve(4) } else { return Promise.reject() } })",
|
||||
"
|
||||
None,
|
||||
),
|
||||
(r"promise.then(() => Promise.resolve(bar))", None),
|
||||
(r"promise.then(() => { return Promise.resolve(bar); })", None),
|
||||
(r"promise.then(async () => Promise.reject(bar))", None),
|
||||
(r"promise.then(async () => { return Promise.reject(bar); })", None),
|
||||
(r"promise.catch(() => Promise.resolve(bar))", None),
|
||||
(r"promise.catch(() => { return Promise.resolve(bar); })", None),
|
||||
(r"promise.catch(async () => Promise.reject(bar))", None),
|
||||
(r"promise.catch(async () => { return Promise.reject(bar); })", None),
|
||||
(r"promise.finally(() => Promise.resolve(bar))", None),
|
||||
(r"promise.finally(() => { return Promise.resolve(bar); })", None),
|
||||
(r"promise.finally(async () => Promise.reject(bar))", None),
|
||||
(r"promise.finally(async () => { return Promise.reject(bar); })", None),
|
||||
(r"promise.then(() => {}, () => Promise.resolve(bar))", None),
|
||||
(r"promise.then(() => Promise.resolve(bar), () => Promise.resolve(baz))", None),
|
||||
(r"promise.then(() => {}, () => { return Promise.resolve(bar); })", None),
|
||||
(r"promise.then(() => {}, async () => Promise.reject(bar))", None),
|
||||
(r"promise.then(() => {}, async () => { return Promise.reject(bar); })", None),
|
||||
(r"doThing().then(function() { return Promise.resolve(4) })", None),
|
||||
(r"doThing().then(null, function() { return Promise.resolve(4) })", None),
|
||||
(r"doThing().catch(function() { return Promise.resolve(4) })", None),
|
||||
(r"doThing().then(function() { return Promise.reject(4) })", None),
|
||||
(r"doThing().then(null, function() { return Promise.reject(4) })", None),
|
||||
(r"doThing().catch(function() { return Promise.reject(4) })", None),
|
||||
(
|
||||
r#"doThing().then(function(x) { if (x>1) { return Promise.resolve(4) } else { throw "bad" } })"#,
|
||||
None,
|
||||
),
|
||||
(r"doThing().then(function(x) { if (x>1) { return Promise.reject(4) } })", None),
|
||||
(
|
||||
r"doThing().then(null, function() { if (true && false) { return Promise.resolve() } })",
|
||||
None,
|
||||
),
|
||||
(
|
||||
r"doThing().catch(function(x) {if (x) { return Promise.resolve(4) } else { return Promise.reject() } })",
|
||||
None,
|
||||
),
|
||||
(
|
||||
"
|
||||
fn(function() {
|
||||
doThing().then(function() {
|
||||
return Promise.resolve(4)
|
||||
})
|
||||
return
|
||||
})",
|
||||
"
|
||||
None,
|
||||
),
|
||||
(
|
||||
"
|
||||
fn(function() {
|
||||
doThing().then(function nm() {
|
||||
return Promise.resolve(4)
|
||||
})
|
||||
return
|
||||
})",
|
||||
"
|
||||
None,
|
||||
),
|
||||
(
|
||||
"
|
||||
fn(function() {
|
||||
fn2(function() {
|
||||
doThing().then(function() {
|
||||
|
|
@ -737,7 +914,10 @@ fn test() {
|
|||
})
|
||||
})
|
||||
})",
|
||||
"
|
||||
None,
|
||||
),
|
||||
(
|
||||
"
|
||||
fn(function() {
|
||||
fn2(function() {
|
||||
doThing().then(function() {
|
||||
|
|
@ -748,7 +928,10 @@ fn test() {
|
|||
})
|
||||
})
|
||||
})",
|
||||
"
|
||||
None,
|
||||
),
|
||||
(
|
||||
"
|
||||
const o = {
|
||||
fn: function() {
|
||||
return doThing().then(function() {
|
||||
|
|
@ -757,25 +940,33 @@ fn test() {
|
|||
},
|
||||
}
|
||||
",
|
||||
"
|
||||
None,
|
||||
),
|
||||
(
|
||||
"
|
||||
fn(
|
||||
doThing().then(function() {
|
||||
return Promise.resolve(5);
|
||||
})
|
||||
);
|
||||
",
|
||||
r"doThing().then((function() { return Promise.resolve(4) }).bind(this))",
|
||||
r"doThing().then((function() { return Promise.resolve(4) }).bind(this).bind(this))",
|
||||
r"doThing().then(() => { return Promise.resolve(4) })",
|
||||
"
|
||||
None,
|
||||
),
|
||||
(r"doThing().then((function() { return Promise.resolve(4) }).bind(this))", None),
|
||||
(r"doThing().then((function() { return Promise.resolve(4) }).bind(this).bind(this))", None),
|
||||
(r"doThing().then(() => { return Promise.resolve(4) })", None),
|
||||
(
|
||||
"
|
||||
function a () {
|
||||
return p.then(function(val) {
|
||||
return Promise.resolve(val * 4)
|
||||
})
|
||||
}
|
||||
",
|
||||
r"doThing().then(() => Promise.resolve(4))",
|
||||
r"doThing().then(() => Promise.reject(4))",
|
||||
None,
|
||||
),
|
||||
(r"doThing().then(() => Promise.resolve(4))", None),
|
||||
(r"doThing().then(() => Promise.reject(4))", None),
|
||||
];
|
||||
|
||||
let fix = vec![
|
||||
|
|
|
|||
Loading…
Reference in a new issue