Skip to content

Commit 1fa5004

Browse files
committed
lib,test: improve faulty assert usage detection
This improves our custom eslint rules to detect assertions to detect assertions with only a single argument and fixes false negatives in case unary expressions are used. Some rules were extended to also lint our docs and tools and the lib rule was simplified to prohibit most assertion calls. PR-URL: #26569 Refs: #26565 Reviewed-By: James M Snell <[email protected]> Reviewed-By: Rich Trott <[email protected]> Reviewed-By: Richard Lau <[email protected]>
1 parent 1a0004d commit 1fa5004

File tree

5 files changed

+51
-60
lines changed

5 files changed

+51
-60
lines changed

Diff for: .eslintrc.js

+40-8
Original file line numberDiff line numberDiff line change
@@ -170,32 +170,32 @@ module.exports = {
170170
message: '__defineSetter__ is deprecated.',
171171
},
172172
],
173-
// If this list is modified, please copy the change to lib/.eslintrc.yaml
174-
// and test/.eslintrc.yaml.
173+
// If this list is modified, please copy changes that should apply to ./lib
174+
// as well to lib/.eslintrc.yaml.
175175
'no-restricted-syntax': [
176176
'error',
177177
{
178-
selector: "CallExpression[callee.object.name='assert'][callee.property.name='deepStrictEqual'][arguments.2.type='Literal']",
178+
selector: "CallExpression[callee.property.name='deepStrictEqual'][arguments.2.type='Literal']",
179179
message: 'Do not use a literal for the third argument of assert.deepStrictEqual()',
180180
},
181181
{
182-
selector: "CallExpression[callee.object.name='assert'][callee.property.name='doesNotThrow']",
182+
selector: "CallExpression[callee.property.name='doesNotThrow']",
183183
message: 'Please replace `assert.doesNotThrow()` and add a comment next to the code instead.',
184184
},
185185
{
186-
selector: "CallExpression[callee.object.name='assert'][callee.property.name='rejects'][arguments.length<2]",
186+
selector: "CallExpression[callee.property.name='rejects'][arguments.length<2]",
187187
message: '`assert.rejects()` must be invoked with at least two arguments.',
188188
},
189189
{
190-
selector: "CallExpression[callee.object.name='assert'][callee.property.name='strictEqual'][arguments.2.type='Literal']",
190+
selector: "CallExpression[callee.property.name='strictEqual'][arguments.2.type='Literal']",
191191
message: 'Do not use a literal for the third argument of assert.strictEqual()',
192192
},
193193
{
194-
selector: "CallExpression[callee.object.name='assert'][callee.property.name='throws'][arguments.1.type='Literal']:not([arguments.1.regex])",
194+
selector: "CallExpression[callee.property.name='throws'][arguments.1.type='Literal']:not([arguments.1.regex])",
195195
message: 'Use an object as second argument of `assert.throws()`.',
196196
},
197197
{
198-
selector: "CallExpression[callee.object.name='assert'][callee.property.name='throws'][arguments.length<2]",
198+
selector: "CallExpression[callee.property.name='throws'][arguments.length<2]",
199199
message: '`assert.throws()` must be invoked with at least two arguments.',
200200
},
201201
{
@@ -210,6 +210,38 @@ module.exports = {
210210
selector: 'ThrowStatement > CallExpression[callee.name=/Error$/]',
211211
message: 'Use `new` keyword when throwing an `Error`.',
212212
},
213+
{
214+
selector: "CallExpression[callee.property.name='notDeepStrictEqual'][arguments.length<2]",
215+
message: 'assert.notDeepStrictEqual() must be invoked with at least two arguments.',
216+
},
217+
{
218+
selector: "CallExpression[callee.property.name='deepStrictEqual'][arguments.length<2]",
219+
message: 'assert.deepStrictEqual() must be invoked with at least two arguments.',
220+
},
221+
{
222+
selector: "CallExpression[callee.property.name='notStrictEqual'][arguments.length<2]",
223+
message: 'assert.notStrictEqual() must be invoked with at least two arguments.',
224+
},
225+
{
226+
selector: "CallExpression[callee.property.name='strictEqual'][arguments.length<2]",
227+
message: 'assert.strictEqual() must be invoked with at least two arguments.',
228+
},
229+
{
230+
selector: "CallExpression[callee.property.name='notDeepStrictEqual'][arguments.0.type='Literal']:not([arguments.1.type='Literal']):not([arguments.1.type='ObjectExpression']):not([arguments.1.type='ArrayExpression']):not([arguments.1.type='UnaryExpression'])",
231+
message: 'The first argument should be the `actual`, not the `expected` value.',
232+
},
233+
{
234+
selector: "CallExpression[callee.property.name='notStrictEqual'][arguments.0.type='Literal']:not([arguments.1.type='Literal']):not([arguments.1.type='ObjectExpression']):not([arguments.1.type='ArrayExpression']):not([arguments.1.type='UnaryExpression'])",
235+
message: 'The first argument should be the `actual`, not the `expected` value.',
236+
},
237+
{
238+
selector: "CallExpression[callee.property.name='deepStrictEqual'][arguments.0.type='Literal']:not([arguments.1.type='Literal']):not([arguments.1.type='ObjectExpression']):not([arguments.1.type='ArrayExpression']):not([arguments.1.type='UnaryExpression'])",
239+
message: 'The first argument should be the `actual`, not the `expected` value.',
240+
},
241+
{
242+
selector: "CallExpression[callee.property.name='strictEqual'][arguments.0.type='Literal']:not([arguments.1.type='Literal']):not([arguments.1.type='ObjectExpression']):not([arguments.1.type='ArrayExpression']):not([arguments.1.type='UnaryExpression'])",
243+
message: 'The first argument should be the `actual`, not the `expected` value.',
244+
}
213245
],
214246
/* eslint-enable max-len */
215247
'no-return-await': 'error',

Diff for: lib/.eslintrc.yaml

+2-12
Original file line numberDiff line numberDiff line change
@@ -4,18 +4,8 @@ rules:
44
no-restricted-syntax:
55
# Config copied from .eslintrc.js
66
- error
7-
- selector: "CallExpression[callee.object.name='assert'][callee.property.name='deepStrictEqual'][arguments.2.type='Literal']"
8-
message: "Do not use a literal for the third argument of assert.deepStrictEqual()"
9-
- selector: "CallExpression[callee.object.name='assert'][callee.property.name='doesNotThrow']"
10-
message: "Please replace `assert.doesNotThrow()` and add a comment next to the code instead."
11-
- selector: "CallExpression[callee.object.name='assert'][callee.property.name='rejects'][arguments.length<2]"
12-
message: "assert.rejects() must be invoked with at least two arguments."
13-
- selector: "CallExpression[callee.object.name='assert'][callee.property.name='strictEqual'][arguments.2.type='Literal']"
14-
message: "Do not use a literal for the third argument of assert.strictEqual()"
15-
- selector: "CallExpression[callee.object.name='assert'][callee.property.name='throws'][arguments.1.type='Literal']:not([arguments.1.regex])"
16-
message: "Use an object as second argument of assert.throws()"
17-
- selector: "CallExpression[callee.object.name='assert'][callee.property.name='throws'][arguments.length<2]"
18-
message: "assert.throws() must be invoked with at least two arguments."
7+
- selector: "CallExpression[callee.object.name='assert']:not([callee.property.name='ok']):not([callee.property.name='fail']):not([callee.property.name='ifError'])"
8+
message: "Please only use simple assertions in ./lib"
199
- selector: "CallExpression[callee.name='setTimeout'][arguments.length<2]"
2010
message: "setTimeout() must be invoked with at least two arguments."
2111
- selector: "CallExpression[callee.name='setInterval'][arguments.length<2]"

Diff for: lib/internal/js_stream_socket.js

+6-6
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
'use strict';
22

3-
const assert = require('assert');
3+
const assert = require('internal/assert');
44
const util = require('util');
55
const { Socket } = require('net');
66
const { JSStream } = internalBinding('js_stream');
@@ -122,8 +122,8 @@ class JSStreamSocket extends Socket {
122122
this[kPendingShutdownRequest] = req;
123123
return 0;
124124
}
125-
assert.strictEqual(this[kCurrentWriteRequest], null);
126-
assert.strictEqual(this[kCurrentShutdownRequest], null);
125+
assert(this[kCurrentWriteRequest] === null);
126+
assert(this[kCurrentShutdownRequest] === null);
127127
this[kCurrentShutdownRequest] = req;
128128

129129
const handle = this._handle;
@@ -148,8 +148,8 @@ class JSStreamSocket extends Socket {
148148
}
149149

150150
doWrite(req, bufs) {
151-
assert.strictEqual(this[kCurrentWriteRequest], null);
152-
assert.strictEqual(this[kCurrentShutdownRequest], null);
151+
assert(this[kCurrentWriteRequest] === null);
152+
assert(this[kCurrentShutdownRequest] === null);
153153

154154
const handle = this._handle;
155155
const self = this;
@@ -214,7 +214,7 @@ class JSStreamSocket extends Socket {
214214

215215
setImmediate(() => {
216216
// Should be already set by net.js
217-
assert.strictEqual(this._handle, null);
217+
assert(this._handle === null);
218218

219219
this.finishWrite(handle, uv.UV_ECANCELED);
220220
this.finishShutdown(handle, uv.UV_ECANCELED);

Diff for: test/.eslintrc.yaml

-30
Original file line numberDiff line numberDiff line change
@@ -20,36 +20,6 @@ rules:
2020
node-core/required-modules: [error, common]
2121
node-core/no-duplicate-requires: off
2222

23-
no-restricted-syntax:
24-
# Config copied from .eslintrc.js
25-
- error
26-
- selector: "CallExpression[callee.object.name='assert'][callee.property.name='deepStrictEqual'][arguments.2.type='Literal']"
27-
message: "Do not use a literal for the third argument of assert.deepStrictEqual()"
28-
- selector: "CallExpression[callee.object.name='assert'][callee.property.name='doesNotThrow']"
29-
message: "Please replace `assert.doesNotThrow()` and add a comment next to the code instead."
30-
- selector: "CallExpression[callee.object.name='assert'][callee.property.name='rejects'][arguments.length<2]"
31-
message: "assert.rejects() must be invoked with at least two arguments."
32-
- selector: "CallExpression[callee.object.name='assert'][callee.property.name='strictEqual'][arguments.2.type='Literal']"
33-
message: "Do not use a literal for the third argument of assert.strictEqual()"
34-
- selector: "CallExpression[callee.object.name='assert'][callee.property.name='throws'][arguments.1.type='Literal']:not([arguments.1.regex])"
35-
message: "Use an object as second argument of assert.throws()"
36-
- selector: "CallExpression[callee.object.name='assert'][callee.property.name='throws'][arguments.length<2]"
37-
message: "assert.throws() must be invoked with at least two arguments."
38-
- selector: "CallExpression[callee.name='setTimeout'][arguments.length<2]"
39-
message: "setTimeout() must be invoked with at least two arguments."
40-
- selector: "CallExpression[callee.name='setInterval'][arguments.length<2]"
41-
message: "setInterval() must be invoked with at least 2 arguments."
42-
- selector: "ThrowStatement > CallExpression[callee.name=/Error$/]"
43-
message: "Use new keyword when throwing an Error."
44-
# Specific to /test
45-
- selector: "CallExpression[callee.object.name='assert'][callee.property.name='notDeepStrictEqual'][arguments.0.type='Literal']:not([arguments.1.type='Literal']):not([arguments.1.type='ObjectExpression']):not([arguments.1.type='ArrayExpression'])"
46-
message: "The first argument should be the `actual`, not the `expected` value."
47-
- selector: "CallExpression[callee.object.name='assert'][callee.property.name='notStrictEqual'][arguments.0.type='Literal']:not([arguments.1.type='Literal']):not([arguments.1.type='ObjectExpression']):not([arguments.1.type='ArrayExpression'])"
48-
message: "The first argument should be the `actual`, not the `expected` value."
49-
- selector: "CallExpression[callee.object.name='assert'][callee.property.name='deepStrictEqual'][arguments.0.type='Literal']:not([arguments.1.type='Literal']):not([arguments.1.type='ObjectExpression']):not([arguments.1.type='ArrayExpression'])"
50-
message: "The first argument should be the `actual`, not the `expected` value."
51-
- selector: "CallExpression[callee.object.name='assert'][callee.property.name='strictEqual'][arguments.0.type='Literal']:not([arguments.1.type='Literal']):not([arguments.1.type='ObjectExpression']):not([arguments.1.type='ArrayExpression'])"
52-
message: "The first argument should be the `actual`, not the `expected` value."
5323
# Global scoped methods and vars
5424
globals:
5525
WebAssembly: false

Diff for: test/parallel/test-assert.js

+3-4
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ assert.throws(() => a.notEqual(true, true),
6565
assert.throws(() => a.strictEqual(2, '2'),
6666
a.AssertionError, 'strictEqual(2, \'2\')');
6767

68+
/* eslint-disable no-restricted-syntax */
6869
assert.throws(() => a.strictEqual(null, undefined),
6970
a.AssertionError, 'strictEqual(null, undefined)');
7071

@@ -95,11 +96,9 @@ function thrower(errorConstructor) {
9596
// The basic calls work.
9697
assert.throws(() => thrower(a.AssertionError), a.AssertionError, 'message');
9798
assert.throws(() => thrower(a.AssertionError), a.AssertionError);
98-
// eslint-disable-next-line no-restricted-syntax
9999
assert.throws(() => thrower(a.AssertionError));
100100

101101
// If not passing an error, catch all.
102-
// eslint-disable-next-line no-restricted-syntax
103102
assert.throws(() => thrower(TypeError));
104103

105104
// When passing a type, only catch errors of the appropriate type.
@@ -309,7 +308,7 @@ try {
309308
}
310309

311310
try {
312-
assert.strictEqual(1, 2, 'oh no'); // eslint-disable-line no-restricted-syntax
311+
assert.strictEqual(1, 2, 'oh no');
313312
} catch (e) {
314313
assert.strictEqual(e.message, 'oh no');
315314
// Message should not be marked as generated.
@@ -833,7 +832,6 @@ common.expectsError(
833832
);
834833

835834
common.expectsError(
836-
// eslint-disable-next-line no-restricted-syntax
837835
() => assert.throws(() => {}, 'Error message', 'message'),
838836
{
839837
code: 'ERR_INVALID_ARG_TYPE',
@@ -1010,6 +1008,7 @@ assert.throws(
10101008
'The error message "foo" is identical to the message.'
10111009
}
10121010
);
1011+
/* eslint-enable no-restricted-syntax */
10131012

10141013
// Should not throw.
10151014
// eslint-disable-next-line no-restricted-syntax, no-throw-literal

0 commit comments

Comments
 (0)