11// @ts -check
2- const { ESLintUtils, AST_NODE_TYPES } = require ( '@typescript-eslint/utils' ) ;
2+ const { ESLintUtils } = require ( '@typescript-eslint/utils' ) ;
33const utils = require ( '@typescript-eslint/type-utils' ) ;
44const {
55 createRule,
6- findClosest,
6+ isInHandledContext,
7+ typesToUnionString,
78 hasThrowsTag,
8- getOptionsFromContext,
99 getCalleeDeclaration,
1010 getDeclarationTSNodeOfESTreeNode,
1111 getJSDocThrowsTags,
@@ -28,35 +28,23 @@ module.exports = createRule({
2828 fixable : 'code' ,
2929 messages : {
3030 implicitPropagation :
31- 'Implicit propagation of exceptions is not allowed. Use try/catch to handle exceptions .' ,
31+ 'Implicit propagation of exceptions is not allowed. Add JSDoc comment with @throws (or @exception) tag .' ,
3232 throwTypeMismatch :
3333 'The type of the exception thrown does not match the type specified in the @throws (or @exception) tag.' ,
3434 } ,
35- defaultOptions : [
36- { tabLength : 4 } ,
37- ] ,
38- schema : [
39- {
40- type : 'object' ,
41- properties : {
42- tabLength : {
43- type : 'number' ,
44- default : 4 ,
45- } ,
46- } ,
47- additionalProperties : false ,
48- } ,
49- ] ,
35+ schema : [ ] ,
5036 } ,
5137 create ( context ) {
52- const options = getOptionsFromContext ( context ) ;
53-
5438 const sourceCode = context . sourceCode ;
5539 const services = ESLintUtils . getParserServices ( context ) ;
5640 const checker = services . program . getTypeChecker ( ) ;
5741
42+ const warnedNodes = new Set ( ) ;
43+
5844 /** @param {import('@typescript-eslint/utils').TSESTree.ExpressionStatement } node */
5945 const visitExpressionStatement = ( node ) => {
46+ if ( isInHandledContext ( node ) ) return ;
47+
6048 const callerDeclaration = findClosestFunctionNode ( node ) ;
6149 if ( ! callerDeclaration ) return ;
6250
@@ -67,8 +55,8 @@ module.exports = createRule({
6755 const isCommented =
6856 comments . length &&
6957 comments
70- . map ( ( { value } ) => value )
71- . some ( hasThrowsTag ) ;
58+ . map ( ( { value } ) => value )
59+ . some ( hasThrowsTag ) ;
7260
7361 // TODO: Branching type checking or not
7462 if ( isCommented ) {
@@ -146,55 +134,49 @@ module.exports = createRule({
146134 return ;
147135 }
148136
137+ if ( warnedNodes . has ( node . range [ 0 ] ) ) return ;
138+ warnedNodes . add ( node . range [ 0 ] ) ;
139+
149140 const calleeDeclaration = getCalleeDeclaration ( services , node ) ;
150141 if ( ! calleeDeclaration ) return ;
151142
152143 const calleeTags = getJSDocThrowsTags ( calleeDeclaration ) ;
153- const isCalleeThrows = calleeTags . length > 0 ;
154144
145+ const isCalleeThrows = calleeTags . length > 0 ;
155146 if ( ! isCalleeThrows ) return ;
156-
157- const lines = sourceCode . getLines ( ) ;
158-
159- const currentLine = lines [ node . loc . start . line - 1 ] ;
160- const prevLine = lines [ node . loc . start . line - 2 ] ;
161-
162- const indent = currentLine . match ( / ^ \s * / ) ?. [ 0 ] ?? '' ;
163- const newIndent = indent + ' ' . repeat ( options . tabLength ) ;
164-
165- // TODO: Better way to handle this?
166- if ( / ^ \s * t r y \s * \{ / . test ( prevLine ) ) return ;
167-
168- const nodeToWrap = findClosest ( node , ( n ) =>
169- n . type === AST_NODE_TYPES . BlockStatement ||
170- n . type === AST_NODE_TYPES . ExpressionStatement ||
171- n . type === AST_NODE_TYPES . VariableDeclaration
172- ) ;
173- if ( ! nodeToWrap ) return ;
147+
148+ const calleeThrowsTypes = getJSDocThrowsTagTypes ( checker , calleeDeclaration ) ;
174149
175150 context . report ( {
176151 node,
177152 messageId : 'implicitPropagation' ,
178153 fix ( fixer ) {
179- return [
180- fixer . insertTextBefore ( nodeToWrap , `try {\n${ newIndent } ` ) ,
181- fixer . insertTextAfter ( nodeToWrap , `\n${ indent } } catch {}` ) ,
182- ] ;
154+ const lines = sourceCode . getLines ( ) ;
155+ const currentLine = lines [ nodeToComment . loc . start . line - 1 ] ;
156+ const indent = currentLine . match ( / ^ \s * / ) ?. [ 0 ] ?? '' ;
157+ return fixer
158+ . insertTextBefore (
159+ nodeToComment ,
160+ `/**\n` +
161+ `${ indent } * @throws {${ typesToUnionString ( checker , calleeThrowsTypes ) } }\n` +
162+ `${ indent } */\n` +
163+ `${ indent } `
164+ ) ;
183165 } ,
184166 } ) ;
185167 } ;
186168
187169 return {
188- 'ArrowFunctionExpression :not(TryStatement[handler!=null]) ExpressionStatement MemberExpression[property.type="Identifier"]' : visitExpressionStatement ,
189- 'FunctionDeclaration :not(TryStatement[handler!=null]) ExpressionStatement MemberExpression[property.type="Identifier"]' : visitExpressionStatement ,
190- 'FunctionExpression :not(TryStatement[handler!=null]) ExpressionStatement MemberExpression[property.type="Identifier"]' : visitExpressionStatement ,
191- 'ArrowFunctionExpression :not(TryStatement[handler!=null]) ExpressionStatement CallExpression[callee.type="Identifier"]' : visitExpressionStatement ,
192- 'FunctionDeclaration :not(TryStatement[handler!=null]) ExpressionStatement CallExpression[callee.type="Identifier"]' : visitExpressionStatement ,
193- 'FunctionExpression :not(TryStatement[handler!=null]) ExpressionStatement CallExpression[callee.type="Identifier"]' : visitExpressionStatement ,
194- 'ArrowFunctionExpression :not(TryStatement[handler!=null]) ExpressionStatement:has(> AssignmentExpression[left.type="MemberExpression"]) ' : visitExpressionStatement ,
195- 'FunctionDeclaration :not(TryStatement[handler!=null]) ExpressionStatement:has(> AssignmentExpression[left.type="MemberExpression"]) ' : visitExpressionStatement ,
196- 'FunctionExpression :not(TryStatement[handler!=null]) ExpressionStatement:has(> AssignmentExpression[left.type="MemberExpression"]) ' : visitExpressionStatement ,
170+ 'ArrowFunctionExpression ExpressionStatement MemberExpression[property.type="Identifier"]' : visitExpressionStatement ,
171+ 'FunctionDeclaration ExpressionStatement MemberExpression[property.type="Identifier"]' : visitExpressionStatement ,
172+ 'FunctionExpression ExpressionStatement MemberExpression[property.type="Identifier"]' : visitExpressionStatement ,
173+ 'ArrowFunctionExpression ExpressionStatement CallExpression[callee.type="Identifier"]' : visitExpressionStatement ,
174+ 'FunctionDeclaration ExpressionStatement CallExpression[callee.type="Identifier"]' : visitExpressionStatement ,
175+ 'FunctionExpression ExpressionStatement CallExpression[callee.type="Identifier"]' : visitExpressionStatement ,
176+ 'ArrowFunctionExpression ExpressionStatement AssignmentExpression[left.type="MemberExpression"]' : visitExpressionStatement ,
177+ 'FunctionDeclaration ExpressionStatement AssignmentExpression[left.type="MemberExpression"]' : visitExpressionStatement ,
178+ 'FunctionExpression ExpressionStatement AssignmentExpression[left.type="MemberExpression"]' : visitExpressionStatement ,
197179 } ;
198180 } ,
199- defaultOptions : [ { tabLength : 4 } ] ,
181+ defaultOptions : [ ] ,
200182} ) ;
0 commit comments