• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1/*
2 * Copyright (c) 2021-2022 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 *     http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16import * as ts from "typescript";
17import { hasDefaultKeywordModifier, hasExportKeywordModifier } from "./base/util";
18import { CmdOptions } from "./cmdOptions";
19import { DiagnosticCode, DiagnosticError } from "./diagnostic";
20import { findInnerExprOfParenthesis } from "./expression/parenthesizedExpression";
21import * as jshelpers from "./jshelpers";
22import { ModuleScope, Scope } from "./scope";
23import { checkStrictModeStatementList } from "./strictMode";
24import {
25    isAssignmentOperator,
26    isEvalOrArgumentsIdentifier,
27    isInBlockScope,
28    isIncludeOctalEscapeSequence,
29    isLeftHandSideExpression,
30    isOctalNumber,
31    isOriginalKeyword,
32    stringLiteralIsInRegExp
33} from "./syntaxCheckHelper";
34
35function checkDeleteStatement(node: ts.DeleteExpression) {
36    let unaryExpr = node.expression;
37    if (ts.isIdentifier(unaryExpr)) {
38        throw new DiagnosticError(unaryExpr, DiagnosticCode.A_delete_cannot_be_called_on_an_identifier_in_strict_mode);
39    }
40}
41
42function checkNumericLiteral(node: ts.NumericLiteral) {
43    let num = jshelpers.getTextOfNode(node);
44    if (!isOctalNumber(num)) {
45        return;
46    }
47
48    throw new DiagnosticError(node, DiagnosticCode.Octal_literals_are_not_allowed_in_strict_mode);
49}
50
51function checkString(node: ts.Node, text: string) {
52
53    if (isIncludeOctalEscapeSequence(text)) {
54        throw new DiagnosticError(node, DiagnosticCode.Octal_escape_sequences_are_not_allowed_in_strict_mode);
55    }
56
57    if (isIncludeOctalEscapeSequence(text)) {
58        throw new DiagnosticError(node, DiagnosticCode._8_and_9_are_not_allowed_in_strict_mode);
59    }
60}
61
62function checkStringLiteral(node: ts.StringLiteral) {
63    // Octal escape has been deprecated in ES5, but it can be used in regular expressions
64    if (stringLiteralIsInRegExp(node)) {
65        return;
66    }
67
68    let text = jshelpers.getTextOfNode(node);
69    checkString(node, text);
70}
71
72function checkEvalOrArgumentsOrOriginalKeyword(contextNode: ts.Node, name: ts.Node | undefined) {
73    if (!name || !ts.isIdentifier(name)) {
74        return;
75    }
76
77    let identifier = <ts.Identifier>name;
78    if (!isEvalOrArgumentsIdentifier(identifier) && !isOriginalKeyword(identifier)) {
79        return;
80    }
81
82    let file = jshelpers.getSourceFileOfNode(name);
83    let args = [ts.idText(identifier)];
84    throw new DiagnosticError(name, getStrictModeEvalOrArgumentsDiagnosticCode(contextNode), file, args);
85}
86
87
88function getStrictModeEvalOrArgumentsDiagnosticCode(node: ts.Node) {
89    if (jshelpers.getContainingClass(node)) {
90        return DiagnosticCode.Invalid_use_of_0_Class_definitions_are_automatically_in_strict_mode;
91    }
92
93    return DiagnosticCode.Invalid_use_of_0_in_strict_mode;
94}
95
96function getStrictModeIdentifierDiagnosticCode(node: ts.Node) {
97    if (jshelpers.getContainingClass(node)) {
98        return DiagnosticCode.Identifier_expected_0_is_a_reserved_word_in_strict_mode_Class_definitions_are_automatically_in_strict_mode;
99    }
100
101    return DiagnosticCode.Identifier_expected_0_is_a_reserved_word_in_strict_mode;
102}
103
104function checkBinaryExpression(node: ts.BinaryExpression) {
105    if (!isLeftHandSideExpression(node.left) || !isAssignmentOperator(node.operatorToken.kind)) {
106        return;
107    }
108
109    let contextNode = <ts.Node>node;
110    let name = node.left;
111    switch (node.left.kind) {
112        case ts.SyntaxKind.ParenthesizedExpression: {
113            let expr = findInnerExprOfParenthesis(<ts.ParenthesizedExpression>(node.left));
114            contextNode = <ts.Node>expr;
115            name = expr;
116            break;
117        }
118        default:
119            break;
120    }
121
122    checkEvalOrArgumentsOrOriginalKeyword(contextNode, <ts.Identifier>name);
123}
124
125function checkContextualIdentifier(node: ts.Identifier) {
126    let file = jshelpers.getSourceFileOfNode(node);
127    if (jshelpers.getTextOfIdentifierOrLiteral(node) == 'await' && CmdOptions.isModules()) {
128        throw new DiagnosticError(node, DiagnosticCode.Identifier_expected_0_is_a_reserved_word_at_the_top_level_of_a_module, file, ['await']);
129    }
130
131    if (jshelpers.isIdentifierName(node)) {
132        return;
133    }
134
135    if (isOriginalKeyword(node)) {
136        throw new DiagnosticError(node, getStrictModeIdentifierDiagnosticCode(node), file, jshelpers.declarationNameToString(node));
137    }
138
139}
140
141function checkParameters(decl: ts.FunctionLikeDeclaration | ts.FunctionExpression) {
142    let parameters: ts.NodeArray<ts.ParameterDeclaration> = decl.parameters;
143    let obj = new Map();
144    for (let i = 0; i < parameters.length; i++) {
145        let param = parameters[i];
146        checkEvalOrArgumentsOrOriginalKeyword(param, param.name);
147        let name = jshelpers.getTextOfIdentifierOrLiteral(<ts.Identifier>param.name);
148        if (obj.has(name)) {
149            let args: (string | number)[] = [jshelpers.declarationNameToString(param.name)];
150            throw new DiagnosticError(param.name, DiagnosticCode.Duplicate_identifier_0, undefined, args);
151        }
152
153        if (name) {
154            obj.set(name, true);
155        }
156
157        if (param.initializer || param.dotDotDotToken) {
158            if (checkStrictModeStatementList(decl)) {
159                throw new DiagnosticError(param, DiagnosticCode.use_strict_directive_cannot_be_used_with_non_simple_parameter_list);
160            }
161        }
162    }
163}
164
165function checkWithStatement(node: ts.WithStatement) {
166    let file = jshelpers.getSourceFileOfNode(node);
167    throw new DiagnosticError(node, DiagnosticCode.A_with_statements_are_not_allowed_in_strict_mode, file);
168}
169
170function checkNoSubstitutionTemplateLiteral(expr: ts.NoSubstitutionTemplateLiteral) {
171    let text = jshelpers.getTextOfNode(expr);
172    checkString(expr, text.substring(1, text.length - 1));
173}
174
175function checkFunctionDeclaration(node: ts.FunctionDeclaration) {
176    checkEvalOrArgumentsOrOriginalKeyword(node, node.name);
177    checkParameters(node);
178    if (!isInBlockScope(node.parent!)) {
179        throw new DiagnosticError(node, DiagnosticCode.In_strict_mode_code_functions_can_only_be_declared_at_top_level_or_inside_a_block);
180    }
181}
182
183function checkClassDeclaration(node: ts.ClassDeclaration) {
184    if (!hasExportKeywordModifier(node) && !node.name) {
185        if (!node.name && !hasDefaultKeywordModifier(node)) {
186            throw new DiagnosticError(node, DiagnosticCode.Identifier_expected);
187        }
188    }
189}
190
191function checkImportDeclaration(node: ts.ImportDeclaration, scope: Scope) {
192    if (!(scope instanceof ModuleScope)) {
193        throw new DiagnosticError(node, DiagnosticCode.An_import_declaration_can_only_be_used_in_a_namespace_or_module);
194    }
195
196    if (node.modifiers) {
197        throw new DiagnosticError(node, DiagnosticCode.An_import_declaration_cannot_have_modifiers);
198    }
199
200    if (node.importClause && node.importClause.namedBindings) {
201        let namedBindings = node.importClause.namedBindings;
202        if (ts.isNamedImports(namedBindings)) {
203            namedBindings.elements.forEach((element: any) => {
204                if (jshelpers.getTextOfIdentifierOrLiteral(element.name) == 'arguments'
205                    || jshelpers.getTextOfIdentifierOrLiteral(element.name) == 'eval') {
206                    throw new DiagnosticError(node, DiagnosticCode.Unexpected_eval_or_arguments_in_strict_mode);
207                }
208            });
209        }
210    }
211}
212
213function checkExportAssignment(node: ts.ExportAssignment, scope: Scope) {
214    if (!(scope instanceof ModuleScope)) {
215        throw new DiagnosticError(node, DiagnosticCode.An_export_assignment_must_be_at_the_top_level_of_a_file_or_module_declaration);
216    }
217
218    if (node.modifiers) {
219        throw new DiagnosticError(node, DiagnosticCode.An_export_assignment_cannot_have_modifiers);
220    }
221}
222
223function checkExportDeclaration(node: ts.ExportDeclaration, scope: Scope) {
224    if (!(scope instanceof ModuleScope)) {
225        throw new DiagnosticError(node, DiagnosticCode.An_export_declaration_can_only_be_used_in_a_module);
226    }
227
228    if (node.modifiers) {
229        throw new DiagnosticError(node, DiagnosticCode.An_export_declaration_cannot_have_modifiers);
230    }
231}
232
233export function checkSyntaxErrorForStrictMode(node: ts.Node, scope: Scope) {
234    switch (node.kind) {
235        case ts.SyntaxKind.NumericLiteral:
236            checkNumericLiteral(<ts.NumericLiteral>node);
237            break;
238        case ts.SyntaxKind.StringLiteral:
239            checkStringLiteral(<ts.StringLiteral>node);
240            break;
241        case ts.SyntaxKind.FunctionDeclaration:
242            checkFunctionDeclaration(<ts.FunctionDeclaration>node);
243            break;
244        case ts.SyntaxKind.FunctionExpression:
245            let funcNode = <ts.FunctionExpression>node;
246            checkEvalOrArgumentsOrOriginalKeyword(funcNode, funcNode.name);
247            checkParameters(funcNode);
248            break;
249        case ts.SyntaxKind.SetAccessor:
250        case ts.SyntaxKind.ArrowFunction:
251            checkParameters(<ts.FunctionLikeDeclaration | ts.FunctionExpression>node);
252            break;
253        case ts.SyntaxKind.ClassDeclaration:
254            checkClassDeclaration(<ts.ClassDeclaration>node);
255            break;
256        case ts.SyntaxKind.VariableDeclaration:
257            let varNode = <ts.VariableDeclaration>node;
258            checkEvalOrArgumentsOrOriginalKeyword(varNode, varNode.name);
259            break;
260        case ts.SyntaxKind.BindingElement:
261            let bindNode = <ts.BindingElement>node;
262            checkEvalOrArgumentsOrOriginalKeyword(node, bindNode.name);
263            break;
264        case ts.SyntaxKind.BinaryExpression:
265            checkBinaryExpression(<ts.BinaryExpression>node);
266            break;
267        case ts.SyntaxKind.PrefixUnaryExpression:
268        case ts.SyntaxKind.PostfixUnaryExpression:
269            let unaryNode = <ts.PrefixUnaryExpression | ts.PostfixUnaryExpression>node;
270            checkEvalOrArgumentsOrOriginalKeyword(node, unaryNode.operand);
271            break;
272        case ts.SyntaxKind.DeleteExpression:
273            checkDeleteStatement(<ts.DeleteExpression>node);
274            break;
275        case ts.SyntaxKind.WithStatement:
276            checkWithStatement(<ts.WithStatement>node);
277            break;
278        case ts.SyntaxKind.Identifier:
279            checkContextualIdentifier(<ts.Identifier>node);
280            break;
281        case ts.SyntaxKind.NoSubstitutionTemplateLiteral:
282        case ts.SyntaxKind.FirstTemplateToken:
283        case ts.SyntaxKind.LastLiteralToken:
284            checkNoSubstitutionTemplateLiteral(<ts.NoSubstitutionTemplateLiteral>node);
285            break;
286        case ts.SyntaxKind.ImportDeclaration:
287            checkImportDeclaration(<ts.ImportDeclaration>node, scope);
288            break;
289        case ts.SyntaxKind.ExportAssignment:
290            checkExportAssignment(<ts.ExportAssignment>node, scope);
291            break;
292        case ts.SyntaxKind.ExportDeclaration:
293            checkExportDeclaration(<ts.ExportDeclaration>node, scope);
294            break;
295        default:
296            break;
297    }
298}
299