@typescript-eslint/eslint-plugin 8.52.1-alpha.9 → 8.53.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/configs/eslintrc/all.d.ts +1 -0
- package/dist/configs/eslintrc/all.js +1 -0
- package/dist/configs/eslintrc/disable-type-checked.d.ts +1 -0
- package/dist/configs/eslintrc/disable-type-checked.js +1 -0
- package/dist/configs/flat/all.js +1 -0
- package/dist/configs/flat/disable-type-checked.js +1 -0
- package/dist/index.d.ts +7 -0
- package/dist/raw-plugin.d.ts +7 -0
- package/dist/rules/consistent-indexed-object-style.js +2 -1
- package/dist/rules/index.d.ts +5 -0
- package/dist/rules/index.js +2 -0
- package/dist/rules/no-unused-vars.d.ts +4 -1
- package/dist/rules/no-unused-vars.js +373 -40
- package/dist/rules/no-useless-default-assignment.js +5 -0
- package/dist/rules/strict-void-return.d.ts +10 -0
- package/dist/rules/strict-void-return.js +356 -0
- package/dist/util/getBaseTypesOfClassMember.d.ts +11 -0
- package/dist/util/getBaseTypesOfClassMember.js +31 -0
- package/dist/util/index.d.ts +2 -0
- package/dist/util/index.js +2 -0
- package/dist/util/walkStatements.d.ts +7 -0
- package/dist/util/walkStatements.js +56 -0
- package/package.json +8 -8
|
@@ -148,6 +148,7 @@ declare const _default: {
|
|
|
148
148
|
'no-return-await': "off";
|
|
149
149
|
'@typescript-eslint/return-await': "error";
|
|
150
150
|
'@typescript-eslint/strict-boolean-expressions': "error";
|
|
151
|
+
'@typescript-eslint/strict-void-return': "error";
|
|
151
152
|
'@typescript-eslint/switch-exhaustiveness-check': "error";
|
|
152
153
|
'@typescript-eslint/triple-slash-reference': "error";
|
|
153
154
|
'@typescript-eslint/unbound-method': "error";
|
|
@@ -155,6 +155,7 @@ module.exports = {
|
|
|
155
155
|
'no-return-await': 'off',
|
|
156
156
|
'@typescript-eslint/return-await': 'error',
|
|
157
157
|
'@typescript-eslint/strict-boolean-expressions': 'error',
|
|
158
|
+
'@typescript-eslint/strict-void-return': 'error',
|
|
158
159
|
'@typescript-eslint/switch-exhaustiveness-check': 'error',
|
|
159
160
|
'@typescript-eslint/triple-slash-reference': 'error',
|
|
160
161
|
'@typescript-eslint/unbound-method': 'error',
|
|
@@ -62,6 +62,7 @@ declare const _default: {
|
|
|
62
62
|
'@typescript-eslint/restrict-template-expressions': "off";
|
|
63
63
|
'@typescript-eslint/return-await': "off";
|
|
64
64
|
'@typescript-eslint/strict-boolean-expressions': "off";
|
|
65
|
+
'@typescript-eslint/strict-void-return': "off";
|
|
65
66
|
'@typescript-eslint/switch-exhaustiveness-check': "off";
|
|
66
67
|
'@typescript-eslint/unbound-method': "off";
|
|
67
68
|
'@typescript-eslint/use-unknown-in-catch-callback-variable': "off";
|
|
@@ -65,6 +65,7 @@ module.exports = {
|
|
|
65
65
|
'@typescript-eslint/restrict-template-expressions': 'off',
|
|
66
66
|
'@typescript-eslint/return-await': 'off',
|
|
67
67
|
'@typescript-eslint/strict-boolean-expressions': 'off',
|
|
68
|
+
'@typescript-eslint/strict-void-return': 'off',
|
|
68
69
|
'@typescript-eslint/switch-exhaustiveness-check': 'off',
|
|
69
70
|
'@typescript-eslint/unbound-method': 'off',
|
|
70
71
|
'@typescript-eslint/use-unknown-in-catch-callback-variable': 'off',
|
package/dist/configs/flat/all.js
CHANGED
|
@@ -168,6 +168,7 @@ exports.default = (plugin, parser) => [
|
|
|
168
168
|
'no-return-await': 'off',
|
|
169
169
|
'@typescript-eslint/return-await': 'error',
|
|
170
170
|
'@typescript-eslint/strict-boolean-expressions': 'error',
|
|
171
|
+
'@typescript-eslint/strict-void-return': 'error',
|
|
171
172
|
'@typescript-eslint/switch-exhaustiveness-check': 'error',
|
|
172
173
|
'@typescript-eslint/triple-slash-reference': 'error',
|
|
173
174
|
'@typescript-eslint/unbound-method': 'error',
|
|
@@ -70,6 +70,7 @@ exports.default = (_plugin, _parser) => ({
|
|
|
70
70
|
'@typescript-eslint/restrict-template-expressions': 'off',
|
|
71
71
|
'@typescript-eslint/return-await': 'off',
|
|
72
72
|
'@typescript-eslint/strict-boolean-expressions': 'off',
|
|
73
|
+
'@typescript-eslint/strict-void-return': 'off',
|
|
73
74
|
'@typescript-eslint/switch-exhaustiveness-check': 'off',
|
|
74
75
|
'@typescript-eslint/unbound-method': 'off',
|
|
75
76
|
'@typescript-eslint/use-unknown-in-catch-callback-variable': 'off',
|
package/dist/index.d.ts
CHANGED
|
@@ -150,6 +150,7 @@ declare const _default: {
|
|
|
150
150
|
'no-return-await': "off";
|
|
151
151
|
'@typescript-eslint/return-await': "error";
|
|
152
152
|
'@typescript-eslint/strict-boolean-expressions': "error";
|
|
153
|
+
'@typescript-eslint/strict-void-return': "error";
|
|
153
154
|
'@typescript-eslint/switch-exhaustiveness-check': "error";
|
|
154
155
|
'@typescript-eslint/triple-slash-reference': "error";
|
|
155
156
|
'@typescript-eslint/unbound-method': "error";
|
|
@@ -228,6 +229,7 @@ declare const _default: {
|
|
|
228
229
|
'@typescript-eslint/restrict-template-expressions': "off";
|
|
229
230
|
'@typescript-eslint/return-await': "off";
|
|
230
231
|
'@typescript-eslint/strict-boolean-expressions': "off";
|
|
232
|
+
'@typescript-eslint/strict-void-return': "off";
|
|
231
233
|
'@typescript-eslint/switch-exhaustiveness-check': "off";
|
|
232
234
|
'@typescript-eslint/unbound-method': "off";
|
|
233
235
|
'@typescript-eslint/use-unknown-in-catch-callback-variable': "off";
|
|
@@ -1096,6 +1098,11 @@ declare const _default: {
|
|
|
1096
1098
|
'strict-boolean-expressions': import("@typescript-eslint/utils/ts-eslint").RuleModule<import("./rules/strict-boolean-expressions").MessageId, import("./rules/strict-boolean-expressions").Options, import("../rules").ESLintPluginDocs, import("@typescript-eslint/utils/ts-eslint").RuleListener> & {
|
|
1097
1099
|
name: string;
|
|
1098
1100
|
};
|
|
1101
|
+
'strict-void-return': import("@typescript-eslint/utils/ts-eslint").RuleModule<"asyncFunc" | "nonVoidFunc" | "nonVoidReturn", [{
|
|
1102
|
+
allowReturnAny?: boolean;
|
|
1103
|
+
}], import("../rules").ESLintPluginDocs, import("@typescript-eslint/utils/ts-eslint").RuleListener> & {
|
|
1104
|
+
name: string;
|
|
1105
|
+
};
|
|
1099
1106
|
'switch-exhaustiveness-check': import("@typescript-eslint/utils/ts-eslint").RuleModule<import("./rules/switch-exhaustiveness-check").MessageIds, import("./rules/switch-exhaustiveness-check").Options, import("../rules").ESLintPluginDocs, import("@typescript-eslint/utils/ts-eslint").RuleListener> & {
|
|
1100
1107
|
name: string;
|
|
1101
1108
|
};
|
package/dist/raw-plugin.d.ts
CHANGED
|
@@ -172,6 +172,7 @@ declare const _default: {
|
|
|
172
172
|
'no-return-await': "off";
|
|
173
173
|
'@typescript-eslint/return-await': "error";
|
|
174
174
|
'@typescript-eslint/strict-boolean-expressions': "error";
|
|
175
|
+
'@typescript-eslint/strict-void-return': "error";
|
|
175
176
|
'@typescript-eslint/switch-exhaustiveness-check': "error";
|
|
176
177
|
'@typescript-eslint/triple-slash-reference': "error";
|
|
177
178
|
'@typescript-eslint/unbound-method': "error";
|
|
@@ -250,6 +251,7 @@ declare const _default: {
|
|
|
250
251
|
'@typescript-eslint/restrict-template-expressions': "off";
|
|
251
252
|
'@typescript-eslint/return-await': "off";
|
|
252
253
|
'@typescript-eslint/strict-boolean-expressions': "off";
|
|
254
|
+
'@typescript-eslint/strict-void-return': "off";
|
|
253
255
|
'@typescript-eslint/switch-exhaustiveness-check': "off";
|
|
254
256
|
'@typescript-eslint/unbound-method': "off";
|
|
255
257
|
'@typescript-eslint/use-unknown-in-catch-callback-variable': "off";
|
|
@@ -1119,6 +1121,11 @@ declare const _default: {
|
|
|
1119
1121
|
'strict-boolean-expressions': TSESLint.RuleModule<import("./rules/strict-boolean-expressions").MessageId, import("./rules/strict-boolean-expressions").Options, import("../rules").ESLintPluginDocs, TSESLint.RuleListener> & {
|
|
1120
1122
|
name: string;
|
|
1121
1123
|
};
|
|
1124
|
+
'strict-void-return': TSESLint.RuleModule<"asyncFunc" | "nonVoidFunc" | "nonVoidReturn", [{
|
|
1125
|
+
allowReturnAny?: boolean;
|
|
1126
|
+
}], import("../rules").ESLintPluginDocs, TSESLint.RuleListener> & {
|
|
1127
|
+
name: string;
|
|
1128
|
+
};
|
|
1122
1129
|
'switch-exhaustiveness-check': TSESLint.RuleModule<import("./rules/switch-exhaustiveness-check").MessageIds, import("./rules/switch-exhaustiveness-check").Options, import("../rules").ESLintPluginDocs, TSESLint.RuleListener> & {
|
|
1123
1130
|
name: string;
|
|
1124
1131
|
};
|
|
@@ -114,7 +114,8 @@ exports.default = (0, util_1.createRule)({
|
|
|
114
114
|
.map(p => context.sourceCode.getText(p))
|
|
115
115
|
.join(', ')}>`;
|
|
116
116
|
}
|
|
117
|
-
checkMembers(node.body.body, node, node.id, `type ${node.id.name}${genericTypes} = `, ';', !node.extends.length
|
|
117
|
+
checkMembers(node.body.body, node, node.id, `type ${node.id.name}${genericTypes} = `, ';', !node.extends.length &&
|
|
118
|
+
node.parent.type !== utils_1.AST_NODE_TYPES.ExportDefaultDeclaration);
|
|
118
119
|
},
|
|
119
120
|
TSMappedType(node) {
|
|
120
121
|
const key = node.key;
|
package/dist/rules/index.d.ts
CHANGED
|
@@ -419,6 +419,11 @@ declare const rules: {
|
|
|
419
419
|
'strict-boolean-expressions': import("@typescript-eslint/utils/ts-eslint").RuleModule<import("./strict-boolean-expressions").MessageId, import("./strict-boolean-expressions").Options, import("../../rules").ESLintPluginDocs, import("@typescript-eslint/utils/ts-eslint").RuleListener> & {
|
|
420
420
|
name: string;
|
|
421
421
|
};
|
|
422
|
+
'strict-void-return': import("@typescript-eslint/utils/ts-eslint").RuleModule<"asyncFunc" | "nonVoidFunc" | "nonVoidReturn", [{
|
|
423
|
+
allowReturnAny?: boolean;
|
|
424
|
+
}], import("../../rules").ESLintPluginDocs, import("@typescript-eslint/utils/ts-eslint").RuleListener> & {
|
|
425
|
+
name: string;
|
|
426
|
+
};
|
|
422
427
|
'switch-exhaustiveness-check': import("@typescript-eslint/utils/ts-eslint").RuleModule<import("./switch-exhaustiveness-check").MessageIds, import("./switch-exhaustiveness-check").Options, import("../../rules").ESLintPluginDocs, import("@typescript-eslint/utils/ts-eslint").RuleListener> & {
|
|
423
428
|
name: string;
|
|
424
429
|
};
|
package/dist/rules/index.js
CHANGED
|
@@ -129,6 +129,7 @@ const restrict_template_expressions_1 = __importDefault(require("./restrict-temp
|
|
|
129
129
|
const return_await_1 = __importDefault(require("./return-await"));
|
|
130
130
|
const sort_type_constituents_1 = __importDefault(require("./sort-type-constituents"));
|
|
131
131
|
const strict_boolean_expressions_1 = __importDefault(require("./strict-boolean-expressions"));
|
|
132
|
+
const strict_void_return_1 = __importDefault(require("./strict-void-return"));
|
|
132
133
|
const switch_exhaustiveness_check_1 = __importDefault(require("./switch-exhaustiveness-check"));
|
|
133
134
|
const triple_slash_reference_1 = __importDefault(require("./triple-slash-reference"));
|
|
134
135
|
const typedef_1 = __importDefault(require("./typedef"));
|
|
@@ -263,6 +264,7 @@ const rules = {
|
|
|
263
264
|
'return-await': return_await_1.default,
|
|
264
265
|
'sort-type-constituents': sort_type_constituents_1.default,
|
|
265
266
|
'strict-boolean-expressions': strict_boolean_expressions_1.default,
|
|
267
|
+
'strict-void-return': strict_void_return_1.default,
|
|
266
268
|
'switch-exhaustiveness-check': switch_exhaustiveness_check_1.default,
|
|
267
269
|
'triple-slash-reference': triple_slash_reference_1.default,
|
|
268
270
|
typedef: typedef_1.default,
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { TSESLint } from '@typescript-eslint/utils';
|
|
2
|
-
export type MessageIds = 'unusedVar' | 'usedIgnoredVar' | 'usedOnlyAsType';
|
|
2
|
+
export type MessageIds = 'removeUnusedImportDeclaration' | 'removeUnusedVar' | 'unusedVar' | 'usedIgnoredVar' | 'usedOnlyAsType';
|
|
3
3
|
export type Options = [
|
|
4
4
|
'all' | 'local' | {
|
|
5
5
|
args?: 'after-used' | 'all' | 'none';
|
|
@@ -7,6 +7,9 @@ export type Options = [
|
|
|
7
7
|
caughtErrors?: 'all' | 'none';
|
|
8
8
|
caughtErrorsIgnorePattern?: string;
|
|
9
9
|
destructuredArrayIgnorePattern?: string;
|
|
10
|
+
enableAutofixRemoval?: {
|
|
11
|
+
imports?: boolean;
|
|
12
|
+
};
|
|
10
13
|
ignoreClassWithStaticInitBlock?: boolean;
|
|
11
14
|
ignoreRestSiblings?: boolean;
|
|
12
15
|
ignoreUsingDeclarations?: boolean;
|
|
@@ -4,6 +4,45 @@ const scope_manager_1 = require("@typescript-eslint/scope-manager");
|
|
|
4
4
|
const utils_1 = require("@typescript-eslint/utils");
|
|
5
5
|
const util_1 = require("../util");
|
|
6
6
|
const referenceContainsTypeQuery_1 = require("../util/referenceContainsTypeQuery");
|
|
7
|
+
// this is a superset of DefinitionType which defines sub-types for better granularity
|
|
8
|
+
var VariableType;
|
|
9
|
+
(function (VariableType) {
|
|
10
|
+
// New sub-types
|
|
11
|
+
VariableType[VariableType["ArrayDestructure"] = 0] = "ArrayDestructure";
|
|
12
|
+
// DefinitionType
|
|
13
|
+
VariableType[VariableType["CatchClause"] = 1] = "CatchClause";
|
|
14
|
+
VariableType[VariableType["ClassName"] = 2] = "ClassName";
|
|
15
|
+
VariableType[VariableType["FunctionName"] = 3] = "FunctionName";
|
|
16
|
+
VariableType[VariableType["ImportBinding"] = 4] = "ImportBinding";
|
|
17
|
+
VariableType[VariableType["ImplicitGlobalVariable"] = 5] = "ImplicitGlobalVariable";
|
|
18
|
+
VariableType[VariableType["Parameter"] = 6] = "Parameter";
|
|
19
|
+
VariableType[VariableType["TSEnumMember"] = 7] = "TSEnumMember";
|
|
20
|
+
VariableType[VariableType["TSEnumName"] = 8] = "TSEnumName";
|
|
21
|
+
VariableType[VariableType["TSModuleName"] = 9] = "TSModuleName";
|
|
22
|
+
VariableType[VariableType["Type"] = 10] = "Type";
|
|
23
|
+
VariableType[VariableType["Variable"] = 11] = "Variable";
|
|
24
|
+
})(VariableType || (VariableType = {}));
|
|
25
|
+
const isCommaToken = {
|
|
26
|
+
predicate: (token) => token.type === utils_1.AST_TOKEN_TYPES.Punctuator && token.value === ',',
|
|
27
|
+
tokenChar: ',',
|
|
28
|
+
};
|
|
29
|
+
const isLeftCurlyToken = {
|
|
30
|
+
predicate: (token) => token.type === utils_1.AST_TOKEN_TYPES.Punctuator && token.value === '{',
|
|
31
|
+
tokenChar: '{',
|
|
32
|
+
};
|
|
33
|
+
const isRightCurlyToken = {
|
|
34
|
+
predicate: (token) => token.type === utils_1.AST_TOKEN_TYPES.Punctuator && token.value === '}',
|
|
35
|
+
tokenChar: '}',
|
|
36
|
+
};
|
|
37
|
+
function assertToken({ predicate, tokenChar }, token) {
|
|
38
|
+
if (token == null) {
|
|
39
|
+
throw new Error(`Expected a valid "${tokenChar}" token, but found no token`);
|
|
40
|
+
}
|
|
41
|
+
if (!predicate(token)) {
|
|
42
|
+
throw new Error(`Expected a valid "${tokenChar}" token, but got "${token.value}" instead`);
|
|
43
|
+
}
|
|
44
|
+
return token;
|
|
45
|
+
}
|
|
7
46
|
exports.default = (0, util_1.createRule)({
|
|
8
47
|
name: 'no-unused-vars',
|
|
9
48
|
meta: {
|
|
@@ -13,7 +52,11 @@ exports.default = (0, util_1.createRule)({
|
|
|
13
52
|
extendsBaseRule: true,
|
|
14
53
|
recommended: 'recommended',
|
|
15
54
|
},
|
|
55
|
+
fixable: 'code',
|
|
56
|
+
hasSuggestions: true,
|
|
16
57
|
messages: {
|
|
58
|
+
removeUnusedImportDeclaration: 'Remove unused import declaration.',
|
|
59
|
+
removeUnusedVar: 'Remove unused variable "{{varName}}".',
|
|
17
60
|
unusedVar: "'{{varName}}' is {{action}} but never used{{additional}}.",
|
|
18
61
|
usedIgnoredVar: "'{{varName}}' is marked as ignored but is used{{additional}}.",
|
|
19
62
|
usedOnlyAsType: "'{{varName}}' is {{action}} but only used as a type{{additional}}.",
|
|
@@ -52,6 +95,17 @@ exports.default = (0, util_1.createRule)({
|
|
|
52
95
|
type: 'string',
|
|
53
96
|
description: 'Regular expressions of destructured array variable names to not check for usage.',
|
|
54
97
|
},
|
|
98
|
+
enableAutofixRemoval: {
|
|
99
|
+
type: 'object',
|
|
100
|
+
additionalProperties: false,
|
|
101
|
+
description: 'Configurable automatic fixes for different types of unused variables.',
|
|
102
|
+
properties: {
|
|
103
|
+
imports: {
|
|
104
|
+
type: 'boolean',
|
|
105
|
+
description: 'Whether to enable automatic removal of unused imports.',
|
|
106
|
+
},
|
|
107
|
+
},
|
|
108
|
+
},
|
|
55
109
|
ignoreClassWithStaticInitBlock: {
|
|
56
110
|
type: 'boolean',
|
|
57
111
|
description: 'Whether to ignore classes with at least one static initialization block.',
|
|
@@ -86,10 +140,130 @@ exports.default = (0, util_1.createRule)({
|
|
|
86
140
|
defaultOptions: [{}],
|
|
87
141
|
create(context, [firstOption]) {
|
|
88
142
|
const MODULE_DECL_CACHE = new Map();
|
|
143
|
+
const reportedUnusedVariables = new Set();
|
|
144
|
+
function areAllSpecifiersUnused(decl) {
|
|
145
|
+
return context.sourceCode.getDeclaredVariables(decl).every(variable => {
|
|
146
|
+
return reportedUnusedVariables.has(variable);
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
const report = (unusedVar, opts) => {
|
|
150
|
+
reportedUnusedVariables.add(unusedVar);
|
|
151
|
+
const writeReferences = unusedVar.references.filter(ref => ref.isWrite() &&
|
|
152
|
+
ref.from.variableScope === unusedVar.scope.variableScope);
|
|
153
|
+
const id = writeReferences.length
|
|
154
|
+
? writeReferences[writeReferences.length - 1].identifier
|
|
155
|
+
: unusedVar.identifiers[0];
|
|
156
|
+
const { start } = id.loc;
|
|
157
|
+
const idLength = id.name.length;
|
|
158
|
+
const loc = {
|
|
159
|
+
start,
|
|
160
|
+
end: {
|
|
161
|
+
column: start.column + idLength,
|
|
162
|
+
line: start.line,
|
|
163
|
+
},
|
|
164
|
+
};
|
|
165
|
+
const fixer = (() => {
|
|
166
|
+
const { messageId, fix, useAutofix } = (() => {
|
|
167
|
+
if (unusedVar.defs.length !== 1) {
|
|
168
|
+
// If there's multiple definitions then we'd have to clean them all
|
|
169
|
+
// up! That's complicated and messy so for now let's just ignore it.
|
|
170
|
+
return {};
|
|
171
|
+
}
|
|
172
|
+
const { type, def } = defToVariableType(unusedVar.defs[0]);
|
|
173
|
+
switch (type) {
|
|
174
|
+
case VariableType.ArrayDestructure:
|
|
175
|
+
// TODO(bradzacher) -- this would be really easy to implement and
|
|
176
|
+
// is side-effect free!
|
|
177
|
+
return {};
|
|
178
|
+
case VariableType.CatchClause:
|
|
179
|
+
// TODO(bradzacher) -- this would be really easy to implement and
|
|
180
|
+
// is side-effect free!
|
|
181
|
+
return {};
|
|
182
|
+
case VariableType.ClassName:
|
|
183
|
+
// This would be easy to implement -- but classes can have
|
|
184
|
+
// side-effects in static initializers / static blocks. So it's
|
|
185
|
+
// dangerous to ever auto-fix remove them.
|
|
186
|
+
//
|
|
187
|
+
// Perhaps as an always-suggestion fixer...?
|
|
188
|
+
return {};
|
|
189
|
+
case VariableType.FunctionName:
|
|
190
|
+
// TODO(bradzacher) -- this would be really easy to implement and
|
|
191
|
+
// is side-effect free!
|
|
192
|
+
return {};
|
|
193
|
+
case VariableType.ImportBinding:
|
|
194
|
+
return {
|
|
195
|
+
...getImportFixer(def),
|
|
196
|
+
useAutofix: options.enableAutofixRemoval.imports,
|
|
197
|
+
};
|
|
198
|
+
case VariableType.ImplicitGlobalVariable:
|
|
199
|
+
// We don't report these via this code path, so no fixer is possible
|
|
200
|
+
return {};
|
|
201
|
+
case VariableType.Parameter:
|
|
202
|
+
// This is easy to implement -- but we cannot implement it cos it
|
|
203
|
+
// changes the signature of the function which in turn might
|
|
204
|
+
// introduce type errors in consumers.
|
|
205
|
+
//
|
|
206
|
+
// Also parameters can have default values which might have
|
|
207
|
+
// side-effects.
|
|
208
|
+
//
|
|
209
|
+
// Perhaps as an always-suggestion fixer...?
|
|
210
|
+
return {};
|
|
211
|
+
case VariableType.TSEnumMember:
|
|
212
|
+
// We don't report unused enum members so no fixer is ever possible
|
|
213
|
+
return {};
|
|
214
|
+
case VariableType.TSEnumName:
|
|
215
|
+
// TODO(bradzacher) -- this would be really easy to implement and
|
|
216
|
+
// is side-effect free!
|
|
217
|
+
return {};
|
|
218
|
+
case VariableType.TSModuleName:
|
|
219
|
+
// This is easy to implement -- but TS namespaces are eagerly
|
|
220
|
+
// initialized -- meaning that they might have side-effects in
|
|
221
|
+
// the body. So it's dangerous to ever auto-fix remove them.
|
|
222
|
+
//
|
|
223
|
+
// Perhaps as an always-suggestion fixer...?
|
|
224
|
+
return {};
|
|
225
|
+
case VariableType.Type:
|
|
226
|
+
// TODO(bradzacher) -- this would be really easy to implement and
|
|
227
|
+
// is side-effect free!
|
|
228
|
+
return {};
|
|
229
|
+
case VariableType.Variable:
|
|
230
|
+
// TODO(bradzacher) -- this would be really easy to implement
|
|
231
|
+
return {};
|
|
232
|
+
}
|
|
233
|
+
})();
|
|
234
|
+
if (!fix) {
|
|
235
|
+
return {};
|
|
236
|
+
}
|
|
237
|
+
if (useAutofix) {
|
|
238
|
+
return { fix };
|
|
239
|
+
}
|
|
240
|
+
const data = {
|
|
241
|
+
varName: unusedVar.name,
|
|
242
|
+
};
|
|
243
|
+
return {
|
|
244
|
+
suggest: [
|
|
245
|
+
{
|
|
246
|
+
messageId: messageId ?? 'removeUnusedVar',
|
|
247
|
+
data,
|
|
248
|
+
fix,
|
|
249
|
+
},
|
|
250
|
+
],
|
|
251
|
+
};
|
|
252
|
+
})();
|
|
253
|
+
context.report({
|
|
254
|
+
...opts,
|
|
255
|
+
...fixer,
|
|
256
|
+
loc,
|
|
257
|
+
node: id,
|
|
258
|
+
});
|
|
259
|
+
};
|
|
89
260
|
const options = (() => {
|
|
90
261
|
const options = {
|
|
91
262
|
args: 'after-used',
|
|
92
263
|
caughtErrors: 'all',
|
|
264
|
+
enableAutofixRemoval: {
|
|
265
|
+
imports: false,
|
|
266
|
+
},
|
|
93
267
|
ignoreClassWithStaticInitBlock: false,
|
|
94
268
|
ignoreRestSiblings: false,
|
|
95
269
|
ignoreUsingDeclarations: false,
|
|
@@ -126,9 +300,171 @@ exports.default = (0, util_1.createRule)({
|
|
|
126
300
|
if (firstOption.destructuredArrayIgnorePattern) {
|
|
127
301
|
options.destructuredArrayIgnorePattern = new RegExp(firstOption.destructuredArrayIgnorePattern, 'u');
|
|
128
302
|
}
|
|
303
|
+
if (firstOption.enableAutofixRemoval) {
|
|
304
|
+
// eslint-disable-next-line unicorn/no-lonely-if -- will add more cases later
|
|
305
|
+
if (firstOption.enableAutofixRemoval.imports != null) {
|
|
306
|
+
options.enableAutofixRemoval.imports =
|
|
307
|
+
firstOption.enableAutofixRemoval.imports;
|
|
308
|
+
}
|
|
309
|
+
}
|
|
129
310
|
}
|
|
130
311
|
return options;
|
|
131
312
|
})();
|
|
313
|
+
function getImportFixer(def) {
|
|
314
|
+
switch (def.node.type) {
|
|
315
|
+
case utils_1.AST_NODE_TYPES.TSImportEqualsDeclaration:
|
|
316
|
+
// import equals declarations can only have one binding -- so we can
|
|
317
|
+
// just remove entire import declaration
|
|
318
|
+
return {
|
|
319
|
+
messageId: 'removeUnusedImportDeclaration',
|
|
320
|
+
fix: fixer => fixer.remove(def.node),
|
|
321
|
+
};
|
|
322
|
+
case utils_1.AST_NODE_TYPES.ImportDefaultSpecifier: {
|
|
323
|
+
const importDecl = def.node.parent;
|
|
324
|
+
if (importDecl.specifiers.length === 1 ||
|
|
325
|
+
areAllSpecifiersUnused(importDecl)) {
|
|
326
|
+
// all specifiers are unused -- so we can just remove entire import
|
|
327
|
+
// declaration
|
|
328
|
+
return {
|
|
329
|
+
messageId: 'removeUnusedImportDeclaration',
|
|
330
|
+
fix: fixer => fixer.remove(importDecl),
|
|
331
|
+
};
|
|
332
|
+
}
|
|
333
|
+
// in this branch we know the following things:
|
|
334
|
+
// 1) there is at least one specifier that is used
|
|
335
|
+
// 2) the default specifier is unused
|
|
336
|
+
//
|
|
337
|
+
// by process of elimination we can deduce that there is at least one
|
|
338
|
+
// named specifier that is used
|
|
339
|
+
//
|
|
340
|
+
// i.e. the code must be import Unused, { Used, ... } from 'module';
|
|
341
|
+
//
|
|
342
|
+
// there's one or more unused named specifiers, so we must remove the
|
|
343
|
+
// default specifier in isolation including the trailing comma.
|
|
344
|
+
//
|
|
345
|
+
// import Unused, { Used, ... } from 'module';
|
|
346
|
+
// ^^^^^^^ remove this
|
|
347
|
+
//
|
|
348
|
+
// NOTE: we could also remove the spaces between the comma and the
|
|
349
|
+
// opening curly brace -- but this does risk removing comments. To be
|
|
350
|
+
// safe we'll be conservative for now
|
|
351
|
+
//
|
|
352
|
+
// TODO(bradzacher) -- consider removing the extra space whilst also
|
|
353
|
+
// preserving comments.
|
|
354
|
+
return {
|
|
355
|
+
messageId: 'removeUnusedVar',
|
|
356
|
+
fix: fixer => {
|
|
357
|
+
const comma = (0, util_1.nullThrows)(context.sourceCode.getTokenAfter(def.node), util_1.NullThrowsReasons.MissingToken(',', 'import specifier'));
|
|
358
|
+
assertToken(isCommaToken, comma);
|
|
359
|
+
return fixer.removeRange([
|
|
360
|
+
Math.min(def.node.range[0], comma.range[0]),
|
|
361
|
+
Math.max(def.node.range[1], comma.range[1]),
|
|
362
|
+
]);
|
|
363
|
+
},
|
|
364
|
+
};
|
|
365
|
+
}
|
|
366
|
+
case utils_1.AST_NODE_TYPES.ImportSpecifier: {
|
|
367
|
+
// guaranteed to NOT be in an export statement as we're inspecting an
|
|
368
|
+
// import
|
|
369
|
+
const importDecl = def.node.parent;
|
|
370
|
+
if (importDecl.specifiers.length === 1 ||
|
|
371
|
+
areAllSpecifiersUnused(importDecl)) {
|
|
372
|
+
// all specifiers are unused -- so we can just remove entire import
|
|
373
|
+
// declaration
|
|
374
|
+
return {
|
|
375
|
+
messageId: 'removeUnusedImportDeclaration',
|
|
376
|
+
fix: fixer => fixer.remove(importDecl),
|
|
377
|
+
};
|
|
378
|
+
}
|
|
379
|
+
return {
|
|
380
|
+
messageId: 'removeUnusedVar',
|
|
381
|
+
fix: fixer => {
|
|
382
|
+
const usedNamedSpecifiers = context.sourceCode
|
|
383
|
+
.getDeclaredVariables(importDecl)
|
|
384
|
+
.map(variable => {
|
|
385
|
+
if (reportedUnusedVariables.has(variable)) {
|
|
386
|
+
return null;
|
|
387
|
+
}
|
|
388
|
+
const specifier = variable.defs[0].node;
|
|
389
|
+
if (specifier.type !== utils_1.AST_NODE_TYPES.ImportSpecifier) {
|
|
390
|
+
return null;
|
|
391
|
+
}
|
|
392
|
+
return specifier;
|
|
393
|
+
})
|
|
394
|
+
.filter(v => v != null);
|
|
395
|
+
if (usedNamedSpecifiers.length === 0) {
|
|
396
|
+
// in this branch we know the following things:
|
|
397
|
+
// 1) there is at least one specifier that is used
|
|
398
|
+
// 2) all named specifiers are unused
|
|
399
|
+
//
|
|
400
|
+
// by process of elimination we can deduce that there is a
|
|
401
|
+
// default specifier and it is the only used specifier
|
|
402
|
+
//
|
|
403
|
+
// i.e. the code must be import Used, { Unused, ... } from
|
|
404
|
+
// 'module';
|
|
405
|
+
//
|
|
406
|
+
// So we can just remove the entire curly content and the comma
|
|
407
|
+
// before, eg import Used, { Unused, ... } from 'module';
|
|
408
|
+
// ^^^^^^^^^^^^^^^^^ remove this
|
|
409
|
+
const leftCurly = assertToken(isLeftCurlyToken, context.sourceCode.getFirstToken(importDecl, isLeftCurlyToken.predicate));
|
|
410
|
+
const leftToken = assertToken(isCommaToken, context.sourceCode.getTokenBefore(leftCurly));
|
|
411
|
+
const rightToken = assertToken(isRightCurlyToken, context.sourceCode.getFirstToken(importDecl, isRightCurlyToken.predicate));
|
|
412
|
+
return fixer.removeRange([
|
|
413
|
+
leftToken.range[0],
|
|
414
|
+
rightToken.range[1],
|
|
415
|
+
]);
|
|
416
|
+
}
|
|
417
|
+
// in this branch we know there is at least one used named
|
|
418
|
+
// specifier which means we have to remove each unused specifier
|
|
419
|
+
// in isolation.
|
|
420
|
+
//
|
|
421
|
+
// there's 3 possible cases to care about: import { Unused,
|
|
422
|
+
// Used... } from 'module'; import { ...Used, Unused } from
|
|
423
|
+
// 'module'; import { ...Used, Unused, } from 'module';
|
|
424
|
+
//
|
|
425
|
+
// Note that because of the above usedNamedSpecifiers check we
|
|
426
|
+
// know that we don't have one of these cases: import { Unused }
|
|
427
|
+
// from 'module'; import { Unused, Unused... } from 'module';
|
|
428
|
+
// import { ...Unused, Unused, } from 'module';
|
|
429
|
+
//
|
|
430
|
+
// The result is that we know that there _must_ be a comma that
|
|
431
|
+
// needs cleaning up
|
|
432
|
+
//
|
|
433
|
+
// try to remove the leading comma first as it leads to a nicer
|
|
434
|
+
// fix output in most cases
|
|
435
|
+
//
|
|
436
|
+
// leading preferred: import { Used, Unused, Used } from 'module';
|
|
437
|
+
// ^^^^^^^^ remove import { Used, Used } from 'module';
|
|
438
|
+
//
|
|
439
|
+
// trailing preferred: import { Used, Unused, Used } from
|
|
440
|
+
// 'module'; ^^^^^^^ remove import { Used, Used } from
|
|
441
|
+
// 'module'; ^^ ugly double space
|
|
442
|
+
//
|
|
443
|
+
// But we need to still fallback to the trailing comma for cases
|
|
444
|
+
// where the unused specifier is the first in the import eg:
|
|
445
|
+
// import { Unused, Used } from 'module';
|
|
446
|
+
const maybeComma = context.sourceCode.getTokenBefore(def.node);
|
|
447
|
+
const comma = maybeComma && isCommaToken.predicate(maybeComma)
|
|
448
|
+
? maybeComma
|
|
449
|
+
: assertToken(isCommaToken, context.sourceCode.getTokenAfter(def.node));
|
|
450
|
+
return fixer.removeRange([
|
|
451
|
+
Math.min(def.node.range[0], comma.range[0]),
|
|
452
|
+
Math.max(def.node.range[1], comma.range[1]),
|
|
453
|
+
]);
|
|
454
|
+
},
|
|
455
|
+
};
|
|
456
|
+
}
|
|
457
|
+
case utils_1.AST_NODE_TYPES.ImportNamespaceSpecifier: {
|
|
458
|
+
// namespace specifiers cannot be used with any other specifier -- so
|
|
459
|
+
// we can just remove entire import declaration
|
|
460
|
+
const importDecl = def.node.parent;
|
|
461
|
+
return {
|
|
462
|
+
messageId: 'removeUnusedImportDeclaration',
|
|
463
|
+
fix: fixer => fixer.remove(importDecl),
|
|
464
|
+
};
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
}
|
|
132
468
|
/**
|
|
133
469
|
* Determines what variable type a def is.
|
|
134
470
|
* @param def the declaration to check
|
|
@@ -146,15 +482,31 @@ exports.default = (0, util_1.createRule)({
|
|
|
146
482
|
*/
|
|
147
483
|
if (options.destructuredArrayIgnorePattern &&
|
|
148
484
|
def.name.parent.type === utils_1.AST_NODE_TYPES.ArrayPattern) {
|
|
149
|
-
return
|
|
485
|
+
return { type: VariableType.ArrayDestructure, def };
|
|
150
486
|
}
|
|
151
487
|
switch (def.type) {
|
|
152
488
|
case scope_manager_1.DefinitionType.CatchClause:
|
|
153
|
-
return
|
|
489
|
+
return { type: VariableType.CatchClause, def };
|
|
490
|
+
case scope_manager_1.DefinitionType.ClassName:
|
|
491
|
+
return { type: VariableType.ClassName, def };
|
|
492
|
+
case scope_manager_1.DefinitionType.FunctionName:
|
|
493
|
+
return { type: VariableType.FunctionName, def };
|
|
494
|
+
case scope_manager_1.DefinitionType.ImplicitGlobalVariable:
|
|
495
|
+
return { type: VariableType.ImplicitGlobalVariable, def };
|
|
496
|
+
case scope_manager_1.DefinitionType.ImportBinding:
|
|
497
|
+
return { type: VariableType.ImportBinding, def };
|
|
154
498
|
case scope_manager_1.DefinitionType.Parameter:
|
|
155
|
-
return
|
|
156
|
-
|
|
157
|
-
return
|
|
499
|
+
return { type: VariableType.Parameter, def };
|
|
500
|
+
case scope_manager_1.DefinitionType.TSEnumName:
|
|
501
|
+
return { type: VariableType.TSEnumName, def };
|
|
502
|
+
case scope_manager_1.DefinitionType.TSEnumMember:
|
|
503
|
+
return { type: VariableType.TSEnumMember, def };
|
|
504
|
+
case scope_manager_1.DefinitionType.TSModuleName:
|
|
505
|
+
return { type: VariableType.TSModuleName, def };
|
|
506
|
+
case scope_manager_1.DefinitionType.Type:
|
|
507
|
+
return { type: VariableType.Type, def };
|
|
508
|
+
case scope_manager_1.DefinitionType.Variable:
|
|
509
|
+
return { type: VariableType.Variable, def };
|
|
158
510
|
}
|
|
159
511
|
}
|
|
160
512
|
/**
|
|
@@ -166,22 +518,22 @@ exports.default = (0, util_1.createRule)({
|
|
|
166
518
|
*/
|
|
167
519
|
function getVariableDescription(variableType) {
|
|
168
520
|
switch (variableType) {
|
|
169
|
-
case
|
|
521
|
+
case VariableType.ArrayDestructure:
|
|
170
522
|
return {
|
|
171
523
|
pattern: options.destructuredArrayIgnorePattern?.toString(),
|
|
172
524
|
variableDescription: 'elements of array destructuring',
|
|
173
525
|
};
|
|
174
|
-
case
|
|
526
|
+
case VariableType.CatchClause:
|
|
175
527
|
return {
|
|
176
528
|
pattern: options.caughtErrorsIgnorePattern?.toString(),
|
|
177
529
|
variableDescription: 'caught errors',
|
|
178
530
|
};
|
|
179
|
-
case
|
|
531
|
+
case VariableType.Parameter:
|
|
180
532
|
return {
|
|
181
533
|
pattern: options.argsIgnorePattern?.toString(),
|
|
182
534
|
variableDescription: 'args',
|
|
183
535
|
};
|
|
184
|
-
|
|
536
|
+
default:
|
|
185
537
|
return {
|
|
186
538
|
pattern: options.varsIgnorePattern?.toString(),
|
|
187
539
|
variableDescription: 'vars',
|
|
@@ -198,7 +550,7 @@ exports.default = (0, util_1.createRule)({
|
|
|
198
550
|
const def = unusedVar.defs.at(0);
|
|
199
551
|
let additionalMessageData = '';
|
|
200
552
|
if (def) {
|
|
201
|
-
const { pattern, variableDescription } = getVariableDescription(defToVariableType(def));
|
|
553
|
+
const { pattern, variableDescription } = getVariableDescription(defToVariableType(def).type);
|
|
202
554
|
if (pattern && variableDescription) {
|
|
203
555
|
additionalMessageData = `. Allowed unused ${variableDescription} must match ${pattern}`;
|
|
204
556
|
}
|
|
@@ -219,7 +571,7 @@ exports.default = (0, util_1.createRule)({
|
|
|
219
571
|
const def = unusedVar.defs.at(0);
|
|
220
572
|
let additionalMessageData = '';
|
|
221
573
|
if (def) {
|
|
222
|
-
const { pattern, variableDescription } = getVariableDescription(defToVariableType(def));
|
|
574
|
+
const { pattern, variableDescription } = getVariableDescription(defToVariableType(def).type);
|
|
223
575
|
if (pattern && variableDescription) {
|
|
224
576
|
additionalMessageData = `. Allowed unused ${variableDescription} must match ${pattern}`;
|
|
225
577
|
}
|
|
@@ -318,10 +670,9 @@ exports.default = (0, util_1.createRule)({
|
|
|
318
670
|
def.name.type === utils_1.AST_NODE_TYPES.Identifier &&
|
|
319
671
|
options.destructuredArrayIgnorePattern?.test(def.name.name)) {
|
|
320
672
|
if (options.reportUsedIgnorePattern && used) {
|
|
321
|
-
|
|
322
|
-
node: def.name,
|
|
673
|
+
report(variable, {
|
|
323
674
|
messageId: 'usedIgnoredVar',
|
|
324
|
-
data: getUsedIgnoredMessageData(variable,
|
|
675
|
+
data: getUsedIgnoredMessageData(variable, VariableType.ArrayDestructure),
|
|
325
676
|
});
|
|
326
677
|
}
|
|
327
678
|
continue;
|
|
@@ -341,10 +692,9 @@ exports.default = (0, util_1.createRule)({
|
|
|
341
692
|
if (def.name.type === utils_1.AST_NODE_TYPES.Identifier &&
|
|
342
693
|
options.caughtErrorsIgnorePattern?.test(def.name.name)) {
|
|
343
694
|
if (options.reportUsedIgnorePattern && used) {
|
|
344
|
-
|
|
345
|
-
node: def.name,
|
|
695
|
+
report(variable, {
|
|
346
696
|
messageId: 'usedIgnoredVar',
|
|
347
|
-
data: getUsedIgnoredMessageData(variable,
|
|
697
|
+
data: getUsedIgnoredMessageData(variable, VariableType.CatchClause),
|
|
348
698
|
});
|
|
349
699
|
}
|
|
350
700
|
continue;
|
|
@@ -359,10 +709,9 @@ exports.default = (0, util_1.createRule)({
|
|
|
359
709
|
if (def.name.type === utils_1.AST_NODE_TYPES.Identifier &&
|
|
360
710
|
options.argsIgnorePattern?.test(def.name.name)) {
|
|
361
711
|
if (options.reportUsedIgnorePattern && used) {
|
|
362
|
-
|
|
363
|
-
node: def.name,
|
|
712
|
+
report(variable, {
|
|
364
713
|
messageId: 'usedIgnoredVar',
|
|
365
|
-
data: getUsedIgnoredMessageData(variable,
|
|
714
|
+
data: getUsedIgnoredMessageData(variable, VariableType.Parameter),
|
|
366
715
|
});
|
|
367
716
|
}
|
|
368
717
|
continue;
|
|
@@ -382,10 +731,9 @@ exports.default = (0, util_1.createRule)({
|
|
|
382
731
|
/* enum members are always marked as 'used' by `collectVariables`, but in reality they may be used or
|
|
383
732
|
unused. either way, don't complain about their naming. */
|
|
384
733
|
def.type !== utils_1.TSESLint.Scope.DefinitionType.TSEnumMember) {
|
|
385
|
-
|
|
386
|
-
node: def.name,
|
|
734
|
+
report(variable, {
|
|
387
735
|
messageId: 'usedIgnoredVar',
|
|
388
|
-
data: getUsedIgnoredMessageData(variable,
|
|
736
|
+
data: getUsedIgnoredMessageData(variable, VariableType.Variable),
|
|
389
737
|
});
|
|
390
738
|
}
|
|
391
739
|
continue;
|
|
@@ -455,28 +803,13 @@ exports.default = (0, util_1.createRule)({
|
|
|
455
803
|
// Report the first declaration.
|
|
456
804
|
if (unusedVar.defs.length > 0) {
|
|
457
805
|
const usedOnlyAsType = unusedVar.references.some(ref => (0, referenceContainsTypeQuery_1.referenceContainsTypeQuery)(ref.identifier));
|
|
806
|
+
const messageId = usedOnlyAsType ? 'usedOnlyAsType' : 'unusedVar';
|
|
458
807
|
const isImportUsedOnlyAsType = usedOnlyAsType &&
|
|
459
808
|
unusedVar.defs.some(def => def.type === scope_manager_1.DefinitionType.ImportBinding);
|
|
460
809
|
if (isImportUsedOnlyAsType) {
|
|
461
810
|
continue;
|
|
462
811
|
}
|
|
463
|
-
|
|
464
|
-
ref.from.variableScope === unusedVar.scope.variableScope);
|
|
465
|
-
const id = writeReferences.length
|
|
466
|
-
? writeReferences[writeReferences.length - 1].identifier
|
|
467
|
-
: unusedVar.identifiers[0];
|
|
468
|
-
const messageId = usedOnlyAsType ? 'usedOnlyAsType' : 'unusedVar';
|
|
469
|
-
const { start } = id.loc;
|
|
470
|
-
const idLength = id.name.length;
|
|
471
|
-
const loc = {
|
|
472
|
-
start,
|
|
473
|
-
end: {
|
|
474
|
-
column: start.column + idLength,
|
|
475
|
-
line: start.line,
|
|
476
|
-
},
|
|
477
|
-
};
|
|
478
|
-
context.report({
|
|
479
|
-
loc,
|
|
812
|
+
report(unusedVar, {
|
|
480
813
|
messageId,
|
|
481
814
|
data: unusedVar.references.some(ref => ref.isWrite())
|
|
482
815
|
? getAssignedMessageData(unusedVar)
|
|
@@ -111,6 +111,11 @@ exports.default = (0, util_1.createRule)({
|
|
|
111
111
|
const params = signatures[0].getParameters();
|
|
112
112
|
if (paramIndex < params.length) {
|
|
113
113
|
const paramSymbol = params[paramIndex];
|
|
114
|
+
if (paramSymbol.valueDeclaration &&
|
|
115
|
+
ts.isParameter(paramSymbol.valueDeclaration) &&
|
|
116
|
+
paramSymbol.valueDeclaration.dotDotDotToken != null) {
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
114
119
|
if ((paramSymbol.flags & ts.SymbolFlags.Optional) === 0) {
|
|
115
120
|
const paramType = checker.getTypeOfSymbol(paramSymbol);
|
|
116
121
|
if (!canBeUndefined(paramType)) {
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
type Options = [
|
|
2
|
+
{
|
|
3
|
+
allowReturnAny?: boolean;
|
|
4
|
+
}
|
|
5
|
+
];
|
|
6
|
+
type MessageId = `asyncFunc` | `nonVoidFunc` | `nonVoidReturn`;
|
|
7
|
+
declare const _default: import("@typescript-eslint/utils/ts-eslint").RuleModule<MessageId, Options, import("../../rules").ESLintPluginDocs, import("@typescript-eslint/utils/ts-eslint").RuleListener> & {
|
|
8
|
+
name: string;
|
|
9
|
+
};
|
|
10
|
+
export default _default;
|
|
@@ -0,0 +1,356 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
const utils_1 = require("@typescript-eslint/utils");
|
|
37
|
+
const tsutils = __importStar(require("ts-api-utils"));
|
|
38
|
+
const ts = __importStar(require("typescript"));
|
|
39
|
+
const util = __importStar(require("../util"));
|
|
40
|
+
exports.default = util.createRule({
|
|
41
|
+
name: 'strict-void-return',
|
|
42
|
+
meta: {
|
|
43
|
+
type: 'problem',
|
|
44
|
+
docs: {
|
|
45
|
+
description: 'Disallow passing a value-returning function in a position accepting a void function',
|
|
46
|
+
requiresTypeChecking: true,
|
|
47
|
+
},
|
|
48
|
+
messages: {
|
|
49
|
+
asyncFunc: 'Async function used in a context where a void function is expected.',
|
|
50
|
+
nonVoidFunc: 'Value-returning function used in a context where a void function is expected.',
|
|
51
|
+
nonVoidReturn: 'Value returned in a context where a void return is expected.',
|
|
52
|
+
},
|
|
53
|
+
schema: [
|
|
54
|
+
{
|
|
55
|
+
type: 'object',
|
|
56
|
+
additionalProperties: false,
|
|
57
|
+
properties: {
|
|
58
|
+
allowReturnAny: {
|
|
59
|
+
type: 'boolean',
|
|
60
|
+
description: 'Whether to allow functions returning `any` to be used in place expecting a `void` function.',
|
|
61
|
+
},
|
|
62
|
+
},
|
|
63
|
+
},
|
|
64
|
+
],
|
|
65
|
+
},
|
|
66
|
+
defaultOptions: [
|
|
67
|
+
{
|
|
68
|
+
allowReturnAny: false,
|
|
69
|
+
},
|
|
70
|
+
],
|
|
71
|
+
create(context, [options]) {
|
|
72
|
+
const sourceCode = context.sourceCode;
|
|
73
|
+
const parserServices = util.getParserServices(context);
|
|
74
|
+
const checker = parserServices.program.getTypeChecker();
|
|
75
|
+
return {
|
|
76
|
+
ArrayExpression: (node) => {
|
|
77
|
+
for (const elemNode of node.elements) {
|
|
78
|
+
if (elemNode != null &&
|
|
79
|
+
elemNode.type !== utils_1.AST_NODE_TYPES.SpreadElement) {
|
|
80
|
+
checkExpressionNode(elemNode);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
},
|
|
84
|
+
ArrowFunctionExpression: (node) => {
|
|
85
|
+
if (node.body.type !== utils_1.AST_NODE_TYPES.BlockStatement) {
|
|
86
|
+
checkExpressionNode(node.body);
|
|
87
|
+
}
|
|
88
|
+
},
|
|
89
|
+
AssignmentExpression: (node) => {
|
|
90
|
+
checkExpressionNode(node.right); // should ignore operators like `+=` or `-=` automatically
|
|
91
|
+
},
|
|
92
|
+
'CallExpression, NewExpression': checkFunctionCallNode,
|
|
93
|
+
JSXAttribute: (node) => {
|
|
94
|
+
if (node.value?.type === utils_1.AST_NODE_TYPES.JSXExpressionContainer &&
|
|
95
|
+
node.value.expression.type !== utils_1.AST_NODE_TYPES.JSXEmptyExpression) {
|
|
96
|
+
checkExpressionNode(node.value.expression);
|
|
97
|
+
}
|
|
98
|
+
},
|
|
99
|
+
MethodDefinition: checkClassMethodNode,
|
|
100
|
+
ObjectExpression: (node) => {
|
|
101
|
+
for (const propNode of node.properties) {
|
|
102
|
+
if (propNode.type !== utils_1.AST_NODE_TYPES.SpreadElement) {
|
|
103
|
+
checkObjectPropertyNode(propNode);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
},
|
|
107
|
+
PropertyDefinition: checkClassPropertyNode,
|
|
108
|
+
ReturnStatement: (node) => {
|
|
109
|
+
if (node.argument != null) {
|
|
110
|
+
checkExpressionNode(node.argument);
|
|
111
|
+
}
|
|
112
|
+
},
|
|
113
|
+
VariableDeclarator: (node) => {
|
|
114
|
+
if (node.init != null) {
|
|
115
|
+
checkExpressionNode(node.init);
|
|
116
|
+
}
|
|
117
|
+
},
|
|
118
|
+
};
|
|
119
|
+
function isVoidReturningFunctionType(type) {
|
|
120
|
+
const returnTypes = tsutils
|
|
121
|
+
.getCallSignaturesOfType(type)
|
|
122
|
+
.flatMap(signature => tsutils.unionConstituents(signature.getReturnType()));
|
|
123
|
+
return (returnTypes.length > 0 &&
|
|
124
|
+
returnTypes.every(type => tsutils.isTypeFlagSet(type, ts.TypeFlags.Void)));
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Finds errors in any expression node.
|
|
128
|
+
*
|
|
129
|
+
* Compares the type of the node against the contextual (expected) type.
|
|
130
|
+
*
|
|
131
|
+
* @returns `true` if the expected type was void function.
|
|
132
|
+
*/
|
|
133
|
+
function checkExpressionNode(node) {
|
|
134
|
+
const tsNode = parserServices.esTreeNodeToTSNodeMap.get(node);
|
|
135
|
+
const expectedType = checker.getContextualType(tsNode);
|
|
136
|
+
if (expectedType != null && isVoidReturningFunctionType(expectedType)) {
|
|
137
|
+
reportIfNonVoidFunction(node);
|
|
138
|
+
return true;
|
|
139
|
+
}
|
|
140
|
+
return false;
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* Finds errors in function calls.
|
|
144
|
+
*
|
|
145
|
+
* When checking arguments, we also manually figure out the argument types
|
|
146
|
+
* by iterating over all the function signatures.
|
|
147
|
+
* Thanks to this, we can find arguments like `(() => void) | (() => any)`
|
|
148
|
+
* and treat them as void too.
|
|
149
|
+
* This is done to also support checking functions like `addEventListener`
|
|
150
|
+
* which have overloads where one callback returns any.
|
|
151
|
+
*
|
|
152
|
+
* Implementation mostly based on no-misused-promises,
|
|
153
|
+
* which does this to find `(() => void) | (() => NotThenable)`
|
|
154
|
+
* and report them too.
|
|
155
|
+
*/
|
|
156
|
+
function checkFunctionCallNode(callNode) {
|
|
157
|
+
const callTsNode = parserServices.esTreeNodeToTSNodeMap.get(callNode);
|
|
158
|
+
const funcType = checker.getTypeAtLocation(callTsNode.expression);
|
|
159
|
+
const funcSignatures = tsutils
|
|
160
|
+
.unionConstituents(funcType)
|
|
161
|
+
.flatMap(type => ts.isCallExpression(callTsNode)
|
|
162
|
+
? type.getCallSignatures()
|
|
163
|
+
: type.getConstructSignatures());
|
|
164
|
+
for (const [argIdx, argNode] of callNode.arguments.entries()) {
|
|
165
|
+
if (argNode.type === utils_1.AST_NODE_TYPES.SpreadElement) {
|
|
166
|
+
continue;
|
|
167
|
+
}
|
|
168
|
+
// Check against the contextual type first
|
|
169
|
+
if (checkExpressionNode(argNode)) {
|
|
170
|
+
continue;
|
|
171
|
+
}
|
|
172
|
+
// Check against the types from all of the call signatures
|
|
173
|
+
const argExpectedReturnTypes = funcSignatures
|
|
174
|
+
.map(s => s.parameters[argIdx])
|
|
175
|
+
.filter(Boolean)
|
|
176
|
+
.map(param => checker.getTypeOfSymbolAtLocation(param, callTsNode.expression))
|
|
177
|
+
.flatMap(paramType => tsutils.unionConstituents(paramType))
|
|
178
|
+
.flatMap(paramType => paramType.getCallSignatures())
|
|
179
|
+
.map(paramSignature => paramSignature.getReturnType());
|
|
180
|
+
if (
|
|
181
|
+
// At least one return type is void
|
|
182
|
+
argExpectedReturnTypes.some(type => tsutils.isTypeFlagSet(type, ts.TypeFlags.Void)) &&
|
|
183
|
+
// The rest are nullish or any
|
|
184
|
+
argExpectedReturnTypes.every(type => tsutils.isTypeFlagSet(type, ts.TypeFlags.VoidLike |
|
|
185
|
+
ts.TypeFlags.Undefined |
|
|
186
|
+
ts.TypeFlags.Null |
|
|
187
|
+
ts.TypeFlags.Any |
|
|
188
|
+
ts.TypeFlags.Never))) {
|
|
189
|
+
// We treat this argument as void even though it might be technically any.
|
|
190
|
+
reportIfNonVoidFunction(argNode);
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
/**
|
|
195
|
+
* Finds errors in an object property.
|
|
196
|
+
*
|
|
197
|
+
* Object properties require different logic
|
|
198
|
+
* when the property is a method shorthand.
|
|
199
|
+
*/
|
|
200
|
+
function checkObjectPropertyNode(propNode) {
|
|
201
|
+
const valueNode = propNode.value;
|
|
202
|
+
const propTsNode = parserServices.esTreeNodeToTSNodeMap.get(propNode);
|
|
203
|
+
if (propTsNode.kind === ts.SyntaxKind.MethodDeclaration) {
|
|
204
|
+
// Object property is a method shorthand.
|
|
205
|
+
if (propTsNode.name.kind === ts.SyntaxKind.ComputedPropertyName) {
|
|
206
|
+
// Don't check object methods with computed name.
|
|
207
|
+
return;
|
|
208
|
+
}
|
|
209
|
+
const objTsNode = propTsNode.parent;
|
|
210
|
+
const objType = checker.getContextualType(objTsNode);
|
|
211
|
+
if (objType == null) {
|
|
212
|
+
// Expected object type is unknown.
|
|
213
|
+
return;
|
|
214
|
+
}
|
|
215
|
+
const propSymbol = checker.getPropertyOfType(objType, propTsNode.name.text);
|
|
216
|
+
if (propSymbol == null) {
|
|
217
|
+
// Expected object type is known, but it doesn't have this method.
|
|
218
|
+
return;
|
|
219
|
+
}
|
|
220
|
+
const propExpectedType = checker.getTypeOfSymbolAtLocation(propSymbol, propTsNode);
|
|
221
|
+
if (isVoidReturningFunctionType(propExpectedType)) {
|
|
222
|
+
reportIfNonVoidFunction(valueNode);
|
|
223
|
+
}
|
|
224
|
+
return;
|
|
225
|
+
}
|
|
226
|
+
// Object property is a regular property.
|
|
227
|
+
checkExpressionNode(valueNode);
|
|
228
|
+
}
|
|
229
|
+
/**
|
|
230
|
+
* Finds errors in a class property.
|
|
231
|
+
*
|
|
232
|
+
* In addition to the regular check against the contextual type,
|
|
233
|
+
* we also check against the base class property (when the class extends another class)
|
|
234
|
+
* and the implemented interfaces (when the class implements an interface).
|
|
235
|
+
*/
|
|
236
|
+
function checkClassPropertyNode(propNode) {
|
|
237
|
+
if (propNode.value == null) {
|
|
238
|
+
return;
|
|
239
|
+
}
|
|
240
|
+
// Check in comparison to the base types.
|
|
241
|
+
for (const { baseMemberType } of util.getBaseTypesOfClassMember(parserServices, propNode)) {
|
|
242
|
+
if (isVoidReturningFunctionType(baseMemberType)) {
|
|
243
|
+
reportIfNonVoidFunction(propNode.value);
|
|
244
|
+
return; // Report at most one error.
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
// Check in comparison to the contextual type.
|
|
248
|
+
checkExpressionNode(propNode.value);
|
|
249
|
+
}
|
|
250
|
+
/**
|
|
251
|
+
* Finds errors in a class method.
|
|
252
|
+
*
|
|
253
|
+
* We check against the base class method (when the class extends another class)
|
|
254
|
+
* and the implemented interfaces (when the class implements an interface).
|
|
255
|
+
*/
|
|
256
|
+
function checkClassMethodNode(methodNode) {
|
|
257
|
+
if (methodNode.value.type === utils_1.AST_NODE_TYPES.TSEmptyBodyFunctionExpression) {
|
|
258
|
+
return;
|
|
259
|
+
}
|
|
260
|
+
// Check in comparison to the base types.
|
|
261
|
+
for (const { baseMemberType } of util.getBaseTypesOfClassMember(parserServices, methodNode)) {
|
|
262
|
+
if (isVoidReturningFunctionType(baseMemberType)) {
|
|
263
|
+
reportIfNonVoidFunction(methodNode.value);
|
|
264
|
+
return; // Report at most one error.
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
/**
|
|
269
|
+
* Reports an error if the provided node is not allowed in a void function context.
|
|
270
|
+
*/
|
|
271
|
+
function reportIfNonVoidFunction(funcNode) {
|
|
272
|
+
const allowedReturnType = ts.TypeFlags.Void |
|
|
273
|
+
ts.TypeFlags.Never |
|
|
274
|
+
ts.TypeFlags.Undefined |
|
|
275
|
+
(options.allowReturnAny ? ts.TypeFlags.Any : 0);
|
|
276
|
+
const tsNode = parserServices.esTreeNodeToTSNodeMap.get(funcNode);
|
|
277
|
+
const actualType = checker.getApparentType(checker.getTypeAtLocation(tsNode));
|
|
278
|
+
if (tsutils
|
|
279
|
+
.getCallSignaturesOfType(actualType)
|
|
280
|
+
.map(signature => signature.getReturnType())
|
|
281
|
+
.flatMap(returnType => tsutils.unionConstituents(returnType))
|
|
282
|
+
.every(type => tsutils.isTypeFlagSet(type, allowedReturnType))) {
|
|
283
|
+
// The function is already void.
|
|
284
|
+
return;
|
|
285
|
+
}
|
|
286
|
+
if (funcNode.type !== utils_1.AST_NODE_TYPES.ArrowFunctionExpression &&
|
|
287
|
+
funcNode.type !== utils_1.AST_NODE_TYPES.FunctionExpression) {
|
|
288
|
+
// The provided function is not a function literal.
|
|
289
|
+
// Report a generic error.
|
|
290
|
+
return context.report({
|
|
291
|
+
node: funcNode,
|
|
292
|
+
messageId: `nonVoidFunc`,
|
|
293
|
+
});
|
|
294
|
+
}
|
|
295
|
+
// The provided function is a function literal.
|
|
296
|
+
if (funcNode.generator) {
|
|
297
|
+
// The provided function is a generator function.
|
|
298
|
+
// Generator functions are not allowed.
|
|
299
|
+
return context.report({
|
|
300
|
+
loc: util.getFunctionHeadLoc(funcNode, sourceCode),
|
|
301
|
+
messageId: `nonVoidFunc`,
|
|
302
|
+
});
|
|
303
|
+
}
|
|
304
|
+
if (funcNode.async) {
|
|
305
|
+
// The provided function is an async function.
|
|
306
|
+
// Async functions aren't allowed.
|
|
307
|
+
return context.report({
|
|
308
|
+
loc: util.getFunctionHeadLoc(funcNode, sourceCode),
|
|
309
|
+
messageId: `asyncFunc`,
|
|
310
|
+
});
|
|
311
|
+
}
|
|
312
|
+
if (funcNode.body.type !== utils_1.AST_NODE_TYPES.BlockStatement) {
|
|
313
|
+
// The provided function is an arrow function shorthand without braces.
|
|
314
|
+
return context.report({
|
|
315
|
+
node: funcNode.body,
|
|
316
|
+
messageId: `nonVoidReturn`,
|
|
317
|
+
});
|
|
318
|
+
}
|
|
319
|
+
// The function is a regular or arrow function with a block body.
|
|
320
|
+
// Check return type annotation.
|
|
321
|
+
if (funcNode.returnType != null) {
|
|
322
|
+
// The provided function has an explicit return type annotation.
|
|
323
|
+
const typeAnnotationNode = funcNode.returnType.typeAnnotation;
|
|
324
|
+
if (typeAnnotationNode.type !== utils_1.AST_NODE_TYPES.TSVoidKeyword) {
|
|
325
|
+
// The explicit return type is not `void`.
|
|
326
|
+
return context.report({
|
|
327
|
+
node: typeAnnotationNode,
|
|
328
|
+
messageId: `nonVoidFunc`,
|
|
329
|
+
});
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
// Iterate over all function's return statements.
|
|
333
|
+
for (const statement of util.walkStatements(funcNode.body.body)) {
|
|
334
|
+
if (statement.type !== utils_1.AST_NODE_TYPES.ReturnStatement ||
|
|
335
|
+
statement.argument == null) {
|
|
336
|
+
// We only care about return statements with a value.
|
|
337
|
+
continue;
|
|
338
|
+
}
|
|
339
|
+
const returnType = checker.getTypeAtLocation(parserServices.esTreeNodeToTSNodeMap.get(statement.argument));
|
|
340
|
+
if (tsutils.isTypeFlagSet(returnType, allowedReturnType)) {
|
|
341
|
+
// Only visit return statements with invalid type.
|
|
342
|
+
continue;
|
|
343
|
+
}
|
|
344
|
+
// This return statement causes the non-void return type.
|
|
345
|
+
const returnKeyword = util.nullThrows(sourceCode.getFirstToken(statement, {
|
|
346
|
+
filter: token => token.value === 'return',
|
|
347
|
+
}), util.NullThrowsReasons.MissingToken('return keyword', statement.type));
|
|
348
|
+
context.report({
|
|
349
|
+
node: returnKeyword,
|
|
350
|
+
messageId: `nonVoidReturn`,
|
|
351
|
+
});
|
|
352
|
+
}
|
|
353
|
+
// No invalid returns found. The function is allowed.
|
|
354
|
+
}
|
|
355
|
+
},
|
|
356
|
+
});
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { TSESTree, ParserServicesWithTypeInformation } from '@typescript-eslint/utils';
|
|
2
|
+
import type * as ts from 'typescript';
|
|
3
|
+
/**
|
|
4
|
+
* Given a member of a class which extends another class or implements an interface,
|
|
5
|
+
* yields the corresponding member type for each of the base class/interfaces.
|
|
6
|
+
*/
|
|
7
|
+
export declare function getBaseTypesOfClassMember(services: ParserServicesWithTypeInformation, memberNode: TSESTree.MethodDefinition | TSESTree.PropertyDefinition): Generator<{
|
|
8
|
+
baseType: ts.Type;
|
|
9
|
+
baseMemberType: ts.Type;
|
|
10
|
+
heritageToken: ts.SyntaxKind.ExtendsKeyword | ts.SyntaxKind.ImplementsKeyword;
|
|
11
|
+
}>;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getBaseTypesOfClassMember = getBaseTypesOfClassMember;
|
|
4
|
+
/**
|
|
5
|
+
* Given a member of a class which extends another class or implements an interface,
|
|
6
|
+
* yields the corresponding member type for each of the base class/interfaces.
|
|
7
|
+
*/
|
|
8
|
+
function* getBaseTypesOfClassMember(services, memberNode) {
|
|
9
|
+
const memberTsNode = services.esTreeNodeToTSNodeMap.get(memberNode);
|
|
10
|
+
if (memberTsNode.name == null) {
|
|
11
|
+
return;
|
|
12
|
+
}
|
|
13
|
+
const checker = services.program.getTypeChecker();
|
|
14
|
+
const memberSymbol = checker.getSymbolAtLocation(memberTsNode.name);
|
|
15
|
+
if (memberSymbol == null) {
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
const classNode = memberTsNode.parent;
|
|
19
|
+
for (const clauseNode of classNode.heritageClauses ?? []) {
|
|
20
|
+
for (const baseTypeNode of clauseNode.types) {
|
|
21
|
+
const baseType = checker.getTypeAtLocation(baseTypeNode);
|
|
22
|
+
const baseMemberSymbol = checker.getPropertyOfType(baseType, memberSymbol.name);
|
|
23
|
+
if (baseMemberSymbol == null) {
|
|
24
|
+
continue;
|
|
25
|
+
}
|
|
26
|
+
const baseMemberType = checker.getTypeOfSymbolAtLocation(baseMemberSymbol, memberTsNode);
|
|
27
|
+
const heritageToken = clauseNode.token;
|
|
28
|
+
yield { baseMemberType, baseType, heritageToken };
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|
package/dist/util/index.d.ts
CHANGED
|
@@ -3,6 +3,7 @@ export * from './astUtils';
|
|
|
3
3
|
export * from './baseTypeUtils';
|
|
4
4
|
export * from './collectUnusedVariables';
|
|
5
5
|
export * from './createRule';
|
|
6
|
+
export * from './getBaseTypesOfClassMember';
|
|
6
7
|
export * from './getFixOrSuggest';
|
|
7
8
|
export * from './getFunctionHeadLoc';
|
|
8
9
|
export * from './getOperatorPrecedence';
|
|
@@ -29,6 +30,7 @@ export * from './getValueOfLiteralType';
|
|
|
29
30
|
export * from './isHigherPrecedenceThanAwait';
|
|
30
31
|
export * from './skipChainExpression';
|
|
31
32
|
export * from './truthinessUtils';
|
|
33
|
+
export * from './walkStatements';
|
|
32
34
|
export * from '@typescript-eslint/type-utils';
|
|
33
35
|
export declare const applyDefault: typeof ESLintUtils.applyDefault, deepMerge: typeof ESLintUtils.deepMerge, getParserServices: typeof ESLintUtils.getParserServices, isObjectNotArray: typeof ESLintUtils.isObjectNotArray, nullThrows: typeof ESLintUtils.nullThrows, NullThrowsReasons: {
|
|
34
36
|
readonly MissingParent: "Expected node to have a parent.";
|
package/dist/util/index.js
CHANGED
|
@@ -20,6 +20,7 @@ __exportStar(require("./astUtils"), exports);
|
|
|
20
20
|
__exportStar(require("./baseTypeUtils"), exports);
|
|
21
21
|
__exportStar(require("./collectUnusedVariables"), exports);
|
|
22
22
|
__exportStar(require("./createRule"), exports);
|
|
23
|
+
__exportStar(require("./getBaseTypesOfClassMember"), exports);
|
|
23
24
|
__exportStar(require("./getFixOrSuggest"), exports);
|
|
24
25
|
__exportStar(require("./getFunctionHeadLoc"), exports);
|
|
25
26
|
__exportStar(require("./getOperatorPrecedence"), exports);
|
|
@@ -46,6 +47,7 @@ __exportStar(require("./getValueOfLiteralType"), exports);
|
|
|
46
47
|
__exportStar(require("./isHigherPrecedenceThanAwait"), exports);
|
|
47
48
|
__exportStar(require("./skipChainExpression"), exports);
|
|
48
49
|
__exportStar(require("./truthinessUtils"), exports);
|
|
50
|
+
__exportStar(require("./walkStatements"), exports);
|
|
49
51
|
// this is done for convenience - saves migrating all of the old rules
|
|
50
52
|
__exportStar(require("@typescript-eslint/type-utils"), exports);
|
|
51
53
|
exports.applyDefault = utils_1.ESLintUtils.applyDefault, exports.deepMerge = utils_1.ESLintUtils.deepMerge, exports.getParserServices = utils_1.ESLintUtils.getParserServices, exports.isObjectNotArray = utils_1.ESLintUtils.isObjectNotArray, exports.nullThrows = utils_1.ESLintUtils.nullThrows, exports.NullThrowsReasons = utils_1.ESLintUtils.NullThrowsReasons;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { TSESTree } from '@typescript-eslint/utils';
|
|
2
|
+
/**
|
|
3
|
+
* Yields all statement nodes in a block, including nested blocks.
|
|
4
|
+
*
|
|
5
|
+
* You can use it to find all return statements in a function body.
|
|
6
|
+
*/
|
|
7
|
+
export declare function walkStatements(body: readonly TSESTree.Statement[]): Generator<TSESTree.Statement>;
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.walkStatements = walkStatements;
|
|
4
|
+
const utils_1 = require("@typescript-eslint/utils");
|
|
5
|
+
/**
|
|
6
|
+
* Yields all statement nodes in a block, including nested blocks.
|
|
7
|
+
*
|
|
8
|
+
* You can use it to find all return statements in a function body.
|
|
9
|
+
*/
|
|
10
|
+
function* walkStatements(body) {
|
|
11
|
+
for (const statement of body) {
|
|
12
|
+
switch (statement.type) {
|
|
13
|
+
case utils_1.AST_NODE_TYPES.BlockStatement: {
|
|
14
|
+
yield* walkStatements(statement.body);
|
|
15
|
+
continue;
|
|
16
|
+
}
|
|
17
|
+
case utils_1.AST_NODE_TYPES.SwitchStatement: {
|
|
18
|
+
for (const switchCase of statement.cases) {
|
|
19
|
+
yield* walkStatements(switchCase.consequent);
|
|
20
|
+
}
|
|
21
|
+
continue;
|
|
22
|
+
}
|
|
23
|
+
case utils_1.AST_NODE_TYPES.IfStatement: {
|
|
24
|
+
yield* walkStatements([statement.consequent]);
|
|
25
|
+
if (statement.alternate) {
|
|
26
|
+
yield* walkStatements([statement.alternate]);
|
|
27
|
+
}
|
|
28
|
+
continue;
|
|
29
|
+
}
|
|
30
|
+
case utils_1.AST_NODE_TYPES.WhileStatement:
|
|
31
|
+
case utils_1.AST_NODE_TYPES.DoWhileStatement:
|
|
32
|
+
case utils_1.AST_NODE_TYPES.ForStatement:
|
|
33
|
+
case utils_1.AST_NODE_TYPES.ForInStatement:
|
|
34
|
+
case utils_1.AST_NODE_TYPES.ForOfStatement:
|
|
35
|
+
case utils_1.AST_NODE_TYPES.WithStatement:
|
|
36
|
+
case utils_1.AST_NODE_TYPES.LabeledStatement: {
|
|
37
|
+
yield* walkStatements([statement.body]);
|
|
38
|
+
continue;
|
|
39
|
+
}
|
|
40
|
+
case utils_1.AST_NODE_TYPES.TryStatement: {
|
|
41
|
+
yield* walkStatements([statement.block]);
|
|
42
|
+
if (statement.handler) {
|
|
43
|
+
yield* walkStatements([statement.handler.body]);
|
|
44
|
+
}
|
|
45
|
+
if (statement.finalizer) {
|
|
46
|
+
yield* walkStatements([statement.finalizer]);
|
|
47
|
+
}
|
|
48
|
+
continue;
|
|
49
|
+
}
|
|
50
|
+
default: {
|
|
51
|
+
yield statement;
|
|
52
|
+
continue;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@typescript-eslint/eslint-plugin",
|
|
3
|
-
"version": "8.
|
|
3
|
+
"version": "8.53.1",
|
|
4
4
|
"description": "TypeScript plugin for ESLint",
|
|
5
5
|
"files": [
|
|
6
6
|
"dist",
|
|
@@ -59,10 +59,10 @@
|
|
|
59
59
|
},
|
|
60
60
|
"dependencies": {
|
|
61
61
|
"@eslint-community/regexpp": "^4.12.2",
|
|
62
|
-
"@typescript-eslint/scope-manager": "8.
|
|
63
|
-
"@typescript-eslint/type-utils": "8.
|
|
64
|
-
"@typescript-eslint/utils": "8.
|
|
65
|
-
"@typescript-eslint/visitor-keys": "8.
|
|
62
|
+
"@typescript-eslint/scope-manager": "8.53.1",
|
|
63
|
+
"@typescript-eslint/type-utils": "8.53.1",
|
|
64
|
+
"@typescript-eslint/utils": "8.53.1",
|
|
65
|
+
"@typescript-eslint/visitor-keys": "8.53.1",
|
|
66
66
|
"ignore": "^7.0.5",
|
|
67
67
|
"natural-compare": "^1.4.0",
|
|
68
68
|
"ts-api-utils": "^2.4.0"
|
|
@@ -70,8 +70,8 @@
|
|
|
70
70
|
"devDependencies": {
|
|
71
71
|
"@types/mdast": "^4.0.4",
|
|
72
72
|
"@types/natural-compare": "*",
|
|
73
|
-
"@typescript-eslint/rule-schema-to-typescript-types": "8.
|
|
74
|
-
"@typescript-eslint/rule-tester": "8.
|
|
73
|
+
"@typescript-eslint/rule-schema-to-typescript-types": "8.53.1",
|
|
74
|
+
"@typescript-eslint/rule-tester": "8.53.1",
|
|
75
75
|
"@vitest/coverage-v8": "^3.2.4",
|
|
76
76
|
"ajv": "^6.12.6",
|
|
77
77
|
"eslint": "*",
|
|
@@ -90,7 +90,7 @@
|
|
|
90
90
|
"vitest": "^3.2.4"
|
|
91
91
|
},
|
|
92
92
|
"peerDependencies": {
|
|
93
|
-
"@typescript-eslint/parser": "^8.
|
|
93
|
+
"@typescript-eslint/parser": "^8.53.1",
|
|
94
94
|
"eslint": "^8.57.0 || ^9.0.0",
|
|
95
95
|
"typescript": ">=4.8.4 <6.0.0"
|
|
96
96
|
},
|