Skip to content

Commit

Permalink
errors: consistent format for error message
Browse files Browse the repository at this point in the history
Consistently use printf-style strings for error messages that
do not need a custom argument order or processing of arguments.

Backport-PR-URL: #17624
PR-URL: #16904
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Michael Dawson <michael_dawson@ca.ibm.com>
Reviewed-By: Anna Henningsen <anna@addaleax.net>
  • Loading branch information
apapirovski authored and MylesBorins committed Dec 12, 2017
1 parent dcd87ac commit a333e71
Show file tree
Hide file tree
Showing 3 changed files with 93 additions and 41 deletions.
68 changes: 27 additions & 41 deletions lib/internal/errors.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
/* eslint documented-errors: "error" */
/* eslint alphabetize-errors: "error" */
/* eslint prefer-util-format-errors: "error" */

'use strict';

Expand Down Expand Up @@ -146,8 +147,8 @@ module.exports = exports = {
// Note: Please try to keep these in alphabetical order
E('ERR_ARG_NOT_ITERABLE', '%s must be iterable');
E('ERR_ASSERTION', '%s');
E('ERR_ASYNC_CALLBACK', (name) => `${name} must be a function`);
E('ERR_ASYNC_TYPE', (s) => `Invalid name for async "type": ${s}`);
E('ERR_ASYNC_CALLBACK', '%s must be a function');
E('ERR_ASYNC_TYPE', 'Invalid name for async "type": %s');
E('ERR_BUFFER_OUT_OF_BOUNDS', bufferOutOfBounds);
E('ERR_BUFFER_TOO_LARGE',
`Cannot create a Buffer larger than 0x${kMaxLength.toString(16)} bytes`);
Expand All @@ -169,12 +170,10 @@ E('ERR_CRYPTO_INVALID_DIGEST', 'Invalid digest: %s');
E('ERR_CRYPTO_SIGN_KEY_REQUIRED', 'No key provided to sign');
E('ERR_CRYPTO_TIMING_SAFE_EQUAL_LENGTH',
'Input buffers must have the same length');
E('ERR_DNS_SET_SERVERS_FAILED', (err, servers) =>
`c-ares failed to set servers: "${err}" [${servers}]`);
E('ERR_DNS_SET_SERVERS_FAILED', 'c-ares failed to set servers: "%s" [%s]');
E('ERR_ENCODING_INVALID_ENCODED_DATA',
(enc) => `The encoded data was not valid for encoding ${enc}`);
E('ERR_ENCODING_NOT_SUPPORTED',
(enc) => `The "${enc}" encoding is not supported`);
'The encoded data was not valid for encoding %s');
E('ERR_ENCODING_NOT_SUPPORTED', 'The "%s" encoding is not supported');
E('ERR_FALSY_VALUE_REJECTION', 'Promise was rejected with falsy value');
E('ERR_HTTP2_CONNECT_AUTHORITY',
':authority header is required for CONNECT requests');
Expand All @@ -194,10 +193,9 @@ E('ERR_HTTP2_HEADERS_AFTER_RESPOND',
'Cannot specify additional headers after response initiated');
E('ERR_HTTP2_HEADERS_OBJECT', 'Headers must be an object');
E('ERR_HTTP2_HEADERS_SENT', 'Response has already been initiated.');
E('ERR_HTTP2_HEADER_REQUIRED',
(name) => `The ${name} header is required`);
E('ERR_HTTP2_HEADER_REQUIRED', 'The %s header is required');
E('ERR_HTTP2_HEADER_SINGLE_VALUE',
(name) => `Header field "${name}" must have only a single value`);
'Header field "%s" must have only a single value');
E('ERR_HTTP2_INFO_HEADERS_AFTER_RESPOND',
'Cannot send informational headers after the HTTP message has been sent');
E('ERR_HTTP2_INFO_STATUS_NOT_ALLOWED',
Expand All @@ -206,23 +204,23 @@ E('ERR_HTTP2_INVALID_CONNECTION_HEADERS',
'HTTP/1 Connection specific headers are forbidden: "%s"');
E('ERR_HTTP2_INVALID_HEADER_VALUE', 'Invalid value "%s" for header "%s"');
E('ERR_HTTP2_INVALID_INFO_STATUS',
(code) => `Invalid informational status code: ${code}`);
'Invalid informational status code: %s');
E('ERR_HTTP2_INVALID_PACKED_SETTINGS_LENGTH',
'Packed settings length must be a multiple of six');
E('ERR_HTTP2_INVALID_PSEUDOHEADER',
(name) => `"${name}" is an invalid pseudoheader or is used incorrectly`);
'"%s" is an invalid pseudoheader or is used incorrectly');
E('ERR_HTTP2_INVALID_SESSION', 'The session has been destroyed');
E('ERR_HTTP2_INVALID_SETTING_VALUE',
(name, value) => `Invalid value for setting "${name}": ${value}`);
'Invalid value for setting "%s": %s');
E('ERR_HTTP2_INVALID_STREAM', 'The stream has been destroyed');
E('ERR_HTTP2_MAX_PENDING_SETTINGS_ACK',
(max) => `Maximum number of pending settings acknowledgements (${max})`);
'Maximum number of pending settings acknowledgements (%s)');
E('ERR_HTTP2_NO_SOCKET_MANIPULATION',
'HTTP/2 sockets should not be directly manipulated (e.g. read and written)');
E('ERR_HTTP2_OUT_OF_STREAMS',
'No stream ID is available because maximum stream ID has been reached');
E('ERR_HTTP2_PAYLOAD_FORBIDDEN',
(code) => `Responses with ${code} status must not have a payload`);
'Responses with %s status must not have a payload');
E('ERR_HTTP2_PING_CANCEL', 'HTTP2 ping cancelled');
E('ERR_HTTP2_PING_LENGTH', 'HTTP2 ping payload must be 8 bytes');
E('ERR_HTTP2_PSEUDOHEADER_NOT_ALLOWED', 'Cannot set HTTP/2 pseudo-headers');
Expand All @@ -232,19 +230,15 @@ E('ERR_HTTP2_SOCKET_BOUND',
'The socket is already bound to an Http2Session');
E('ERR_HTTP2_STATUS_101',
'HTTP status code 101 (Switching Protocols) is forbidden in HTTP/2');
E('ERR_HTTP2_STATUS_INVALID',
(code) => `Invalid status code: ${code}`);
E('ERR_HTTP2_STATUS_INVALID', 'Invalid status code: %s');
E('ERR_HTTP2_STREAM_CLOSED', 'The stream is already closed');
E('ERR_HTTP2_STREAM_ERROR',
(code) => `Stream closed with error code ${code}`);
E('ERR_HTTP2_STREAM_ERROR', 'Stream closed with error code %s');
E('ERR_HTTP2_STREAM_SELF_DEPENDENCY', 'A stream cannot depend on itself');
E('ERR_HTTP2_UNSUPPORTED_PROTOCOL',
(protocol) => `protocol "${protocol}" is unsupported.`);
E('ERR_HTTP2_UNSUPPORTED_PROTOCOL', 'protocol "%s" is unsupported.');
E('ERR_HTTP_HEADERS_SENT',
'Cannot %s headers after they are sent to the client');
E('ERR_HTTP_INVALID_CHAR', 'Invalid character in statusMessage.');
E('ERR_HTTP_INVALID_STATUS_CODE',
(originalStatusCode) => `Invalid status code: ${originalStatusCode}`);
E('ERR_HTTP_INVALID_STATUS_CODE', 'Invalid status code: %s');
E('ERR_HTTP_TRAILER_INVALID',
'Trailers are invalid with this transfer encoding');
E('ERR_INDEX_OUT_OF_RANGE', 'Index out of range');
Expand All @@ -253,16 +247,14 @@ E('ERR_INSPECTOR_CLOSED', 'Session was closed');
E('ERR_INSPECTOR_NOT_AVAILABLE', 'Inspector is not available');
E('ERR_INSPECTOR_NOT_CONNECTED', 'Session is not connected');
E('ERR_INVALID_ARG_TYPE', invalidArgType);
E('ERR_INVALID_ARG_VALUE',
(name, value) => {
return `The value "${String(value)}" is invalid for argument "${name}"`;
});
E('ERR_INVALID_ARG_VALUE', (name, value) =>
`The value "${String(value)}" is invalid for argument "${name}"`);
E('ERR_INVALID_ARRAY_LENGTH',
(name, len, actual) => {
internalAssert(typeof actual === 'number', 'actual must be a number');
return `The array "${name}" (length ${actual}) must be of length ${len}.`;
});
E('ERR_INVALID_ASYNC_ID', (type, id) => `Invalid ${type} value: ${id}`);
E('ERR_INVALID_ASYNC_ID', 'Invalid %s value: %s');
E('ERR_INVALID_BUFFER_SIZE', 'Buffer size must be a multiple of %s');
E('ERR_INVALID_CALLBACK', 'Callback must be a function');
E('ERR_INVALID_CHAR', invalidChar);
Expand All @@ -277,15 +269,12 @@ E('ERR_INVALID_FILE_URL_PATH', 'File URL path %s');
E('ERR_INVALID_HANDLE_TYPE', 'This handle type cannot be sent');
E('ERR_INVALID_HTTP_TOKEN', '%s must be a valid HTTP token ["%s"]');
E('ERR_INVALID_IP_ADDRESS', 'Invalid IP address: %s');
E('ERR_INVALID_OPT_VALUE',
(name, value) => {
return `The value "${String(value)}" is invalid for option "${name}"`;
});
E('ERR_INVALID_OPT_VALUE', (name, value) =>
`The value "${String(value)}" is invalid for option "${name}"`);
E('ERR_INVALID_OPT_VALUE_ENCODING',
(value) => `The value "${String(value)}" is invalid for option "encoding"`);
'The value "%s" is invalid for option "encoding"');
E('ERR_INVALID_PERFORMANCE_MARK', 'The "%s" performance mark has not been set');
E('ERR_INVALID_PROTOCOL', (protocol, expectedProtocol) =>
`Protocol "${protocol}" not supported. Expected "${expectedProtocol}"`);
E('ERR_INVALID_PROTOCOL', 'Protocol "%s" not supported. Expected "%s"');
E('ERR_INVALID_REPL_EVAL_CONFIG',
'Cannot specify both "breakEvalOnSigint" and "eval" for REPL');
E('ERR_INVALID_SYNC_FORK_INPUT',
Expand Down Expand Up @@ -325,8 +314,7 @@ E('ERR_SOCKET_BAD_BUFFER_SIZE', 'Buffer size must be a positive integer');
E('ERR_SOCKET_BAD_PORT', 'Port should be > 0 and < 65536. Received %s.');
E('ERR_SOCKET_BAD_TYPE',
'Bad socket type specified. Valid types are: udp4, udp6');
E('ERR_SOCKET_BUFFER_SIZE',
(reason) => `Could not get or set buffer size: ${reason}`);
E('ERR_SOCKET_BUFFER_SIZE', 'Could not get or set buffer size: %s');
E('ERR_SOCKET_CANNOT_SEND', 'Unable to send data');
E('ERR_SOCKET_CLOSED', 'Socket is closed');
E('ERR_SOCKET_DGRAM_NOT_RUNNING', 'Not running');
Expand All @@ -341,8 +329,7 @@ E('ERR_STREAM_WRAP', 'Stream has StringDecoder set or is in objectMode');
E('ERR_STREAM_WRITE_AFTER_END', 'write after end');
E('ERR_TLS_CERT_ALTNAME_INVALID',
'Hostname/IP does not match certificate\'s altnames: %s');
E('ERR_TLS_DH_PARAM_SIZE', (size) =>
`DH parameter size ${size} is less than 2048`);
E('ERR_TLS_DH_PARAM_SIZE', 'DH parameter size %s is less than 2048');
E('ERR_TLS_HANDSHAKE_TIMEOUT', 'TLS handshake timeout');
E('ERR_TLS_RENEGOTIATION_FAILED', 'Failed to renegotiate');
E('ERR_TLS_REQUIRED_SERVER_NAME',
Expand All @@ -352,8 +339,7 @@ E('ERR_TRANSFORM_ALREADY_TRANSFORMING',
'Calling transform done when still transforming');
E('ERR_TRANSFORM_WITH_LENGTH_0',
'Calling transform done when writableState.length != 0');
E('ERR_UNESCAPED_CHARACTERS',
(name) => `${name} contains unescaped characters`);
E('ERR_UNESCAPED_CHARACTERS', '%s contains unescaped characters');
E('ERR_UNHANDLED_ERROR',
(err) => {
const msg = 'Unhandled error.';
Expand Down
27 changes: 27 additions & 0 deletions test/parallel/test-eslint-prefer-util-format-errors.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
'use strict';

/* eslint-disable no-template-curly-in-string */

require('../common');

const RuleTester = require('../../tools/eslint').RuleTester;
const rule = require('../../tools/eslint-rules/prefer-util-format-errors');

new RuleTester({ parserOptions: { ecmaVersion: 6 } })
.run('prefer-util-format-errors', rule, {
valid: [
'E(\'ABC\', \'abc\');',
'E(\'ABC\', (arg1, arg2) => `${arg2}${arg1}`);',
'E(\'ABC\', (arg1, arg2) => `${arg1}{arg2.something}`);',
'E(\'ABC\', (arg1, arg2) => fn(arg1, arg2));'
],
invalid: [
{
code: 'E(\'ABC\', (arg1, arg2) => `${arg1}${arg2}`);',
errors: [{
message: 'Please use a printf-like formatted string that ' +
'util.format can consume.'
}]
}
]
});
39 changes: 39 additions & 0 deletions tools/eslint-rules/prefer-util-format-errors.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
'use strict';

const errMsg = 'Please use a printf-like formatted string that util.format' +
' can consume.';

function isArrowFunctionWithTemplateLiteral(node) {
return node.type === 'ArrowFunctionExpression' &&
node.body.type === 'TemplateLiteral';
}

function isDefiningError(node) {
return node.expression &&
node.expression.type === 'CallExpression' &&
node.expression.callee &&
node.expression.callee.name === 'E';
}

module.exports = {
create: function(context) {
return {
ExpressionStatement: function(node) {
if (!isDefiningError(node))
return;

const msg = node.expression.arguments[1];
if (!isArrowFunctionWithTemplateLiteral(msg))
return;

const { expressions } = msg.body;
const hasSequentialParams = msg.params.every((param, index) => {
const expr = expressions[index];
return expr && expr.type === 'Identifier' && param.name === expr.name;
});
if (hasSequentialParams)
context.report(msg, errMsg);
}
};
}
};

0 comments on commit a333e71

Please sign in to comment.