UNPKG

js-slang

Version:

Javascript-based implementations of Source, written in Typescript

208 lines 9.73 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.SourceTypedParser = void 0; const parser_1 = require("@babel/parser"); const constants_1 = require("../../../constants"); const typeErrorChecker_1 = require("../../../typeChecker/typeErrorChecker"); const errors_1 = require("../../errors"); const utils_1 = require("../../utils"); const __1 = require(".."); const typeParser_1 = require("./typeParser"); const utils_2 = require("./utils"); class SourceTypedParser extends __1.SourceParser { parse(programStr, context, options, throwOnError) { // Parse with acorn type parser first to catch errors such as // import/export not at top level, trailing commas, missing semicolons try { typeParser_1.default.parse(programStr, (0, utils_1.createAcornParserOptions)(constants_1.DEFAULT_ECMA_VERSION, context.errors, options)); } catch (error) { if (error instanceof SyntaxError) { error = new errors_1.FatalSyntaxError((0, utils_1.positionToSourceLocation)(error.loc, options?.sourceFile), error.toString()); } if (throwOnError) throw error; context.errors.push(error); return null; } // Parse again with babel parser to capture all type syntax // and catch remaining syntax errors not caught by acorn type parser const ast = (0, parser_1.parse)(programStr, { ...utils_1.defaultBabelOptions, sourceFilename: options?.sourceFile, errorRecovery: throwOnError ?? true }); if (ast.errors.length) { ast.errors .filter(error => error instanceof SyntaxError) .forEach(error => { context.errors.push(new errors_1.FatalSyntaxError((0, utils_1.positionToSourceLocation)(error.loc, options?.sourceFile), error.toString())); }); return null; } const typedProgram = ast.program; if (context.prelude !== programStr) { // Check for any declaration only if the program is not the prelude checkForAnyDeclaration(typedProgram, context); } const typedCheckedProgram = (0, typeErrorChecker_1.checkForTypeErrors)(typedProgram, context); (0, utils_2.transformBabelASTToESTreeCompliantAST)(typedCheckedProgram); return typedCheckedProgram; } toString() { return 'SourceTypedParser'; } } exports.SourceTypedParser = SourceTypedParser; function checkForAnyDeclaration(program, context) { function parseConfigOption(option) { return option === 'true' || option === undefined; } const config = { allowAnyInVariables: parseConfigOption(context.languageOptions['typedAllowAnyInVariables']), allowAnyInParameters: parseConfigOption(context.languageOptions['typedAllowAnyInParameters']), allowAnyInReturnType: parseConfigOption(context.languageOptions['typedAllowAnyInReturnType']), allowAnyInTypeAnnotationParameters: parseConfigOption(context.languageOptions['typedAllowAnyInTypeAnnotationParameters']), allowAnyInTypeAnnotationReturnType: parseConfigOption(context.languageOptions['typedAllowAnyInTypeAnnotationReturnType']) }; function pushAnyUsageError(message, node) { if (node.loc) { context.errors.push(new errors_1.FatalSyntaxError(node.loc, message)); } } function isAnyType(node) { return node?.typeAnnotation?.type === 'TSAnyKeyword' || node?.typeAnnotation === undefined; } function checkNode(node) { switch (node.type) { case 'VariableDeclaration': { node.declarations.forEach(decl => { const tsType = decl.id?.typeAnnotation; if (!config.allowAnyInVariables && isAnyType(tsType)) { pushAnyUsageError('Usage of "any" in variable declaration is not allowed.', node); } if (decl.init) { // check for lambdas checkNode(decl.init); } }); break; } case 'FunctionDeclaration': { if (!config.allowAnyInParameters || !config.allowAnyInReturnType) { const func = node; // Check parameters func.params?.forEach((param) => { if (!config.allowAnyInParameters && isAnyType(param.typeAnnotation)) { pushAnyUsageError('Usage of "any" in function parameter is not allowed.', param); } }); // Check return type if (!config.allowAnyInReturnType && isAnyType(func.returnType)) { pushAnyUsageError('Usage of "any" in function return type is not allowed.', node); } checkNode(node.body); } break; } case 'ArrowFunctionExpression': { if (!config.allowAnyInParameters || !config.allowAnyInReturnType) { const arrow = node; // Check parameters arrow.params?.forEach((param) => { if (!config.allowAnyInParameters && isAnyType(param.typeAnnotation)) { pushAnyUsageError('Usage of "any" in arrow function parameter is not allowed.', param); } }); // Recursively check return type if present if (!config.allowAnyInReturnType && isAnyType(arrow.returnType)) { pushAnyUsageError('Usage of "any" in arrow function return type is not allowed.', arrow); } if (!config.allowAnyInReturnType && arrow.params?.some((param) => isAnyType(param.typeAnnotation))) { pushAnyUsageError('Usage of "any" in arrow function return type is not allowed.', arrow); } checkNode(node.body); } break; } case 'ReturnStatement': { if (node.argument) { checkNode(node.argument); } break; } case 'BlockStatement': node.body.forEach(checkNode); break; default: break; } } function checkTSNode(node) { if (!node) { // Happens when there is no type annotation // This should have been caught by checkNode function return; } switch (node.type) { case 'VariableDeclaration': { node.declarations.forEach(decl => { const tsType = decl.id?.typeAnnotation; checkTSNode(tsType); }); break; } case 'TSTypeAnnotation': { const annotation = node; // If it's a function type annotation, check params and return if (annotation.typeAnnotation?.type === 'TSFunctionType') { annotation.typeAnnotation.parameters?.forEach(param => { // Recursively check nested TSTypeAnnotations in parameters if (!config.allowAnyInTypeAnnotationParameters && isAnyType(param.typeAnnotation)) { pushAnyUsageError('Usage of "any" in type annotation\'s function parameter is not allowed.', param); } if (param.typeAnnotation) { checkTSNode(param.typeAnnotation); } }); const returnAnno = annotation.typeAnnotation.typeAnnotation; if (!config.allowAnyInTypeAnnotationReturnType && isAnyType(returnAnno)) { pushAnyUsageError('Usage of "any" in type annotation\'s function return type is not allowed.', annotation); } // Recursively check nested TSTypeAnnotations in return type checkTSNode(returnAnno); } break; } case 'FunctionDeclaration': { // Here we also check param type annotations + return type via config if (!config.allowAnyInTypeAnnotationParameters || !config.allowAnyInTypeAnnotationReturnType) { const func = node; // Check parameters if (!config.allowAnyInTypeAnnotationParameters) { func.params?.forEach((param) => { checkTSNode(param.typeAnnotation); }); } // Recursively check the function return type annotation checkTSNode(func.returnType); } break; } case 'BlockStatement': node.body.forEach(checkTSNode); break; default: break; } } if (!config.allowAnyInVariables || !config.allowAnyInParameters || !config.allowAnyInReturnType) { program.body.forEach(checkNode); } if (!config.allowAnyInTypeAnnotationParameters || !config.allowAnyInTypeAnnotationReturnType) { program.body.forEach(checkTSNode); } } //# sourceMappingURL=index.js.map