@flint.fyi/ts 0.14.2 → 0.14.3

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.
Files changed (209) hide show
  1. package/CHANGELOG.md +7 -0
  2. package/lib/index.d.ts +1 -0
  3. package/lib/index.js +1 -0
  4. package/lib/nodes.d.ts +1 -0
  5. package/lib/plugin.d.ts +121 -1
  6. package/lib/plugin.js +60 -0
  7. package/lib/rules/asyncPromiseExecutors.d.ts +6 -0
  8. package/lib/rules/asyncPromiseExecutors.js +51 -0
  9. package/lib/rules/asyncPromiseExecutors.test.d.ts +1 -0
  10. package/lib/rules/asyncPromiseExecutors.test.js +71 -0
  11. package/lib/rules/caseDeclarations.d.ts +6 -0
  12. package/lib/rules/caseDeclarations.js +53 -0
  13. package/lib/rules/caseDeclarations.test.d.ts +1 -0
  14. package/lib/rules/caseDeclarations.test.js +227 -0
  15. package/lib/rules/chainedAssignments.d.ts +6 -0
  16. package/lib/rules/chainedAssignments.js +48 -0
  17. package/lib/rules/chainedAssignments.test.d.ts +1 -0
  18. package/lib/rules/chainedAssignments.test.js +238 -0
  19. package/lib/rules/classAssignments.d.ts +6 -0
  20. package/lib/rules/classAssignments.js +42 -0
  21. package/lib/rules/classAssignments.test.d.ts +1 -0
  22. package/lib/rules/classAssignments.test.js +156 -0
  23. package/lib/rules/constantAssignments.d.ts +6 -0
  24. package/lib/rules/constantAssignments.js +61 -0
  25. package/lib/rules/constantAssignments.test.d.ts +1 -0
  26. package/lib/rules/constantAssignments.test.js +221 -0
  27. package/lib/rules/constructorReturns.d.ts +6 -0
  28. package/lib/rules/constructorReturns.js +52 -0
  29. package/lib/rules/constructorReturns.test.d.ts +1 -0
  30. package/lib/rules/constructorReturns.test.js +96 -0
  31. package/lib/rules/defaultCaseLast.d.ts +6 -0
  32. package/lib/rules/defaultCaseLast.js +43 -0
  33. package/lib/rules/defaultCaseLast.test.d.ts +1 -0
  34. package/lib/rules/defaultCaseLast.test.js +195 -0
  35. package/lib/rules/duplicateArguments.d.ts +6 -0
  36. package/lib/rules/duplicateArguments.js +51 -0
  37. package/lib/rules/duplicateArguments.test.d.ts +1 -0
  38. package/lib/rules/duplicateArguments.test.js +139 -0
  39. package/lib/rules/emptyDestructures.d.ts +6 -0
  40. package/lib/rules/emptyDestructures.js +40 -0
  41. package/lib/rules/emptyDestructures.test.d.ts +1 -0
  42. package/lib/rules/emptyDestructures.test.js +186 -0
  43. package/lib/rules/emptyStaticBlocks.d.ts +6 -0
  44. package/lib/rules/emptyStaticBlocks.js +54 -0
  45. package/lib/rules/emptyStaticBlocks.test.d.ts +1 -0
  46. package/lib/rules/emptyStaticBlocks.test.js +167 -0
  47. package/lib/rules/exceptionAssignments.d.ts +6 -0
  48. package/lib/rules/exceptionAssignments.js +61 -0
  49. package/lib/rules/exceptionAssignments.test.d.ts +1 -0
  50. package/lib/rules/exceptionAssignments.test.js +264 -0
  51. package/lib/rules/functionAssignments.d.ts +6 -0
  52. package/lib/rules/functionAssignments.js +42 -0
  53. package/lib/rules/functionAssignments.test.d.ts +1 -0
  54. package/lib/rules/functionAssignments.test.js +209 -0
  55. package/lib/rules/generatorFunctionYields.d.ts +6 -0
  56. package/lib/rules/generatorFunctionYields.js +57 -0
  57. package/lib/rules/generatorFunctionYields.test.d.ts +1 -0
  58. package/lib/rules/generatorFunctionYields.test.js +151 -0
  59. package/lib/rules/globalAssignments.d.ts +6 -0
  60. package/lib/rules/globalAssignments.js +59 -0
  61. package/lib/rules/globalAssignments.test.d.ts +1 -0
  62. package/lib/rules/globalAssignments.test.js +215 -0
  63. package/lib/rules/globalObjectCalls.d.ts +6 -0
  64. package/lib/rules/globalObjectCalls.js +43 -0
  65. package/lib/rules/globalObjectCalls.test.d.ts +1 -0
  66. package/lib/rules/globalObjectCalls.test.js +93 -0
  67. package/lib/rules/negativeZeroComparisons.d.ts +6 -0
  68. package/lib/rules/negativeZeroComparisons.js +83 -0
  69. package/lib/rules/negativeZeroComparisons.test.d.ts +1 -0
  70. package/lib/rules/negativeZeroComparisons.test.js +209 -0
  71. package/lib/rules/newExpressions.d.ts +6 -0
  72. package/lib/rules/newExpressions.js +54 -0
  73. package/lib/rules/newExpressions.test.d.ts +1 -0
  74. package/lib/rules/newExpressions.test.js +79 -0
  75. package/lib/rules/newNativeNonConstructors.d.ts +6 -0
  76. package/lib/rules/newNativeNonConstructors.js +49 -0
  77. package/lib/rules/newNativeNonConstructors.test.d.ts +1 -0
  78. package/lib/rules/newNativeNonConstructors.test.js +94 -0
  79. package/lib/rules/objectProto.d.ts +6 -0
  80. package/lib/rules/objectProto.js +54 -0
  81. package/lib/rules/objectProto.test.d.ts +1 -0
  82. package/lib/rules/objectProto.test.js +96 -0
  83. package/lib/rules/octalNumbers.d.ts +6 -0
  84. package/lib/rules/octalNumbers.js +68 -0
  85. package/lib/rules/octalNumbers.test.d.ts +1 -0
  86. package/lib/rules/octalNumbers.test.js +245 -0
  87. package/lib/rules/returnAssignments.d.ts +6 -0
  88. package/lib/rules/returnAssignments.js +50 -0
  89. package/lib/rules/returnAssignments.test.d.ts +1 -0
  90. package/lib/rules/returnAssignments.test.js +185 -0
  91. package/lib/rules/sparseArrays.d.ts +6 -0
  92. package/lib/rules/sparseArrays.js +54 -0
  93. package/lib/rules/sparseArrays.test.d.ts +1 -0
  94. package/lib/rules/sparseArrays.test.js +110 -0
  95. package/lib/rules/symbolDescriptions.d.ts +6 -0
  96. package/lib/rules/symbolDescriptions.js +41 -0
  97. package/lib/rules/symbolDescriptions.test.d.ts +1 -0
  98. package/lib/rules/symbolDescriptions.test.js +89 -0
  99. package/lib/rules/unassignedVariables.d.ts +6 -0
  100. package/lib/rules/unassignedVariables.js +55 -0
  101. package/lib/rules/unassignedVariables.test.d.ts +1 -0
  102. package/lib/rules/unassignedVariables.test.js +72 -0
  103. package/lib/rules/undefinedVariables.d.ts +6 -0
  104. package/lib/rules/undefinedVariables.js +76 -0
  105. package/lib/rules/undefinedVariables.test.d.ts +1 -0
  106. package/lib/rules/undefinedVariables.test.js +80 -0
  107. package/lib/rules/unicodeBOMs.d.ts +6 -0
  108. package/lib/rules/unicodeBOMs.js +41 -0
  109. package/lib/rules/unicodeBOMs.test.d.ts +1 -0
  110. package/lib/rules/unicodeBOMs.test.js +65 -0
  111. package/lib/rules/unnecessaryCatches.d.ts +6 -0
  112. package/lib/rules/unnecessaryCatches.js +68 -0
  113. package/lib/rules/unnecessaryCatches.test.d.ts +1 -0
  114. package/lib/rules/unnecessaryCatches.test.js +210 -0
  115. package/lib/rules/unsafeNegations.d.ts +6 -0
  116. package/lib/rules/unsafeNegations.js +50 -0
  117. package/lib/rules/unsafeNegations.test.d.ts +1 -0
  118. package/lib/rules/unsafeNegations.test.js +63 -0
  119. package/lib/rules/variableDeletions.d.ts +6 -0
  120. package/lib/rules/variableDeletions.js +39 -0
  121. package/lib/rules/variableDeletions.test.d.ts +1 -0
  122. package/lib/rules/variableDeletions.test.js +75 -0
  123. package/lib/rules/voidOperator.d.ts +6 -0
  124. package/lib/rules/voidOperator.js +36 -0
  125. package/lib/rules/voidOperator.test.d.ts +1 -0
  126. package/lib/rules/voidOperator.test.js +98 -0
  127. package/lib/utils/declarationsIncludeGlobal.d.ts +2 -0
  128. package/lib/utils/declarationsIncludeGlobal.js +7 -0
  129. package/lib/utils/getModifyingReferences.d.ts +8 -0
  130. package/lib/utils/getModifyingReferences.js +42 -0
  131. package/lib/utils/isGlobalDeclaration.d.ts +2 -0
  132. package/lib/utils/isGlobalDeclaration.js +5 -0
  133. package/lib/utils/isGlobalVariable.d.ts +8 -0
  134. package/lib/utils/isGlobalVariable.js +21 -0
  135. package/lib/utils/unwrapParentParenthesizedExpressions.d.ts +2 -0
  136. package/lib/utils/unwrapParentParenthesizedExpressions.js +6 -0
  137. package/lib/utils/unwrapParenthesizedExpression.d.ts +2 -0
  138. package/lib/utils/unwrapParenthesizedExpression.js +6 -0
  139. package/package.json +1 -1
  140. package/src/index.ts +1 -0
  141. package/src/nodes.ts +1 -0
  142. package/src/plugin.ts +60 -0
  143. package/src/rules/asyncPromiseExecutors.test.ts +72 -0
  144. package/src/rules/asyncPromiseExecutors.ts +61 -0
  145. package/src/rules/caseDeclarations.test.ts +228 -0
  146. package/src/rules/caseDeclarations.ts +70 -0
  147. package/src/rules/chainedAssignments.test.ts +239 -0
  148. package/src/rules/chainedAssignments.ts +55 -0
  149. package/src/rules/classAssignments.test.ts +157 -0
  150. package/src/rules/classAssignments.ts +50 -0
  151. package/src/rules/constantAssignments.test.ts +222 -0
  152. package/src/rules/constantAssignments.ts +78 -0
  153. package/src/rules/constructorReturns.test.ts +97 -0
  154. package/src/rules/constructorReturns.ts +59 -0
  155. package/src/rules/defaultCaseLast.test.ts +196 -0
  156. package/src/rules/defaultCaseLast.ts +54 -0
  157. package/src/rules/duplicateArguments.test.ts +140 -0
  158. package/src/rules/duplicateArguments.ts +58 -0
  159. package/src/rules/emptyDestructures.test.ts +187 -0
  160. package/src/rules/emptyDestructures.ts +48 -0
  161. package/src/rules/emptyStaticBlocks.test.ts +168 -0
  162. package/src/rules/emptyStaticBlocks.ts +60 -0
  163. package/src/rules/exceptionAssignments.test.ts +265 -0
  164. package/src/rules/exceptionAssignments.ts +77 -0
  165. package/src/rules/functionAssignments.test.ts +210 -0
  166. package/src/rules/functionAssignments.ts +51 -0
  167. package/src/rules/generatorFunctionYields.test.ts +152 -0
  168. package/src/rules/generatorFunctionYields.ts +71 -0
  169. package/src/rules/globalAssignments.test.ts +216 -0
  170. package/src/rules/globalAssignments.ts +67 -0
  171. package/src/rules/globalObjectCalls.test.ts +94 -0
  172. package/src/rules/globalObjectCalls.ts +52 -0
  173. package/src/rules/negativeZeroComparisons.test.ts +210 -0
  174. package/src/rules/negativeZeroComparisons.ts +106 -0
  175. package/src/rules/newExpressions.test.ts +80 -0
  176. package/src/rules/newExpressions.ts +67 -0
  177. package/src/rules/newNativeNonConstructors.test.ts +95 -0
  178. package/src/rules/newNativeNonConstructors.ts +59 -0
  179. package/src/rules/objectProto.test.ts +97 -0
  180. package/src/rules/objectProto.ts +62 -0
  181. package/src/rules/octalNumbers.test.ts +246 -0
  182. package/src/rules/octalNumbers.ts +75 -0
  183. package/src/rules/returnAssignments.test.ts +186 -0
  184. package/src/rules/returnAssignments.ts +57 -0
  185. package/src/rules/sparseArrays.test.ts +111 -0
  186. package/src/rules/sparseArrays.ts +61 -0
  187. package/src/rules/symbolDescriptions.test.ts +90 -0
  188. package/src/rules/symbolDescriptions.ts +47 -0
  189. package/src/rules/unassignedVariables.test.ts +73 -0
  190. package/src/rules/unassignedVariables.ts +69 -0
  191. package/src/rules/undefinedVariables.test.ts +81 -0
  192. package/src/rules/undefinedVariables.ts +97 -0
  193. package/src/rules/unicodeBOMs.test.ts +66 -0
  194. package/src/rules/unicodeBOMs.ts +44 -0
  195. package/src/rules/unnecessaryCatches.test.ts +211 -0
  196. package/src/rules/unnecessaryCatches.ts +84 -0
  197. package/src/rules/unsafeNegations.test.ts +64 -0
  198. package/src/rules/unsafeNegations.ts +59 -0
  199. package/src/rules/variableDeletions.test.ts +76 -0
  200. package/src/rules/variableDeletions.ts +43 -0
  201. package/src/rules/voidOperator.test.ts +99 -0
  202. package/src/rules/voidOperator.ts +38 -0
  203. package/src/utils/declarationsIncludeGlobal.ts +11 -0
  204. package/src/utils/getModifyingReferences.ts +58 -0
  205. package/src/utils/isGlobalDeclaration.ts +12 -0
  206. package/src/utils/isGlobalVariable.ts +30 -0
  207. package/src/utils/unwrapParentParenthesizedExpressions.ts +7 -0
  208. package/src/utils/unwrapParenthesizedExpression.ts +9 -0
  209. package/tsconfig.tsbuildinfo +1 -1
@@ -0,0 +1,39 @@
1
+ import * as ts from "typescript";
2
+ import { typescriptLanguage } from "../language.js";
3
+ export default typescriptLanguage.createRule({
4
+ about: {
5
+ description: "Reports attempting to delete variables with the delete operator.",
6
+ id: "variableDeletions",
7
+ preset: "untyped",
8
+ },
9
+ messages: {
10
+ noDeleteVar: {
11
+ primary: "Deleting a variable with the delete operator outside of strict mode will silently fail and return false.",
12
+ secondary: [
13
+ "The delete operator is only meant to remove properties from objects, not variables.",
14
+ "Attempting to delete a variable in strict mode will cause a syntax error.",
15
+ "In non-strict mode, it will silently fail and return false without actually deleting the variable.",
16
+ ],
17
+ suggestions: [
18
+ "Remove the delete statement, or if you need to unset the value, assign undefined instead.",
19
+ ],
20
+ },
21
+ },
22
+ setup(context) {
23
+ return {
24
+ visitors: {
25
+ DeleteExpression: (node) => {
26
+ if (node.expression.kind === ts.SyntaxKind.Identifier) {
27
+ context.report({
28
+ message: "noDeleteVar",
29
+ range: {
30
+ begin: node.getStart(context.sourceFile),
31
+ end: node.getEnd(),
32
+ },
33
+ });
34
+ }
35
+ },
36
+ },
37
+ };
38
+ },
39
+ });
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,75 @@
1
+ import { ruleTester } from "./ruleTester.js";
2
+ import rule from "./variableDeletions.js";
3
+ ruleTester.describe(rule, {
4
+ invalid: [
5
+ {
6
+ code: `
7
+ delete value;
8
+ `,
9
+ snapshot: `
10
+ delete value;
11
+ ~~~~~~~~~~~~
12
+ Deleting a variable with the delete operator outside of strict mode will silently fail and return false.
13
+ `,
14
+ },
15
+ {
16
+ code: `
17
+ let x = 5;
18
+ delete x;
19
+ `,
20
+ snapshot: `
21
+ let x = 5;
22
+ delete x;
23
+ ~~~~~~~~
24
+ Deleting a variable with the delete operator outside of strict mode will silently fail and return false.
25
+ `,
26
+ },
27
+ {
28
+ code: `
29
+ const variable = 10;
30
+ delete variable;
31
+ `,
32
+ snapshot: `
33
+ const variable = 10;
34
+ delete variable;
35
+ ~~~~~~~~~~~~~~~
36
+ Deleting a variable with the delete operator outside of strict mode will silently fail and return false.
37
+ `,
38
+ },
39
+ {
40
+ code: `
41
+ function test(parameter: number) {
42
+ delete parameter;
43
+ }
44
+ `,
45
+ snapshot: `
46
+ function test(parameter: number) {
47
+ delete parameter;
48
+ ~~~~~~~~~~~~~~~~
49
+ Deleting a variable with the delete operator outside of strict mode will silently fail and return false.
50
+ }
51
+ `,
52
+ },
53
+ {
54
+ code: `
55
+ for (let index = 0; index < 10; index++) {
56
+ delete index;
57
+ }
58
+ `,
59
+ snapshot: `
60
+ for (let index = 0; index < 10; index++) {
61
+ delete index;
62
+ ~~~~~~~~~~~~
63
+ Deleting a variable with the delete operator outside of strict mode will silently fail and return false.
64
+ }
65
+ `,
66
+ },
67
+ ],
68
+ valid: [
69
+ `const obj = { prop: 1 }; delete obj.prop;`,
70
+ `delete obj.property;`,
71
+ `delete obj["property"];`,
72
+ `const obj = { nested: { prop: 1 } }; delete obj.nested.prop;`,
73
+ `const array = [1, 2, 3]; delete array[0];`,
74
+ ],
75
+ });
@@ -0,0 +1,6 @@
1
+ declare const _default: import("@flint.fyi/core").Rule<{
2
+ readonly description: "Reports using the void operator.";
3
+ readonly id: "voidOperator";
4
+ readonly preset: "stylistic";
5
+ }, import("../nodes.js").TSNodesByName, import("../language.js").TypeScriptServices, "noVoid", undefined>;
6
+ export default _default;
@@ -0,0 +1,36 @@
1
+ import { typescriptLanguage } from "../language.js";
2
+ export default typescriptLanguage.createRule({
3
+ about: {
4
+ description: "Reports using the void operator.",
5
+ id: "voidOperator",
6
+ preset: "stylistic",
7
+ },
8
+ messages: {
9
+ noVoid: {
10
+ primary: "Prefer an explicit value over using the void operator to produce undefined.",
11
+ secondary: [
12
+ "The void operator evaluates an expression and returns undefined, regardless of the expression's value.",
13
+ "This is often confusing and can be replaced with more explicit code.",
14
+ "Instead of using void, return or assign undefined directly.",
15
+ ],
16
+ suggestions: [
17
+ "Replace void expressions with undefined or restructure the code to be more explicit.",
18
+ ],
19
+ },
20
+ },
21
+ setup(context) {
22
+ return {
23
+ visitors: {
24
+ VoidExpression: (node) => {
25
+ context.report({
26
+ message: "noVoid",
27
+ range: {
28
+ begin: node.getStart(context.sourceFile),
29
+ end: node.getEnd(),
30
+ },
31
+ });
32
+ },
33
+ },
34
+ };
35
+ },
36
+ });
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,98 @@
1
+ import { ruleTester } from "./ruleTester.js";
2
+ import rule from "./voidOperator.js";
3
+ ruleTester.describe(rule, {
4
+ invalid: [
5
+ {
6
+ code: `
7
+ void 0;
8
+ `,
9
+ snapshot: `
10
+ void 0;
11
+ ~~~~~~
12
+ Prefer an explicit value over using the void operator to produce undefined.
13
+ `,
14
+ },
15
+ {
16
+ code: `
17
+ const value = void 0;
18
+ `,
19
+ snapshot: `
20
+ const value = void 0;
21
+ ~~~~~~
22
+ Prefer an explicit value over using the void operator to produce undefined.
23
+ `,
24
+ },
25
+ {
26
+ code: `
27
+ void someFunction();
28
+ `,
29
+ snapshot: `
30
+ void someFunction();
31
+ ~~~~~~~~~~~~~~~~~~~
32
+ Prefer an explicit value over using the void operator to produce undefined.
33
+ `,
34
+ },
35
+ {
36
+ code: `
37
+ return void calculate();
38
+ `,
39
+ snapshot: `
40
+ return void calculate();
41
+ ~~~~~~~~~~~~~~~~
42
+ Prefer an explicit value over using the void operator to produce undefined.
43
+ `,
44
+ },
45
+ {
46
+ code: `
47
+ const result = void (a + b);
48
+ `,
49
+ snapshot: `
50
+ const result = void (a + b);
51
+ ~~~~~~~~~~~~
52
+ Prefer an explicit value over using the void operator to produce undefined.
53
+ `,
54
+ },
55
+ {
56
+ code: `
57
+ void expression, anotherExpression;
58
+ `,
59
+ snapshot: `
60
+ void expression, anotherExpression;
61
+ ~~~~~~~~~~~~~~~
62
+ Prefer an explicit value over using the void operator to produce undefined.
63
+ `,
64
+ },
65
+ {
66
+ code: `
67
+ function process() {
68
+ void doSomething();
69
+ }
70
+ `,
71
+ snapshot: `
72
+ function process() {
73
+ void doSomething();
74
+ ~~~~~~~~~~~~~~~~~~
75
+ Prefer an explicit value over using the void operator to produce undefined.
76
+ }
77
+ `,
78
+ },
79
+ {
80
+ code: `
81
+ const callback = () => void action();
82
+ `,
83
+ snapshot: `
84
+ const callback = () => void action();
85
+ ~~~~~~~~~~~~~
86
+ Prefer an explicit value over using the void operator to produce undefined.
87
+ `,
88
+ },
89
+ ],
90
+ valid: [
91
+ `const value = undefined;`,
92
+ `return undefined;`,
93
+ `someFunction();`,
94
+ `const result = doSomething();`,
95
+ `if (value === undefined) {}`,
96
+ `function returns() { return; }`,
97
+ ],
98
+ });
@@ -0,0 +1,2 @@
1
+ import * as ts from "typescript";
2
+ export declare function declarationsIncludeGlobal(declarations: ts.Declaration[]): boolean;
@@ -0,0 +1,7 @@
1
+ export function declarationsIncludeGlobal(declarations) {
2
+ return declarations.some((declaration) => {
3
+ const sourceFile = declaration.getSourceFile();
4
+ return (sourceFile.hasNoDefaultLib ||
5
+ /\/lib\.[^/]*\.d\.ts$/.test(sourceFile.fileName));
6
+ });
7
+ }
@@ -0,0 +1,8 @@
1
+ import * as ts from "typescript";
2
+ /**
3
+ * Gets all references to a variable that modify it (assignments, increments, decrements).
4
+ *
5
+ * TODO: Replace with a proper scope manager when available (see #400).
6
+ * @returns An array of identifier nodes that modify the variable.
7
+ */
8
+ export declare function getModifyingReferences(identifier: ts.Identifier, sourceFile: ts.SourceFile, typeChecker: ts.TypeChecker): ts.Identifier[];
@@ -0,0 +1,42 @@
1
+ import * as tsutils from "ts-api-utils";
2
+ import * as ts from "typescript";
3
+ /**
4
+ * Gets all references to a variable that modify it (assignments, increments, decrements).
5
+ *
6
+ * TODO: Replace with a proper scope manager when available (see #400).
7
+ * @returns An array of identifier nodes that modify the variable.
8
+ */
9
+ export function getModifyingReferences(identifier, sourceFile, typeChecker) {
10
+ const symbol = typeChecker.getSymbolAtLocation(identifier);
11
+ if (!symbol?.valueDeclaration) {
12
+ return [];
13
+ }
14
+ const valueDeclaration = symbol.valueDeclaration;
15
+ const modifyingReferences = [];
16
+ function visit(node) {
17
+ // Check if this is an identifier that refers to the same symbol
18
+ if (ts.isIdentifier(node)) {
19
+ const nodeSymbol = typeChecker.getSymbolAtLocation(node);
20
+ if (nodeSymbol?.valueDeclaration &&
21
+ nodeSymbol.valueDeclaration === valueDeclaration) {
22
+ // Check if this identifier is being modified
23
+ const parent = node.parent;
24
+ // Assignment expressions (=, +=, -=, etc.)
25
+ if (ts.isBinaryExpression(parent) &&
26
+ tsutils.isAssignmentKind(parent.operatorToken.kind) &&
27
+ parent.left === node) {
28
+ modifyingReferences.push(node);
29
+ }
30
+ // Unary expressions (++, --)
31
+ else if ((ts.isPostfixUnaryExpression(parent) ||
32
+ ts.isPrefixUnaryExpression(parent)) &&
33
+ parent.operand === node) {
34
+ modifyingReferences.push(node);
35
+ }
36
+ }
37
+ }
38
+ ts.forEachChild(node, visit);
39
+ }
40
+ visit(sourceFile);
41
+ return modifyingReferences;
42
+ }
@@ -0,0 +1,2 @@
1
+ import * as ts from "typescript";
2
+ export declare function isGlobalDeclaration(node: ts.Expression, typeChecker: ts.TypeChecker): boolean;
@@ -0,0 +1,5 @@
1
+ import { declarationsIncludeGlobal } from "./declarationsIncludeGlobal.js";
2
+ export function isGlobalDeclaration(node, typeChecker) {
3
+ const declarations = typeChecker.getSymbolAtLocation(node)?.getDeclarations();
4
+ return !!declarations && declarationsIncludeGlobal(declarations);
5
+ }
@@ -0,0 +1,8 @@
1
+ import * as ts from "typescript";
2
+ /**
3
+ * Checks if a node is a reference to a global variable (e.g., Object, undefined, NaN).
4
+ * Global variables are those declared in TypeScript's lib files or are global identifiers
5
+ * like undefined which have symbols but no user-defined declarations.
6
+ * TODO: Use a scope manager (#400).
7
+ */
8
+ export declare function isGlobalVariable(node: ts.Expression, typeChecker: ts.TypeChecker): boolean;
@@ -0,0 +1,21 @@
1
+ import { declarationsIncludeGlobal } from "./declarationsIncludeGlobal.js";
2
+ /**
3
+ * Checks if a node is a reference to a global variable (e.g., Object, undefined, NaN).
4
+ * Global variables are those declared in TypeScript's lib files or are global identifiers
5
+ * like undefined which have symbols but no user-defined declarations.
6
+ * TODO: Use a scope manager (#400).
7
+ */
8
+ export function isGlobalVariable(node, typeChecker) {
9
+ const symbol = typeChecker.getSymbolAtLocation(node);
10
+ if (!symbol) {
11
+ return false;
12
+ }
13
+ const declarations = symbol.getDeclarations();
14
+ // undefined, NaN, Infinity etc. may have no declarations in some contexts
15
+ // but are still global identifiers. Check if they're global scope symbols.
16
+ if (!declarations?.length) {
17
+ // If there are no declarations, it's likely a built-in global like undefined
18
+ return true;
19
+ }
20
+ return declarationsIncludeGlobal(declarations);
21
+ }
@@ -0,0 +1,2 @@
1
+ import * as ts from "typescript";
2
+ export declare function unwrapParenthesizedExpressionsParent(node: ts.Node): ts.Node;
@@ -0,0 +1,6 @@
1
+ import * as ts from "typescript";
2
+ export function unwrapParenthesizedExpressionsParent(node) {
3
+ return ts.isParenthesizedExpression(node.parent)
4
+ ? unwrapParenthesizedExpressionsParent(node.parent)
5
+ : node.parent;
6
+ }
@@ -0,0 +1,2 @@
1
+ import * as ts from "typescript";
2
+ export declare function unwrapParenthesizedExpression(expression: ts.Expression): ts.Expression;
@@ -0,0 +1,6 @@
1
+ import * as ts from "typescript";
2
+ export function unwrapParenthesizedExpression(expression) {
3
+ return ts.isParenthesizedExpression(expression)
4
+ ? unwrapParenthesizedExpression(expression.expression)
5
+ : expression;
6
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@flint.fyi/ts",
3
- "version": "0.14.2",
3
+ "version": "0.14.3",
4
4
  "description": "[Experimental] TypeScript language plugin for Flint.",
5
5
  "repository": {
6
6
  "type": "git",
package/src/index.ts CHANGED
@@ -1,3 +1,4 @@
1
1
  export { getTSNodeRange } from "./getTSNodeRange.js";
2
2
  export * from "./language.js";
3
3
  export { ts } from "./plugin.js";
4
+ export { isGlobalDeclaration } from "./utils/isGlobalDeclaration.js";
package/src/nodes.ts CHANGED
@@ -26,6 +26,7 @@ export interface TSNodesByName {
26
26
  ComputedPropertyName: ts.ComputedPropertyName;
27
27
  ConditionalExpression: ts.ConditionalExpression;
28
28
  ConditionalType: ts.ConditionalType;
29
+ Constructor: ts.ConstructorDeclaration;
29
30
  ContinueStatement: ts.ContinueStatement;
30
31
  DebuggerStatement: ts.DebuggerStatement;
31
32
  Decorator: ts.Decorator;
package/src/plugin.ts CHANGED
@@ -1,10 +1,40 @@
1
1
  import { createPlugin } from "@flint.fyi/core";
2
2
 
3
+ import asyncPromiseExecutors from "./rules/asyncPromiseExecutors.js";
4
+ import caseDeclarations from "./rules/caseDeclarations.js";
5
+ import chainedAssignments from "./rules/chainedAssignments.js";
6
+ import classAssignments from "./rules/classAssignments.js";
3
7
  import consecutiveNonNullAssertions from "./rules/consecutiveNonNullAssertions.js";
8
+ import constantAssignments from "./rules/constantAssignments.js";
9
+ import constructorReturns from "./rules/constructorReturns.js";
4
10
  import debuggerStatements from "./rules/debuggerStatements.js";
11
+ import defaultCaseLast from "./rules/defaultCaseLast.js";
12
+ import duplicateArguments from "./rules/duplicateArguments.js";
13
+ import emptyDestructures from "./rules/emptyDestructures.js";
14
+ import emptyStaticBlocks from "./rules/emptyStaticBlocks.js";
15
+ import exceptionAssignments from "./rules/exceptionAssignments.js";
5
16
  import forInArrays from "./rules/forInArrays.js";
17
+ import functionAssignments from "./rules/functionAssignments.js";
18
+ import generatorFunctionYields from "./rules/generatorFunctionYields.js";
19
+ import globalAssignments from "./rules/globalAssignments.js";
20
+ import globalObjectCalls from "./rules/globalObjectCalls.js";
6
21
  import namespaceDeclarations from "./rules/namespaceDeclarations.js";
22
+ import negativeZeroComparisons from "./rules/negativeZeroComparisons.js";
23
+ import newExpressions from "./rules/newExpressions.js";
24
+ import newNativeNonConstructors from "./rules/newNativeNonConstructors.js";
25
+ import objectProto from "./rules/objectProto.js";
7
26
  import octalEscapes from "./rules/octalEscapes.js";
27
+ import octalNumbers from "./rules/octalNumbers.js";
28
+ import returnAssignments from "./rules/returnAssignments.js";
29
+ import sparseArrays from "./rules/sparseArrays.js";
30
+ import symbolDescriptions from "./rules/symbolDescriptions.js";
31
+ import unassignedVariables from "./rules/unassignedVariables.js";
32
+ import undefinedVariables from "./rules/undefinedVariables.js";
33
+ import unicodeBOMs from "./rules/unicodeBOMs.js";
34
+ import unnecessaryCatches from "./rules/unnecessaryCatches.js";
35
+ import unsafeNegations from "./rules/unsafeNegations.js";
36
+ import variableDeletions from "./rules/variableDeletions.js";
37
+ import voidOperator from "./rules/voidOperator.js";
8
38
 
9
39
  export const ts = createPlugin({
10
40
  files: {
@@ -12,10 +42,40 @@ export const ts = createPlugin({
12
42
  },
13
43
  name: "ts",
14
44
  rules: [
45
+ asyncPromiseExecutors,
46
+ caseDeclarations,
47
+ chainedAssignments,
48
+ classAssignments,
15
49
  consecutiveNonNullAssertions,
50
+ constantAssignments,
51
+ constructorReturns,
16
52
  debuggerStatements,
53
+ defaultCaseLast,
54
+ duplicateArguments,
55
+ emptyDestructures,
56
+ emptyStaticBlocks,
57
+ exceptionAssignments,
17
58
  forInArrays,
59
+ functionAssignments,
60
+ generatorFunctionYields,
61
+ globalAssignments,
62
+ globalObjectCalls,
18
63
  namespaceDeclarations,
64
+ negativeZeroComparisons,
65
+ newExpressions,
66
+ newNativeNonConstructors,
67
+ objectProto,
19
68
  octalEscapes,
69
+ octalNumbers,
70
+ returnAssignments,
71
+ sparseArrays,
72
+ symbolDescriptions,
73
+ unassignedVariables,
74
+ undefinedVariables,
75
+ unicodeBOMs,
76
+ unnecessaryCatches,
77
+ unsafeNegations,
78
+ variableDeletions,
79
+ voidOperator,
20
80
  ],
21
81
  });
@@ -0,0 +1,72 @@
1
+ import rule from "./asyncPromiseExecutors.js";
2
+ import { ruleTester } from "./ruleTester.js";
3
+
4
+ ruleTester.describe(rule, {
5
+ invalid: [
6
+ {
7
+ code: `
8
+ new Promise(async (resolve, reject) => {
9
+ resolve(42);
10
+ });
11
+ `,
12
+ snapshot: `
13
+ new Promise(async (resolve, reject) => {
14
+ ~~~~~
15
+ Async Promise executor functions are not able to properly catch thrown errors and often indicate unnecessarily complex logic.
16
+ resolve(42);
17
+ });
18
+ `,
19
+ },
20
+ {
21
+ code: `
22
+ new Promise(async function(resolve, reject) {
23
+ resolve(42);
24
+ });
25
+ `,
26
+ snapshot: `
27
+ new Promise(async function(resolve, reject) {
28
+ ~~~~~
29
+ Async Promise executor functions are not able to properly catch thrown errors and often indicate unnecessarily complex logic.
30
+ resolve(42);
31
+ });
32
+ `,
33
+ },
34
+ {
35
+ code: `
36
+ const p = new Promise(async (resolve) => {
37
+ resolve();
38
+ });
39
+ `,
40
+ snapshot: `
41
+ const p = new Promise(async (resolve) => {
42
+ ~~~~~
43
+ Async Promise executor functions are not able to properly catch thrown errors and often indicate unnecessarily complex logic.
44
+ resolve();
45
+ });
46
+ `,
47
+ },
48
+ ],
49
+ valid: [
50
+ `
51
+ new Promise((resolve, reject) => {
52
+ resolve(42);
53
+ });
54
+ `,
55
+ `
56
+ new Promise(function(resolve, reject) {
57
+ resolve(42);
58
+ });
59
+ `,
60
+ `
61
+ const p = new Promise((resolve) => {
62
+ doSomething().then(resolve);
63
+ });
64
+ `,
65
+ `
66
+ // Not a Promise constructor
67
+ new SomethingElse(async (resolve) => {
68
+ resolve();
69
+ });
70
+ `,
71
+ ],
72
+ });
@@ -0,0 +1,61 @@
1
+ import * as ts from "typescript";
2
+
3
+ import { typescriptLanguage } from "../language.js";
4
+ import { isGlobalDeclaration } from "../utils/isGlobalDeclaration.js";
5
+
6
+ export default typescriptLanguage.createRule({
7
+ about: {
8
+ description: "Reports using async functions as Promise executor functions.",
9
+ id: "asyncPromiseExecutors",
10
+ preset: "logical",
11
+ },
12
+ messages: {
13
+ asyncPromiseExecutor: {
14
+ primary:
15
+ "Async Promise executor functions are not able to properly catch thrown errors and often indicate unnecessarily complex logic.",
16
+ secondary: [
17
+ "The Promise executor function is called synchronously by the Promise constructor.",
18
+ "If an async function is used as a Promise executor, thrown errors will not be caught by the Promise and will instead result in unhandled rejections.",
19
+ "Additionally, if a Promise executor function is using `await`, there's probably no need to use the `new Promise` constructor.",
20
+ ],
21
+ suggestions: [
22
+ "Remove the `async` keyword from the executor function.",
23
+ "If you need to use `await` inside the Promise, consider restructuring your code to avoid the Promise constructor.",
24
+ ],
25
+ },
26
+ },
27
+ setup(context) {
28
+ return {
29
+ visitors: {
30
+ NewExpression: (node) => {
31
+ if (
32
+ !isGlobalDeclaration(node.expression, context.typeChecker) ||
33
+ !node.arguments?.length
34
+ ) {
35
+ return;
36
+ }
37
+
38
+ const executor = node.arguments[0];
39
+ if (!ts.isFunctionLike(executor)) {
40
+ return;
41
+ }
42
+
43
+ const asyncModifier = executor.modifiers?.find(
44
+ (mod) => mod.kind === ts.SyntaxKind.AsyncKeyword,
45
+ );
46
+ if (!asyncModifier) {
47
+ return;
48
+ }
49
+
50
+ context.report({
51
+ message: "asyncPromiseExecutor",
52
+ range: {
53
+ begin: asyncModifier.getStart(context.sourceFile),
54
+ end: asyncModifier.getEnd(),
55
+ },
56
+ });
57
+ },
58
+ },
59
+ };
60
+ },
61
+ });