Skip to content

Commit 721cc78

Browse files
authored
Merge pull request #25 from Xvezda/feature/update-examples
feature/update examples
2 parents 0f7d248 + 4a75ea0 commit 721cc78

File tree

6 files changed

+144
-35
lines changed

6 files changed

+144
-35
lines changed

examples/fixed.js

Lines changed: 18 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,26 @@
11
/**
2-
* @throws {Error}
2+
* @throws {RangeError}
33
*/
44
function foo() {
5-
throw new Error();
5+
throw new RangeError();
66
}
77

8+
/**
9+
* @throws {TypeError}
10+
*/
811
function bar() {
9-
try {
10-
foo();
11-
} catch {}
12+
throw new TypeError();
1213
}
13-
bar();
1414

15-
/** @throws {Error} */
15+
/**
16+
* @throws {RangeError | TypeError}
17+
*/
1618
function baz() {
17-
foo();
19+
if (Math.random() > 0.5) {
20+
foo();
21+
} else {
22+
bar();
23+
}
1824
}
1925
baz();
2026

@@ -41,10 +47,11 @@ const egg = {
4147
}
4248
};
4349

50+
/**
51+
* @throws {Error}
52+
*/
4453
const lol = () => {
45-
try {
46-
console.log(egg.ham.spam);
47-
} catch {}
54+
console.log(egg.ham.spam);
4855
};
4956
lol();
5057

examples/unsafe.js

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,17 @@
11
function foo() {
2-
throw new Error();
2+
throw new RangeError();
33
}
44

55
function bar() {
6-
foo();
6+
throw new TypeError();
77
}
8-
bar();
98

109
function baz() {
11-
foo();
10+
if (Math.random() > 0.5) {
11+
foo();
12+
} else {
13+
bar();
14+
}
1215
}
1316
baz();
1417

src/rules/no-implicit-propagation.js

Lines changed: 16 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -161,10 +161,10 @@ module.exports = createRule({
161161
const calleeDeclaration = getCalleeDeclaration(services, node);
162162
if (!calleeDeclaration) return;
163163

164-
if (hasJSDocThrowsTag(sourceCode, nodeToComment)) {
165-
const calleeThrowsTypes = toFlattenedTypeArray(getJSDocThrowsTagTypes(checker, calleeDeclaration));
166-
if (!calleeThrowsTypes.length) return;
164+
const calleeThrowsTypes = toFlattenedTypeArray(getJSDocThrowsTagTypes(checker, calleeDeclaration));
165+
if (!calleeThrowsTypes.length) return;
167166

167+
if (hasJSDocThrowsTag(sourceCode, nodeToComment)) {
168168
const callerDeclarationTSNode =
169169
getDeclarationTSNodeOfESTreeNode(services, callerDeclaration);
170170

@@ -176,10 +176,10 @@ module.exports = createRule({
176176
.map(tag => tag.typeExpression?.type)
177177
.filter(tag => !!tag);
178178

179-
const callerThrowsTypes = getJSDocThrowsTagTypes(checker, callerDeclarationTSNode);
179+
const callerThrowsTypes = toFlattenedTypeArray(getJSDocThrowsTagTypes(checker, callerDeclarationTSNode));
180180

181181
if (
182-
isTypesAssignableTo(checker, calleeThrowsTypes, callerThrowsTypes)
182+
isTypesAssignableTo(services.program, calleeThrowsTypes, callerThrowsTypes)
183183
) {
184184
return;
185185
}
@@ -188,8 +188,16 @@ module.exports = createRule({
188188
if (!lastThrowsTypeNode) return;
189189

190190
const notAssignableThrows = calleeThrowsTypes
191-
.filter((t) => !callerThrowsTypes
192-
.some((n) => checker.isTypeAssignableTo(t, n)));
191+
.filter((calleeType) => !callerThrowsTypes
192+
.some((callerType) => {
193+
if (
194+
utils.isErrorLike(services.program, callerType) &&
195+
utils.isErrorLike(services.program, calleeType)
196+
) {
197+
return utils.typeIsOrHasBaseType(calleeType, callerType);
198+
}
199+
return checker.isTypeAssignableTo(calleeType, callerType);
200+
}));
193201

194202
if (!notAssignableThrows.length) return;
195203

@@ -228,21 +236,14 @@ module.exports = createRule({
228236
// If there is only one throws tag, make it as a union type
229237
return fixer.replaceTextRange(
230238
[lastThrowsTypeNode.pos, lastThrowsTypeNode.end],
231-
calleeThrowsTypes
232-
.map(t => utils.getTypeName(checker, t)).join(' | '),
239+
typesToUnionString(checker, [...callerThrowsTypes, ...calleeThrowsTypes])
233240
);
234241
},
235242
});
236243

237244
return;
238245
}
239246

240-
const calleeTags = getJSDocThrowsTags(calleeDeclaration);
241-
242-
const isCalleeThrows = calleeTags.length > 0;
243-
if (!isCalleeThrows) return;
244-
245-
const calleeThrowsTypes = getJSDocThrowsTagTypes(checker, calleeDeclaration);
246247
context.report({
247248
node,
248249
messageId: 'implicitPropagation',

src/rules/no-implicit-propagation.test.js

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -315,6 +315,35 @@ ruleTester.run(
315315
}
316316
`,
317317
},
318+
{
319+
code: `
320+
/**
321+
* @throws {TypeError}
322+
*/
323+
function foo() {
324+
throw new TypeError();
325+
}
326+
327+
/**
328+
* @throws {RangeError}
329+
*/
330+
function bar() {
331+
throw new RangeError();
332+
}
333+
334+
/**
335+
* @throws {Error}
336+
*/
337+
function baz() {
338+
if (Math.random() > 0.5) {
339+
foo();
340+
} else {
341+
bar();
342+
}
343+
}
344+
baz();
345+
`,
346+
},
318347
],
319348
invalid: [
320349
{
@@ -900,6 +929,65 @@ ruleTester.run(
900929
{ messageId: 'implicitPropagation' },
901930
],
902931
},
932+
{
933+
code: `
934+
/**
935+
* @throws {TypeError}
936+
*/
937+
function foo() {
938+
throw new TypeError();
939+
}
940+
941+
/**
942+
* @throws {RangeError}
943+
*/
944+
function bar() {
945+
throw new RangeError();
946+
}
947+
948+
/**
949+
* @throws {TypeError}
950+
*/
951+
function baz() {
952+
if (Math.random() > 0.5) {
953+
foo();
954+
} else {
955+
bar();
956+
}
957+
}
958+
baz();
959+
`,
960+
output: `
961+
/**
962+
* @throws {TypeError}
963+
*/
964+
function foo() {
965+
throw new TypeError();
966+
}
967+
968+
/**
969+
* @throws {RangeError}
970+
*/
971+
function bar() {
972+
throw new RangeError();
973+
}
974+
975+
/**
976+
* @throws {TypeError | RangeError}
977+
*/
978+
function baz() {
979+
if (Math.random() > 0.5) {
980+
foo();
981+
} else {
982+
bar();
983+
}
984+
}
985+
baz();
986+
`,
987+
errors: [
988+
{ messageId: 'throwTypeMismatch' },
989+
],
990+
},
903991
],
904992
},
905993
);

src/rules/no-undocumented-throws.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@ module.exports = createRule({
113113
.map(t => node.async ? checker.getAwaitedType(t) : t)
114114
.filter(t => !!t);
115115

116-
if (isTypesAssignableTo(checker, throwTypes, throwsTagTypes)) return;
116+
if (isTypesAssignableTo(services.program, throwTypes, throwsTagTypes)) return;
117117

118118
const lastTagtypeNode = getLast(throwsTagTypeNodes);
119119
if (!lastTagtypeNode) return;

src/utils.js

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -170,17 +170,27 @@ const toFlattenedTypeArray = (types) =>
170170
types.flatMap(type => type.isUnion() ? type.types : type);
171171

172172
/**
173-
* @param {import('typescript').TypeChecker} checker
173+
* @param {import('@typescript-eslint/utils').ParserServicesWithTypeInformation['program']} program
174174
* @param {import('typescript').Type[]} source
175175
* @param {import('typescript').Type[]} target
176176
* @returns {boolean}
177177
*/
178-
const isTypesAssignableTo = (checker, source, target) =>
179-
source
178+
const isTypesAssignableTo = (program, source, target) => {
179+
const checker = program.getTypeChecker();
180+
return source
180181
.every(sourceType =>
181182
target
182-
.some(targetType => checker.isTypeAssignableTo(sourceType, targetType))
183+
.some(targetType => {
184+
if (
185+
utils.isErrorLike(program, sourceType) &&
186+
utils.isErrorLike(program, targetType)
187+
) {
188+
return utils.typeIsOrHasBaseType(sourceType, targetType);
189+
}
190+
return checker.isTypeAssignableTo(sourceType, targetType);
191+
})
183192
);
193+
}
184194

185195
/**
186196
* Find closest function where exception is thrown

0 commit comments

Comments
 (0)