Skip to content

Commit eb51bdc

Browse files
author
Andrew Eisenberg
committed
Add basic recoverability.
1 parent 66ec67c commit eb51bdc

File tree

2 files changed

+192
-5
lines changed

2 files changed

+192
-5
lines changed

doctrine.js

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -888,7 +888,7 @@
888888
expr = {
889889
type: Syntax.ParameterType,
890890
name: expr.name,
891-
expression: parseTypeExpression(),
891+
expression: parseTypeExpression()
892892
};
893893
}
894894
if (token === Token.EQUAL) {
@@ -1197,7 +1197,8 @@
11971197
(function (exports) {
11981198
var index,
11991199
length,
1200-
source;
1200+
source,
1201+
recoverable;
12011202

12021203
function advance() {
12031204
var ch = source[index];
@@ -1374,7 +1375,7 @@
13741375
title = scanTitle();
13751376

13761377
// empty title
1377-
if (!title) {
1378+
if (!title && !recoverable) {
13781379
return;
13791380
}
13801381

@@ -1389,15 +1390,15 @@
13891390
// type required titles
13901391
if (isTypeParameterRequired(title)) {
13911392
tag.type = parseType(title, last);
1392-
if (!tag.type) {
1393+
if (!tag.type && !recoverable) {
13931394
return;
13941395
}
13951396
}
13961397

13971398
// param, property requires name
13981399
if (title === 'param' || title === 'property') {
13991400
tag.name = parseName(last);
1400-
if (!tag.name) {
1401+
if (!tag.name && !recoverable) {
14011402
return;
14021403
}
14031404
}
@@ -1450,6 +1451,7 @@
14501451

14511452
length = source.length;
14521453
index = 0;
1454+
recoverable = options.recoverable;
14531455

14541456
description = trim(scanDescription());
14551457

test/parse.js

Lines changed: 185 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -380,7 +380,192 @@ describe('invalid tags', function() {
380380
" */"
381381
].join('\n'), { tags: ['a', 1], unwrap:true }).should.throw();
382382
});
383+
});
383384

385+
describe('recovery tests', function() {
386+
it ('not recoverable', function () {
387+
var res = doctrine.parse(
388+
[
389+
"@param f"
390+
].join('\n'), { recoverable: false });
391+
392+
// parser will mistakenly think that the type is 'f' and there is no name
393+
res.tags.should.have.length(0);
394+
});
395+
396+
it ('params 1', function () {
397+
var res = doctrine.parse(
398+
[
399+
"@param f"
400+
].join('\n'), { recoverable: true });
401+
402+
// parser will mistakenly think that the type is 'f' and there is no name
403+
res.tags.should.have.length(1);
404+
res.tags[0].should.have.property('title', 'param');
405+
res.tags[0].should.have.property('type');
406+
res.tags[0].type.should.have.property('name', 'f');
407+
res.tags[0].type.should.have.property('type', 'NameExpression');
408+
res.tags[0].should.not.have.property('name');
409+
});
410+
it ('params 2', function () {
411+
var res = doctrine.parse(
412+
[
413+
"@param f",
414+
"@param {string} f2"
415+
].join('\n'), { recoverable: true });
416+
417+
// ensure second parameter is OK
418+
res.tags.should.have.length(2);
419+
res.tags[0].should.have.property('title', 'param');
420+
res.tags[0].should.have.property('type');
421+
res.tags[0].type.should.have.property('name', 'f');
422+
res.tags[0].type.should.have.property('type', 'NameExpression');
423+
res.tags[0].should.not.have.property('name');
424+
425+
res.tags[1].should.have.property('title', 'param');
426+
res.tags[1].should.have.property('type');
427+
res.tags[1].type.should.have.property('name', 'string');
428+
res.tags[1].type.should.have.property('type', 'NameExpression');
429+
res.tags[1].should.have.property('name', 'f2');
430+
});
431+
432+
it ('params 2', function () {
433+
var res = doctrine.parse(
434+
[
435+
"@param string f",
436+
"@param {string} f2"
437+
].join('\n'), { recoverable: true });
438+
439+
// ensure first parameter is OK even with invalid type name
440+
res.tags.should.have.length(2);
441+
res.tags[0].should.have.property('title', 'param');
442+
res.tags[0].should.have.property('type');
443+
res.tags[0].type.should.have.property('name', 'string');
444+
res.tags[0].type.should.have.property('type', 'NameExpression');
445+
res.tags[0].should.have.property('name', 'f');
446+
447+
res.tags[1].should.have.property('title', 'param');
448+
res.tags[1].should.have.property('type');
449+
res.tags[1].type.should.have.property('name', 'string');
450+
res.tags[1].type.should.have.property('type', 'NameExpression');
451+
res.tags[1].should.have.property('name', 'f2');
452+
});
453+
454+
it ('return 1', function() {
455+
var res = doctrine.parse(
456+
[
457+
"@returns"
458+
].join('\n'), { recoverable: true });
459+
460+
// return tag should exist
461+
res.tags.should.have.length(1);
462+
res.tags[0].should.have.property('title', 'returns');
463+
res.tags[0].should.not.have.property('type');
464+
});
465+
it ('return 2', function() {
466+
var res = doctrine.parse(
467+
[
468+
"@returns",
469+
"@param {string} f2"
470+
].join('\n'), { recoverable: true });
471+
472+
// return tag should exist as well as next tag
473+
res.tags.should.have.length(2);
474+
res.tags[0].should.have.property('title', 'returns');
475+
res.tags[0].should.not.have.property('type');
476+
477+
res.tags[1].should.have.property('title', 'param');
478+
res.tags[1].should.have.property('type');
479+
res.tags[1].type.should.have.property('name', 'string');
480+
res.tags[1].type.should.have.property('type', 'NameExpression');
481+
res.tags[1].should.have.property('name', 'f2');
482+
});
483+
484+
it ('extra @ 1', function() {
485+
var res = doctrine.parse(
486+
[
487+
"@",
488+
"@returns",
489+
"@param {string} f2"
490+
].join('\n'), { recoverable: true });
491+
492+
// empty tag name shouldn't affect subsequent tags
493+
res.tags.should.have.length(3);
494+
res.tags[0].should.have.property('title', '');
495+
res.tags[0].should.not.have.property('type');
496+
497+
res.tags[1].should.have.property('title', 'returns');
498+
res.tags[1].should.not.have.property('type');
499+
500+
res.tags[2].should.have.property('title', 'param');
501+
res.tags[2].should.have.property('type');
502+
res.tags[2].type.should.have.property('name', 'string');
503+
res.tags[2].type.should.have.property('type', 'NameExpression');
504+
res.tags[2].should.have.property('name', 'f2');
505+
});
506+
507+
it ('extra @ 2', function() {
508+
var res = doctrine.parse(
509+
[
510+
"@ invalid name",
511+
"@param {string} f2"
512+
].join('\n'), { recoverable: true });
513+
514+
// empty tag name shouldn't affect subsequent tags
515+
res.tags.should.have.length(2);
516+
res.tags[0].should.have.property('title', '');
517+
res.tags[0].should.not.have.property('type');
518+
res.tags[0].should.not.have.property('name');
519+
res.tags[0].should.have.property('description', 'invalid name');
520+
521+
res.tags[1].should.have.property('title', 'param');
522+
res.tags[1].should.have.property('type');
523+
res.tags[1].type.should.have.property('name', 'string');
524+
res.tags[1].type.should.have.property('type', 'NameExpression');
525+
res.tags[1].should.have.property('name', 'f2');
526+
});
527+
528+
it ('invalid tag 1', function() {
529+
var res = doctrine.parse(
530+
[
531+
"@111 invalid name",
532+
"@param {string} f2"
533+
].join('\n'), { recoverable: true });
534+
535+
// invalid tag name shouldn't affect subsequent tags
536+
res.tags.should.have.length(2);
537+
res.tags[0].should.have.property('title', '111');
538+
res.tags[0].should.not.have.property('type');
539+
res.tags[0].should.not.have.property('name');
540+
res.tags[0].should.have.property('description', 'invalid name');
541+
542+
res.tags[1].should.have.property('title', 'param');
543+
res.tags[1].should.have.property('type');
544+
res.tags[1].type.should.have.property('name', 'string');
545+
res.tags[1].type.should.have.property('type', 'NameExpression');
546+
res.tags[1].should.have.property('name', 'f2');
547+
});
548+
549+
it ('invalid tag 1', function() {
550+
var res = doctrine.parse(
551+
[
552+
"@111",
553+
"@param {string} f2"
554+
].join('\n'), { recoverable: true });
555+
556+
// invalid tag name shouldn't affect subsequent tags
557+
res.tags.should.have.length(2);
558+
res.tags[0].should.have.property('title', '111');
559+
res.tags[0].should.not.have.property('type');
560+
res.tags[0].should.not.have.property('name');
561+
res.tags[0].should.have.property('description', null);
562+
563+
res.tags[1].should.have.property('title', 'param');
564+
res.tags[1].should.have.property('type');
565+
res.tags[1].type.should.have.property('name', 'string');
566+
res.tags[1].type.should.have.property('type', 'NameExpression');
567+
res.tags[1].should.have.property('name', 'f2');
568+
});
384569

385570
});
386571

0 commit comments

Comments
 (0)