From 6bf0f6faa2d7546772b3886b4667f2ff7d0fe956 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Sun, 26 Oct 2014 18:12:52 -0700 Subject: [PATCH 1/2] Fix the resolveName function --- src/compiler/checker.ts | 230 +++++++++++++++++++++------------------- 1 file changed, 118 insertions(+), 112 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 7a959c0dbfc..a72b2c13a5d 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -306,29 +306,124 @@ module ts { // return undefined if we can't find a symbol. } - function resolveName(location: Node, name: string, meaning: SymbolFlags, nameNotFoundMessage: DiagnosticMessage, nameArg: string): Symbol { - var errorLocation = location; + // Resolve a given name for a given meaning at a given location. An error is reported if the name was not found and + // the nameNotFoundMessage argument is not undefined. Returns the resolved symbol, or undefined if no symbol with + // the given name can be found. + function resolveName(location: Node, name: string, meaning: SymbolFlags, nameNotFoundMessage: DiagnosticMessage, nameArg: string | Identifier): Symbol { + var result: Symbol; var lastLocation: Node; + var propertyWithInvalidInitializer: Node; + var errorLocation = location; - var memberWithInitializerThatReferencesIdentifierFromConstructor: Node; + loop: while (location) { + // Locals of a source file are not in scope (because they get merged into the global symbol table) + if (location.locals && !isGlobalSourceFile(location)) { + if (result = getSymbol(location.locals, name, meaning)) { + break loop; + } + } + switch (location.kind) { + case SyntaxKind.SourceFile: + if (!isExternalModule(location)) break; + case SyntaxKind.ModuleDeclaration: + if (result = getSymbol(getSymbolOfNode(location).exports, name, meaning & SymbolFlags.ModuleMember)) { + break loop; + } + break; + case SyntaxKind.EnumDeclaration: + if (result = getSymbol(getSymbolOfNode(location).exports, name, meaning & SymbolFlags.EnumMember)) { + break loop; + } + break; + case SyntaxKind.Property: + // TypeScript 1.0 spec (April 2014): 8.4.1 + // Initializer expressions for instance member variables are evaluated in the scope + // of the class constructor body but are not permitted to reference parameters or + // local variables of the constructor.This effectively means that entities from outer scopes + // by the same name as a constructor parameter or local variable are inaccessible + // in initializer expressions for instance member variables. + if (location.parent.kind === SyntaxKind.ClassDeclaration && !(location.flags & NodeFlags.Static)) { + var ctor = findConstructorDeclaration(location.parent); + if (ctor && ctor.locals) { + if (getSymbol(ctor.locals, name, meaning & SymbolFlags.Value)) { + // save the property node - later it will be used by 'returnResolvedSymbol' to report appropriate error + propertyWithInvalidInitializer = location; + } + } + } + break; + case SyntaxKind.ClassDeclaration: + case SyntaxKind.InterfaceDeclaration: + if (result = getSymbol(getSymbolOfNode(location).members, name, meaning & SymbolFlags.Type)) { + if (lastLocation && lastLocation.flags & NodeFlags.Static) { + // TypeScript 1.0 spec (April 2014): 3.4.1 + // The scope of a type parameter extends over the entire declaration + // with which the type parameter list is associated, with the exception of static member declarations in classes. + error(errorLocation, Diagnostics.Static_members_cannot_reference_class_type_parameters); + return undefined; + } + break loop; + } + break; + case SyntaxKind.Method: + case SyntaxKind.Constructor: + case SyntaxKind.GetAccessor: + case SyntaxKind.SetAccessor: + case SyntaxKind.FunctionDeclaration: + case SyntaxKind.ArrowFunction: + if (name === "arguments") { + result = argumentsSymbol; + break loop; + } + break; + case SyntaxKind.FunctionExpression: + if (name === "arguments") { + result = argumentsSymbol; + break loop; + } + var id = (location).name; + if (id && name === id.text) { + result = location.symbol; + break loop; + } + break; + case SyntaxKind.CatchBlock: + var id = (location).variable; + if (name === id.text) { + result = location.symbol; + break loop; + } + break; + } + lastLocation = location; + location = location.parent; + } - function returnResolvedSymbol(s: Symbol) { - // we've seen member with initializer that references identifier defined in constructor during the search. - // if this was the only result with given name then just report default 'nameNotFound' message. - // however if we met something else that was 'shadowed' by the identifier in constructor - report more specific error - if (s && memberWithInitializerThatReferencesIdentifierFromConstructor) { - var propertyName = (memberWithInitializerThatReferencesIdentifierFromConstructor).name; - error(errorLocation, Diagnostics.Initializer_of_instance_member_variable_0_cannot_reference_identifier_1_declared_in_the_constructor, identifierToString(propertyName), nameArg); + if (!result) { + result = getSymbol(globals, name, meaning); + } + + if (!result) { + if (nameNotFoundMessage) { + error(errorLocation, nameNotFoundMessage, typeof nameArg === "string" ? nameArg : identifierToString(nameArg)); + } + return undefined; + } + + // Perform extra checks only if error reporting was requested + if (nameNotFoundMessage) { + if (propertyWithInvalidInitializer) { + // We have a match, but the reference occurred within a property initializer and the identifier also binds + // to a local variable in the constructor where the code will be emitted. + var propertyName = (propertyWithInvalidInitializer).name; + error(errorLocation, Diagnostics.Initializer_of_instance_member_variable_0_cannot_reference_identifier_1_declared_in_the_constructor, + identifierToString(propertyName), typeof nameArg === "string" ? nameArg : identifierToString(nameArg)); return undefined; } - if (!s && nameNotFoundMessage) { - error(errorLocation, nameNotFoundMessage, nameArg); - } - - if (s && s.flags & SymbolFlags.BlockScopedVariable) { - // Block-scoped variables can not be used before their definition - var declaration = forEach(s.declarations, d => d.flags & NodeFlags.BlockScoped ? d : undefined); + if (result.flags & SymbolFlags.BlockScopedVariable) { + // Block-scoped variables cannot be used before their definition + var declaration = forEach(result.declarations, d => d.flags & NodeFlags.BlockScoped ? d : undefined); Debug.assert(declaration, "Block-scoped variable declaration is undefined"); var declarationSourceFile = getSourceFileOfNode(declaration); var referenceSourceFile = getSourceFileOfNode(errorLocation); @@ -344,95 +439,8 @@ module ts { } } } - return s; } - - while (location) { - // Locals of a source file are not in scope (because they get merged into the global symbol table) - if (location.locals && !isGlobalSourceFile(location)) { - if (result = getSymbol(location.locals, name, meaning)) { - return returnResolvedSymbol(result); - } - } - switch (location.kind) { - case SyntaxKind.SourceFile: - if (!isExternalModule(location)) break; - case SyntaxKind.ModuleDeclaration: - if (result = getSymbol(getSymbolOfNode(location).exports, name, meaning & SymbolFlags.ModuleMember)) { - return returnResolvedSymbol(result); - } - break; - case SyntaxKind.EnumDeclaration: - if (result = getSymbol(getSymbolOfNode(location).exports, name, meaning & SymbolFlags.EnumMember)) { - return returnResolvedSymbol(result); - } - break; - case SyntaxKind.Property: - // TypeScript 1.0 spec (April 2014): 8.4.1 - // Initializer expressions for instance member variables are evaluated in the scope - // of the class constructor body but are not permitted to reference parameters or - // local variables of the constructor.This effectively means that entities from outer scopes - // by the same name as a constructor parameter or local variable are inaccessible - // in initializer expressions for instance member variables. - if (location.parent.kind === SyntaxKind.ClassDeclaration && !(location.flags & NodeFlags.Static)) { - var ctor = findConstructorDeclaration(location.parent); - if (ctor && ctor.locals) { - if (getSymbol(ctor.locals, name, meaning & SymbolFlags.Value)) { - // save the property node - later it will be used by 'returnResolvedSymbol' to report appropriate error - memberWithInitializerThatReferencesIdentifierFromConstructor = location; - } - } - } - break; - case SyntaxKind.ClassDeclaration: - case SyntaxKind.InterfaceDeclaration: - if (result = getSymbol(getSymbolOfNode(location).members, name, meaning & SymbolFlags.Type)) { - if (lastLocation && lastLocation.flags & NodeFlags.Static) { - // TypeScript 1.0 spec (April 2014): 3.4.1 - // The scope of a type parameter extends over the entire declaration - // with which the type parameter list is associated, with the exception of static member declarations in classes. - error(errorLocation, Diagnostics.Static_members_cannot_reference_class_type_parameters); - return undefined; - } - else { - return returnResolvedSymbol(result); - } - } - break; - case SyntaxKind.Method: - case SyntaxKind.Constructor: - case SyntaxKind.GetAccessor: - case SyntaxKind.SetAccessor: - case SyntaxKind.FunctionDeclaration: - case SyntaxKind.ArrowFunction: - if (name === "arguments") { - return returnResolvedSymbol(argumentsSymbol); - } - break; - case SyntaxKind.FunctionExpression: - if (name === "arguments") { - return returnResolvedSymbol(argumentsSymbol); - } - var id = (location).name; - if (id && name === id.text) { - return returnResolvedSymbol(location.symbol); - } - break; - case SyntaxKind.CatchBlock: - var id = (location).variable; - if (name === id.text) { - return returnResolvedSymbol(location.symbol); - } - break; - } - lastLocation = location; - location = location.parent; - } - if (result = getSymbol(globals, name, meaning)) { - return returnResolvedSymbol(result); - } - - return returnResolvedSymbol(undefined); + return result; } function resolveImport(symbol: Symbol): Symbol { @@ -491,8 +499,7 @@ module ts { // Resolves a qualified name and any involved import aliases function resolveEntityName(location: Node, name: EntityName, meaning: SymbolFlags): Symbol { if (name.kind === SyntaxKind.Identifier) { - // TODO: Investigate error recovery for symbols not found - var symbol = resolveName(location, (name).text, meaning, Diagnostics.Cannot_find_name_0, identifierToString(name)); + var symbol = resolveName(location, (name).text, meaning, Diagnostics.Cannot_find_name_0, name); if (!symbol) { return; } @@ -589,7 +596,7 @@ module ts { } if (node.exportName.text) { var meaning = SymbolFlags.Value | SymbolFlags.Type | SymbolFlags.Namespace; - var exportSymbol = resolveName(node, node.exportName.text, meaning, Diagnostics.Cannot_find_name_0, identifierToString(node.exportName)); + var exportSymbol = resolveName(node, node.exportName.text, meaning, Diagnostics.Cannot_find_name_0, node.exportName); } } symbolLinks.exportAssignSymbol = exportSymbol || unknownSymbol; @@ -960,13 +967,12 @@ module ts { function isImportDeclarationEntityNameReferenceDeclarationVisibile(entityName: EntityName): SymbolAccessiblityResult { var firstIdentifier = getFirstIdentifier(entityName); - var firstIdentifierName = identifierToString(firstIdentifier); - var symbolOfNameSpace = resolveName(entityName.parent, (firstIdentifier).text, SymbolFlags.Namespace, Diagnostics.Cannot_find_name_0, firstIdentifierName); + var symbolOfNameSpace = resolveName(entityName.parent, (firstIdentifier).text, SymbolFlags.Namespace, Diagnostics.Cannot_find_name_0, firstIdentifier); // Verify if the symbol is accessible var hasNamespaceDeclarationsVisibile = hasVisibleDeclarations(symbolOfNameSpace); return hasNamespaceDeclarationsVisibile ? { accessibility: SymbolAccessibility.Accessible, aliasesToMakeVisible: hasNamespaceDeclarationsVisibile.aliasesToMakeVisible } : - { accessibility: SymbolAccessibility.NotAccessible, errorSymbolName: firstIdentifierName }; + { accessibility: SymbolAccessibility.NotAccessible, errorSymbolName: identifierToString(firstIdentifier) }; } function releaseStringWriter(writer: StringSymbolWriter) { @@ -4055,7 +4061,7 @@ module ts { function getResolvedSymbol(node: Identifier): Symbol { var links = getNodeLinks(node); if (!links.resolvedSymbol) { - links.resolvedSymbol = resolveName(node, node.text, SymbolFlags.Value | SymbolFlags.ExportValue, Diagnostics.Cannot_find_name_0, identifierToString(node)) || unknownSymbol; + links.resolvedSymbol = resolveName(node, node.text, SymbolFlags.Value | SymbolFlags.ExportValue, Diagnostics.Cannot_find_name_0, node) || unknownSymbol; } return links.resolvedSymbol; } From d4673f97abcbd7689b762d0e932d99c0db9c1bac Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Mon, 27 Oct 2014 06:42:31 -0700 Subject: [PATCH 2/2] Fixing comments --- src/compiler/checker.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index a72b2c13a5d..10d9e66bfb8 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -340,14 +340,14 @@ module ts { // TypeScript 1.0 spec (April 2014): 8.4.1 // Initializer expressions for instance member variables are evaluated in the scope // of the class constructor body but are not permitted to reference parameters or - // local variables of the constructor.This effectively means that entities from outer scopes + // local variables of the constructor. This effectively means that entities from outer scopes // by the same name as a constructor parameter or local variable are inaccessible // in initializer expressions for instance member variables. if (location.parent.kind === SyntaxKind.ClassDeclaration && !(location.flags & NodeFlags.Static)) { var ctor = findConstructorDeclaration(location.parent); if (ctor && ctor.locals) { if (getSymbol(ctor.locals, name, meaning & SymbolFlags.Value)) { - // save the property node - later it will be used by 'returnResolvedSymbol' to report appropriate error + // Remember the property node, it will be used later to report appropriate error propertyWithInvalidInitializer = location; } } @@ -358,8 +358,8 @@ module ts { if (result = getSymbol(getSymbolOfNode(location).members, name, meaning & SymbolFlags.Type)) { if (lastLocation && lastLocation.flags & NodeFlags.Static) { // TypeScript 1.0 spec (April 2014): 3.4.1 - // The scope of a type parameter extends over the entire declaration - // with which the type parameter list is associated, with the exception of static member declarations in classes. + // The scope of a type parameter extends over the entire declaration with which the type + // parameter list is associated, with the exception of static member declarations in classes. error(errorLocation, Diagnostics.Static_members_cannot_reference_class_type_parameters); return undefined; }