Skip to content

Commit 986a812

Browse files
authored
Merge pull request #8 from Xvezda/feature/getter-settter
Add getter/setter support
2 parents d0aeae5 + a2db480 commit 986a812

File tree

4 files changed

+406
-18
lines changed

4 files changed

+406
-18
lines changed

src/rules/no-implicit-propagation.js

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ const {
55
createRule,
66
hasThrowsTag,
77
getOptionsFromContext,
8+
getCalleeDeclaration,
89
getDeclarationTSNodeOfESTreeNode,
910
getJSDocThrowsTags,
1011
getJSDocThrowsTagTypes,
@@ -55,8 +56,6 @@ module.exports = createRule({
5556

5657
/** @param {import('@typescript-eslint/utils').TSESTree.ExpressionStatement} node */
5758
const visitExpressionStatement = (node) => {
58-
if (node.expression.type !== AST_NODE_TYPES.CallExpression) return;
59-
6059
const callerDeclaration = findClosestFunctionNode(node);
6160
if (!callerDeclaration) return;
6261

@@ -72,21 +71,17 @@ module.exports = createRule({
7271

7372
// TODO: Branching type checking or not
7473
if (isCommented) {
75-
const calleeDeclarationTSNode =
76-
getDeclarationTSNodeOfESTreeNode(services, node.expression.callee);
74+
const calleeDeclaration = getCalleeDeclaration(services, node);
75+
if (!calleeDeclaration) return;
7776

78-
if (!calleeDeclarationTSNode) return;
77+
const calleeThrowsTypes = toFlattenedTypeArray(getJSDocThrowsTagTypes(checker, calleeDeclaration));
78+
if (!calleeThrowsTypes.length) return;
7979

8080
const callerDeclarationTSNode =
8181
getDeclarationTSNodeOfESTreeNode(services, callerDeclaration);
8282

8383
if (!callerDeclarationTSNode) return;
8484

85-
const calleeThrowsTypes =
86-
toFlattenedTypeArray(
87-
getJSDocThrowsTagTypes(checker, calleeDeclarationTSNode)
88-
);
89-
9085
const callerThrowsTags = getJSDocThrowsTags(callerDeclarationTSNode);
9186
const callerThrowsTypeNodes =
9287
callerThrowsTags
@@ -142,23 +137,21 @@ module.exports = createRule({
142137
return fixer.replaceTextRange(
143138
[lastThrowsTypeNode.pos, lastThrowsTypeNode.end],
144139
calleeThrowsTypes
145-
.map(t => utils.getTypeName(checker, t)).join(' | '),
140+
.map(t => utils.getTypeName(checker, t)).join(' | '),
146141
);
147142
},
148143
});
149144

150145
return;
151146
}
152147

153-
const calleeType = services.getTypeAtLocation(node.expression.callee);
154-
if (!calleeType.symbol) return;
155-
156-
const calleeTags = calleeType.symbol.getJsDocTags();
148+
const calleeDeclaration = getCalleeDeclaration(services, node);
149+
if (!calleeDeclaration) return;
157150

158-
const isCalleeThrowable = calleeTags
159-
.some((tag) => tag.name === 'throws' || tag.name === 'exception');
151+
const calleeTags = getJSDocThrowsTags(calleeDeclaration);
152+
const isCalleeThrows = calleeTags.length > 0;
160153

161-
if (!isCalleeThrowable) return;
154+
if (!isCalleeThrows) return;
162155

163156
const lines = sourceCode.getLines();
164157

@@ -186,6 +179,13 @@ module.exports = createRule({
186179
return {
187180
'ArrowFunctionExpression :not(TryStatement[handler!=null]) ExpressionStatement:has(> CallExpression)': visitExpressionStatement,
188181
'FunctionDeclaration :not(TryStatement[handler!=null]) ExpressionStatement:has(> CallExpression)': visitExpressionStatement,
182+
'FunctionExpression :not(TryStatement[handler!=null]) ExpressionStatement:has(> CallExpression)': visitExpressionStatement,
183+
'ArrowFunctionExpression :not(TryStatement[handler!=null]) ExpressionStatement:has(> MemberExpression)': visitExpressionStatement,
184+
'FunctionDeclaration :not(TryStatement[handler!=null]) ExpressionStatement:has(> MemberExpression)': visitExpressionStatement,
185+
'FunctionExpression :not(TryStatement[handler!=null]) ExpressionStatement:has(> MemberExpression)': visitExpressionStatement,
186+
'ArrowFunctionExpression :not(TryStatement[handler!=null]) ExpressionStatement:has(> AssignmentExpression[left.type="MemberExpression"])': visitExpressionStatement,
187+
'FunctionDeclaration :not(TryStatement[handler!=null]) ExpressionStatement:has(> AssignmentExpression[left.type="MemberExpression"])': visitExpressionStatement,
188+
'FunctionExpression :not(TryStatement[handler!=null]) ExpressionStatement:has(> AssignmentExpression[left.type="MemberExpression"])': visitExpressionStatement,
189189
};
190190
},
191191
defaultOptions: [{ tabLength: 4 }],

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

Lines changed: 292 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,148 @@ ruleTester.run(
129129
}
130130
`,
131131
},
132+
{
133+
code: `
134+
const foo = {
135+
/**
136+
* @throws {Error}
137+
*/
138+
get bar() {
139+
throw new Error('baz');
140+
},
141+
/**
142+
* @throws {TypeError}
143+
*/
144+
set bar(value) {
145+
throw new TypeError('baz');
146+
},
147+
};
148+
const baz = () => {
149+
try {
150+
foo.bar;
151+
} catch {}
152+
};
153+
`,
154+
},
155+
{
156+
code: `
157+
const foo = {
158+
/**
159+
* @throws {Error}
160+
*/
161+
get bar() {
162+
throw new Error('baz');
163+
},
164+
/**
165+
* @param {number} value
166+
* @throws {TypeError}
167+
*/
168+
set bar(value) {
169+
throw new TypeError('baz');
170+
},
171+
};
172+
/**
173+
* @throws {Error}
174+
* @throws {TypeError}
175+
*/
176+
const baz = () => {
177+
foo.bar;
178+
};
179+
`,
180+
},
181+
{
182+
code: `
183+
const foo = {
184+
/**
185+
* @throws {Error}
186+
*/
187+
get bar() {
188+
throw new Error('baz');
189+
},
190+
/**
191+
* @param {number} value
192+
* @throws {TypeError}
193+
*/
194+
set bar(value) {
195+
throw new TypeError('baz');
196+
},
197+
};
198+
/**
199+
* @throws {Error}
200+
* @throws {TypeError}
201+
*/
202+
const baz = () => {
203+
foo.bar = 42;
204+
};
205+
`,
206+
},
207+
{
208+
code: `
209+
class Foo {
210+
/**
211+
* @throws {Error}
212+
*/
213+
get bar() {
214+
throw new Error('baz');
215+
}
216+
/**
217+
* @throws {TypeError}
218+
*/
219+
set bar(value) {
220+
throw new TypeError('baz');
221+
}
222+
};
223+
const baz = () => {
224+
try {
225+
new Foo().bar;
226+
} catch {}
227+
};
228+
`,
229+
},
230+
{
231+
code: `
232+
class Foo {
233+
/**
234+
* @throws {SyntaxError}
235+
*/
236+
get bar() {
237+
throw new SyntaxError('baz');
238+
}
239+
/**
240+
* @throws {TypeError}
241+
*/
242+
set bar(value) {
243+
throw new TypeError('baz');
244+
}
245+
};
246+
/** @throws {SyntaxError} */
247+
const baz = () => {
248+
new Foo().bar;
249+
};
250+
`,
251+
},
252+
{
253+
code: `
254+
class Foo {
255+
/**
256+
* @throws {SyntaxError}
257+
*/
258+
get bar() {
259+
throw new SyntaxError('baz');
260+
}
261+
/**
262+
* @throws {TypeError}
263+
*/
264+
set bar(value) {
265+
throw new TypeError('baz');
266+
}
267+
};
268+
/** @throws {TypeError} */
269+
const baz = () => {
270+
new Foo().bar = 42;
271+
};
272+
`,
273+
},
132274
],
133275
invalid: [
134276
{
@@ -498,6 +640,156 @@ ruleTester.run(
498640
messageId: 'throwTypeMismatch',
499641
}],
500642
},
643+
{
644+
code: `
645+
const foo = {
646+
/**
647+
* @throws {Error}
648+
*/
649+
get bar() {
650+
throw new Error('baz');
651+
},
652+
/**
653+
* @throws {TypeError}
654+
*/
655+
set bar(value) {
656+
throw new TypeError('baz');
657+
},
658+
};
659+
const baz = () => {
660+
foo.bar = 42;
661+
};
662+
`,
663+
output: `
664+
const foo = {
665+
/**
666+
* @throws {Error}
667+
*/
668+
get bar() {
669+
throw new Error('baz');
670+
},
671+
/**
672+
* @throws {TypeError}
673+
*/
674+
set bar(value) {
675+
throw new TypeError('baz');
676+
},
677+
};
678+
const baz = () => {
679+
try {
680+
foo.bar = 42;
681+
} catch {}
682+
};
683+
`,
684+
errors: [
685+
{ messageId: 'implicitPropagation' },
686+
],
687+
options: [
688+
{
689+
tabLength: 2,
690+
},
691+
],
692+
},
693+
{
694+
code: `
695+
class Foo {
696+
/**
697+
* @throws {Error}
698+
*/
699+
get bar() {
700+
throw new Error('baz');
701+
}
702+
/**
703+
* @throws {TypeError}
704+
*/
705+
set bar(value) {
706+
throw new TypeError('baz');
707+
}
708+
};
709+
const baz = () => {
710+
new Foo().bar = 42;
711+
};
712+
`,
713+
output: `
714+
class Foo {
715+
/**
716+
* @throws {Error}
717+
*/
718+
get bar() {
719+
throw new Error('baz');
720+
}
721+
/**
722+
* @throws {TypeError}
723+
*/
724+
set bar(value) {
725+
throw new TypeError('baz');
726+
}
727+
};
728+
const baz = () => {
729+
try {
730+
new Foo().bar = 42;
731+
} catch {}
732+
};
733+
`,
734+
errors: [
735+
{ messageId: 'implicitPropagation' },
736+
],
737+
options: [
738+
{
739+
tabLength: 2,
740+
},
741+
],
742+
},
743+
{
744+
code: `
745+
class Foo {
746+
/**
747+
* @throws {Error}
748+
*/
749+
get bar() {
750+
throw new Error('baz');
751+
}
752+
/**
753+
* @throws {TypeError}
754+
*/
755+
set bar(value) {
756+
throw new TypeError('baz');
757+
}
758+
};
759+
const baz = () => {
760+
new Foo().bar;
761+
};
762+
`,
763+
output: `
764+
class Foo {
765+
/**
766+
* @throws {Error}
767+
*/
768+
get bar() {
769+
throw new Error('baz');
770+
}
771+
/**
772+
* @throws {TypeError}
773+
*/
774+
set bar(value) {
775+
throw new TypeError('baz');
776+
}
777+
};
778+
const baz = () => {
779+
try {
780+
new Foo().bar;
781+
} catch {}
782+
};
783+
`,
784+
errors: [
785+
{ messageId: 'implicitPropagation' },
786+
],
787+
options: [
788+
{
789+
tabLength: 2,
790+
},
791+
],
792+
},
501793
],
502794
},
503795
);

0 commit comments

Comments
 (0)