8000 Initial strict mode work. · imskojs/TypeScript@08a53b7 · GitHub
[go: up one dir, main page]

Skip to content

Commit 08a53b7

Browse files
Initial strict mode work.
1 parent d49fe6a commit 08a53b7

File tree

5 files changed

+229
-304
lines changed

5 files changed

+229
-304
lines changed

src/compiler/binder.ts

Lines changed: 169 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,10 @@ module ts {
8888
let container: Node;
8989
let blockScopeContainer: Node;
9090
let lastContainer: Node;
91+
92+
let inStrictMode = false;
93+
let noParseDiagnostics = file.parseDiagnostics.length === 0
94+
9195
let symbolCount = 0;
9296
let Symbol = objectAllocator.getSymbolConstructor();
9397

@@ -270,6 +274,14 @@ module ts {
270274
let saveParent = parent;
271275
let saveContainer = container;
272276
let savedBlockScopeContainer = blockScopeContainer;
277+
let savedInStrictMode = inStrictMode;
278+
279+
// If we're not already in strict mode, then check and see if we've entered into
280+
// strict mode. Only bother doing this if we've gotten no parse diagnostics.
281+
// We're not going to report strict mode errors in the presense of parse diagnostics.
282+
if (!inStrictMode && noParseDiagnostics) {
283+
updateStrictMode(node);
284+
}
273285

274286
// This node will now be set as the parent of all of its children as we recurse into them.
275287
parent = node;
@@ -311,6 +323,41 @@ module ts {
311323
container = saveContainer;
312324
parent = saveParent;
313325
blockScopeContainer = savedBlockScopeContainer;
326+
inStrictMode = savedInStrictMode;
327+
}
328+
329+
function updateStrictMode(node: Node) {
330+
switch (node.kind) {
331+
case SyntaxKind.SourceFile:
332+
updateStrictModeStatements((<SourceFile>node).statements);
333+
return;
334+
case SyntaxKind.ModuleBlock:
335+
updateStrictModeStatements((<ModuleBlock>node).statements);
336+
return;
337+
case SyntaxKind.Block:
338+
let block = <Block>node;
339+
if (isFunctionLike(parent)) {
340+
updateStrictModeStatements((<Block>node).statements);
341+
}
342+
return;
343+
}
344+
}
345+
346+
function updateStrictModeStatements(statements: NodeArray<Node>) {
347+
if (statements) {
348+
for (let i = 0, n = statements.length; i < n; i++) {
349+
let statement = statements[i];
350+
if (isPrologueDirective(statement)) {
351+
if (isUseStrictPrologueDirective(statement, file.text)) {
352+
inStrictMode = true;
353+
return;
354+
}
355+
}
356+
else {
357+
return;
358+
}
359+
}
360+
}
314361
}
315362

316363
function getContainerFlags(node: Node): ContainerFlags {
@@ -560,6 +607,12 @@ module ts {
560607
function bind(node: Node) {
561608
node.parent = parent;
562609

610+
// If we're in strict mode, and we didn't have any parse diagnostics, then see if this
611+
// node has any strict mode issues.
612+
if (inStrictMode && noParseDiagnostics) {
613+
checkStrictNode(node);
614+
}
615+
563616
// First we bind declaration nodes to a symbol if possible. We'll both create a symbol
564617
// and then potentially add the symbol to an appropriate symbol table. Possible
565618
// destination symbol tables are:
@@ -578,7 +631,123 @@ module ts {
578631
// a local should go into for example.
579632
bindChildren(node);
580633
}
634 A93C +
635+
function grammarErrorOnNode(node: Node, message: DiagnosticMessage, arg0?: any, arg1?: any, arg2?: any): void {
636+
let span = getErrorSpanForNode(file, node);
637+
file.bindDiagnostics.push(createFileDiagnostic(
638+
file, span.start, span.length, message, arg0, arg1, arg2));
639+
}
640+
641+
function grammarErrorOnFirstToken(node: Node, message: DiagnosticMessage, arg0?: any, arg1?: any, arg2?: any): void {
642+
let span = getSpanOfTokenAtPosition(file, node.pos);
643+
file.bindDiagnostics.push(createFileDiagnostic(
644+
file, span.start, span.length, message, arg0, arg1, arg2));
645+
}
646+
647+
function isReservedWord(node: Identifier): boolean {
648+
// Check that originalKeywordKind is less than LastFutureReservedWord to see if an Identifier is a strict-mode reserved word
649+
return SyntaxKind.FirstFutureReservedWord <= node.originalKeywordKind &&
650+
node.originalKeywordKind <= SyntaxKind.LastFutureReservedWord;
651+
}
652+
653+
function checkStrictNode(node: Node): void {
654+
switch (node.kind) {
655+
case SyntaxKind.DeleteExpression:
656+
return checkDeleteExpressionInStrictMode(<DeleteExpression>node);
657+
case SyntaxKind.ExpressionWithTypeArguments:
658+
return checkExpressionWithTypeArgumentsInStrictMode(<ExpressionWithTypeArguments>node);
659+
case SyntaxKind.ImportDeclaration:
660+
return checkImportDeclarationInStrictMode(<ImportDeclaration>node);
661+
case SyntaxKind.ImportEqualsDeclaration:
662+
663+
case SyntaxKind.WithStatement:
664+
return checkWithStatementInStrictMode(<WithStatement>node);
665+
}
666+
}
667+
668+
669+
// The function takes an identifier itself or an expression which has SyntaxKind.Identifier.
670+
function checkGrammarIdentifierInStrictMode(node: Expression | Identifier, nameText?: string): void {
671+
if (node && node.kind === SyntaxKind.Identifier && isReservedWord(<Identifier>node)) {
672+
if (!nameText) {
673+
nameText = declarationNameToString(<Identifier>node, file);
674+
}
675+
676+
// TODO (yuisu): Fix when module is a strict mode
677+
reportStrictModeGrammarErrorInClassDeclaration(<Identifier>node, Diagnostics.Identifier_expected_0_is_a_reserved_word_in_strict_mode_Class_definitions_are_automatically_in_strict_mode, nameText) ||
678+
grammarErrorOnNode(node, Diagnostics.Identifier_expected_0_is_a_reserved_word_in_strict_mode, nameText);
679+
}
680+
}
681+
682+
function reportStrictModeGrammarErrorInClassDeclaration(identifier: Identifier, message: DiagnosticMessage, arg0?: any, arg1?: any, arg2?: any): boolean {
683+
// We are checking if this name is inside class declaration or class expression (which are under class definitions inside ES6 spec.)
684+
// if so, we would like to give more explicit invalid usage error.
685+
if (getAncestor(parent, SyntaxKind.ClassDeclaration) || getAncestor(parent, SyntaxKind.ClassExpression)) {
686+
grammarErrorOnNode(identifier, message, arg0);
687+
return true;
688+
}
689+
690+
return false;
691+
}
692+
693+
function checkDeleteExpressionInStrictMode(node: DeleteExpression) {
694+
if (node.expression.kind === SyntaxKind.Identifier) {
695+
// When a delete operator occurs within strict mode code, a SyntaxError is thrown if its
696+
// UnaryExpression is a direct reference to a variable, function argument, or function name
697+
grammarErrorOnNode(node.expression, Diagnostics.delete_cannot_be_called_on_an_identifier_in_strict_mode);
698+
}
699+
}
700+
701+
function checkExpressionWithTypeArgumentsInStrictMode(node: ExpressionWithTypeArguments): void {
702+
return checkExpressionWithTypeArgumentsExpressionInStrictMode(node.expression);
703+
}
704+
705+
function checkExpressionWithTypeArgumentsExpressionInStrictMode(node: Expression): void {
706+
// Example:
707+
// class C extends public // error at public
708+
if (node && node.kind === SyntaxKind.Identifier) {
709+
return checkGrammarIdentifierInStrictMode(node);
710+
}
711+
else if (node && node.kind === SyntaxKind.PropertyAccessExpression) {
712+
// Walk from left to right in PropertyAccessExpression until we are at the left most expression
713+
// in PropertyAccessExpression. According to grammar production of MemberExpression,
714+
// the left component expression is a PrimaryExpression (i.e. Identifier) while the other
715+
// component after dots can be IdentifierName.
716+
checkExpressionWithTypeArgumentsExpressionInStrictMode((<PropertyAccessExpression>node).expression);
717+
}
718+
}
719+
720+
function checkImportDeclarationInStrictMode(node: ImportDeclaration): void {
721+
// Check if the import declaration used strict-mode reserved word in its names bindings
722+
if (node.importClause) {
723+
let importClause = node.importClause;
724+
if (importClause.namedBindings) {
725+
let nameBindings = importClause.namedBindings;
726+
if (nameBindings.kind === SyntaxKind.NamespaceImport) {
727+
let name = <Identifier>(<NamespaceImport>nameBindings).name;
728+
if (isReservedWord(name)) {
729+
let nameText = declarationNameToString(name);
730+
return grammarErrorOnNode(name, Diagnostics.Identifier_expected_0_is_a_reserved_word_in_strict_mode, nameText);
731+
}
732+
}
733+
else if (nameBindings.kind === SyntaxKind.NamedImports) {
734+
let reportError = false;
735+
for (let element of (<NamedImports>nameBindings).elements) {
736+
let name = element.name;
737+
if (isReservedWord(name)) {
738+
let nameText = declarationNameToString(name);
739+
return grammarErrorOnNode(name, Diagnostics.Identifier_expected_0_is_a_reserved_word_in_strict_mode, nameText);
740+
}
741+
}
742+
}
743+
}
744+
}
745+
}
581746

747+
function checkWithStatementInStrictMode(node: WithStatement) {
748+
grammarErrorOnFirstToken(node, Diagnostics.with_statements_are_not_allowed_in_strict_mode);
749+
}
750+
582751
function bindWorker(node: Node) {
583752
switch (node.kind) {
584753
case SyntaxKind.TypeParameter:

0 commit comments

Comments
 (0)
0