@flint.fyi/ts 0.14.3 → 0.14.5
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/CHANGELOG.md +16 -0
- package/lib/createTypeScriptFileFromProgram.js +5 -1
- package/lib/index.d.ts +3 -0
- package/lib/index.js +3 -0
- package/lib/language.d.ts +1 -0
- package/lib/plugin.d.ts +61 -1
- package/lib/plugin.js +30 -0
- package/lib/rules/anyReturns.d.ts +6 -0
- package/lib/rules/anyReturns.js +187 -0
- package/lib/rules/anyReturns.test.d.ts +1 -0
- package/lib/rules/anyReturns.test.js +647 -0
- package/lib/rules/caseDuplicates.d.ts +6 -0
- package/lib/rules/caseDuplicates.js +49 -0
- package/lib/rules/caseDuplicates.test.d.ts +1 -0
- package/lib/rules/caseDuplicates.test.js +307 -0
- package/lib/rules/chainedAssignments.js +2 -1
- package/lib/rules/chainedAssignments.test.js +24 -0
- package/lib/rules/debuggerStatements.test.js +4 -4
- package/lib/rules/elseIfDuplicates.d.ts +6 -0
- package/lib/rules/elseIfDuplicates.js +53 -0
- package/lib/rules/elseIfDuplicates.test.d.ts +1 -0
- package/lib/rules/elseIfDuplicates.test.js +176 -0
- package/lib/rules/emptyBlocks.d.ts +6 -0
- package/lib/rules/emptyBlocks.js +66 -0
- package/lib/rules/emptyBlocks.test.d.ts +1 -0
- package/lib/rules/emptyBlocks.test.js +135 -0
- package/lib/rules/forDirections.d.ts +6 -0
- package/lib/rules/forDirections.js +124 -0
- package/lib/rules/forDirections.test.d.ts +1 -0
- package/lib/rules/forDirections.test.js +99 -0
- package/lib/rules/functionNewCalls.d.ts +6 -0
- package/lib/rules/functionNewCalls.js +60 -0
- package/lib/rules/functionNewCalls.test.d.ts +1 -0
- package/lib/rules/functionNewCalls.test.js +115 -0
- package/lib/rules/negativeZeroComparisons.js +4 -22
- package/lib/rules/nonOctalDecimalEscapes.d.ts +6 -0
- package/lib/rules/nonOctalDecimalEscapes.js +57 -0
- package/lib/rules/nonOctalDecimalEscapes.test.d.ts +1 -0
- package/lib/rules/nonOctalDecimalEscapes.test.js +69 -0
- package/lib/rules/numericLiteralParsing.d.ts +6 -0
- package/lib/rules/numericLiteralParsing.js +97 -0
- package/lib/rules/numericLiteralParsing.test.d.ts +1 -0
- package/lib/rules/numericLiteralParsing.test.js +99 -0
- package/lib/rules/octalNumbers.test.js +2 -2
- package/lib/rules/selfAssignments.d.ts +6 -0
- package/lib/rules/selfAssignments.js +44 -0
- package/lib/rules/selfAssignments.test.d.ts +1 -0
- package/lib/rules/selfAssignments.test.js +66 -0
- package/lib/rules/selfComparisons.d.ts +6 -0
- package/lib/rules/selfComparisons.js +41 -0
- package/lib/rules/selfComparisons.test.d.ts +1 -0
- package/lib/rules/selfComparisons.test.js +297 -0
- package/lib/rules/sequences.d.ts +6 -0
- package/lib/rules/sequences.js +34 -0
- package/lib/rules/sequences.test.d.ts +1 -0
- package/lib/rules/sequences.test.js +48 -0
- package/lib/rules/shadowedRestrictedNames.d.ts +6 -0
- package/lib/rules/shadowedRestrictedNames.js +95 -0
- package/lib/rules/shadowedRestrictedNames.test.d.ts +1 -0
- package/lib/rules/shadowedRestrictedNames.test.js +147 -0
- package/lib/rules/symbolDescriptions.test.js +4 -4
- package/lib/rules/typeofComparisons.d.ts +6 -0
- package/lib/rules/typeofComparisons.js +78 -0
- package/lib/rules/typeofComparisons.test.d.ts +1 -0
- package/lib/rules/typeofComparisons.test.js +85 -0
- package/lib/rules/unicodeBOMs.test.js +1 -0
- package/lib/rules/unnecessaryBlocks.d.ts +6 -0
- package/lib/rules/unnecessaryBlocks.js +75 -0
- package/lib/rules/unnecessaryBlocks.test.d.ts +1 -0
- package/lib/rules/unnecessaryBlocks.test.js +147 -0
- package/lib/rules/unnecessaryConcatenation.d.ts +6 -0
- package/lib/rules/unnecessaryConcatenation.js +38 -0
- package/lib/rules/unnecessaryConcatenation.test.d.ts +1 -0
- package/lib/rules/unnecessaryConcatenation.test.js +87 -0
- package/lib/rules/utils/discriminateAnyType.d.ts +12 -0
- package/lib/rules/utils/discriminateAnyType.js +41 -0
- package/lib/rules/utils/getThisExpression.d.ts +2 -0
- package/lib/rules/utils/getThisExpression.js +23 -0
- package/lib/rules/utils/isUnsafeAssignment.d.ts +13 -0
- package/lib/rules/utils/isUnsafeAssignment.js +76 -0
- package/lib/rules/utils/operators.d.ts +4 -0
- package/lib/rules/utils/operators.js +36 -0
- package/lib/rules/voidOperator.test.js +2 -2
- package/lib/utils/declarationIncludesGlobal.d.ts +2 -0
- package/lib/utils/declarationIncludesGlobal.js +5 -0
- package/lib/utils/declarationsIncludeGlobal.js +2 -5
- package/lib/utils/getDeclarationsIfGlobal.d.ts +2 -0
- package/lib/utils/getDeclarationsIfGlobal.js +7 -0
- package/lib/utils/hasSameTokens.d.ts +2 -0
- package/lib/utils/hasSameTokens.js +30 -0
- package/lib/utils/isGlobalDeclaration.js +2 -3
- package/lib/utils/isGlobalDeclarationOfName.d.ts +5 -0
- package/lib/utils/isGlobalDeclarationOfName.js +35 -0
- package/package.json +3 -3
- package/src/createTypeScriptFileFromProgram.ts +6 -1
- package/src/index.ts +3 -0
- package/src/language.ts +1 -0
- package/src/plugin.ts +30 -0
- package/src/rules/anyReturns.test.ts +649 -0
- package/src/rules/anyReturns.ts +263 -0
- package/src/rules/caseDuplicates.test.ts +308 -0
- package/src/rules/caseDuplicates.ts +57 -0
- package/src/rules/chainedAssignments.test.ts +24 -0
- package/src/rules/chainedAssignments.ts +4 -1
- package/src/rules/debuggerStatements.test.ts +4 -4
- package/src/rules/elseIfDuplicates.test.ts +177 -0
- package/src/rules/elseIfDuplicates.ts +69 -0
- package/src/rules/emptyBlocks.test.ts +136 -0
- package/src/rules/emptyBlocks.ts +73 -0
- package/src/rules/forDirections.test.ts +100 -0
- package/src/rules/forDirections.ts +163 -0
- package/src/rules/functionNewCalls.test.ts +116 -0
- package/src/rules/functionNewCalls.ts +75 -0
- package/src/rules/negativeZeroComparisons.ts +8 -27
- package/src/rules/nonOctalDecimalEscapes.test.ts +70 -0
- package/src/rules/nonOctalDecimalEscapes.ts +70 -0
- package/src/rules/numericLiteralParsing.test.ts +100 -0
- package/src/rules/numericLiteralParsing.ts +116 -0
- package/src/rules/octalNumbers.test.ts +2 -2
- package/src/rules/selfAssignments.test.ts +67 -0
- package/src/rules/selfAssignments.ts +50 -0
- package/src/rules/selfComparisons.test.ts +298 -0
- package/src/rules/selfComparisons.ts +45 -0
- package/src/rules/sequences.test.ts +49 -0
- package/src/rules/sequences.ts +37 -0
- package/src/rules/shadowedRestrictedNames.test.ts +148 -0
- package/src/rules/shadowedRestrictedNames.ts +105 -0
- package/src/rules/symbolDescriptions.test.ts +4 -4
- package/src/rules/typeofComparisons.test.ts +86 -0
- package/src/rules/typeofComparisons.ts +88 -0
- package/src/rules/unicodeBOMs.test.ts +1 -0
- package/src/rules/unnecessaryBlocks.test.ts +148 -0
- package/src/rules/unnecessaryBlocks.ts +89 -0
- package/src/rules/unnecessaryConcatenation.test.ts +88 -0
- package/src/rules/unnecessaryConcatenation.ts +44 -0
- package/src/rules/utils/discriminateAnyType.ts +66 -0
- package/src/rules/utils/getThisExpression.ts +24 -0
- package/src/rules/utils/isUnsafeAssignment.ts +113 -0
- package/src/rules/utils/operators.ts +39 -0
- package/src/rules/voidOperator.test.ts +2 -2
- package/src/utils/declarationIncludesGlobal.ts +9 -0
- package/src/utils/declarationsIncludeGlobal.ts +3 -7
- package/src/utils/getDeclarationsIfGlobal.ts +14 -0
- package/src/utils/hasSameTokens.ts +45 -0
- package/src/utils/isGlobalDeclaration.ts +3 -5
- package/src/utils/isGlobalDeclarationOfName.ts +56 -0
- package/tsconfig.tsbuildinfo +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,21 @@
|
|
|
1
1
|
# @flint/ts
|
|
2
2
|
|
|
3
|
+
## 0.14.5
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 7e21021: fix(ts): [chainedAssignments] allow non-assignment right-side operators
|
|
8
|
+
|
|
9
|
+
## 0.14.4
|
|
10
|
+
|
|
11
|
+
### Patch Changes
|
|
12
|
+
|
|
13
|
+
- 158b542: expose \`program\` in TS rules context
|
|
14
|
+
- Updated dependencies [3d19082]
|
|
15
|
+
- Updated dependencies [a3f9043]
|
|
16
|
+
- @flint.fyi/core@0.15.1
|
|
17
|
+
- @flint.fyi/rule-tester@0.14.2
|
|
18
|
+
|
|
3
19
|
## 0.14.3
|
|
4
20
|
|
|
5
21
|
### Patch Changes
|
|
@@ -33,9 +33,13 @@ export function createTypeScriptFileFromProgram(program, sourceFile) {
|
|
|
33
33
|
async runRule(rule, options) {
|
|
34
34
|
const reports = [];
|
|
35
35
|
const context = {
|
|
36
|
+
program,
|
|
36
37
|
report: (report) => {
|
|
37
38
|
reports.push({
|
|
38
39
|
...report,
|
|
40
|
+
fix: report.fix && !Array.isArray(report.fix)
|
|
41
|
+
? [report.fix]
|
|
42
|
+
: report.fix,
|
|
39
43
|
message: rule.messages[report.message],
|
|
40
44
|
range: normalizeRange(report.range, sourceFile),
|
|
41
45
|
});
|
|
@@ -52,7 +56,7 @@ export function createTypeScriptFileFromProgram(program, sourceFile) {
|
|
|
52
56
|
visitors[NodeSyntaxKinds[node.kind]]?.(node);
|
|
53
57
|
node.forEachChild(visit);
|
|
54
58
|
};
|
|
55
|
-
sourceFile
|
|
59
|
+
visit(sourceFile);
|
|
56
60
|
return reports;
|
|
57
61
|
},
|
|
58
62
|
};
|
package/lib/index.d.ts
CHANGED
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
export { getTSNodeRange } from "./getTSNodeRange.js";
|
|
2
2
|
export * from "./language.js";
|
|
3
3
|
export { ts } from "./plugin.js";
|
|
4
|
+
export { getDeclarationsIfGlobal } from "./utils/getDeclarationsIfGlobal.js";
|
|
4
5
|
export { isGlobalDeclaration } from "./utils/isGlobalDeclaration.js";
|
|
6
|
+
export { isGlobalDeclarationOfName } from "./utils/isGlobalDeclarationOfName.js";
|
|
7
|
+
export { isGlobalVariable } from "./utils/isGlobalVariable.js";
|
package/lib/index.js
CHANGED
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
export { getTSNodeRange } from "./getTSNodeRange.js";
|
|
2
2
|
export * from "./language.js";
|
|
3
3
|
export { ts } from "./plugin.js";
|
|
4
|
+
export { getDeclarationsIfGlobal } from "./utils/getDeclarationsIfGlobal.js";
|
|
4
5
|
export { isGlobalDeclaration } from "./utils/isGlobalDeclaration.js";
|
|
6
|
+
export { isGlobalDeclarationOfName } from "./utils/isGlobalDeclarationOfName.js";
|
|
7
|
+
export { isGlobalVariable } from "./utils/isGlobalVariable.js";
|
package/lib/language.d.ts
CHANGED
package/lib/plugin.d.ts
CHANGED
|
@@ -1,4 +1,8 @@
|
|
|
1
|
-
export declare const ts: import("@flint.fyi/core").Plugin<import("@flint.fyi/core").RuleAbout,
|
|
1
|
+
export declare const ts: import("@flint.fyi/core").Plugin<import("@flint.fyi/core").RuleAbout, "all", [import("@flint.fyi/core").Rule<{
|
|
2
|
+
readonly description: "Reports returning a value with type `any` from a function.";
|
|
3
|
+
readonly id: "anyReturns";
|
|
4
|
+
readonly preset: "logical";
|
|
5
|
+
}, import("./nodes.js").TSNodesByName, import("./language.js").TypeScriptServices, "unsafeReturn" | "unsafeReturnAssignment" | "unsafeReturnThis", undefined>, import("@flint.fyi/core").Rule<{
|
|
2
6
|
readonly description: "Reports using async functions as Promise executor functions.";
|
|
3
7
|
readonly id: "asyncPromiseExecutors";
|
|
4
8
|
readonly preset: "logical";
|
|
@@ -7,6 +11,10 @@ export declare const ts: import("@flint.fyi/core").Plugin<import("@flint.fyi/cor
|
|
|
7
11
|
readonly id: "caseDeclarations";
|
|
8
12
|
readonly preset: "untyped";
|
|
9
13
|
}, import("./nodes.js").TSNodesByName, import("./language.js").TypeScriptServices, "unexpectedLexicalDeclaration", undefined>, import("@flint.fyi/core").Rule<{
|
|
14
|
+
readonly description: "Reports switch statements with duplicate case clause test expressions.";
|
|
15
|
+
readonly id: "caseDuplicates";
|
|
16
|
+
readonly preset: "logical";
|
|
17
|
+
}, import("./nodes.js").TSNodesByName, import("./language.js").TypeScriptServices, "duplicateCase", undefined>, import("@flint.fyi/core").Rule<{
|
|
10
18
|
readonly description: "Reports using chained assignment expressions (e.g., a = b = c).";
|
|
11
19
|
readonly id: "chainedAssignments";
|
|
12
20
|
readonly preset: "stylistic";
|
|
@@ -39,6 +47,14 @@ export declare const ts: import("@flint.fyi/core").Plugin<import("@flint.fyi/cor
|
|
|
39
47
|
readonly id: "duplicateArguments";
|
|
40
48
|
readonly preset: "untyped";
|
|
41
49
|
}, import("./nodes.js").TSNodesByName, import("./language.js").TypeScriptServices, "duplicateParam", undefined>, import("@flint.fyi/core").Rule<{
|
|
50
|
+
readonly description: "Reports duplicate conditions in if-else-if chains that make code unreachable.";
|
|
51
|
+
readonly id: "elseIfDuplicates";
|
|
52
|
+
readonly preset: "logical";
|
|
53
|
+
}, import("./nodes.js").TSNodesByName, import("./language.js").TypeScriptServices, "duplicateCondition", undefined>, import("@flint.fyi/core").Rule<{
|
|
54
|
+
readonly description: "Reports empty block statements that should contain code.";
|
|
55
|
+
readonly id: "emptyBlocks";
|
|
56
|
+
readonly preset: "stylistic";
|
|
57
|
+
}, import("./nodes.js").TSNodesByName, import("./language.js").TypeScriptServices, "emptyBlock", undefined>, import("@flint.fyi/core").Rule<{
|
|
42
58
|
readonly description: "Reports using empty destructuring patterns that destructure no values.";
|
|
43
59
|
readonly id: "emptyDestructures";
|
|
44
60
|
readonly preset: "logical";
|
|
@@ -51,6 +67,10 @@ export declare const ts: import("@flint.fyi/core").Plugin<import("@flint.fyi/cor
|
|
|
51
67
|
readonly id: "exceptionAssignments";
|
|
52
68
|
readonly preset: "logical";
|
|
53
69
|
}, import("./nodes.js").TSNodesByName, import("./language.js").TypeScriptServices, "noExAssign", undefined>, import("@flint.fyi/core").Rule<{
|
|
70
|
+
readonly description: "Reports for loops with counter variables that move in the wrong direction.";
|
|
71
|
+
readonly id: "forDirections";
|
|
72
|
+
readonly preset: "stylistic";
|
|
73
|
+
}, import("./nodes.js").TSNodesByName, import("./language.js").TypeScriptServices, "wrongDirection", undefined>, import("@flint.fyi/core").Rule<{
|
|
54
74
|
readonly description: "Reports iterating over an array with a for-in loop.";
|
|
55
75
|
readonly id: "forInArrays";
|
|
56
76
|
readonly preset: "logical";
|
|
@@ -59,6 +79,10 @@ export declare const ts: import("@flint.fyi/core").Plugin<import("@flint.fyi/cor
|
|
|
59
79
|
readonly id: "functionAssignments";
|
|
60
80
|
readonly preset: "untyped";
|
|
61
81
|
}, import("./nodes.js").TSNodesByName, import("./language.js").TypeScriptServices, "noFunctionAssignment", undefined>, import("@flint.fyi/core").Rule<{
|
|
82
|
+
readonly description: "Reports using the Function constructor to create functions from strings.";
|
|
83
|
+
readonly id: "functionNewCalls";
|
|
84
|
+
readonly preset: "logical";
|
|
85
|
+
}, import("./nodes.js").TSNodesByName, import("./language.js").TypeScriptServices, "noFunctionConstructor", undefined>, import("@flint.fyi/core").Rule<{
|
|
62
86
|
readonly description: "Reports generator functions that do not yield values.";
|
|
63
87
|
readonly id: "generatorFunctionYields";
|
|
64
88
|
readonly preset: "logical";
|
|
@@ -90,6 +114,14 @@ export declare const ts: import("@flint.fyi/core").Plugin<import("@flint.fyi/cor
|
|
|
90
114
|
readonly id: "newNativeNonConstructors";
|
|
91
115
|
readonly preset: "untyped";
|
|
92
116
|
}, import("./nodes.js").TSNodesByName, import("./language.js").TypeScriptServices, "noNewNonConstructor", undefined>, import("@flint.fyi/core").Rule<{
|
|
117
|
+
readonly description: "Reports non-octal decimal escape sequences (\\8 and \\9) in string literals.";
|
|
118
|
+
readonly id: "nonOctalDecimalEscapes";
|
|
119
|
+
readonly preset: "logical";
|
|
120
|
+
}, import("./nodes.js").TSNodesByName, import("./language.js").TypeScriptServices, "unexpectedEscape", undefined>, import("@flint.fyi/core").Rule<{
|
|
121
|
+
readonly description: "Reports parseInt calls with binary, hexadecimal, or octal strings that can be replaced with numeric literals.";
|
|
122
|
+
readonly id: "numericLiteralParsing";
|
|
123
|
+
readonly preset: "stylistic";
|
|
124
|
+
}, import("./nodes.js").TSNodesByName, import("./language.js").TypeScriptServices, "preferLiteral", undefined>, import("@flint.fyi/core").Rule<{
|
|
93
125
|
readonly description: "Reports using the deprecated __proto__ property to access or modify an object's prototype.";
|
|
94
126
|
readonly id: "objectProto";
|
|
95
127
|
readonly preset: "untyped";
|
|
@@ -106,6 +138,22 @@ export declare const ts: import("@flint.fyi/core").Plugin<import("@flint.fyi/cor
|
|
|
106
138
|
readonly id: "returnAssignments";
|
|
107
139
|
readonly preset: "stylistic";
|
|
108
140
|
}, import("./nodes.js").TSNodesByName, import("./language.js").TypeScriptServices, "noReturnAssign", undefined>, import("@flint.fyi/core").Rule<{
|
|
141
|
+
readonly description: "Reports self-assignments which have no effect and are likely errors.";
|
|
142
|
+
readonly id: "selfAssignments";
|
|
143
|
+
readonly preset: "logical";
|
|
144
|
+
}, import("./nodes.js").TSNodesByName, import("./language.js").TypeScriptServices, "selfAssignment", undefined>, import("@flint.fyi/core").Rule<{
|
|
145
|
+
readonly description: "Reports comparing a value to itself.";
|
|
146
|
+
readonly id: "selfComparisons";
|
|
147
|
+
readonly preset: "logical";
|
|
148
|
+
}, import("./nodes.js").TSNodesByName, import("./language.js").TypeScriptServices, "noSelfComparison", undefined>, import("@flint.fyi/core").Rule<{
|
|
149
|
+
readonly description: "Reports using the comma operator in expressions.";
|
|
150
|
+
readonly id: "sequences";
|
|
151
|
+
readonly preset: "untyped";
|
|
152
|
+
}, import("./nodes.js").TSNodesByName, import("./language.js").TypeScriptServices, "noSequences", undefined>, import("@flint.fyi/core").Rule<{
|
|
153
|
+
readonly description: "Reports variable declarations that shadow JavaScript's restricted names.";
|
|
154
|
+
readonly id: "shadowedRestrictedNames";
|
|
155
|
+
readonly preset: "untyped";
|
|
156
|
+
}, import("./nodes.js").TSNodesByName, import("./language.js").TypeScriptServices, "shadowedRestrictedName", undefined>, import("@flint.fyi/core").Rule<{
|
|
109
157
|
readonly description: "Reports array literals with holes (sparse arrays).";
|
|
110
158
|
readonly id: "sparseArrays";
|
|
111
159
|
readonly preset: "logical";
|
|
@@ -114,6 +162,10 @@ export declare const ts: import("@flint.fyi/core").Plugin<import("@flint.fyi/cor
|
|
|
114
162
|
readonly id: "symbolDescriptions";
|
|
115
163
|
readonly preset: "stylistic";
|
|
116
164
|
}, import("./nodes.js").TSNodesByName, import("./language.js").TypeScriptServices, "missingDescription", undefined>, import("@flint.fyi/core").Rule<{
|
|
165
|
+
readonly description: "Reports typeof expressions that compare impossible string literals.";
|
|
166
|
+
readonly id: "typeofComparisons";
|
|
167
|
+
readonly preset: "untyped";
|
|
168
|
+
}, import("./nodes.js").TSNodesByName, import("./language.js").TypeScriptServices, "invalidValue", undefined>, import("@flint.fyi/core").Rule<{
|
|
117
169
|
readonly description: "Reports variables that are declared but never assigned a value.";
|
|
118
170
|
readonly id: "unassignedVariables";
|
|
119
171
|
readonly preset: "untyped";
|
|
@@ -126,10 +178,18 @@ export declare const ts: import("@flint.fyi/core").Plugin<import("@flint.fyi/cor
|
|
|
126
178
|
readonly id: "unicodeBOMs";
|
|
127
179
|
readonly preset: "stylistic";
|
|
128
180
|
}, import("./nodes.js").TSNodesByName, import("./language.js").TypeScriptServices, "noBOM", undefined>, import("@flint.fyi/core").Rule<{
|
|
181
|
+
readonly description: "Reports standalone block statements that don't create a meaningful scope.";
|
|
182
|
+
readonly id: "unnecessaryBlocks";
|
|
183
|
+
readonly preset: "stylistic";
|
|
184
|
+
}, import("./nodes.js").TSNodesByName, import("./language.js").TypeScriptServices, "unnecessaryBlock", undefined>, import("@flint.fyi/core").Rule<{
|
|
129
185
|
readonly description: "Reports catch clauses that only rethrow the caught error without modification.";
|
|
130
186
|
readonly id: "unnecessaryCatches";
|
|
131
187
|
readonly preset: "logical";
|
|
132
188
|
}, import("./nodes.js").TSNodesByName, import("./language.js").TypeScriptServices, "unnecessaryCatch", undefined>, import("@flint.fyi/core").Rule<{
|
|
189
|
+
readonly description: "Reports string concatenation using the + operator when both operands are string literals.";
|
|
190
|
+
readonly id: "unnecessaryConcatenation";
|
|
191
|
+
readonly preset: "stylistic";
|
|
192
|
+
}, import("./nodes.js").TSNodesByName, import("./language.js").TypeScriptServices, "unnecessaryConcatenation", undefined>, import("@flint.fyi/core").Rule<{
|
|
133
193
|
readonly description: "Reports negating the left operand of `in` or `instanceof` relations.";
|
|
134
194
|
readonly id: "unsafeNegations";
|
|
135
195
|
readonly preset: "untyped";
|
package/lib/plugin.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { createPlugin } from "@flint.fyi/core";
|
|
2
|
+
import anyReturns from "./rules/anyReturns.js";
|
|
2
3
|
import asyncPromiseExecutors from "./rules/asyncPromiseExecutors.js";
|
|
3
4
|
import caseDeclarations from "./rules/caseDeclarations.js";
|
|
5
|
+
import caseDuplicates from "./rules/caseDuplicates.js";
|
|
4
6
|
import chainedAssignments from "./rules/chainedAssignments.js";
|
|
5
7
|
import classAssignments from "./rules/classAssignments.js";
|
|
6
8
|
import consecutiveNonNullAssertions from "./rules/consecutiveNonNullAssertions.js";
|
|
@@ -9,11 +11,15 @@ import constructorReturns from "./rules/constructorReturns.js";
|
|
|
9
11
|
import debuggerStatements from "./rules/debuggerStatements.js";
|
|
10
12
|
import defaultCaseLast from "./rules/defaultCaseLast.js";
|
|
11
13
|
import duplicateArguments from "./rules/duplicateArguments.js";
|
|
14
|
+
import elseIfDuplicates from "./rules/elseIfDuplicates.js";
|
|
15
|
+
import emptyBlocks from "./rules/emptyBlocks.js";
|
|
12
16
|
import emptyDestructures from "./rules/emptyDestructures.js";
|
|
13
17
|
import emptyStaticBlocks from "./rules/emptyStaticBlocks.js";
|
|
14
18
|
import exceptionAssignments from "./rules/exceptionAssignments.js";
|
|
19
|
+
import forDirections from "./rules/forDirections.js";
|
|
15
20
|
import forInArrays from "./rules/forInArrays.js";
|
|
16
21
|
import functionAssignments from "./rules/functionAssignments.js";
|
|
22
|
+
import functionNewCalls from "./rules/functionNewCalls.js";
|
|
17
23
|
import generatorFunctionYields from "./rules/generatorFunctionYields.js";
|
|
18
24
|
import globalAssignments from "./rules/globalAssignments.js";
|
|
19
25
|
import globalObjectCalls from "./rules/globalObjectCalls.js";
|
|
@@ -21,16 +27,25 @@ import namespaceDeclarations from "./rules/namespaceDeclarations.js";
|
|
|
21
27
|
import negativeZeroComparisons from "./rules/negativeZeroComparisons.js";
|
|
22
28
|
import newExpressions from "./rules/newExpressions.js";
|
|
23
29
|
import newNativeNonConstructors from "./rules/newNativeNonConstructors.js";
|
|
30
|
+
import nonOctalDecimalEscapes from "./rules/nonOctalDecimalEscapes.js";
|
|
31
|
+
import numericLiteralParsing from "./rules/numericLiteralParsing.js";
|
|
24
32
|
import objectProto from "./rules/objectProto.js";
|
|
25
33
|
import octalEscapes from "./rules/octalEscapes.js";
|
|
26
34
|
import octalNumbers from "./rules/octalNumbers.js";
|
|
27
35
|
import returnAssignments from "./rules/returnAssignments.js";
|
|
36
|
+
import selfAssignments from "./rules/selfAssignments.js";
|
|
37
|
+
import selfComparisons from "./rules/selfComparisons.js";
|
|
38
|
+
import sequences from "./rules/sequences.js";
|
|
39
|
+
import shadowedRestrictedNames from "./rules/shadowedRestrictedNames.js";
|
|
28
40
|
import sparseArrays from "./rules/sparseArrays.js";
|
|
29
41
|
import symbolDescriptions from "./rules/symbolDescriptions.js";
|
|
42
|
+
import typeofComparisons from "./rules/typeofComparisons.js";
|
|
30
43
|
import unassignedVariables from "./rules/unassignedVariables.js";
|
|
31
44
|
import undefinedVariables from "./rules/undefinedVariables.js";
|
|
32
45
|
import unicodeBOMs from "./rules/unicodeBOMs.js";
|
|
46
|
+
import unnecessaryBlocks from "./rules/unnecessaryBlocks.js";
|
|
33
47
|
import unnecessaryCatches from "./rules/unnecessaryCatches.js";
|
|
48
|
+
import unnecessaryConcatenation from "./rules/unnecessaryConcatenation.js";
|
|
34
49
|
import unsafeNegations from "./rules/unsafeNegations.js";
|
|
35
50
|
import variableDeletions from "./rules/variableDeletions.js";
|
|
36
51
|
import voidOperator from "./rules/voidOperator.js";
|
|
@@ -40,8 +55,10 @@ export const ts = createPlugin({
|
|
|
40
55
|
},
|
|
41
56
|
name: "ts",
|
|
42
57
|
rules: [
|
|
58
|
+
anyReturns,
|
|
43
59
|
asyncPromiseExecutors,
|
|
44
60
|
caseDeclarations,
|
|
61
|
+
caseDuplicates,
|
|
45
62
|
chainedAssignments,
|
|
46
63
|
classAssignments,
|
|
47
64
|
consecutiveNonNullAssertions,
|
|
@@ -50,11 +67,15 @@ export const ts = createPlugin({
|
|
|
50
67
|
debuggerStatements,
|
|
51
68
|
defaultCaseLast,
|
|
52
69
|
duplicateArguments,
|
|
70
|
+
elseIfDuplicates,
|
|
71
|
+
emptyBlocks,
|
|
53
72
|
emptyDestructures,
|
|
54
73
|
emptyStaticBlocks,
|
|
55
74
|
exceptionAssignments,
|
|
75
|
+
forDirections,
|
|
56
76
|
forInArrays,
|
|
57
77
|
functionAssignments,
|
|
78
|
+
functionNewCalls,
|
|
58
79
|
generatorFunctionYields,
|
|
59
80
|
globalAssignments,
|
|
60
81
|
globalObjectCalls,
|
|
@@ -62,16 +83,25 @@ export const ts = createPlugin({
|
|
|
62
83
|
negativeZeroComparisons,
|
|
63
84
|
newExpressions,
|
|
64
85
|
newNativeNonConstructors,
|
|
86
|
+
nonOctalDecimalEscapes,
|
|
87
|
+
numericLiteralParsing,
|
|
65
88
|
objectProto,
|
|
66
89
|
octalEscapes,
|
|
67
90
|
octalNumbers,
|
|
68
91
|
returnAssignments,
|
|
92
|
+
selfAssignments,
|
|
93
|
+
selfComparisons,
|
|
94
|
+
sequences,
|
|
95
|
+
shadowedRestrictedNames,
|
|
69
96
|
sparseArrays,
|
|
70
97
|
symbolDescriptions,
|
|
98
|
+
typeofComparisons,
|
|
71
99
|
unassignedVariables,
|
|
72
100
|
undefinedVariables,
|
|
73
101
|
unicodeBOMs,
|
|
102
|
+
unnecessaryBlocks,
|
|
74
103
|
unnecessaryCatches,
|
|
104
|
+
unnecessaryConcatenation,
|
|
75
105
|
unsafeNegations,
|
|
76
106
|
variableDeletions,
|
|
77
107
|
voidOperator,
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
declare const _default: import("@flint.fyi/core").Rule<{
|
|
2
|
+
readonly description: "Reports returning a value with type `any` from a function.";
|
|
3
|
+
readonly id: "anyReturns";
|
|
4
|
+
readonly preset: "logical";
|
|
5
|
+
}, import("../nodes.js").TSNodesByName, import("../language.js").TypeScriptServices, "unsafeReturn" | "unsafeReturnAssignment" | "unsafeReturnThis", undefined>;
|
|
6
|
+
export default _default;
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
import * as tsutils from "ts-api-utils";
|
|
2
|
+
import * as ts from "typescript";
|
|
3
|
+
import { typescriptLanguage } from "../language.js";
|
|
4
|
+
import { AnyType, discriminateAnyType } from "./utils/discriminateAnyType.js";
|
|
5
|
+
import { getConstrainedTypeAtLocation } from "./utils/getConstrainedType.js";
|
|
6
|
+
import { getThisExpression } from "./utils/getThisExpression.js";
|
|
7
|
+
import { isUnsafeAssignment } from "./utils/isUnsafeAssignment.js";
|
|
8
|
+
export default typescriptLanguage.createRule({
|
|
9
|
+
about: {
|
|
10
|
+
description: "Reports returning a value with type `any` from a function.",
|
|
11
|
+
id: "anyReturns",
|
|
12
|
+
preset: "logical",
|
|
13
|
+
},
|
|
14
|
+
messages: {
|
|
15
|
+
unsafeReturn: {
|
|
16
|
+
primary: "Unsafe return of a value of type {{ type }}.",
|
|
17
|
+
secondary: [
|
|
18
|
+
"Returning a value of type `any` or a similar unsafe type defeats TypeScript's type safety guarantees.",
|
|
19
|
+
"This can allow unexpected types to propagate through your codebase, potentially causing runtime errors.",
|
|
20
|
+
],
|
|
21
|
+
suggestions: [
|
|
22
|
+
"Ensure the returned value has a well-defined, specific type.",
|
|
23
|
+
],
|
|
24
|
+
},
|
|
25
|
+
unsafeReturnAssignment: {
|
|
26
|
+
primary: "Unsafe return of type `{{ sender }}` from function with return type `{{ receiver }}`.",
|
|
27
|
+
secondary: [
|
|
28
|
+
"The function's declared return type does not safely accept the value being returned.",
|
|
29
|
+
"This can allow unexpected types to propagate through your codebase, potentially causing runtime errors.",
|
|
30
|
+
],
|
|
31
|
+
suggestions: [
|
|
32
|
+
"Adjust the return type of the function to match the returned value, if appropriate.",
|
|
33
|
+
"Otherwise, refine the returned value to ensure it matches the expected return type.",
|
|
34
|
+
],
|
|
35
|
+
},
|
|
36
|
+
unsafeReturnThis: {
|
|
37
|
+
primary: "Unsafe return of a value of type `{{ type }}`. `this` is typed as `any`.",
|
|
38
|
+
secondary: [
|
|
39
|
+
"Returning `this` when it is implicitly typed as `any` introduces type-unsafe behavior.",
|
|
40
|
+
"This can allow unexpected types to propagate through your codebase, potentially causing runtime errors.",
|
|
41
|
+
],
|
|
42
|
+
suggestions: [
|
|
43
|
+
"Enable the `noImplicitThis` compiler option to enforce explicit `this` types.",
|
|
44
|
+
"Add an explicit `this` parameter to the function to clarify its type.",
|
|
45
|
+
],
|
|
46
|
+
},
|
|
47
|
+
},
|
|
48
|
+
setup(context) {
|
|
49
|
+
const isNoImplicitThis = tsutils.isStrictCompilerOptionEnabled(context.program.getCompilerOptions(), "noImplicitThis");
|
|
50
|
+
function checkReturn(returnNode, reportingNode) {
|
|
51
|
+
const type = context.typeChecker.getTypeAtLocation(returnNode);
|
|
52
|
+
const anyType = discriminateAnyType(type, context.typeChecker, context.program, returnNode);
|
|
53
|
+
const functionNode = ts.findAncestor(returnNode,
|
|
54
|
+
// TODO: I believe isFunctionLikeDeclaration was incorrectly marked
|
|
55
|
+
// as deprecated in https://github.com/JoshuaKGoldberg/ts-api-utils/pull/124
|
|
56
|
+
// It says "With TypeScript v5, in favor of typescript's `isFunctionLike`."
|
|
57
|
+
// However, isFunctionLike also checks for signature-like nodes,
|
|
58
|
+
// whereas isFunctionLikeDeclaration checks only for function-like nodes.
|
|
59
|
+
// eslint-disable-next-line @typescript-eslint/no-deprecated
|
|
60
|
+
tsutils.isFunctionLikeDeclaration);
|
|
61
|
+
if (!functionNode) {
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
// function has an explicit return type, so ensure it's a safe return
|
|
65
|
+
const returnNodeType = getConstrainedTypeAtLocation(returnNode, context.typeChecker);
|
|
66
|
+
// function expressions will not have their return type modified based on receiver typing
|
|
67
|
+
// so we have to use the contextual typing in these cases, i.e.
|
|
68
|
+
// const foo1: () => Set<string> = () => new Set<any>();
|
|
69
|
+
// the return type of the arrow function is Set<any> even though the variable is typed as Set<string>
|
|
70
|
+
let functionType = ts.isFunctionExpression(functionNode) ||
|
|
71
|
+
ts.isArrowFunction(functionNode)
|
|
72
|
+
? context.typeChecker.getContextualType(functionNode)
|
|
73
|
+
: context.typeChecker.getTypeAtLocation(functionNode);
|
|
74
|
+
functionType ??= context.typeChecker.getTypeAtLocation(functionNode);
|
|
75
|
+
const callSignatures = tsutils.getCallSignaturesOfType(functionType);
|
|
76
|
+
// If there is an explicit type annotation *and* that type matches the actual
|
|
77
|
+
// function return type, we shouldn't complain (it's intentional, even if unsafe)
|
|
78
|
+
if (functionNode.type) {
|
|
79
|
+
for (const signature of callSignatures) {
|
|
80
|
+
const signatureReturnType = signature.getReturnType();
|
|
81
|
+
if (returnNodeType === signatureReturnType ||
|
|
82
|
+
tsutils.isTypeFlagSet(signatureReturnType, ts.TypeFlags.Any | ts.TypeFlags.Unknown)) {
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
if (tsutils.includesModifier(functionNode.modifiers, ts.SyntaxKind.AsyncKeyword)) {
|
|
86
|
+
const awaitedSignatureReturnType = context.typeChecker.getAwaitedType(signatureReturnType);
|
|
87
|
+
const awaitedReturnNodeType = context.typeChecker.getAwaitedType(returnNodeType);
|
|
88
|
+
if (awaitedReturnNodeType === awaitedSignatureReturnType ||
|
|
89
|
+
(awaitedSignatureReturnType &&
|
|
90
|
+
tsutils.isTypeFlagSet(awaitedSignatureReturnType, ts.TypeFlags.Any | ts.TypeFlags.Unknown))) {
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
if (anyType !== AnyType.Safe) {
|
|
97
|
+
// Allow cases when the declared return type of the function is either unknown or unknown[]
|
|
98
|
+
// and the function is returning any or any[].
|
|
99
|
+
for (const signature of callSignatures) {
|
|
100
|
+
const functionReturnType = signature.getReturnType();
|
|
101
|
+
if (anyType === AnyType.Any &&
|
|
102
|
+
tsutils.isTypeFlagSet(functionReturnType, ts.TypeFlags.Unknown)) {
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
if (anyType === AnyType.AnyArray &&
|
|
106
|
+
context.typeChecker.isArrayType(functionReturnType) &&
|
|
107
|
+
tsutils.isTypeFlagSet(context.typeChecker.getTypeArguments(functionReturnType)[0], ts.TypeFlags.Unknown)) {
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
const awaitedType = context.typeChecker.getAwaitedType(functionReturnType);
|
|
111
|
+
if (awaitedType &&
|
|
112
|
+
anyType === AnyType.PromiseAny &&
|
|
113
|
+
tsutils.isTypeFlagSet(awaitedType, ts.TypeFlags.Unknown)) {
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
if (anyType === AnyType.PromiseAny &&
|
|
118
|
+
!tsutils.includesModifier(functionNode.modifiers, ts.SyntaxKind.AsyncKeyword)) {
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
let message = "unsafeReturn";
|
|
122
|
+
const isErrorType = tsutils.isIntrinsicErrorType(returnNodeType);
|
|
123
|
+
if (!isNoImplicitThis) {
|
|
124
|
+
// `return this`
|
|
125
|
+
const thisExpression = getThisExpression(returnNode);
|
|
126
|
+
if (thisExpression &&
|
|
127
|
+
tsutils.isTypeFlagSet(getConstrainedTypeAtLocation(thisExpression, context.typeChecker), ts.TypeFlags.Any)) {
|
|
128
|
+
message = "unsafeReturnThis";
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
// If the function return type was not unknown/unknown[], mark usage as unsafeReturn.
|
|
132
|
+
context.report({
|
|
133
|
+
data: {
|
|
134
|
+
type: isErrorType
|
|
135
|
+
? "error"
|
|
136
|
+
: anyType === AnyType.Any
|
|
137
|
+
? "`any`"
|
|
138
|
+
: anyType === AnyType.PromiseAny
|
|
139
|
+
? "`Promise<any>`"
|
|
140
|
+
: "`any[]`",
|
|
141
|
+
},
|
|
142
|
+
message,
|
|
143
|
+
range: {
|
|
144
|
+
begin: reportingNode.getStart(),
|
|
145
|
+
end: reportingNode.getEnd(),
|
|
146
|
+
},
|
|
147
|
+
});
|
|
148
|
+
return;
|
|
149
|
+
}
|
|
150
|
+
const signature = functionType.getCallSignatures().at(0);
|
|
151
|
+
if (signature) {
|
|
152
|
+
const functionReturnType = signature.getReturnType();
|
|
153
|
+
const result = isUnsafeAssignment(returnNodeType, functionReturnType, context.typeChecker, returnNode);
|
|
154
|
+
if (!result) {
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
157
|
+
const { receiver, sender } = result;
|
|
158
|
+
context.report({
|
|
159
|
+
data: {
|
|
160
|
+
receiver: context.typeChecker.typeToString(receiver),
|
|
161
|
+
sender: context.typeChecker.typeToString(sender),
|
|
162
|
+
},
|
|
163
|
+
message: "unsafeReturnAssignment",
|
|
164
|
+
range: {
|
|
165
|
+
begin: reportingNode.getStart(),
|
|
166
|
+
end: reportingNode.getEnd(),
|
|
167
|
+
},
|
|
168
|
+
});
|
|
169
|
+
return;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
return {
|
|
173
|
+
visitors: {
|
|
174
|
+
ArrowFunction: (node) => {
|
|
175
|
+
if (!ts.isBlock(node.body)) {
|
|
176
|
+
checkReturn(node.body, node.body);
|
|
177
|
+
}
|
|
178
|
+
},
|
|
179
|
+
ReturnStatement: (node) => {
|
|
180
|
+
if (node.expression != null) {
|
|
181
|
+
checkReturn(node.expression, node);
|
|
182
|
+
}
|
|
183
|
+
},
|
|
184
|
+
},
|
|
185
|
+
};
|
|
186
|
+
},
|
|
187
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|