UNPKG

92.9 kBJavaScriptView Raw
1import generate from "babel-generator";
2
3type Node = {
4 type: string;
5};
6
7type Literal = {
8 type: 'StringLiteral' | 'BooleanLiteral' | 'NumericLiteral' | 'NullLiteral' | 'RegExpLiteral'
9};
10
11type Identifier = {
12 type: string;
13 name: string;
14};
15
16type QualifiedTypeIdentifier = {
17 id: Identifier;
18 qualification: Identifier|QualifiedTypeIdentifier;
19};
20
21type TypeAnnotation = {
22 type: string;
23};
24
25interface StringLiteralTypeAnnotation extends TypeAnnotation {
26 type: 'StringLiteralTypeAnnotation';
27}
28
29interface NumericLiteralTypeAnnotation extends TypeAnnotation {
30 type: 'NumericLiteralTypeAnnotation';
31}
32
33interface BooleanLiteralTypeAnnotation extends TypeAnnotation {
34 type: 'BooleanLiteralTypeAnnotation';
35}
36
37type Scope = {};
38
39type NodePath = {
40 type: string;
41 node: Node;
42 scope: Scope;
43};
44
45/**
46 * # Typecheck Transformer
47 */
48export default function ({types: t, template}): Object {
49
50 /**
51 * Binary Operators that can only produce boolean results.
52 */
53 const BOOLEAN_BINARY_OPERATORS: string[] = [
54 '==',
55 '===',
56 '>=',
57 '<=',
58 '>',
59 '<',
60 'instanceof'
61 ];
62
63 const checks: Object = createChecks();
64 const staticChecks: Object = createStaticChecks();
65
66 const checkIsArray: (() => Node) = expression(`Array.isArray(input)`);
67 const checkIsMap: (() => Node) = expression(`input instanceof Map`);
68 const checkIsSet: (() => Node) = expression(`input instanceof Set`);
69 const checkIsClass: (() => Node) = expression(`typeof input === 'function' && input.prototype && input.prototype.constructor === input`);
70 const checkIsGenerator: (() => Node) = expression(`typeof input === 'function' && input.generator`);
71 const checkIsIterable: (() => Node) = expression(`input && typeof input[Symbol.iterator] === 'function'`);
72 const checkIsObject: (() => Node) = expression(`input != null && typeof input === 'object'`);
73 const checkNotNull: (() => Node) = expression(`input != null`);
74 const checkEquals: (() => Node) = expression(`input === expected`);
75
76 const declareTypeChecker: (() => Node) = template(`
77 const id = function id (input) {
78 return check;
79 };
80 `);
81
82 const guard: (() => Node) = template(`
83 if (!check) {
84 throw new TypeError(message);
85 }
86 `);
87
88 const thrower: (() => Node) = template(`
89 if (check) {
90 ret;
91 }
92 else {
93 throw new TypeError(message);
94 }
95 `);
96
97 const guardInline: (() => Node) = expression(`
98 (id => {
99 if (!check) {
100 throw new TypeError(message);
101 }
102 return id;
103 })(input)
104 `);
105
106 const guardFn: (() => Node) = expression(`
107 function name (id) {
108 if (!check) {
109 throw new TypeError(message);
110 }
111 return id;
112 }
113 `);
114
115 const readableName: (() => Node) = expression(`
116 input === null ? 'null' : typeof input === 'object' && input.constructor ? input.constructor.name || '[Unknown Object]' : typeof input
117 `);
118
119 const checkMapKeys: (() => Node) = expression(`
120 input instanceof Map && Array.from(input.keys()).every(key => keyCheck)
121 `);
122
123 const checkMapValues: (() => Node) = expression(`
124 input instanceof Map && Array.from(input.values()).every(value => valueCheck)
125 `);
126
127 const checkMapEntries: (() => Node) = expression(`
128 input instanceof Map && Array.from(input).every(([key, value]) => keyCheck && valueCheck)
129 `);
130
131 const checkSetEntries: (() => Node) = expression(`
132 input instanceof Set && Array.from(input).every(value => valueCheck)
133 `);
134
135 const checkObjectIndexers: (() => Node) = expression(`
136 Object.keys(input).every(key => {
137 const value = input[key];
138 if (~fixedKeys.indexOf(key)) {
139 return true;
140 }
141 else {
142 return check;
143 }
144 });
145 `);
146
147 const checkObjectIndexersNoFixed: (() => Node) = expression(`
148 Object.keys(input).every(key => {
149 const value = input[key];
150 return check;
151 });
152 `);
153
154 const PRAGMA_IGNORE_STATEMENT = /typecheck:\s*ignore\s+statement/i;
155 const PRAGMA_IGNORE_FILE = /typecheck:\s*ignore\s+file/i;
156
157 const visitors = {
158 Statement (path: NodePath): void {
159 maybeSkip(path);
160 },
161 TypeAlias (path: NodePath): void {
162 if (maybeSkip(path)) {
163 return;
164 }
165 path.replaceWith(createTypeAliasChecks(path));
166 },
167
168 InterfaceDeclaration (path: NodePath): void {
169 if (maybeSkip(path)) {
170 return;
171 }
172 path.replaceWith(createInterfaceChecks(path));
173 },
174
175 ExportNamedDeclaration (path: NodePath): void {
176 if (maybeSkip(path)) {
177 return;
178 }
179 const {node, scope} = path;
180 if (node.declaration && node.declaration.type === 'TypeAlias') {
181 path.replaceWith(t.exportNamedDeclaration(
182 createTypeAliasChecks(path.get('declaration')),
183 [],
184 null
185 ));
186 }
187 },
188
189 ImportDeclaration (path: NodePath): void {
190 if (maybeSkip(path)) {
191 return;
192 }
193 if (path.node.importKind !== 'type') {
194 return;
195 }
196 const [declarators, specifiers] = path.get('specifiers')
197 .map(specifier => {
198 const local = specifier.get('local');
199 const tmpId = path.scope.generateUidIdentifierBasedOnNode(local.node);
200 const replacement = t.importSpecifier(tmpId, specifier.node.imported);
201 const id = t.identifier(local.node.name);
202
203 id.isTypeChecker = true;
204 const declarator = t.variableDeclarator(id, tmpId);
205 declarator.isTypeChecker = true;
206 return [declarator, replacement];
207 })
208 .reduce(([declarators, specifiers], [declarator, specifier]) => {
209 declarators.push(declarator);
210 specifiers.push(specifier);
211 return [declarators, specifiers];
212 }, [[], []]);
213
214 const declaration = t.variableDeclaration('var', declarators);
215 declaration.isTypeChecker = true;
216
217 path.replaceWithMultiple([
218 t.importDeclaration(specifiers, path.node.source),
219 declaration
220 ]);
221 },
222
223 Function: {
224 enter (path: NodePath): void {
225 if (maybeSkip(path)) {
226 return;
227 }
228
229 const {node, scope} = path;
230 const paramChecks = collectParamChecks(path);
231 if (node.type === "ArrowFunctionExpression" && node.expression) {
232 node.expression = false;
233 node.body = t.blockStatement([t.returnStatement(node.body)]);
234 }
235 if (node.returnType) {
236 createFunctionReturnGuard(path);
237 createFunctionYieldGuards(path);
238 }
239 node.body.body.unshift(...paramChecks);
240 node.savedTypeAnnotation = node.returnType;
241 node.returnCount = 0;
242 node.yieldCount = 0;
243 },
244 exit (path: NodePath): void {
245 const {node, scope} = path;
246 const isVoid = node.savedTypeAnnotation ? maybeNullableAnnotation(node.savedTypeAnnotation) : null;
247 if (!node.returnCount && isVoid === false) {
248 let annotation = node.savedTypeAnnotation;
249 if (annotation.type === 'TypeAnnotation') {
250 annotation = annotation.typeAnnotation;
251 }
252 if (node.generator && isGeneratorAnnotation(annotation) && annotation.typeParameters && annotation.typeParameters.params.length > 1) {
253 annotation = annotation.typeParameters.params[1];
254 }
255 throw path.buildCodeFrameError(`Function ${node.id ? `"${node.id.name}" ` : ''}did not return a value, expected ${humanReadableType(annotation)}`);
256 }
257 if (node.nextGuardCount) {
258 path.get('body').get('body')[0].insertBefore(node.nextGuard);
259 }
260 if (node.yieldGuardCount) {
261 path.get('body').get('body')[0].insertBefore(node.yieldGuard);
262 }
263 if (node.returnGuardCount) {
264 path.get('body').get('body')[0].insertBefore(node.returnGuard);
265 }
266 }
267 },
268
269 YieldExpression (path: NodePath): void {
270 const fn = path.getFunctionParent();
271 if (!fn) {
272 return;
273 }
274 fn.node.yieldCount++;
275 if (!isGeneratorAnnotation(fn.node.returnType) || maybeSkip(path)) {
276 return;
277 }
278 const {node, parent, scope} = path;
279 let annotation = fn.node.returnType;
280 if (annotation.type === 'NullableTypeAnnotation' || annotation.type === 'TypeAnnotation') {
281 annotation = annotation.typeAnnotation;
282 }
283 if (!annotation.typeParameters || annotation.typeParameters.params.length === 0) {
284 return;
285 }
286
287 const yieldType = annotation.typeParameters.params[0];
288 const nextType = annotation.typeParameters.params[2];
289 const ok = staticCheckAnnotation(path.get("argument"), yieldType);
290 if (ok === true && !nextType) {
291 return;
292 }
293 else if (ok === false) {
294 throw path.buildCodeFrameError(`Function ${fn.node.id ? `"${fn.node.id.name}" ` : ''}yielded an invalid type, expected ${humanReadableType(yieldType)} got ${humanReadableType(getAnnotation(path.get('argument')))}`);
295 }
296 fn.node.yieldGuardCount++;
297 if (fn.node.yieldGuard) {
298 const yielder = t.yieldExpression(
299 t.callExpression(fn.node.yieldGuardName, [node.argument || t.identifier('undefined')])
300 );
301 yielder.hasBeenTypeChecked = true;
302
303 if (fn.node.nextGuard) {
304 fn.node.nextGuardCount++;
305 path.replaceWith(t.callExpression(fn.node.nextGuardName, [yielder]));
306 }
307 else {
308 path.replaceWith(yielder);
309 }
310 }
311 else if (fn.node.nextGuard) {
312 fn.node.nextGuardCount++;
313 path.replaceWith(t.callExpression(fn.node.nextGuardName, [yielder]));
314 }
315 },
316
317
318 ReturnStatement (path: NodePath): void {
319 const fn = path.getFunctionParent();
320 if (!fn) {
321 return;
322 }
323 fn.node.returnCount++;
324 if (maybeSkip(path)) {
325 return;
326 }
327 const {node, parent, scope} = path;
328 const {returnType, returnGuardName} = fn.node;
329 if (!returnType || !returnGuardName) {
330 return;
331 }
332 if (!node.argument) {
333 if (maybeNullableAnnotation(returnType) === false) {
334 throw path.buildCodeFrameError(`Function ${fn.node.id ? `"${fn.node.id.name}" ` : ''}did not return a value, expected ${humanReadableType(returnType)}`);
335 }
336 return;
337 }
338 let annotation = returnType;
339 if (annotation.type === 'TypeAnnotation') {
340 annotation = annotation.typeAnnotation;
341 }
342 if (isGeneratorAnnotation(annotation)) {
343 annotation = annotation.typeParameters && annotation.typeParameters.params.length > 1 ? annotation.typeParameters.params[1] : t.anyTypeAnnotation();
344 }
345 const ok = staticCheckAnnotation(path.get("argument"), annotation);
346 if (ok === true) {
347 return;
348 }
349 else if (ok === false) {
350 throw path.buildCodeFrameError(`Function ${fn.node.id ? `"${fn.node.id.name}" ` : ''}returned an invalid type, expected ${humanReadableType(annotation)} got ${humanReadableType(getAnnotation(path.get('argument')))}`);
351 }
352 fn.node.returnGuardCount++;
353 const returner = t.returnStatement(t.callExpression(fn.node.returnGuardName, [node.argument]));
354 returner.hasBeenTypeChecked = true;
355 path.replaceWith(returner);
356 },
357
358 VariableDeclaration (path: NodePath): void {
359 if (maybeSkip(path)) {
360 return;
361 }
362 const {node, scope} = path;
363 const collected = [];
364 const declarations = path.get("declarations");
365 for (let i = 0; i < node.declarations.length; i++) {
366 const declaration = node.declarations[i];
367 const {id, init} = declaration;
368 if (!id.typeAnnotation || id.hasBeenTypeChecked) {
369 continue;
370 }
371 id.savedTypeAnnotation = id.typeAnnotation;
372 id.hasBeenTypeChecked = true;
373 const ok = staticCheckAnnotation(declarations[i], id.typeAnnotation);
374 if (ok === true) {
375 continue;
376 }
377 else if (ok === false) {
378 throw path.buildCodeFrameError(`Invalid assignment value, expected ${humanReadableType(id.typeAnnotation)} got ${humanReadableType(getAnnotation(declarations[i]))}`);
379 }
380 const check = checkAnnotation(id, id.typeAnnotation, scope);
381 if (check) {
382 collected.push(guard({
383 check,
384 message: varTypeErrorMessage(id, scope)
385 }));
386 }
387 }
388 if (collected.length > 0) {
389 const check = collected.reduce((check, branch) => {
390 branch.alternate = check;
391 return branch;
392 });
393 if (path.parent.type === 'Program' || path.parent.type === 'BlockStatement') {
394 path.insertAfter(check);
395 }
396 else if (path.parentPath.isForXStatement() || path.parentPath.isForStatement() || path.parentPath.isForInStatement()) {
397 let body = path.parentPath.get('body');
398 if (body.type !== 'BlockStatement') {
399 const block = t.blockStatement([body.node]);
400 body.replaceWith(block);
401 body = path.parentPath.get('body');
402 }
403 const children = body.get('body');
404 if (children.length === 0) {
405 body.replaceWith(check);
406 }
407 else {
408 children[0].insertBefore(check);
409 }
410
411 }
412 else if (path.parent.type === 'ExportNamedDeclaration' || path.parent.type === 'ExportDefaultDeclaration' || path.parent.type === 'ExportAllDeclaration') {
413 path.parentPath.insertAfter(check);
414 }
415 else {
416 path.replaceWith(t.blockStatement([node, check]));
417 }
418 }
419 },
420
421 AssignmentExpression (path: NodePath): void {
422 if (maybeSkip(path)) {
423 return;
424 }
425 const {node, scope} = path;
426 const left = path.get('left');
427 let annotation;
428 if (node.hasBeenTypeChecked || node.left.hasBeenTypeChecked) {
429 return;
430 }
431 else if (left.isMemberExpression()) {
432 annotation = getAnnotation(left);
433 }
434 else if (t.isIdentifier(node.left)) {
435 const binding = scope.getBinding(node.left.name);
436 if (!binding) {
437 return;
438 }
439 else if (binding.path.type !== 'VariableDeclarator') {
440 return;
441 }
442 annotation = left.getTypeAnnotation();
443 if (annotation.type === 'AnyTypeAnnotation') {
444 const item = binding.path.get('id');
445 annotation = item.node.savedTypeAnnotation || item.getTypeAnnotation();
446 }
447 }
448 else {
449 return;
450 }
451
452 node.hasBeenTypeChecked = true;
453 node.left.hasBeenTypeChecked = true;
454 const id = node.left;
455 const right = path.get('right');
456 if (annotation.type === 'AnyTypeAnnotation') {
457 return;
458 }
459 const ok = staticCheckAnnotation(right, annotation);
460 if (ok === true) {
461 return;
462 }
463 else if (ok === false) {
464 throw path.buildCodeFrameError(`Invalid assignment value, expected ${humanReadableType(annotation)} got ${humanReadableType(getAnnotation(right))}`);
465 }
466 const check = checkAnnotation(id, annotation, scope);
467 if (!id.typeAnnotation) {
468 id.typeAnnotation = annotation;
469 }
470 id.hasBeenTypeChecked = true;
471 if (check) {
472 const parent = path.getStatementParent();
473 parent.insertAfter(guard({
474 check,
475 message: varTypeErrorMessage(id, scope)
476 }));
477 }
478 },
479
480 TypeCastExpression (path: NodePath): void {
481 const {node} = path;
482 let target;
483 switch (node.expression.type) {
484 case 'Identifier':
485 target = node.expression;
486 break;
487 case 'AssignmentExpression':
488 target = node.expression.left;
489 break;
490 default:
491 // unsupported.
492 return;
493 }
494 const id = path.scope.getBindingIdentifier(target.name);
495 if (!id) {
496 return;
497 }
498 id.savedTypeAnnotation = path.getTypeAnnotation();
499 },
500
501 ForOfStatement (path: NodePath): void {
502 if (maybeSkip(path)) {
503 return;
504 }
505 const left: NodePath = path.get('left');
506 const right: NodePath = path.get('right');
507 const rightAnnotation: TypeAnnotation = getAnnotation(right);
508 const leftAnnotation: TypeAnnotation = left.isVariableDeclaration() ? getAnnotation(left.get('declarations')[0].get('id')) : getAnnotation(left);
509 if (rightAnnotation.type !== 'VoidTypeAnnotation') {
510 const ok: ?boolean = maybeIterableAnnotation(rightAnnotation);
511 if (ok === false) {
512 throw path.buildCodeFrameError(`Cannot iterate ${humanReadableType(rightAnnotation)}`);
513 }
514 }
515 let id: ?Identifier;
516 if (right.isIdentifier()) {
517 id = right.node;
518 }
519 else {
520 id = path.scope.generateUidIdentifierBasedOnNode(right.node);
521 path.scope.push({id});
522 const replacement: Node = t.expressionStatement(t.assignmentExpression('=', id, right.node));
523 path.insertBefore(replacement);
524 right.replaceWith(id);
525 }
526 path.insertBefore(guard({
527 check: checks.iterable({input: id}),
528 message: t.binaryExpression(
529 '+',
530 t.stringLiteral(`Expected ${generate(right.node).code} to be iterable, got `),
531 readableName({input: id})
532 )
533 }));
534
535 if (rightAnnotation.type !== 'GenericTypeAnnotation' || rightAnnotation.id.name !== 'Iterable' || !rightAnnotation.typeParameters || !rightAnnotation.typeParameters.params.length) {
536 return;
537 }
538
539 const annotation: TypeAnnotation = rightAnnotation.typeParameters.params[0];
540 if (compareAnnotations(annotation, leftAnnotation) === false) {
541 throw path.buildCodeFrameError(`Invalid iterator type, expected ${humanReadableType(annotation)} got ${humanReadableType(leftAnnotation)}`);
542 }
543 }
544 };
545
546
547 return {
548 visitor: {
549 Program (path: NodePath, {opts}) {
550 if (opts && opts.disable && opts.disable[process.env.NODE_ENV]) {
551 return;
552 }
553 for (let child of path.get('body')) {
554 if (maybeSkipFile(child)) {
555 return;
556 }
557 }
558 path.traverse(visitors);
559 }
560 }
561 }
562
563 /**
564 * Create a function which can verify the return type for a function.
565 */
566 function createFunctionReturnGuard (path: NodePath): void {
567 const {node, scope} = path;
568 let annotation = node.returnType;
569 if (annotation.type === 'TypeAnnotation') {
570 annotation = annotation.typeAnnotation;
571 }
572 if (isGeneratorAnnotation(annotation)) {
573 annotation = annotation.typeParameters && annotation.typeParameters.params.length > 1 ? annotation.typeParameters.params[1] : t.anyTypeAnnotation();
574 }
575 const name = scope.generateUidIdentifierBasedOnNode(node);
576 const id = scope.generateUidIdentifier('id');
577 const check = checkAnnotation(id, annotation, scope);
578 if (check) {
579 node.returnGuard = guardFn({
580 id,
581 name,
582 check,
583 message: returnTypeErrorMessage(path, path.node, id)
584 });
585 node.returnGuard.hasBeenTypeChecked = true;
586 node.returnGuardName = name;
587 node.returnGuardCount = 0;
588 }
589 }
590
591 function createFunctionYieldGuards (path: NodePath) {
592 const {node, scope} = path;
593 let annotation = node.returnType;
594 if (annotation.type === 'NullableTypeAnnotation' || annotation.type === 'TypeAnnotation') {
595 annotation = annotation.typeAnnotation;
596 }
597 if (!annotation.typeParameters || annotation.typeParameters.params.length === 0) {
598 return;
599 }
600 if (annotation.type === 'TypeAnnotation') {
601 annotation = annotation.typeAnnotation;
602 }
603 if (!isGeneratorAnnotation(annotation)) {
604 return;
605 }
606
607 const yieldType = annotation.typeParameters.params[0];
608 const nextType = annotation.typeParameters.params[2];
609
610 if (yieldType) {
611 const name = scope.generateUidIdentifier(`check${node.id ? node.id.name.slice(0, 1).toUpperCase() + node.id.name.slice(1) : ''}Yield`);
612 const id = scope.generateUidIdentifier('id');
613 const check = checkAnnotation(id, yieldType, scope);
614 if (check) {
615 node.yieldGuard = guardFn({
616 id,
617 name,
618 check,
619 message: yieldTypeErrorMessage(node, yieldType, id)
620 });
621 node.yieldGuardName = name;
622 node.yieldGuardCount = 0;
623 }
624 }
625
626
627 if (nextType) {
628 const name = scope.generateUidIdentifier(`check${node.id ? node.id.name.slice(0, 1).toUpperCase() + node.id.name.slice(1) : ''}Next`);
629 const id = scope.generateUidIdentifier('id');
630 const check = checkAnnotation(id, nextType, scope);
631 if (check) {
632 node.nextGuard = guardFn({
633 id,
634 name,
635 check,
636 message: yieldNextTypeErrorMessage(node, nextType, id)
637 });
638 node.nextGuardName = name;
639 node.nextGuardCount = 0;
640 }
641 }
642 }
643
644 function isThisMemberExpression (path: NodePath): boolean {
645 const {node} = path;
646 if (node.type === 'ThisExpression') {
647 return true;
648 }
649 else if (node.type === 'MemberExpression') {
650 return isThisMemberExpression(path.get('object'));
651 }
652 else {
653 return false;
654 }
655 }
656
657 function isGeneratorAnnotation (annotation: ?TypeAnnotation): boolean {
658 if (!annotation) {
659 return false;
660 }
661 if (annotation.type === 'TypeAnnotation' || annotation.type === 'NullableTypeAnnotation') {
662 annotation = annotation.typeAnnotation;
663 }
664 return annotation.type === 'GenericTypeAnnotation' && annotation.id.name === 'Generator';
665 }
666
667 function createChecks (): Object {
668 return {
669 number: expression(`typeof input === 'number'`),
670 numericLiteral: checkNumericLiteral,
671 boolean: expression(`typeof input === 'boolean'`),
672 booleanLiteral: checkBooleanLiteral,
673 class: checkClass,
674 function: expression(`typeof input === 'function'`),
675 string: expression(`typeof input === 'string'`),
676 stringLiteral: checkStringLiteral,
677 symbol: expression(`typeof input === 'symbol'`),
678 undefined: expression(`input === undefined`),
679 null: expression(`input === null`),
680 void: expression(`input == null`),
681 instanceof: expression(`input instanceof type`),
682 type: expression(`type(input)`),
683 mixed: () => null,
684 any: () => null,
685 union: checkUnion,
686 intersection: checkIntersection,
687 array: checkArray,
688 map: checkMap,
689 set: checkSet,
690 generator: checkGenerator,
691 iterable: checkIterable,
692 tuple: checkTuple,
693 object: checkObject,
694 nullable: checkNullable,
695 typeof: checkTypeof,
696 int8: expression(`typeof input === 'number' && !isNaN(input) && input >= -128 && input <= 127 && input === Math.floor(input)`),
697 uint8: expression(`typeof input === 'number' && !isNaN(input) && input >= 0 && input <= 255 && input === Math.floor(input)`),
698 int16: expression(`typeof input === 'number' && !isNaN(input) && input >= -32768 && input <= 32767 && input === Math.floor(input)`),
699 uint16: expression(`typeof input === 'number' && !isNaN(input) && input >= 0 && input <= 65535 && input === Math.floor(input)`),
700 int32: expression(`typeof input === 'number' && !isNaN(input) && input >= -2147483648 && input <= 2147483647 && input === Math.floor(input)`),
701 uint32: expression(`typeof input === 'number' && !isNaN(input) && input >= 0 && input <= 4294967295 && input === Math.floor(input)`),
702 float32: expression(`typeof input === 'number' && !isNaN(input) && input >= -3.40282347e+38 && input <= 3.40282347e+38`),
703 float64: expression(`typeof input === 'number' && !isNaN(input)`)
704
705 };
706 }
707
708 function createStaticChecks (): Object {
709 return {
710 symbol (path: NodePath): ?boolean {
711 return maybeSymbolAnnotation(getAnnotation(path));
712 },
713 instanceof ({path, annotation}): ?boolean {
714 const type = createTypeExpression(annotation.id);
715
716 const {node, scope} = path;
717 if (type.name === 'Object' && node.type === 'ObjectExpression' && !scope.hasBinding('Object')) {
718 return true;
719 }
720 else if (type.name === 'Map' && !scope.hasBinding('Map')) {
721 return null;
722 }
723 else if (type.name === 'Set' && !scope.hasBinding('Set')) {
724 return null;
725 }
726 else if (type.name === 'Class' && !scope.hasBinding('Class')) {
727 return null;
728 }
729 else if (type.name === 'int8' && !scope.hasBinding('int8')) {
730 return null;
731 }
732 else if (type.name === 'uint8' && !scope.hasBinding('uint8')) {
733 return null;
734 }
735 else if (type.name === 'int16' && !scope.hasBinding('int16')) {
736 return null;
737 }
738 else if (type.name === 'uint16' && !scope.hasBinding('uint16')) {
739 return null;
740 }
741 else if (type.name === 'int32' && !scope.hasBinding('int32')) {
742 return null;
743 }
744 else if (type.name === 'uint32' && !scope.hasBinding('uint32')) {
745 return null;
746 }
747 else if (type.name === 'float32' && !scope.hasBinding('float32')) {
748 return null;
749 }
750 else if (type.name === 'float64' && !scope.hasBinding('float64')) {
751 return null;
752 }
753 return maybeInstanceOfAnnotation(getAnnotation(path), type, annotation.typeParameters ? annotation.typeParameters.params : []);
754 },
755 type ({path, type}): ?boolean {
756 return null;
757 },
758 };
759 }
760
761 function compareAnnotations (a: TypeAnnotation, b: TypeAnnotation): ?boolean {
762 if (a.type === 'TypeAnnotation') {
763 a = a.typeAnnotation;
764 }
765 if (b.type === 'TypeAnnotation') {
766 b = b.typeAnnotation;
767 }
768 switch (a.type) {
769 case 'StringTypeAnnotation':
770 return maybeStringAnnotation(b);
771 case 'StringLiteralTypeAnnotation':
772 return compareStringLiteralAnnotations(a, b);
773 case 'NumberTypeAnnotation':
774 return maybeNumberAnnotation(b);
775 case 'NumericLiteralTypeAnnotation':
776 return compareNumericLiteralAnnotations(a, b);
777 case 'BooleanTypeAnnotation':
778 return maybeBooleanAnnotation(b);
779 case 'BooleanLiteralTypeAnnotation':
780 return compareBooleanLiteralAnnotations(a, b);
781 case 'FunctionTypeAnnotation':
782 return maybeFunctionAnnotation(b);
783 case 'AnyTypeAnnotation':
784 return null;
785 case 'MixedTypeAnnotation':
786 return null;
787 case 'ObjectTypeAnnotation':
788 return compareObjectAnnotation(a, b);
789 case 'ArrayTypeAnnotation':
790 return compareArrayAnnotation(a, b);
791 case 'GenericTypeAnnotation':
792 return compareGenericAnnotation(a, b);
793 case 'TupleTypeAnnotation':
794 return compareTupleAnnotation(a, b);
795 case 'UnionTypeAnnotation':
796 return compareUnionAnnotation(a, b);
797 case 'IntersectionTypeAnnotation':
798 return compareIntersectionAnnotation(a, b);
799 case 'NullableTypeAnnotation':
800 return compareNullableAnnotation(a, b);
801 default:
802 return null;
803 }
804 }
805
806 function compareStringLiteralAnnotations (a: StringLiteralTypeAnnotation, b: TypeAnnotation): ?boolean {
807 if (b.type === 'StringLiteralTypeAnnotation') {
808 return a.value === b.value;
809 }
810 else {
811 return maybeStringAnnotation(b) === false ? false : null;
812 }
813 }
814
815 function compareBooleanLiteralAnnotations (a: BooleanLiteralTypeAnnotation, b: TypeAnnotation): ?boolean {
816 if (b.type === 'BooleanLiteralTypeAnnotation') {
817 return a.value === b.value;
818 }
819 else {
820 return maybeBooleanAnnotation(b) === false ? false : null;
821 }
822 }
823
824 function compareNumericLiteralAnnotations (a: NumericLiteralTypeAnnotation, b: TypeAnnotation): ?boolean {
825 if (b.type === 'NumericLiteralTypeAnnotation') {
826 return a.value === b.value;
827 }
828 else {
829 return maybeNumberAnnotation(b) === false ? false : null;
830 }
831 }
832
833 function unionComparer (a: TypeAnnotation, b: TypeAnnotation, comparator: (a:TypeAnnotation, b:TypeAnnotation) => ?boolean): ?boolean {
834 if (!a.types || a.types.length === 0) {
835 return null;
836 }
837 let falseCount = 0;
838 let trueCount = 0;
839 if (!a.types) {
840 return null;
841 }
842 for (let type of a.types) {
843 const result = comparator(type, b);
844 if (result === true) {
845 if (b.type !== 'UnionTypeAnnotation') {
846 return true;
847 }
848 trueCount++;
849 }
850 else if (result === false) {
851 if (b.type === 'UnionTypeAnnotation') {
852 return false;
853 }
854 falseCount++;
855 }
856 }
857 if (falseCount === a.types.length) {
858 return false;
859 }
860 else if (trueCount === a.types.length) {
861 return true;
862 }
863 else {
864 return null;
865 }
866 }
867
868 function intersectionComparer (a: TypeAnnotation, b: TypeAnnotation, comparator: (a:TypeAnnotation, b:TypeAnnotation) => ?boolean): ?boolean {
869 let falseCount = 0;
870 let trueCount = 0;
871 if (!a.types) {
872 return null;
873 }
874 for (let type of a.types) {
875 const result = comparator(type, b);
876 if (result === true) {
877 trueCount++;
878 }
879 else if (result === false) {
880 return false;
881 }
882 }
883 if (trueCount === a.types.length) {
884 return true;
885 }
886 else {
887 return null;
888 }
889 }
890
891 function compareObjectAnnotation (a: Node, b: Node): ?boolean {
892 switch (b.type) {
893 case 'ObjectTypeAnnotation':
894 break;
895 case 'TypeAnnotation':
896 case 'FunctionTypeParam':
897 case 'NullableTypeAnnotation':
898 return compareObjectAnnotation(a, b.typeAnnotation);
899 case 'UnionTypeAnnotation':
900 return unionComparer(a, b, compareObjectAnnotation);
901 case 'IntersectionTypeAnnotation':
902 return intersectionComparer(a, b, compareObjectAnnotation);
903 case 'VoidTypeAnnotation':
904 case 'BooleanTypeAnnotation':
905 case 'BooleanLiteralTypeAnnotation':
906 case 'StringTypeAnnotation':
907 case 'StringLiteralTypeAnnotation':
908 case 'NumberTypeAnnotation':
909 case 'NumericLiteralTypeAnnotation':
910 case 'FunctionTypeAnnotation':
911 return false;
912 default:
913 return null;
914 }
915
916 // We're comparing two object annotations.
917 let allTrue = true;
918 for (let aprop of a.properties) {
919 let found = false;
920 for (let bprop of b.properties) {
921 if (bprop.key.name === aprop.key.name) {
922 const result = compareAnnotations(aprop.value, bprop.value);
923 if (result === false && !(aprop.optional && (bprop.optional || maybeNullableAnnotation(bprop.value) === true))) {
924 return false;
925 }
926 else {
927 found = result;
928 }
929 break;
930 }
931 }
932 if (found === false && !aprop.optional) {
933 return false;
934 }
935 allTrue = allTrue && found === true;
936 }
937 return allTrue ? true : null;
938 }
939
940 function compareArrayAnnotation (a: Node, b: Node): ?boolean {
941 switch (b.type) {
942 case 'TypeAnnotation':
943 case 'FunctionTypeParam':
944 case 'NullableTypeAnnotation':
945 return compareArrayAnnotation(a, b.typeAnnotation);
946 case 'UnionTypeAnnotation':
947 return unionComparer(a, b, compareArrayAnnotation);
948 case 'IntersectionTypeAnnotation':
949 return intersectionComparer(a, b, compareArrayAnnotation);
950 case 'VoidTypeAnnotation':
951 case 'BooleanTypeAnnotation':
952 case 'BooleanLiteralTypeAnnotation':
953 case 'StringTypeAnnotation':
954 case 'StringLiteralTypeAnnotation':
955 case 'NumberTypeAnnotation':
956 case 'NumericLiteralTypeAnnotation':
957 case 'FunctionTypeAnnotation':
958 return false;
959 default:
960 return null;
961 }
962 }
963
964 function compareGenericAnnotation (a: Node, b: Node): ?boolean {
965 switch (b.type) {
966 case 'TypeAnnotation':
967 case 'FunctionTypeParam':
968 case 'NullableTypeAnnotation':
969 return compareGenericAnnotation(a, b.typeAnnotation);
970 case 'GenericTypeAnnotation':
971 if (b.id.name === a.id.name) {
972 return true;
973 }
974 else {
975 return null;
976 }
977 case 'UnionTypeAnnotation':
978 return unionComparer(a, b, compareGenericAnnotation);
979 case 'IntersectionTypeAnnotation':
980 return intersectionComparer(a, b, compareGenericAnnotation);
981 default:
982 return null;
983 }
984 }
985
986 function compareTupleAnnotation (a: Node, b: Node): ?boolean {
987 if (b.type === 'TupleTypeAnnotation') {
988 if (b.types.length === 0) {
989 return null;
990 }
991 else if (b.types.length < a.types.length) {
992 return false;
993 }
994 return a.types.every((type, index) => compareAnnotations(type, b.types[index]));
995 }
996 switch (b.type) {
997 case 'TypeAnnotation':
998 case 'FunctionTypeParam':
999 case 'NullableTypeAnnotation':
1000 return compareTupleAnnotation(a, b.typeAnnotation);
1001 case 'UnionTypeAnnotation':
1002 return unionComparer(a, b, compareTupleAnnotation);
1003 case 'IntersectionTypeAnnotation':
1004 return intersectionComparer(a, b, compareTupleAnnotation);
1005 case 'VoidTypeAnnotation':
1006 case 'BooleanTypeAnnotation':
1007 case 'BooleanLiteralTypeAnnotation':
1008 case 'StringTypeAnnotation':
1009 case 'StringLiteralTypeAnnotation':
1010 case 'NumberTypeAnnotation':
1011 case 'NumericLiteralTypeAnnotation':
1012 case 'FunctionTypeAnnotation':
1013 return false;
1014 default:
1015 return null;
1016 }
1017 }
1018
1019 function compareUnionAnnotation (a: Node, b: Node): ?boolean {
1020 switch (b.type) {
1021 case 'NullableTypeAnnotation':
1022 return compareUnionAnnotation(a, b.typeAnnotation);
1023 case 'AnyTypeAnnotation':
1024 case 'MixedTypeAnnotation':
1025 return null;
1026 default:
1027 return unionComparer(a, b, compareAnnotations);
1028 }
1029 }
1030
1031 function compareNullableAnnotation (a: Node, b: Node): ?boolean {
1032 switch (b.type) {
1033 case 'TypeAnnotation':
1034 case 'FunctionTypeParam':
1035 return compareNullableAnnotation(a, b.typeAnnotation);
1036 case 'NullableTypeAnnotation':
1037 case 'VoidTypeAnnotation':
1038 return null;
1039 }
1040 if (compareAnnotations(a.typeAnnotation, b) === true) {
1041 return true;
1042 }
1043 else {
1044 return null;
1045 }
1046 }
1047
1048 function arrayExpressionToTupleAnnotation (path: NodePath): TypeAnnotation {
1049 const elements = path.get('elements');
1050 return t.tupleTypeAnnotation(elements.map(element => getAnnotation(element)));
1051 }
1052
1053 function checkNullable ({input, type, scope}): ?Node {
1054 const check = checkAnnotation(input, type, scope);
1055 if (!check) {
1056 return;
1057 }
1058 return t.logicalExpression(
1059 "||",
1060 checks.void({input}),
1061 check
1062 );
1063 }
1064
1065 function checkTypeof ({input, annotation, scope}): ?Node {
1066 switch (annotation.type) {
1067 case 'GenericTypeAnnotation':
1068 const {id} = annotation;
1069 const path = Object.assign({}, input, {type: id.type, node: id, scope});
1070 return checkAnnotation(input, getAnnotation(path), scope);
1071 default:
1072 return checkAnnotation(input, annotation, scope);
1073 }
1074 }
1075
1076 function checkStringLiteral ({input, annotation}): ?Node {
1077 return checkEquals({input, expected: t.stringLiteral(annotation.value)});
1078 }
1079
1080 function checkNumericLiteral ({input, annotation}): ?Node {
1081 return checkEquals({input, expected: t.numericLiteral(annotation.value)});
1082 }
1083
1084 function checkBooleanLiteral ({input, annotation}): ?Node {
1085 return checkEquals({input, expected: t.booleanLiteral(annotation.value)});
1086 }
1087
1088 function checkUnion ({input, types, scope}): ?Node {
1089 const checks = types.map(type => checkAnnotation(input, type, scope)).filter(identity);
1090 return checks.reduce((last, check, index) => {
1091 if (last == null) {
1092 return check;
1093 }
1094 return t.logicalExpression(
1095 "||",
1096 last,
1097 check
1098 );
1099 }, null);
1100 }
1101
1102
1103 function checkIntersection ({input, types, scope}): ?Node {
1104 const checks = types.map(type => checkAnnotation(input, type, scope)).filter(identity);
1105 return checks.reduce((last, check, index) => {
1106 if (last == null) {
1107 return check;
1108 }
1109 return t.logicalExpression(
1110 "&&",
1111 last,
1112 check
1113 );
1114 }, null);
1115 }
1116
1117
1118 function checkMap ({input, types, scope}): Node {
1119 const [keyType, valueType] = types;
1120 const key = t.identifier('key');
1121 const value = t.identifier('value');
1122 const keyCheck = keyType ? checkAnnotation(key, keyType, scope) : null;
1123 const valueCheck = valueType ? checkAnnotation(value, valueType, scope) : null;
1124 if (!keyCheck) {
1125 if (!valueCheck) {
1126 return checkIsMap({input});
1127 }
1128 else {
1129 return checkMapValues({input, value, valueCheck});
1130 }
1131 }
1132 else {
1133 if (!valueCheck) {
1134 return checkMapKeys({input, key, keyCheck});
1135 }
1136 else {
1137 return checkMapEntries({input, key, value, keyCheck, valueCheck});
1138 }
1139 }
1140 }
1141
1142 function checkSet ({input, types, scope}): Node {
1143 const [valueType] = types;
1144 const value = t.identifier('value');
1145 const valueCheck = valueType ? checkAnnotation(value, valueType, scope) : null;
1146 if (!valueCheck) {
1147 return checkIsSet({input});
1148 }
1149 else {
1150 return checkSetEntries({input, value, valueCheck});
1151 }
1152 }
1153
1154 function checkGenerator ({input, types, scope}): Node {
1155 return checkIsGenerator({input});
1156 }
1157
1158 function checkIterable ({input, types, scope}): Node {
1159 return checkIsIterable({input});
1160 }
1161
1162 function checkClass ({input, types, scope}): Node {
1163 return checkIsClass({input});
1164 }
1165
1166 function checkArray ({input, types, scope}): Node {
1167 if (!types || types.length === 0) {
1168 return checkIsArray({input});
1169 }
1170 else if (types.length === 1) {
1171 const item = t.identifier('item');
1172 const type = types[0];
1173 const check = checkAnnotation(item, type, scope);
1174 if (!check) {
1175 return checkIsArray({input});
1176 }
1177 return t.logicalExpression(
1178 '&&',
1179 checkIsArray({input}),
1180 t.callExpression(
1181 t.memberExpression(input, t.identifier('every')),
1182 [t.functionExpression(null, [item], t.blockStatement([
1183 t.returnStatement(check)
1184 ]))]
1185 )
1186 );
1187 }
1188 else {
1189 // This is a tuple
1190 const checks = types.map(
1191 (type, index) => checkAnnotation(
1192 t.memberExpression(
1193 input,
1194 t.numericLiteral(index),
1195 true
1196 ),
1197 type,
1198 scope
1199 )
1200 ).filter(identity);
1201
1202 const checkLength = t.binaryExpression(
1203 '>=',
1204 t.memberExpression(
1205 input,
1206 t.identifier('length')
1207 ),
1208 t.numericLiteral(types.length)
1209 );
1210
1211 return checks.reduce((last, check, index) => {
1212 return t.logicalExpression(
1213 "&&",
1214 last,
1215 check
1216 );
1217 }, t.logicalExpression(
1218 '&&',
1219 checkIsArray({input}),
1220 checkLength
1221 ));
1222 }
1223 }
1224
1225 function checkTuple ({input, types, scope}): Node {
1226 if (types.length === 0) {
1227 return checkIsArray({input});
1228 }
1229
1230 // This is a tuple
1231 const checks = types.map(
1232 (type, index) => checkAnnotation(
1233 t.memberExpression(
1234 input,
1235 t.numericLiteral(index),
1236 true
1237 ),
1238 type,
1239 scope
1240 )
1241 ).filter(identity);
1242
1243 const checkLength = t.binaryExpression(
1244 '>=',
1245 t.memberExpression(
1246 input,
1247 t.identifier('length')
1248 ),
1249 t.numericLiteral(types.length)
1250 );
1251
1252 return checks.reduce((last, check, index) => {
1253 return t.logicalExpression(
1254 "&&",
1255 last,
1256 check
1257 );
1258 }, t.logicalExpression(
1259 '&&',
1260 checkIsArray({input}),
1261 checkLength
1262 ));
1263 }
1264
1265 function checkObject ({input, properties, indexers, scope}): Node {
1266 if (input.type === 'ObjectPattern') {
1267 return checkObjectPattern({input, properties, scope});
1268 }
1269 const propNames = [];
1270 const check = properties.reduce((expr, prop, index) => {
1271 const target = prop.key.type === 'Identifier' ? t.memberExpression(input, prop.key) : t.memberExpression(input, prop.key, true);
1272 propNames.push(prop.key.type === 'Identifier' ? t.stringLiteral(prop.key.name) : prop.key);
1273 let check = checkAnnotation(target, prop.value, scope);
1274 if (check) {
1275 if (prop.optional) {
1276 check = t.logicalExpression(
1277 '||',
1278 checks.undefined({input: target}),
1279 check
1280 );
1281 }
1282 return t.logicalExpression(
1283 "&&",
1284 expr,
1285 check
1286 );
1287 }
1288 else {
1289 return expr;
1290 }
1291 }, checkIsObject({input}));
1292
1293 if (indexers.length) {
1294 return indexers.reduceRight((expr, indexer) => {
1295 if (indexer.value.type === 'AnyTypeAnnotation') {
1296 return expr;
1297 }
1298 const value = scope.generateUidIdentifier(indexer.id.name);
1299 let check = checkAnnotation(value, indexer.value, scope);
1300 const fixedKeys = t.arrayExpression(propNames);
1301
1302 if (check) {
1303 if (propNames.length) {
1304 return t.logicalExpression('&&', expr, checkObjectIndexers({input, value, check, fixedKeys}));
1305 }
1306 else {
1307 return t.logicalExpression('&&', expr, checkObjectIndexersNoFixed({input, value, check, fixedKeys}));
1308 }
1309 }
1310 else {
1311 return expr;
1312 }
1313 }, check);
1314 }
1315
1316 return check;
1317 }
1318
1319 function checkObjectPattern ({input, properties, scope}): ?Node {
1320 const propNames = properties.reduce((names, prop) => {
1321 names[prop.key.name] = prop;
1322 return names;
1323 }, {});
1324 const propChecks = {};
1325 for (let item of input.properties) {
1326 let {key, value: id} = item;
1327 let prop = propNames[key.name];
1328 if (!prop) {
1329 continue;
1330 }
1331 const check = checkAnnotation(id, prop.value, scope);
1332 if (check) {
1333 propChecks[key.name] = check;
1334 }
1335 }
1336 return Object.keys(propChecks).reduce((last, name) => {
1337 const check = propChecks[name];
1338 if (last === null) {
1339 return check;
1340 }
1341 else {
1342 return t.logicalExpression('&&', last, check);
1343 }
1344 }, null);
1345 }
1346
1347 function createTypeAliasChecks (path: NodePath): Node {
1348 const {node, scope} = path;
1349 const {id, right: annotation} = node;
1350 const input = t.identifier('input');
1351 const check = checkAnnotation(input, annotation, scope) || t.booleanLiteral(true);
1352 const declaration = declareTypeChecker({id, check});
1353 declaration.isTypeChecker = true;
1354 declaration.savedTypeAnnotation = annotation;
1355 declaration.declarations[0].savedTypeAnnotation = annotation;
1356 return declaration;
1357 }
1358
1359
1360 function createInterfaceChecks (path: NodePath): Node {
1361 const {node, scope} = path;
1362 const {id, body: annotation} = node;
1363 const input = t.identifier('input');
1364 const check = node.extends.reduce(
1365 (check, extender) => {
1366 return t.logicalExpression(
1367 '&&',
1368 check,
1369 checkAnnotation(input, t.genericTypeAnnotation(extender.id), path.scope)
1370 );
1371 return check;
1372 },
1373 checkAnnotation(input, annotation, scope) || t.booleanLiteral(true)
1374 );
1375
1376 const declaration = declareTypeChecker({id, check});
1377 declaration.isTypeChecker = true;
1378 return declaration;
1379 }
1380
1381 function checkAnnotation (input: Node, annotation: TypeAnnotation, scope: Scope): ?Node {
1382 switch (annotation.type) {
1383 case 'TypeAnnotation':
1384 case 'FunctionTypeParam':
1385 return checkAnnotation(input, annotation.typeAnnotation, scope);
1386 case 'TypeofTypeAnnotation':
1387 return checks.typeof({input, annotation: annotation.argument, scope});
1388 case 'GenericTypeAnnotation':
1389 if (annotation.id.name === 'Array') {
1390 return checks.array({input, types: annotation.typeParameters ? annotation.typeParameters.params : [], scope});
1391 }
1392 else if (annotation.id.name === 'Generator' && !scope.hasBinding('Generator')) {
1393 return checks.generator({input, types: annotation.typeParameters ? annotation.typeParameters.params : [], scope});
1394 }
1395 else if (annotation.id.name === 'Iterable' && !scope.hasBinding('Iterable')) {
1396 return checks.iterable({input, types: annotation.typeParameters ? annotation.typeParameters.params : [], scope});
1397 }
1398 else if (annotation.id.name === 'Map' && !scope.hasBinding('Map')) {
1399 return checks.map({input, types: annotation.typeParameters ? annotation.typeParameters.params : [], scope});
1400 }
1401 else if (annotation.id.name === 'Set' && !scope.hasBinding('Set')) {
1402 return checks.set({input, types: annotation.typeParameters ? annotation.typeParameters.params : [], scope});
1403 }
1404 else if (annotation.id.name === 'Function') {
1405 return checks.function({input});
1406 }
1407 else if (annotation.id.name === 'Class' && !scope.hasBinding('Class')) {
1408 return checks.class({input, types: annotation.typeParameters ? annotation.typeParameters.params : [], scope});
1409 }
1410 else if (annotation.id.name === 'int8' && !scope.hasBinding('int8')) {
1411 return checks.int8({input});
1412 }
1413 else if (annotation.id.name === 'uint8' && !scope.hasBinding('uint8')) {
1414 return checks.uint8({input});
1415 }
1416 else if (annotation.id.name === 'int16' && !scope.hasBinding('int16')) {
1417 return checks.int16({input});
1418 }
1419 else if (annotation.id.name === 'uint16' && !scope.hasBinding('uint16')) {
1420 return checks.uint16({input});
1421 }
1422 else if (annotation.id.name === 'int32' && !scope.hasBinding('int32')) {
1423 return checks.int32({input});
1424 }
1425 else if (annotation.id.name === 'uint32' && !scope.hasBinding('uint32')) {
1426 return checks.uint32({input});
1427 }
1428 else if (annotation.id.name === 'float32' && !scope.hasBinding('float32')) {
1429 return checks.float32({input});
1430 }
1431 else if (annotation.id.name === 'float64' && !scope.hasBinding('float64')) {
1432 return checks.float64({input});
1433 }
1434 else if (annotation.id.name === 'Symbol' && !scope.hasBinding('Symbol')) {
1435 return checks.symbol({input});
1436 }
1437 else if (isTypeChecker(annotation.id, scope)) {
1438 return checks.type({input, type: annotation.id});
1439 }
1440 else if (isPolymorphicType(annotation.id, scope)) {
1441 return;
1442 }
1443 else {
1444 return checks.instanceof({input, type: createTypeExpression(annotation.id)});
1445 }
1446 case 'TupleTypeAnnotation':
1447 return checks.tuple({input, types: annotation.types, scope});
1448 case 'NumberTypeAnnotation':
1449 return checks.number({input});
1450 case 'NumericLiteralTypeAnnotation':
1451 return checks.numericLiteral({input, annotation});
1452 case 'BooleanTypeAnnotation':
1453 return checks.boolean({input});
1454 case 'BooleanLiteralTypeAnnotation':
1455 return checks.booleanLiteral({input, annotation});
1456 case 'StringTypeAnnotation':
1457 return checks.string({input});
1458 case 'StringLiteralTypeAnnotation':
1459 return checks.stringLiteral({input, annotation});
1460 case 'UnionTypeAnnotation':
1461 return checks.union({input, types: annotation.types, scope});
1462 case 'IntersectionTypeAnnotation':
1463 return checks.intersection({input, types: annotation.types, scope});
1464 case 'ObjectTypeAnnotation':
1465 return checks.object({input, properties: annotation.properties || [], indexers: annotation.indexers, scope});
1466 case 'ArrayTypeAnnotation':
1467 return checks.array({input, types: [annotation.elementType || t.anyTypeAnnotation()], scope});
1468 case 'FunctionTypeAnnotation':
1469 return checks.function({input, params: annotation.params, returnType: annotation.returnType});
1470 case 'MixedTypeAnnotation':
1471 return checks.mixed({input});
1472 case 'AnyTypeAnnotation':
1473 case 'ExistentialTypeParam':
1474 return checks.any({input});
1475 case 'NullableTypeAnnotation':
1476 return checks.nullable({input, type: annotation.typeAnnotation, scope});
1477 case 'VoidTypeAnnotation':
1478 return checks.void({input});
1479 }
1480 }
1481
1482 function staticCheckAnnotation (path: NodePath, annotation: TypeAnnotation): ?boolean {
1483 const other = getAnnotation(path);
1484 switch (annotation.type) {
1485 case 'TypeAnnotation':
1486 case 'FunctionTypeParam':
1487 return staticCheckAnnotation(path, annotation.typeAnnotation);
1488 case 'GenericTypeAnnotation':
1489 if (isTypeChecker(annotation.id, path.scope)) {
1490 return staticChecks.type({path, type: annotation.id});
1491 }
1492 else if (isPolymorphicType(annotation.id, path.scope)) {
1493 return;
1494 }
1495 else if (annotation.id.name === 'Symbol') {
1496 return staticChecks.symbol(path);
1497 }
1498 else {
1499 return staticChecks.instanceof({path, annotation});
1500 }
1501 }
1502 return compareAnnotations(annotation, other);
1503 }
1504
1505 /**
1506 * Get the type annotation for a given node.
1507 */
1508 function getAnnotation (path: NodePath): TypeAnnotation {
1509 let annotation;
1510 try {
1511 annotation = getAnnotationShallow(path);
1512 }
1513 catch (e) {
1514 if (e instanceof SyntaxError) {
1515 throw e;
1516 }
1517 if (process.env.TYPECHECK_DEBUG) {
1518 console.error(e.stack);
1519 }
1520 }
1521 while (annotation && annotation.type === 'TypeAnnotation') {
1522 annotation = annotation.typeAnnotation;
1523 }
1524 return annotation || t.anyTypeAnnotation();
1525 }
1526
1527 function getAnnotationShallow (path: NodePath): ?TypeAnnotation {
1528 if (!path || !path.node) {
1529 return t.voidTypeAnnotation();
1530 }
1531 const {node, scope} = path;
1532
1533 if (node.type === 'TypeAlias') {
1534 return node.right;
1535 }
1536 else if (node.type === 'ClassProperty' && node.typeAnnotation) {
1537 return getClassPropertyAnnotation(path);
1538 }
1539 else if (node.type === 'ClassMethod' && node.returnType) {
1540 return getClassMethodAnnotation(path);
1541 }
1542 else if (node.type === 'ObjectProperty' && node.typeAnnotation) {
1543 return getObjectPropertyAnnotation(path);
1544 }
1545 else if (node.type === 'SpreadProperty' && node.typeAnnotation) {
1546 return getSpreadPropertyAnnotation(path);
1547 }
1548 else if (node.type === 'ObjectMethod' && node.returnType) {
1549 return getObjectMethodAnnotation(path);
1550 }
1551 else if (!node.typeAnnotation && !node.savedTypeAnnotation && !node.returnType) {
1552 switch (path.type) {
1553 case 'Identifier':
1554 const binding = scope.getBinding(node.name);
1555 if (!binding || !binding.identifier) {
1556 return path.getTypeAnnotation();
1557 }
1558 const id = binding.identifier;
1559 if (binding.path.type === 'ObjectPattern') {
1560 return getObjectPatternAnnotation(binding.path, node.name);
1561 }
1562 if (id.savedTypeAnnotation) {
1563 return id.savedTypeAnnotation;
1564 }
1565 else if (id.returnType) {
1566 return id.returnType;
1567 }
1568 else if (id.typeAnnotation) {
1569 return id.typeAnnotation;
1570 }
1571 else if (isPolymorphicType(id, scope)) {
1572 return t.anyTypeAnnotation();
1573 }
1574 return binding.constant ? binding.path.getTypeAnnotation() : path.getTypeAnnotation();
1575 case 'StringLiteral':
1576 case 'NumericLiteral':
1577 case 'BooleanLiteral':
1578 return createLiteralTypeAnnotation(path);
1579 case 'CallExpression':
1580 const callee = path.get('callee');
1581 if (callee.type === 'Identifier') {
1582 if (callee.name === 'Symbol') {
1583 return t.genericTypeAnnotation('Symbol');
1584 }
1585 const fn = getFunctionForIdentifier(callee);
1586 if (fn) {
1587 return getAnnotation(fn);
1588 }
1589 }
1590 break;
1591 case 'ThisExpression':
1592 return getThisExpressionAnnotation(path);
1593 case 'AssignmentExpression':
1594 return getAssignmentExpressionAnnotation(path);
1595 case 'MemberExpression':
1596 return getMemberExpressionAnnotation(path);
1597 case 'ArrayExpression':
1598 return getArrayExpressionAnnotation(path);
1599 case 'ObjectExpression':
1600 return getObjectExpressionAnnotation(path);
1601 case 'BinaryExpression':
1602 return getBinaryExpressionAnnotation(path);
1603 case 'BinaryExpression':
1604 return getBinaryExpressionAnnotation(path);
1605 case 'LogicalExpression':
1606 return getLogicalExpressionAnnotation(path);
1607 case 'ConditionalExpression':
1608 return getConditionalExpressionAnnotation(path);
1609 case 'ObjectMethod':
1610 return getObjectMethodAnnotation(path);
1611 case 'SpreadProperty':
1612 return getSpreadPropertyAnnotation(path);
1613 case 'ObjectProperty':
1614 return getObjectPropertyAnnotation(path);
1615 case 'ClassDeclaration':
1616 return getClassDeclarationAnnotation(path);
1617 case 'ClassMethod':
1618 return getClassMethodAnnotation(path);
1619 case 'ClassProperty':
1620 return getClassPropertyAnnotation(path);
1621 default:
1622 return path.getTypeAnnotation();
1623
1624 }
1625 }
1626 return node.savedTypeAnnotation || node.returnType || node.typeAnnotation || path.getTypeAnnotation();
1627 }
1628
1629 function createLiteralTypeAnnotation (path: NodePath): ?TypeAnnotation {
1630 let annotation;
1631 if (path.isStringLiteral()) {
1632 annotation = t.stringLiteralTypeAnnotation();
1633 }
1634 else if (path.isNumericLiteral()) {
1635 annotation = t.numericLiteralTypeAnnotation();
1636 }
1637 else if (path.isBooleanLiteral()) {
1638 annotation = t.booleanLiteralTypeAnnotation();
1639 }
1640 else {
1641 return path.getTypeAnnotation();
1642 }
1643 annotation.value = path.node.value;
1644 return annotation;
1645 }
1646
1647 function getObjectPatternAnnotation (path: NodePath, name: string): ?TypeAnnotation {
1648 let annotation = keyByName(getAnnotation(path), name);
1649 let found;
1650 if (!path.node.properties) {
1651 return;
1652 }
1653 for (let prop of path.get('properties')) {
1654 if (prop.node.value && prop.node.value.name === name) {
1655 found = prop.get('key');
1656 break;
1657 }
1658 else if (prop.node.key.type === 'Identifier' && prop.node.key.name === name) {
1659 found = prop.get('key');
1660 break;
1661 }
1662 }
1663 if (!annotation || !found) {
1664 return;
1665 }
1666 if (found.type === 'Identifier') {
1667 annotation.value.authoritative = false;
1668 return annotation.value;
1669 }
1670 }
1671
1672
1673 function keyByName (node: Node, name: string): ?Node {
1674 if (!node.properties) {
1675 return;
1676 }
1677 for (let prop of node.properties) {
1678 if (prop.key && prop.key.name === name) {
1679 return prop;
1680 }
1681 }
1682 }
1683
1684 function valueByName (node: Node, name: string): ?Node {
1685 if (!node.properties) {
1686 return;
1687 }
1688 for (let prop of node.properties) {
1689 if (prop.value && prop.value.name === name) {
1690 return prop;
1691 }
1692 }
1693 }
1694
1695 function getSpreadPropertyAnnotation (path: NodePath): ?TypeAnnotation {
1696 const {node} = path;
1697 let annotation = node.typeAnnotation || node.savedTypeAnnotation;
1698 if (!annotation) {
1699 annotation = getAnnotation(path.get('argument'));
1700 }
1701 return annotation;
1702 }
1703
1704 function getObjectPropertyAnnotation (path: NodePath): ?TypeAnnotation {
1705 const {node} = path;
1706 let annotation = node.typeAnnotation || node.savedTypeAnnotation;
1707 if (!annotation) {
1708 if (node.value) {
1709 annotation = node.value.typeAnnotation || node.value.savedTypeAnnotation || t.anyTypeAnnotation();
1710 }
1711 else {
1712 annotation = t.anyTypeAnnotation();
1713 }
1714 }
1715 return t.objectTypeProperty(
1716 node.key,
1717 annotation
1718 );
1719 }
1720
1721 function getObjectMethodAnnotation (path: NodePath): ?TypeAnnotation {
1722 const {node} = path;
1723 return t.objectTypeProperty(
1724 t.identifier(node.key.name),
1725 t.functionTypeAnnotation(
1726 null,
1727 node.params.map(param => param.savedTypeAnnotation || param.typeAnnotation),
1728 null,
1729 node.savedTypeAnnotation || node.returnType || node.typeAnnotation || t.anyTypeAnnotation()
1730 )
1731 );
1732 }
1733
1734 function getThisExpressionAnnotation (path: NodePath): ?TypeAnnotation {
1735 let parent = path.parentPath;
1736 loop: while (parent) {
1737 switch (parent.type) {
1738 case 'ClassDeclaration':
1739 return getAnnotation(parent);
1740 case 'ClassBody':
1741 return getAnnotation(parent.parentPath);
1742 case 'ClassMethod':
1743 case 'ClassProperty':
1744 return getAnnotation(parent.parentPath.parentPath);
1745 case 'ObjectProperty':
1746 return getAnnotation(parent.parentPath);
1747 case 'ObjectMethod':
1748 return getAnnotation(parent.parentPath);
1749 case 'FunctionExpression':
1750 if (parent.parentPath.type === 'ObjectProperty') {
1751 return getAnnotation(parent.parentPath.parentPath);
1752 }
1753 break loop;
1754 case 'ArrowFunctionExpression':
1755 parent = parent.parentPath;
1756 continue;
1757 }
1758 if (parent.isFunction()) {
1759 break;
1760 }
1761 parent = parent.parentPath;
1762 }
1763 return t.objectTypeAnnotation([]);
1764 }
1765
1766 function getClassDeclarationAnnotation (path: NodePath): ?TypeAnnotation {
1767 const body = path.get('body').get('body').map(getAnnotation).filter(annotation => annotation && annotation.type !== 'AnyTypeAnnotation');
1768 return t.objectTypeAnnotation(body);
1769 }
1770
1771 function getAssignmentExpressionAnnotation (path: NodePath): ?TypeAnnotation {
1772 if (path.node.operator === '=') {
1773 return getAnnotation(path.get('right'));
1774 }
1775 }
1776
1777 function getClassPropertyAnnotation (path: NodePath): ?TypeAnnotation {
1778 const {node} = path;
1779 if (node.computed) {
1780 return;
1781 }
1782 const annotation = node.typeAnnotation || (node.value ? node.value.savedTypeAnnotation || node.value.typeAnnotation : t.anyTypeAnnotation());
1783 return t.objectTypeProperty(
1784 node.key,
1785 annotation || t.anyTypeAnnotation()
1786 );
1787 }
1788
1789 function getClassMethodAnnotation (path: NodePath): ?TypeAnnotation {
1790 const {node} = path;
1791 if (node.computed) {
1792 return;
1793 }
1794 if (node.kind === 'get') {
1795 return t.objectTypeProperty(
1796 node.key,
1797 node.savedTypeAnnotation || node.returnType || node.typeAnnotation || t.anyTypeAnnotation()
1798 );
1799 }
1800 else if (node.kind === 'set') {
1801 return t.objectTypeProperty(
1802 node.key,
1803 node.params.map(param => param.savedTypeAnnotation || param.typeAnnotation).shift() || t.anyTypeAnnotation()
1804 );
1805 }
1806 else {
1807 return t.objectTypeProperty(
1808 node.key,
1809 t.functionTypeAnnotation(
1810 null,
1811 node.params.map(param => param.savedTypeAnnotation || param.typeAnnotation || t.anyTypeAnnotation()),
1812 null,
1813 node.savedTypeAnnotation || node.returnType || node.typeAnnotation || t.anyTypeAnnotation()
1814 )
1815 );
1816 }
1817 }
1818
1819 function getBinaryExpressionAnnotation (path: NodePath): TypeAnnotation {
1820 const {node} = path;
1821 if (isBooleanExpression(node)) {
1822 return t.booleanTypeAnnotation();
1823 }
1824 else {
1825 return t.anyTypeAnnotation();
1826 }
1827 }
1828
1829 function getLogicalExpressionAnnotation (path: NodePath): TypeAnnotation {
1830 const {node} = path;
1831 if (isBooleanExpression(node)) {
1832 return t.booleanTypeAnnotation();
1833 }
1834 else {
1835 let left = path.get('left');
1836 let right = path.get('right');
1837 switch (node.operator) {
1838 case '&&':
1839 case '||':
1840 ([left, right] = [getAnnotation(left), getAnnotation(right)]);
1841 if (t.isUnionTypeAnnotation(left)) {
1842 if (t.isUnionTypeAnnotation(right)) {
1843 return t.unionTypeAnnotation(left.types.concat(right.types));
1844 }
1845 else {
1846 return t.unionTypeAnnotation(left.types.concat(right));
1847 }
1848 }
1849 else {
1850 return t.unionTypeAnnotation([left, right]);
1851 }
1852 }
1853 return t.anyTypeAnnotation();
1854 }
1855 }
1856
1857
1858 function getConditionalExpressionAnnotation (path: NodePath): TypeAnnotation {
1859 const {node} = path;
1860 const consequent = getAnnotation(path.get('consequent'));
1861 const alternate = getAnnotation(path.get('alternate'));
1862 if (t.isUnionTypeAnnotation(consequent)) {
1863 if (t.isUnionTypeAnnotation(alternate)) {
1864 return t.unionTypeAnnotation(consequent.types.concat(alternate.types));
1865 }
1866 else {
1867 return t.unionTypeAnnotation(consequent.types.concat(alternate));
1868 }
1869 }
1870 else {
1871 return t.unionTypeAnnotation([consequent, alternate]);
1872 }
1873 }
1874
1875 function getArrayExpressionAnnotation (path: NodePath): TypeAnnotation {
1876 return t.genericTypeAnnotation(
1877 t.identifier('Array'),
1878 t.typeParameterDeclaration(path.get('elements').map(getAnnotation))
1879 );
1880 }
1881
1882 function getObjectExpressionAnnotation (path: NodePath): TypeAnnotation {
1883 const annotation = t.objectTypeAnnotation(
1884 path.get('properties')
1885 .filter(prop => !prop.node.computed)
1886 .map(getAnnotation)
1887 .reduce((properties, prop) => {
1888 if (t.isObjectTypeProperty(prop)) {
1889 properties.push(prop);
1890 }
1891 else if (t.isObjectTypeAnnotation(prop)) {
1892 properties.push(...prop.properties);
1893 }
1894 return properties;
1895 }, [])
1896 .filter(annotation => !t.isAnyTypeAnnotation(annotation.value))
1897 );
1898 return annotation;
1899 }
1900
1901 function getMemberExpressionAnnotation (path: NodePath): TypeAnnotation {
1902 if (path.node.computed) {
1903 return getComputedMemberExpressionAnnotation(path);
1904 }
1905 const stack = [];
1906 let target = path;
1907 while (target.isMemberExpression()) {
1908 stack.push(target);
1909 if (target.node.computed) {
1910 break;
1911 }
1912 target = target.get('object');
1913 }
1914 const objectAnnotation = stack.reduceRight((last, target) => {
1915 let annotation = last;
1916 if (annotation == null) {
1917 if (stack.length === 1) {
1918 annotation = getAnnotation(target.get('object'));
1919 }
1920 else {
1921 return getAnnotation(target);
1922 }
1923 }
1924
1925 switch (annotation.type) {
1926 case 'AnyTypeAnnotation':
1927 return annotation;
1928 case 'NullableTypeAnnotation':
1929 case 'TypeAnnotation':
1930 annotation = annotation.typeAnnotation;
1931 }
1932
1933 if (annotation.type === 'GenericTypeAnnotation') {
1934 const typeChecker = getTypeChecker(annotation.id, path.scope);
1935 if (typeChecker) {
1936 annotation = getAnnotation(typeChecker);
1937 }
1938 else {
1939 const binding = path.scope.getBinding(annotation.id.name);
1940 if (binding) {
1941 annotation = getAnnotation(binding.path);
1942 }
1943 }
1944 }
1945 switch (annotation.type) {
1946 case 'AnyTypeAnnotation':
1947 return annotation;
1948 case 'ObjectTypeAnnotation':
1949 const id = target.get('property').node;
1950 for (let {key, value} of annotation.properties || []) {
1951 if (key.name === id.name) {
1952 return value.type === 'VoidTypeAnnotation' ? t.anyTypeAnnotation() : value;
1953 }
1954 }
1955 }
1956 return t.anyTypeAnnotation();
1957 }, null);
1958
1959 return objectAnnotation || path.getTypeAnnotation();
1960 }
1961
1962 function getComputedMemberExpressionAnnotation (path: NodePath): TypeAnnotation {
1963 const object = path.get('object');
1964 const property = path.get('property');
1965 let objectAnnotation = getAnnotation(object);
1966 if (objectAnnotation.type === 'TypeAnnotation' || objectAnnotation.type === 'NullableTypeAnnotation') {
1967 objectAnnotation = objectAnnotation.typeAnnotation;
1968 }
1969 let propertyAnnotation = getAnnotation(property);
1970 if (propertyAnnotation.type === 'TypeAnnotation' || propertyAnnotation.type === 'NullableTypeAnnotation') {
1971 propertyAnnotation = propertyAnnotation.typeAnnotation;
1972 }
1973 const {confident, value} = property.evaluate();
1974 if (!confident) {
1975 return path.getTypeAnnotation();
1976 }
1977 switch (objectAnnotation.type) {
1978 case 'TupleTypeAnnotation':
1979 if (objectAnnotation.types.length === 0) {
1980 break;
1981 }
1982 else if (typeof value === 'number') {
1983 if (!objectAnnotation.types[value]) {
1984 throw path.buildCodeFrameError(`Invalid computed member expression for tuple: ` + humanReadableType(objectAnnotation));
1985 }
1986 return objectAnnotation.types[value];
1987 }
1988 else {
1989 throw path.buildCodeFrameError(`Invalid computed member expression for tuple: ` + humanReadableType(objectAnnotation));
1990 }
1991 break;
1992 }
1993 return path.getTypeAnnotation();
1994 }
1995
1996 function getFunctionForIdentifier (path: NodePath): boolean|Node {
1997 if (path.type !== 'Identifier') {
1998 return false;
1999 }
2000 const ref = path.scope.getBinding(path.node.name);
2001 if (!ref) {
2002 return false;
2003 }
2004 return t.isFunction(ref.path.parent) && ref.path.parentPath;
2005 }
2006
2007 /**
2008 * Returns `true` if the annotation is definitely for an array,
2009 * otherwise `false`.
2010 */
2011 function isStrictlyArrayAnnotation (annotation: TypeAnnotation): boolean {
2012 switch (annotation.type) {
2013 case 'TypeAnnotation':
2014 case 'FunctionTypeParam':
2015 return isStrictlyArrayAnnotation(annotation.typeAnnotation);
2016 case 'GenericTypeAnnotation':
2017 return annotation.id.name === 'Array';
2018 case 'UnionTypeAnnotation':
2019 return annotation.types.every(isStrictlyArrayAnnotation);
2020 default:
2021 return false;
2022 }
2023 }
2024
2025 function compareMaybeUnion (annotation: TypeAnnotation, comparator: (node: TypeAnnotation) => ?boolean): ?boolean {
2026 let falseCount = 0;
2027 for (let type of annotation.types) {
2028 const result = comparator(type);
2029 if (result === true) {
2030 return true;
2031 }
2032 else if (result === false) {
2033 falseCount++;
2034 }
2035 }
2036 if (falseCount === annotation.types.length) {
2037 return false;
2038 }
2039 else {
2040 return null;
2041 }
2042 }
2043
2044 /**
2045 * Returns `true` if the annotation is compatible with a number,
2046 * `false` if it definitely isn't, or `null` if we're not sure.
2047 */
2048 function maybeNumberAnnotation (annotation: TypeAnnotation): ?boolean {
2049 switch (annotation.type) {
2050 case 'TypeAnnotation':
2051 case 'FunctionTypeParam':
2052 case 'NullableTypeAnnotation':
2053 return maybeNumberAnnotation(annotation.typeAnnotation);
2054 case 'NumberTypeAnnotation':
2055 case 'NumericLiteralTypeAnnotation':
2056 return true;
2057 case 'GenericTypeAnnotation':
2058 switch (annotation.id.name) {
2059 case 'Array':
2060 case 'Function':
2061 case 'Object':
2062 case 'String':
2063 case 'Boolean':
2064 case 'Date':
2065 case 'RegExp':
2066 return false;
2067 default:
2068 return null;
2069 }
2070 case 'UnionTypeAnnotation':
2071 return compareMaybeUnion(annotation, maybeNumberAnnotation);
2072 case 'AnyTypeAnnotation':
2073 case 'MixedTypeAnnotation':
2074 case 'IntersectionTypeAnnotation':
2075 return null;
2076 default:
2077 return false;
2078 }
2079 }
2080
2081 /**
2082 * Returns `true` if the annotation is compatible with a string,
2083 * `false` if it definitely isn't, or `null` if we're not sure.
2084 */
2085 function maybeStringAnnotation (annotation: TypeAnnotation): ?boolean {
2086 switch (annotation.type) {
2087 case 'TypeAnnotation':
2088 case 'FunctionTypeParam':
2089 case 'NullableTypeAnnotation':
2090 return maybeStringAnnotation(annotation.typeAnnotation);
2091 case 'StringTypeAnnotation':
2092 return true;
2093 case 'StringLiteralTypeAnnotation':
2094 return null;
2095 case 'GenericTypeAnnotation':
2096 switch (annotation.id.name) {
2097 case 'Array':
2098 case 'Function':
2099 case 'Object':
2100 case 'Number':
2101 case 'Boolean':
2102 case 'Date':
2103 case 'RegExp':
2104 return false;
2105 default:
2106 return null;
2107 }
2108 case 'UnionTypeAnnotation':
2109 let falseCount = 0;
2110 for (let type of annotation.types) {
2111 const result = maybeStringAnnotation(type);
2112 if (result === true) {
2113 return true;
2114 }
2115 else if (result === false) {
2116 falseCount++;
2117 }
2118 }
2119 if (falseCount === annotation.types.length) {
2120 return false;
2121 }
2122 else {
2123 return null;
2124 }
2125 case 'AnyTypeAnnotation':
2126 case 'MixedTypeAnnotation':
2127 case 'IntersectionTypeAnnotation':
2128 return null;
2129 default:
2130 return false;
2131 }
2132 }
2133
2134/**
2135 * Returns `true` if the annotation is compatible with a symbol,
2136 * `false` if it definitely isn't, or `null` if we're not sure.
2137 */
2138 function maybeSymbolAnnotation (annotation: TypeAnnotation): ?boolean {
2139 switch (annotation.type) {
2140 case 'TypeAnnotation':
2141 case 'FunctionTypeParam':
2142 case 'NullableTypeAnnotation':
2143 return maybeSymbolAnnotation(annotation.typeAnnotation);
2144 case 'GenericTypeAnnotation':
2145 switch (annotation.id.name) {
2146 case 'Array':
2147 case 'Function':
2148 case 'Object':
2149 case 'Number':
2150 case 'Boolean':
2151 case 'Date':
2152 case 'RegExp':
2153 return false;
2154 case 'Symbol':
2155 return true;
2156 default:
2157 return null;
2158 }
2159 case 'UnionTypeAnnotation':
2160 let falseCount = 0;
2161 for (let type of annotation.types) {
2162 const result = maybeSymbolAnnotation(type);
2163 if (result === true) {
2164 return true;
2165 }
2166 else if (result === false) {
2167 falseCount++;
2168 }
2169 }
2170 if (falseCount === annotation.types.length) {
2171 return false;
2172 }
2173 else {
2174 return null;
2175 }
2176 case 'AnyTypeAnnotation':
2177 case 'MixedTypeAnnotation':
2178 case 'IntersectionTypeAnnotation':
2179 return null;
2180 default:
2181 return false;
2182 }
2183 }
2184
2185
2186 /**
2187 * Returns `true` if the annotation is compatible with a boolean,
2188 * `false` if it definitely isn't, or `null` if we're not sure.
2189 */
2190 function maybeBooleanAnnotation (annotation: TypeAnnotation): ?boolean {
2191 switch (annotation.type) {
2192 case 'TypeAnnotation':
2193 case 'FunctionTypeParam':
2194 case 'NullableTypeAnnotation':
2195 return maybeBooleanAnnotation(annotation.typeAnnotation);
2196 case 'BooleanTypeAnnotation':
2197 case 'BooleanLiteralTypeAnnotation':
2198 return true;
2199 case 'GenericTypeAnnotation':
2200 switch (annotation.id.name) {
2201 case 'Array':
2202 case 'Function':
2203 case 'Object':
2204 case 'String':
2205 case 'Number':
2206 case 'Date':
2207 case 'RegExp':
2208 return false;
2209 default:
2210 return null;
2211 }
2212 case 'UnionTypeAnnotation':
2213 let falseCount = 0;
2214 for (let type of annotation.types) {
2215 const result = maybeBooleanAnnotation(type);
2216 if (result === true) {
2217 return true;
2218 }
2219 else if (result === false) {
2220 falseCount++;
2221 }
2222 }
2223 if (falseCount === annotation.types.length) {
2224 return false;
2225 }
2226 else {
2227 return null;
2228 }
2229 case 'AnyTypeAnnotation':
2230 case 'MixedTypeAnnotation':
2231 case 'IntersectionTypeAnnotation':
2232 return null;
2233 default:
2234 return false;
2235 }
2236 }
2237
2238
2239 /**
2240 * Returns `true` if the annotation is compatible with a function,
2241 * `false` if it definitely isn't, or `null` if we're not sure.
2242 */
2243 function maybeFunctionAnnotation (annotation: TypeAnnotation): ?boolean {
2244 switch (annotation.type) {
2245 case 'TypeAnnotation':
2246 case 'FunctionTypeParam':
2247 case 'NullableTypeAnnotation':
2248 return maybeFunctionAnnotation(annotation.typeAnnotation);
2249 case 'FunctionTypeAnnotation':
2250 return true;
2251 case 'GenericTypeAnnotation':
2252 switch (annotation.id.name) {
2253 case 'Array':
2254 case 'Number':
2255 case 'Object':
2256 case 'String':
2257 case 'Boolean':
2258 case 'Date':
2259 case 'RegExp':
2260 return false;
2261 default:
2262 return null;
2263 }
2264 case 'UnionTypeAnnotation':
2265 let falseCount = 0;
2266 for (let type of annotation.types) {
2267 const result = maybeFunctionAnnotation(type);
2268 if (result === true) {
2269 return true;
2270 }
2271 else if (result === false) {
2272 falseCount++;
2273 }
2274 }
2275 if (falseCount === annotation.types.length) {
2276 return false;
2277 }
2278 else {
2279 return null;
2280 }
2281 case 'AnyTypeAnnotation':
2282 case 'MixedTypeAnnotation':
2283 case 'IntersectionTypeAnnotation':
2284 return null;
2285 default:
2286 return false;
2287 }
2288 }
2289
2290 /**
2291 * Returns `true` if the annotation is compatible with an undefined or null type,
2292 * `false` if it definitely isn't, or `null` if we're not sure.
2293 */
2294 function maybeNullableAnnotation (annotation: TypeAnnotation): ?boolean {
2295 switch (annotation.type) {
2296 case 'NullableTypeAnnotation':
2297 case 'VoidTypeAnnotation':
2298 case 'MixedTypeAnnotation':
2299 return true;
2300 case 'TypeAnnotation':
2301 case 'FunctionTypeParam':
2302 return maybeNullableAnnotation(annotation.typeAnnotation);
2303 case 'GenericTypeAnnotation':
2304 switch (annotation.id.name) {
2305 case 'Array':
2306 case 'Number':
2307 case 'Object':
2308 case 'String':
2309 case 'Boolean':
2310 case 'Date':
2311 case 'RegExp':
2312 return false;
2313 case 'Generator':
2314 if (annotation.typeParameters && annotation.typeParameters.params.length > 1) {
2315 return maybeNullableAnnotation(annotation.typeParameters.params[1]);
2316 }
2317 else {
2318 return null;
2319 }
2320 default:
2321 return null;
2322 }
2323 case 'UnionTypeAnnotation':
2324 let falseCount = 0;
2325 for (let type of annotation.types) {
2326 const result = maybeNullableAnnotation(type);
2327 if (result === true) {
2328 return true;
2329 }
2330 else if (result === false) {
2331 falseCount++;
2332 }
2333 }
2334 if (falseCount === annotation.types.length) {
2335 return false;
2336 }
2337 else {
2338 return null;
2339 }
2340 default:
2341 return false;
2342 }
2343 }
2344
2345 /**
2346 * Returns `true` if the annotation is compatible with an object type,
2347 * `false` if it definitely isn't, or `null` if we're not sure.
2348 */
2349 function maybeInstanceOfAnnotation (annotation: TypeAnnotation, expected: Identifier, typeParameters: TypeAnnotation[]): ?boolean {
2350 switch (annotation.type) {
2351 case 'TypeAnnotation':
2352 case 'FunctionTypeParam':
2353 case 'NullableTypeAnnotation':
2354 return maybeInstanceOfAnnotation(annotation.typeAnnotation, expected, typeParameters);
2355 case 'GenericTypeAnnotation':
2356 if (annotation.id.name === expected.name) {
2357 if (typeParameters.length === 0) {
2358 return true;
2359 }
2360 if (annotation.typeParameters && annotation.typeParameters.params.length) {
2361 let trueCount = 0;
2362 let nullCount = 0;
2363 for (let i = 0; i < typeParameters.length && i < annotation.typeParameters.params.length; i++) {
2364 const result = compareAnnotations(typeParameters[i], annotation.typeParameters.params[i]);
2365 if (result === false) {
2366 return false;
2367 }
2368 else if (result === true) {
2369 trueCount++;
2370 }
2371 else {
2372 nullCount++;
2373 }
2374 }
2375 return trueCount > 0 && nullCount === 0 ? true : null;
2376 }
2377 }
2378 return null;
2379 case 'UnionTypeAnnotation':
2380 let falseCount = 0;
2381 for (let type of annotation.types) {
2382 const result = maybeInstanceOfAnnotation(type, expected, typeParameters);
2383 if (result === true) {
2384 return true;
2385 }
2386 else if (result === false) {
2387 falseCount++;
2388 }
2389 }
2390 if (falseCount === annotation.types.length) {
2391 return false;
2392 }
2393 else {
2394 return null;
2395 }
2396 case 'VoidTypeAnnotation':
2397 case 'BooleanTypeAnnotation':
2398 case 'BooleanLiteralTypeAnnotation':
2399 case 'StringTypeAnnotation':
2400 case 'StringLiteralTypeAnnotation':
2401 case 'NumberTypeAnnotation':
2402 case 'NumericLiteralTypeAnnotation':
2403 case 'FunctionTypeAnnotation':
2404 return false;
2405 default:
2406 return null;
2407 }
2408 }
2409
2410 /**
2411 * Returns `true` if the annotation is compatible with an array,
2412 * `false` if it definitely isn't, or `null` if we're not sure.
2413 */
2414 function maybeArrayAnnotation (annotation: TypeAnnotation): ?boolean {
2415 switch (annotation.type) {
2416 case 'TypeAnnotation':
2417 case 'FunctionTypeParam':
2418 case 'NullableTypeAnnotation':
2419 return maybeArrayAnnotation(annotation.typeAnnotation);
2420 case 'TupleTypeAnnotation':
2421 case 'ArrayTypeAnnotation':
2422 return true;
2423 case 'GenericTypeAnnotation':
2424 return annotation.id.name === 'Array' ? true : null;
2425 case 'UnionTypeAnnotation':
2426 let falseCount = 0;
2427 for (let type of annotation.types) {
2428 const result = maybeArrayAnnotation(type);
2429 if (result === true) {
2430 return true;
2431 }
2432 else if (result === false) {
2433 falseCount++;
2434 }
2435 }
2436 if (falseCount === annotation.types.length) {
2437 return false;
2438 }
2439 else {
2440 return null;
2441 }
2442 case 'AnyTypeAnnotation':
2443 case 'MixedTypeAnnotation':
2444 case 'IntersectionTypeAnnotation':
2445 return null;
2446 default:
2447 return false;
2448 }
2449 }
2450
2451 /**
2452 * Returns `true` if the annotation is compatible with an iterable,
2453 * `false` if it definitely isn't, or `null` if we're not sure.
2454 */
2455 function maybeIterableAnnotation (annotation: TypeAnnotation): ?boolean {
2456 switch (annotation.type) {
2457 case 'TypeAnnotation':
2458 case 'FunctionTypeParam':
2459 case 'NullableTypeAnnotation':
2460 return maybeIterableAnnotation(annotation.typeAnnotation);
2461 case 'TupleTypeAnnotation':
2462 case 'ArrayTypeAnnotation':
2463 return true;
2464 case 'GenericTypeAnnotation':
2465 return annotation.id.name === 'Iterable' ? true : null;
2466 case 'UnionTypeAnnotation':
2467 let falseCount = 0;
2468 for (let type of annotation.types) {
2469 const result = maybeIterableAnnotation(type);
2470 if (result === true) {
2471 return true;
2472 }
2473 else if (result === false) {
2474 falseCount++;
2475 }
2476 }
2477 if (falseCount === annotation.types.length) {
2478 return false;
2479 }
2480 else {
2481 return null;
2482 }
2483 case 'BooleanTypeAnnotation':
2484 case 'BooleanLiteralTypeAnnotation':
2485 case 'NumericLiteralTypeAnnotation':
2486 case 'NumberTypeAnnotation':
2487 case 'VoidTypeAnnotation':
2488 return false;
2489 default:
2490 return null;
2491 }
2492 }
2493
2494 /**
2495 * Returns `true` if the annotation is compatible with a tuple,
2496 * `false` if it definitely isn't, or `null` if we're not sure.
2497 */
2498 function maybeTupleAnnotation (annotation: TypeAnnotation): ?boolean {
2499 switch (annotation.type) {
2500 case 'TypeAnnotation':
2501 case 'FunctionTypeParam':
2502 case 'NullableTypeAnnotation':
2503 return maybeTupleAnnotation(annotation.typeAnnotation);
2504 case 'TupleTypeAnnotation':
2505 return true;
2506 case 'UnionTypeAnnotation':
2507 let falseCount = 0;
2508 for (let type of annotation.types) {
2509 const result = maybeTupleAnnotation(type);
2510 if (result === true) {
2511 return true;
2512 }
2513 else if (result === false) {
2514 falseCount++;
2515 }
2516 }
2517 if (falseCount === annotation.types.length) {
2518 return false;
2519 }
2520 else {
2521 return null;
2522 }
2523 case 'GenericTypeAnnotation':
2524 case 'AnyTypeAnnotation':
2525 case 'ArrayTypeAnnotation':
2526 case 'MixedTypeAnnotation':
2527 case 'IntersectionTypeAnnotation':
2528 return null;
2529 default:
2530 return false;
2531 }
2532 }
2533
2534 function humanReadableType (annotation: TypeAnnotation): string {
2535 switch (annotation.type) {
2536 case 'TypeAnnotation':
2537 case 'FunctionTypeParam':
2538 return humanReadableType(annotation.typeAnnotation);
2539
2540 case 'FunctionTypeAnnotation':
2541 // @fixme babel doesn't seem to like generating FunctionTypeAnnotations yet
2542 return `(${annotation.params.map(humanReadableType).join(', ')}) => ${humanReadableType(annotation.returnType)}`;
2543 default:
2544 return generate(annotation).code;
2545 }
2546 }
2547
2548 function getTypeChecker (id: Identifier|QualifiedTypeIdentifier, scope: Scope): NodePath|false {
2549 const binding = scope.getBinding(id.name);
2550 if (binding === undefined) {
2551 return false;
2552 }
2553 const {path} = binding;
2554 if (path == null) {
2555 return false;
2556 }
2557 else if (path.type === 'TypeAlias') {
2558 return path;
2559 }
2560 else if (path.type === 'VariableDeclaration' && path.node.isTypeChecker) {
2561 return path.get('declarations')[0];
2562 }
2563 else if (path.isImportSpecifier() && path.parent.importKind === 'type') {
2564 return path;
2565 }
2566 return false;
2567 }
2568
2569 function isTypeChecker (id: Identifier|QualifiedTypeIdentifier, scope: Scope): boolean {
2570 const binding = scope.getBinding(id.name);
2571 if (binding === undefined) {
2572 return false;
2573 }
2574 const {path} = binding;
2575 if (path == null) {
2576 return false;
2577 }
2578 else if (path.type === 'TypeAlias' || (path.type === 'VariableDeclaration' && path.node.isTypeChecker)) {
2579 return true;
2580 }
2581 else if (path.isImportSpecifier() && path.parent.importKind === 'type') {
2582 return true;
2583 }
2584 return false;
2585 }
2586
2587 function isPolymorphicType (id: Identifier|QualifiedTypeIdentifier, scope: Scope): boolean {
2588 const binding = scope.getBinding(id.name);
2589 if (binding !== undefined) {
2590 return false;
2591 }
2592 let {path} = scope;
2593 while (path && path.type !== 'Program') {
2594 const {node} = path;
2595 if ((t.isFunction(node) || t.isClass(node)) && node.typeParameters) {
2596 for (let param of node.typeParameters.params) {
2597 param.isPolymorphicType = true;
2598 if (param.name === id.name) {
2599 return true;
2600 }
2601 }
2602 }
2603 path = path.parentPath;
2604 }
2605 return false;
2606 }
2607
2608 function getPolymorphicType (id: Identifier|QualifiedTypeIdentifier, scope: Scope): ?Node {
2609 const binding = scope.getBinding(id.name);
2610 if (binding !== undefined) {
2611 return false;
2612 }
2613 let {path} = scope;
2614 while (path && path.type !== 'Program') {
2615 const {node} = path;
2616 if (t.isFunction(node) && node.typeParameters) {
2617 for (let param of node.typeParameters.params) {
2618 param.isPolymorphicType = true;
2619 if (param.name === id.name) {
2620 return param;
2621 }
2622 }
2623 }
2624 path = path.parent;
2625 }
2626 return null;
2627 }
2628
2629 function collectParamChecks (path: NodePath): Node[] {
2630 return path.get('params').map((param) => {
2631 const {node} = param;
2632 if (node.type === 'AssignmentPattern') {
2633 if (node.left.typeAnnotation) {
2634 return createDefaultParamGuard(param);
2635 }
2636 }
2637 else if (node.type === 'RestElement') {
2638 if (node.typeAnnotation) {
2639 return createRestParamGuard(param);
2640 }
2641 }
2642 else if (node.typeAnnotation) {
2643 return createParamGuard(param);
2644 }
2645 }).filter(identity);
2646 }
2647
2648 function createParamGuard (path: NodePath): ?Node {
2649 const {node, scope} = path;
2650
2651 node.hasBeenTypeChecked = true;
2652 node.savedTypeAnnotation = node.typeAnnotation;
2653 if (node.type === 'ObjectPattern') {
2654 return;
2655 }
2656 let check = checkAnnotation(node, node.typeAnnotation, scope);
2657 if (!check) {
2658 return;
2659 }
2660 if (node.optional) {
2661 check = t.logicalExpression(
2662 '||',
2663 checks.undefined({input: node}),
2664 check
2665 );
2666 }
2667 const message = paramTypeErrorMessage(node, scope);
2668 return guard({
2669 check,
2670 message
2671 });
2672 }
2673
2674 function createDefaultParamGuard (path: NodePath): ?Node {
2675 const {node, scope} = path;
2676 const {left: id, right: value} = node;
2677 const ok = staticCheckAnnotation(path.get('right'), id.typeAnnotation);
2678 if (ok === false) {
2679 throw path.buildCodeFrameError(`Invalid default value for argument "${id.name}", expected ${humanReadableType(id.typeAnnotation)}.`);
2680 }
2681 return createParamGuard(path.get('left'));
2682 }
2683
2684 function createRestParamGuard (path: NodePath): ?Node {
2685 const {node, scope} = path;
2686 const {argument: id} = node;
2687 id.hasBeenTypeChecked = true;
2688 node.savedTypeAnnotation = node.typeAnnotation;
2689 if (!isStrictlyArrayAnnotation(node.typeAnnotation)) {
2690 throw path.buildCodeFrameError(`Invalid type annotation for rest argument "${id.name}", expected an Array, got: ${humanReadableType(node.typeAnnotation)}.`);
2691 }
2692 let check = checkAnnotation(id, node.typeAnnotation, scope);
2693 if (!check) {
2694 return;
2695 }
2696 if (node.optional) {
2697 check = t.logicalExpression(
2698 '||',
2699 checks.undefined({input: id}),
2700 check
2701 );
2702 }
2703 const message = paramTypeErrorMessage(id, scope, node.typeAnnotation);
2704 return guard({
2705 check,
2706 message
2707 });
2708 }
2709
2710 function returnTypeErrorMessage (path: NodePath, fn: Node, id: ?Identifier|Literal): Node {
2711 const {node, scope} = path;
2712 const name = fn.id ? fn.id.name : '';
2713 let annotation = fn.returnType;
2714 if (annotation.type === 'TypeAnnotation') {
2715 annotation = annotation.typeAnnotation;
2716 }
2717 if (fn.generator && isGeneratorAnnotation(annotation) && annotation.typeParameters && annotation.typeParameters.params.length > 1) {
2718 annotation = annotation.typeParameters.params[1];
2719 }
2720 const message = `Function ${name ? `"${name}" ` : ''}return value violates contract, expected ${humanReadableType(annotation)} got `;
2721
2722 return t.binaryExpression(
2723 '+',
2724 t.stringLiteral(message),
2725 id ? readableName({input: id}) : node.argument ? readableName({input: node.argument}) : t.stringLiteral('undefined')
2726 );
2727 }
2728
2729 function yieldTypeErrorMessage (fn: Node, annotation: TypeAnnotation, id: Identifier|Literal): Node {
2730 const name = fn.id ? fn.id.name : '';
2731 const message = `Function ${name ? `"${name}" ` : ''} yielded an invalid value, expected ${humanReadableType(annotation)} got `;
2732
2733 return t.binaryExpression(
2734 '+',
2735 t.stringLiteral(message),
2736 readableName({input: id})
2737 );
2738 }
2739 function yieldNextTypeErrorMessage (fn: Node, annotation: TypeAnnotation, id: Identifier|Literal): Node {
2740 const name = fn.id ? fn.id.name : '';
2741 const message = `Generator ${name ? `"${name}" ` : ''}received an invalid next value, expected ${humanReadableType(annotation)} got `;
2742
2743 return t.binaryExpression(
2744 '+',
2745 t.stringLiteral(message),
2746 readableName({input: id})
2747 );
2748 }
2749
2750 function paramTypeErrorMessage (node: Node, scope: Scope, typeAnnotation: TypeAnnotation = node.typeAnnotation): Node {
2751 const name = node.name;
2752 const message = `Value of ${node.optional ? 'optional ' : ''}argument "${name}" violates contract, expected ${humanReadableType(typeAnnotation)} got `;
2753
2754 return t.binaryExpression(
2755 '+',
2756 t.stringLiteral(message),
2757 readableName({input: node})
2758 );
2759 }
2760
2761 function varTypeErrorMessage (node: Node, scope: Scope, annotation?: TypeAnnotation = node.typeAnnotation): Node {
2762 if (node.type === 'Identifier') {
2763 const name = node.name;
2764 const message = `Value of variable "${name}" violates contract, expected ${humanReadableType(annotation)} got `;
2765 return t.binaryExpression(
2766 '+',
2767 t.stringLiteral(message),
2768 readableName({input: node})
2769 );
2770 }
2771 else {
2772 const message = `Value of "${generate(node).code}" violates contract, expected ${humanReadableType(annotation)} got `;
2773 return t.binaryExpression(
2774 '+',
2775 t.stringLiteral(message),
2776 readableName({input: node})
2777 );
2778 }
2779 }
2780
2781 /**
2782 * Determine whether the given node can produce purely boolean results.
2783 */
2784 function isBooleanExpression (node: Node) {
2785 if (node.type === 'BinaryExpression' && BOOLEAN_BINARY_OPERATORS.indexOf(node.operator) > -1) {
2786 return true;
2787 }
2788 else if (node.type === 'LogicalExpression') {
2789 return isBooleanExpression(node.left) && isBooleanExpression(node.right);
2790 }
2791 else {
2792 return false;
2793 }
2794 }
2795
2796 /**
2797 * Convert type specifier to expression.
2798 */
2799 function createTypeExpression (node: Node) : Object {
2800 if (node.type == 'Identifier') {
2801 return node;
2802 }
2803 else if (node.type == 'QualifiedTypeIdentifier') {
2804 return t.memberExpression(
2805 createTypeExpression(node.qualification),
2806 createTypeExpression(node.id)
2807 );
2808 }
2809
2810 throw this.errorWithNode(`Unsupported type: ${node.type}`);
2811 }
2812
2813 /**
2814 * Get name of a type as a string.
2815 */
2816 function getTypeName (node: Node): string {
2817 if(node.type == 'Identifier') {
2818 return node.name
2819 }
2820 else if(node.type == 'QualifiedTypeIdentifier') {
2821 return getTypeName(node.qualification) + '.' + getTypeName(node.id);
2822 }
2823
2824 throw this.errorWithNode(`Unsupported type: ${node.type}`);
2825 }
2826
2827
2828 /**
2829 * Union two arrays.
2830 */
2831 function union (arr1: Array, arr2: Array): Array {
2832 for (let i = 0; i < arr2.length; i++) {
2833 let item = arr2[i];
2834 if (arr1.indexOf(item) === -1) {
2835 arr1.push(item);
2836 }
2837 }
2838 return arr1;
2839 }
2840
2841
2842 /**
2843 * Determine whether the given annotation allows any value.
2844 */
2845 function allowsAny (annotation: TypeAnnotation): boolean {
2846 if (annotation.type === 'TypeAnnotation' || annotation.type === 'NullableTypeAnnotation') {
2847 return allowsAny(annotation.typeAnnotation);
2848 }
2849 else if (annotation.type === 'AnyTypeAnnotation' || annotation.type === 'MixedTypeAnnotation') {
2850 return true;
2851 }
2852 else if (annotation.type === 'UnionTypeAnnotation') {
2853 return annotation.types.some(allowsAny);
2854 }
2855 else {
2856 return false;
2857 }
2858 }
2859
2860 /**
2861 * Determine whether a given node is nully (null or undefined).
2862 */
2863 function isNodeNully (node: ?Node): boolean {
2864 if (node == null) {
2865 return true;
2866 }
2867 else if (node.type === 'Identifier' && node.name === 'undefined') {
2868 return true;
2869 }
2870 else if (node.type === 'Literal' && node.value === null) {
2871 return true;
2872 }
2873 else if (node.type === 'UnaryExpression' && node.operator === 'void') {
2874 return true;
2875 }
2876 else {
2877 return false;
2878 }
2879 }
2880
2881 /**
2882 * Determine whether the file should be skipped, based on the comments attached to the given node.
2883 */
2884 function maybeSkipFile (path: NodePath): boolean {
2885 if (path.node.leadingComments && path.node.leadingComments.length) {
2886 return path.node.leadingComments.some(comment => PRAGMA_IGNORE_FILE.test(comment.value));
2887 }
2888 return false;
2889 }
2890
2891 /**
2892 * Maybe skip the given path if it has a relevant pragma.
2893 */
2894 function maybeSkip (path: NodePath): boolean {
2895 const {node} = path;
2896 if (node.hasBeenTypeChecked) {
2897 return true;
2898 }
2899 if (node.leadingComments && node.leadingComments.length) {
2900 const comment = node.leadingComments[node.leadingComments.length - 1];
2901 if (PRAGMA_IGNORE_STATEMENT.test(comment.value)) {
2902 path.skip();
2903 return true;
2904 }
2905 }
2906 return false;
2907 }
2908
2909 /**
2910 * A function that returns its first argument, useful when filtering.
2911 */
2912 function identity <T> (input: T): T {
2913 return input;
2914 }
2915
2916 function getExpression (node: Node): Node {
2917 return t.isExpressionStatement(node) ? node.expression : node;
2918 }
2919
2920 function expression (input: string): Function {
2921 const fn: Function = template(input);
2922 return function (...args) {
2923 const node: Node = fn(...args);
2924 return getExpression(node);
2925 };
2926 }
2927}