eslint-plugin-jsdoc 63.0.1 → 63.0.2

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.
@@ -1,7 +1,27 @@
1
1
  import iterateJsdoc from '../iterateJsdoc.js';
2
2
  import {
3
+ getDocumentNamepathDefiningTags,
3
4
  strictNativeTypes,
4
5
  } from '../jsdocUtils.js';
6
+ import {
7
+ traverse,
8
+ tryParse as tryParseType,
9
+ } from '@es-joy/jsdoccomment';
10
+
11
+ /**
12
+ * @type {Partial<Record<import('../jsdocUtils.js').ParserMode, import('jsdoc-type-pratt-parser').ParseMode[]>>}
13
+ */
14
+ const parseTypeModes = {
15
+ closure: [
16
+ 'closure',
17
+ ],
18
+ jsdoc: [
19
+ 'jsdoc',
20
+ ],
21
+ typescript: [
22
+ 'typescript',
23
+ ],
24
+ };
5
25
 
6
26
  /**
7
27
  * @param {import('../iterateJsdoc.js').Utils} utils
@@ -36,11 +56,35 @@ const canSkip = (utils, settings) => {
36
56
  settings.mode === 'closure' && utils.classHasTag('record');
37
57
  };
38
58
 
59
+ /**
60
+ * @param {import('jsdoc-type-pratt-parser').RootResult} parsedType
61
+ * @returns {import('jsdoc-type-pratt-parser').RootResult}
62
+ */
63
+ const getReturnTypeLevelNode = (parsedType) => {
64
+ return parsedType.type === 'JsdocTypeParenthesis' ?
65
+ parsedType.element :
66
+ parsedType;
67
+ };
68
+
69
+ /**
70
+ * @param {import('eslint').Rule.RuleContext} context
71
+ * @param {import('../jsdocUtils.js').ParserMode} mode
72
+ * @returns {import('../jsdocUtils.js').ParserMode}
73
+ */
74
+ const getParseMode = (context, mode) => {
75
+ const {
76
+ jsdoc,
77
+ } = /** @type {{jsdoc?: {mode?: import('../jsdocUtils.js').ParserMode}}} */ (context.settings);
78
+
79
+ return jsdoc?.mode ?? mode;
80
+ };
81
+
39
82
  export default iterateJsdoc(({
40
83
  context,
41
84
  node,
42
85
  report,
43
86
  settings,
87
+ sourceCode,
44
88
  utils,
45
89
  }) => {
46
90
  const {
@@ -49,6 +93,10 @@ export default iterateJsdoc(({
49
93
  noNativeTypes = true,
50
94
  reportMissingReturnForUndefinedTypes = false,
51
95
  } = context.options[0] || {};
96
+ const {
97
+ mode,
98
+ } = settings;
99
+ const parseMode = getParseMode(context, mode);
52
100
 
53
101
  if (canSkip(utils, settings)) {
54
102
  return;
@@ -90,6 +138,67 @@ export default iterateJsdoc(({
90
138
  }
91
139
 
92
140
  const returnNever = type === 'never';
141
+ const typedefs = getDocumentNamepathDefiningTags(sourceCode);
142
+
143
+ /**
144
+ * @param {import('comment-parser').Spec} returnTag
145
+ * @returns {boolean}
146
+ */
147
+ const mayReturnUndefined = (returnTag) => {
148
+ if (utils.mayBeUndefinedTypeTag(returnTag)) {
149
+ return true;
150
+ }
151
+
152
+ const returnType = returnTag.type.trim();
153
+ let parsedType;
154
+ try {
155
+ parsedType = tryParseType(returnType, parseTypeModes[parseMode]);
156
+ } catch {
157
+ return false;
158
+ }
159
+
160
+ const returnTypeLevelNode = getReturnTypeLevelNode(parsedType);
161
+ let returnIsUndefined = false;
162
+ traverse(returnTypeLevelNode, (nde, parentNode) => {
163
+ const isReturnLevelNode = !parentNode ||
164
+ returnTypeLevelNode.type === 'JsdocTypeUnion' && parentNode === returnTypeLevelNode;
165
+ if (!isReturnLevelNode) {
166
+ return;
167
+ }
168
+
169
+ const {
170
+ type: nodeType,
171
+ } = /** @type {import('jsdoc-type-pratt-parser').RootResult} */ (nde);
172
+
173
+ if (nodeType === 'JsdocTypeUndefined') {
174
+ returnIsUndefined = true;
175
+ return;
176
+ }
177
+
178
+ if (nodeType !== 'JsdocTypeName') {
179
+ return;
180
+ }
181
+
182
+ const {
183
+ value,
184
+ } = /** @type {import('jsdoc-type-pratt-parser').NameResult} */ (nde);
185
+
186
+ if (value === 'void') {
187
+ returnIsUndefined = true;
188
+ return;
189
+ }
190
+
191
+ const referencedTypedef = typedefs.find((typedefTag) => {
192
+ return typedefTag.name === value;
193
+ });
194
+
195
+ if (referencedTypedef && utils.mayBeUndefinedTypeTag(referencedTypedef)) {
196
+ returnIsUndefined = true;
197
+ }
198
+ });
199
+
200
+ return returnIsUndefined;
201
+ };
93
202
 
94
203
  if (returnNever && utils.hasValueOrExecutorHasNonEmptyResolveValue(false)) {
95
204
  report(`JSDoc @${tagName} declaration set with "never" but return expression is present in function.`);
@@ -107,7 +216,7 @@ export default iterateJsdoc(({
107
216
  !returnNever &&
108
217
  (
109
218
  reportMissingReturnForUndefinedTypes ||
110
- !utils.mayBeUndefinedTypeTag(tag)
219
+ !mayReturnUndefined(tag)
111
220
  ) &&
112
221
  (tag.type === '' && !utils.hasValueOrExecutorHasNonEmptyResolveValue(
113
222
  exemptAsync,