Skip to content

Commit 588a0b4

Browse files
committed
Merge pull request #59 from zxqfox/feature/checkType-primitives-case
checkTypes: new mode strictNativeCase
2 parents 789fd27 + a18685a commit 588a0b4

File tree

4 files changed

+230
-6
lines changed

4 files changed

+230
-6
lines changed

README.md

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -300,9 +300,11 @@ function method() {}
300300

301301
Reports invalid types for bunch of tags
302302

303-
Type: `Boolean`
303+
In `strictNativeCase` mode ensures that case of natives is the same as in this list: `boolean`, `number`, `string`, `null`, `Object`, `Array`, `Date`, `RegExp`.
304304

305-
Values: `true`
305+
Type: `Boolean` or `String`
306+
307+
Values: `true` or `"strictNativeCase"`
306308

307309
Context: `*`
308310

@@ -344,6 +346,21 @@ function method(x) {}
344346

345347
##### Invalid
346348

349+
```js
350+
/** @type {some~number} */
351+
var x = 1;
352+
353+
/**
354+
* @param {function(redundantName: Number)} x
355+
*/
356+
function method(x) {}
357+
358+
/**
359+
* @param {Number|Boolean|object|array} x invalid for strictNativeCase
360+
*/
361+
function method(x) {}
362+
```
363+
347364
```js
348365
/** @type {some~number} */
349366
var x = 1;

lib/jsdoc.js

Lines changed: 102 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,7 @@ function DocType(type, loc) {
126126
this.valid = parsed.valid;
127127

128128
if (parsed.valid && parsed.variable) {
129-
this.loc.column = this.loc.column + 3;
129+
this.loc.column = this.loc.column + 3; // ' * '.length
130130
}
131131

132132
/**
@@ -137,6 +137,17 @@ function DocType(type, loc) {
137137
this.match = function(node) {
138138
return jsDocMatchType(data, node);
139139
};
140+
141+
/**
142+
* @param {function(TypeBuilder)} cb Calling for each node
143+
* @returns {Array}
144+
*/
145+
this.iterate = function(cb) {
146+
if (!parsed.valid) {
147+
return [];
148+
}
149+
return _iterateDocTypes(parsed, cb);
150+
};
140151
}
141152

142153
/**
@@ -296,6 +307,96 @@ function _parseDocType(typeString) {
296307
return node;
297308
}
298309

310+
function _iterateDocTypes(node, cb) {
311+
var res;
312+
313+
switch (true) {
314+
case node instanceof TypeBuilder.TypeUnion:
315+
// optional: boolean,
316+
// nullable: boolean,
317+
// variable: boolean,
318+
// nonNullable: boolean,
319+
// all: boolean,
320+
// unknown: boolean,
321+
// types: Array.<TypeName|GenericType|FunctionType|RecordType>
322+
res = node.types.map(function(subnode) {
323+
if (node.selfNode) {
324+
subnode.parentNode = node.selfNode;
325+
}
326+
return _iterateDocTypes(subnode, cb);
327+
});
328+
break;
329+
330+
case node instanceof TypeBuilder.TypeName:
331+
// name: string
332+
node.typeName = node.name;
333+
res = cb(node);
334+
break;
335+
336+
case node instanceof TypeBuilder.GenericType:
337+
// genericTypeName: string,
338+
// parameterTypeUnions: Array.<TypeUnion>
339+
node.typeName = node.genericTypeName;
340+
res = cb(node);
341+
if (node.parameterTypeUnions) {
342+
node.parameterTypeUnions.selfNode = node;
343+
res.concat(_iterateDocTypes(node.parameterTypeUnions, cb));
344+
}
345+
break;
346+
347+
case node instanceof TypeBuilder.FunctionType:
348+
// parameterTypeUnions: Array.<TypeUnion>,
349+
// returnTypeUnion: TypeUnion|null,
350+
// isConstructor: boolean,
351+
// contextTypeUnion: TypeUnion|null
352+
node.typeName = 'Function';
353+
res = cb(node) || [];
354+
res.concat(node.parameterTypeUnions.map(function(subnode) {
355+
subnode.parentNode = node;
356+
_iterateDocTypes(subnode, cb);
357+
}));
358+
if (node.returnTypeUnion) {
359+
node.returnTypeUnion.selfNode = node;
360+
res.concat(_iterateDocTypes(node.returnTypeUnion, cb));
361+
}
362+
if (node.contextTypeUnion) {
363+
node.contextTypeUnion.selfNode = node;
364+
res.concat(_iterateDocTypes(node.contextTypeUnion, cb));
365+
}
366+
break;
367+
368+
case node instanceof TypeBuilder.RecordType:
369+
// entries: Array.<RecordEntry>
370+
node.typeName = 'Object';
371+
res = cb(node) || [];
372+
if (node.entries) {
373+
res.concat(node.entries.map(function(subnode) {
374+
subnode.parentNode = node;
375+
_iterateDocTypes(subnode, cb);
376+
}));
377+
}
378+
break;
379+
380+
case node instanceof TypeBuilder.RecordType.Entry:
381+
// name: string,
382+
// typeUnion: TypeUnion
383+
node.typeUnion.selfNode = node;
384+
res = _iterateDocTypes(node.typeUnion, cb);
385+
break;
386+
387+
case node instanceof TypeBuilder.ModuleName:
388+
node.typeName = node.name;
389+
node.module = true;
390+
res = cb(node);
391+
break;
392+
393+
default:
394+
throw new Error('DocType: Unsupported doc node');
395+
}
396+
397+
return res;
398+
}
399+
299400
/**
300401
* Converts AST jsDoc node to simple object
301402
* @param {Object} node

lib/rules/validate-jsdoc/check-types.js

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ var jsdoc = require('../../jsdoc');
33
module.exports = validateTypesInTags;
44
module.exports.scopes = ['file'];
55
module.exports.options = {
6-
checkTypes: {allowedValues: [true]}
6+
checkTypes: {allowedValues: [true, 'strictNativeCase']}
77
};
88

99
var allowedTags = {
@@ -32,12 +32,27 @@ var allowedTags = {
3232
define: true
3333
};
3434

35+
var strictNatives = {
36+
// lowercased
37+
boolean: 'boolean',
38+
number: 'number',
39+
string: 'string',
40+
'null': 'null',
41+
// titlecased
42+
array: 'Array',
43+
object: 'Object',
44+
regexp: 'RegExp',
45+
date: 'Date',
46+
'function': 'Function'
47+
};
48+
3549
/**
3650
* validator for types in tags
3751
* @param {JSCS.JSFile} file
3852
* @param {JSCS.Errors} errors
3953
*/
4054
function validateTypesInTags(file, errors) {
55+
var strictNativeCase = this._options.checkTypes === 'strictNativeCase';
4156
var comments = file.getComments();
4257
comments.forEach(function(commentNode) {
4358
if (commentNode.type !== 'Block' || commentNode.value[0] !== '*') {
@@ -59,6 +74,22 @@ function validateTypesInTags(file, errors) {
5974
// throw an error if not valid
6075
errors.add('expects valid type instead of ' + tag.type.value, tag.type.loc);
6176
}
77+
if (strictNativeCase) {
78+
tag.type.iterate(function(node) {
79+
// it shouldn't be!
80+
if (!node.typeName) {
81+
// skip untyped unknown things
82+
return;
83+
}
84+
85+
// is native?
86+
var type = node.typeName;
87+
var lowerType = type.toLowerCase();
88+
if (lowerType in strictNatives && type !== strictNatives[lowerType]) {
89+
errors.add('invalid case of type `' + type + '`', tag.type.loc);
90+
}
91+
});
92+
}
6293
});
6394
});
6495
}

test/lib/rules/validate-jsdoc/check-types.js

Lines changed: 77 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ describe('lib/rules/validate-jsdoc/check-types', function() {
2323
checker.rules({checkTypes: true});
2424

2525
checker.cases([
26-
/* jshint ignore:start */
26+
/* jshint ignore:start *//* jscs: disable */
2727
{
2828
it: 'should not report',
2929
code: function() {
@@ -156,7 +156,82 @@ describe('lib/rules/validate-jsdoc/check-types', function() {
156156
}
157157
}
158158
}
159-
/* jshint ignore:end */
159+
/* jshint ignore:end *//* jscs: enable */
160+
]);
161+
162+
});
163+
164+
describe('with strictNativeCase', function() {
165+
checker.rules({checkTypes: 'strictNativeCase'});
166+
167+
checker.cases([
168+
/* jshint ignore:start *//* jscs: disable */
169+
{
170+
it: 'should not report strict natives',
171+
code: function() {
172+
/**
173+
* @param {number}
174+
* @param {string}
175+
* @param {boolean}
176+
* @param {null}
177+
* @param {Array}
178+
* @param {Object}
179+
* @param {Date}
180+
* @param {Function}
181+
*/
182+
function _f () {}
183+
}
184+
}, {
185+
it: 'should not report joined strict natives',
186+
code: function() {
187+
/**
188+
* @param {number|string|boolean|null|Array|Object|Date}
189+
*/
190+
function _f () {}
191+
}
192+
}, {
193+
it: 'should not report strict natives declared as function arguments',
194+
code: function() {
195+
/**
196+
* @param {function(number, Array)}
197+
*/
198+
function _f () {}
199+
}
200+
}, {
201+
it: 'should report strict natives',
202+
errors: 6,
203+
code: function() {
204+
/**
205+
* @param {Number}
206+
* @param {String}
207+
* @param {Boolean}
208+
* @param {array}
209+
* @param {object}
210+
* @param {date}
211+
*/
212+
function _f () {}
213+
}
214+
}, {
215+
it: 'should report joined wrong cased strict natives',
216+
errors: 7,
217+
code: function() {
218+
/**
219+
* @param {Number|String|Boolean|Null}
220+
* @param {array|object|date}
221+
*/
222+
function _f () {}
223+
}
224+
}, {
225+
it: 'should report joined strict wrong cased natives',
226+
code: function() {
227+
/**
228+
* @param {Number|String|Boolean|Null|array|object|date|regexp|function}
229+
*/
230+
function _f () {}
231+
},
232+
errors: 9
233+
}
234+
/* jshint ignore:end *//* jscs: enable */
160235
]);
161236

162237
});

0 commit comments

Comments
 (0)