Value Used From A Call That Returns None
ID |
javascript.void_return_used |
Severity |
low |
Remediation Complexity |
medium |
Remediation Risk |
low |
Remediation Effort |
low |
Resource |
Reliability |
Language |
JavaScript |
Tags |
CWE:394, reliability, suspicious-assignment |
Description
Reports a call whose value is consumed although the function it resolves to never produces one. The expression is undefined at run time, so the assignment stores nothing, the comparison is false and the condition never holds.
The callee has to be resolvable in the file, which limits the rule to two shapes and keeps it from guessing:
-
a declaration annotated
: void— the author’s own statement that there is no value; -
a locally declared function, reached through the file’s symbol table, whose body has no
returnthat yields a value.
Only a plain name(…) call is considered: a method call resolves through a receiver whose type single-file analysis does not know.
Three exemptions keep idiomatic code quiet. An async function returns a promise and a generator returns an iterator, whatever their bodies do. A function whose body ends in throw never returns at all, which is the assertNever shape written precisely to be used in an expression. And return f() and the concise arrow body () ⇒ f() are how a delegating wrapper is written, so neither counts as consuming the value.
Rationale
// Bad
function render(node) {
node.draw();
}
const shown = render(box); // always undefined
if (render(box)) { // never taken
retry();
}
report(render(box)); // reports undefined
-
Nothing throws, so the defect shows up as a falsy value or a branch that never runs, far from the call.
-
The shape usually means the function was expected to return something — a status, the node it drew — and either never did or stopped doing so in an edit that left the call sites behind.
-
A condition on a value that is always
undefinedis dead code that reads as a live check.
Remediation
Return the value the caller needs, or call the function as a statement and take the value from where it actually lives.
// Good
function render(node) {
node.draw();
return node;
}
const shown = render(box);
// Or, when the function really is an effect
render(box);
if (box.isVisible) {
retry();
}