From 3fe1e80896d69b2125e3a264d0707fdbc6f37740 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Mon, 18 Mar 2019 03:47:55 +0100 Subject: [PATCH] lib: validate Error.captureStackTrace() calls MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This adds a custom eslint rule to verify that `Error.captureStackTrace()` is only called if necessary. In most cases the helper function should be used instead. PR-URL: https://github.com/nodejs/node/pull/26738 Reviewed-By: Gus Caplan Reviewed-By: Matteo Collina Reviewed-By: Michaƫl Zasso Reviewed-By: Joyee Cheung --- lib/.eslintrc.yaml | 2 ++ lib/assert.js | 1 + lib/events.js | 1 + lib/fs.js | 2 ++ lib/internal/assert/assertion_error.js | 1 + lib/internal/async_hooks.js | 1 + lib/internal/console/constructor.js | 1 + lib/internal/errors.js | 5 +++++ lib/internal/process/warning.js | 1 + 9 files changed, 15 insertions(+) diff --git a/lib/.eslintrc.yaml b/lib/.eslintrc.yaml index fcb9ea7febac52..3b041d5db4d7d2 100644 --- a/lib/.eslintrc.yaml +++ b/lib/.eslintrc.yaml @@ -15,6 +15,8 @@ rules: # Config specific to lib - selector: "NewExpression[callee.name=/Error$/]:not([callee.name=/^(AssertionError|NghttpError)$/])" message: "Use an error exported by the internal/errors module." + - selector: "CallExpression[callee.object.name='Error'][callee.property.name='captureStackTrace']" + message: "Please use `require('internal/errors').hideStackFrames()` instead." # Custom rules in tools/eslint-rules node-core/require-globals: error node-core/no-let-in-for-declaration: error diff --git a/lib/assert.js b/lib/assert.js index 99c000e31652c0..51ee781b59b472 100644 --- a/lib/assert.js +++ b/lib/assert.js @@ -240,6 +240,7 @@ function getErrMessage(message, fn) { // We only need the stack trace. To minimize the overhead use an object // instead of an error. const err = {}; + // eslint-disable-next-line no-restricted-syntax Error.captureStackTrace(err, fn); Error.stackTraceLimit = tmpLimit; diff --git a/lib/events.js b/lib/events.js index 8a676773b6507a..6a141f44d8abd9 100644 --- a/lib/events.js +++ b/lib/events.js @@ -158,6 +158,7 @@ EventEmitter.prototype.emit = function emit(type, ...args) { try { const { kExpandStackSymbol } = require('internal/util'); const capture = {}; + // eslint-disable-next-line no-restricted-syntax Error.captureStackTrace(capture, EventEmitter.prototype.emit); Object.defineProperty(er, kExpandStackSymbol, { value: enhanceStackTrace.bind(null, er, capture), diff --git a/lib/fs.js b/lib/fs.js index 46dd447d3efd5b..cfe856eaebd41c 100644 --- a/lib/fs.js +++ b/lib/fs.js @@ -117,6 +117,7 @@ function showTruncateDeprecation() { function handleErrorFromBinding(ctx) { if (ctx.errno !== undefined) { // libuv error numbers const err = uvException(ctx); + // eslint-disable-next-line no-restricted-syntax Error.captureStackTrace(err, handleErrorFromBinding); throw err; } @@ -124,6 +125,7 @@ function handleErrorFromBinding(ctx) { // TODO(joyeecheung): currently, ctx.error are encoding errors // usually caused by memory problems. We need to figure out proper error // code(s) for this. + // eslint-disable-next-line no-restricted-syntax Error.captureStackTrace(ctx.error, handleErrorFromBinding); throw ctx.error; } diff --git a/lib/internal/assert/assertion_error.js b/lib/internal/assert/assertion_error.js index ded5ef00c931bc..c8edf13eca8830 100644 --- a/lib/internal/assert/assertion_error.js +++ b/lib/internal/assert/assertion_error.js @@ -398,6 +398,7 @@ class AssertionError extends Error { this.actual = actual; this.expected = expected; this.operator = operator; + // eslint-disable-next-line no-restricted-syntax Error.captureStackTrace(this, stackStartFn); // Create error message including the error code in the name. this.stack; diff --git a/lib/internal/async_hooks.js b/lib/internal/async_hooks.js index 206cdc5c155884..55f6ea1c07a128 100644 --- a/lib/internal/async_hooks.js +++ b/lib/internal/async_hooks.js @@ -105,6 +105,7 @@ function fatalError(e) { process._rawDebug(e.stack); } else { const o = { message: e }; + // eslint-disable-next-line no-restricted-syntax Error.captureStackTrace(o, fatalError); process._rawDebug(o.stack); } diff --git a/lib/internal/console/constructor.js b/lib/internal/console/constructor.js index b8f2ae29244f9c..7c141ce89877b9 100644 --- a/lib/internal/console/constructor.js +++ b/lib/internal/console/constructor.js @@ -331,6 +331,7 @@ const consoleMethods = { name: 'Trace', message: this[kFormatForStderr](args) }; + // eslint-disable-next-line no-restricted-syntax Error.captureStackTrace(err, this.trace); this.error(err.stack); }, diff --git a/lib/internal/errors.js b/lib/internal/errors.js index 0112eb6278594a..d4456854fa6a2e 100644 --- a/lib/internal/errors.js +++ b/lib/internal/errors.js @@ -354,6 +354,7 @@ function uvException(ctx) { err.dest = dest; } + // eslint-disable-next-line no-restricted-syntax Error.captureStackTrace(err, excludedStackFn || uvException); return err; } @@ -396,6 +397,7 @@ function uvExceptionWithHostPort(err, syscall, address, port) { ex.port = port; } + // eslint-disable-next-line no-restricted-syntax Error.captureStackTrace(ex, excludedStackFn || uvExceptionWithHostPort); return ex; } @@ -424,6 +426,7 @@ function errnoException(err, syscall, original) { ex.code = ex.errno = code; ex.syscall = syscall; + // eslint-disable-next-line no-restricted-syntax Error.captureStackTrace(ex, excludedStackFn || errnoException); return ex; } @@ -472,6 +475,7 @@ function exceptionWithHostPort(err, syscall, address, port, additional) { ex.port = port; } + // eslint-disable-next-line no-restricted-syntax Error.captureStackTrace(ex, excludedStackFn || exceptionWithHostPort); return ex; } @@ -512,6 +516,7 @@ function dnsException(code, syscall, hostname) { ex.hostname = hostname; } + // eslint-disable-next-line no-restricted-syntax Error.captureStackTrace(ex, excludedStackFn || dnsException); return ex; } diff --git a/lib/internal/process/warning.js b/lib/internal/process/warning.js index 2052ecf7075f6c..e0e1709bdbd791 100644 --- a/lib/internal/process/warning.js +++ b/lib/internal/process/warning.js @@ -112,6 +112,7 @@ function emitWarning(warning, type, code, ctor, now) { warning.name = String(type || 'Warning'); if (code !== undefined) warning.code = code; if (detail !== undefined) warning.detail = detail; + // eslint-disable-next-line no-restricted-syntax Error.captureStackTrace(warning, ctor || process.emitWarning); } else if (!(warning instanceof Error)) { throw new ERR_INVALID_ARG_TYPE('warning', ['Error', 'string'], warning);