private ISymbol ResolveName()

in Public/Src/FrontEnd/TypeScript.Net/TypeScript.Net/TypeChecking/Checker.cs [1038:1424]


        private ISymbol ResolveName(INode location, string name, SymbolFlags meaning, IDiagnosticMessage nameNotFoundMessage, string nameArgAsString, IIdentifier nameArg = null)
        {
            ISymbol result = null;
            INode lastLocation = null;
            INode propertyWithInvalidInitializer = null;
            INode errorLocation = location;
            INode grandparent = null;

            // HINT: TypeScript implementation uses goto-style label to break out of loop from the switch statement
            //       Switching to state check in the loop's condition.
            bool breakLoop = false;

            // DScript-specific. If we are trying to resolve the special namespace $, then
            // the resolution is fixed: this name cannot be shadowed by any other name
            // (there are lint rules that block naming any entity $ anyway)
            // Additionally, $ has to be the first qualification on a qualified name, and therefore it is always
            // a reference to the module (or source file in legacy modules) associated to the place
            // where the reference occurs
            if (name == Names.RootNamespace && (meaning & SymbolFlags.Namespace) != SymbolFlags.None)
            {
                var sourceFile = location.GetSourceFile();
                ModuleName moduleName;

                // V2 case, we return a (unique per module) symbol representing all internal values
                if (TryGetOwningModuleWithImplicitSemantics(sourceFile.FileName, out moduleName))
                {
                    return GetInternalModuleSymbol(moduleName.Name);
                }

                // V1 case, the $ symbol represents the source file, there are no implicit semantics
                return sourceFile.Symbol;
            }

            while (location != null && !breakLoop)
            {
                // Locals of a source file are not in scope (because they get merged into the global symbol table)
                if (location.Locals != null && !IsGlobalSourceFile(location))
                {
                    result = GetSymbol(location.Locals, name, meaning);
                    if (result != null)
                    {
                        var useResult = true;
                        if (IsFunctionLike(location) != null &&
                            lastLocation != null &&

                            // Can't compare nodes directly, because Body could be a union wrapper
                            lastLocation.ResolveUnionType() != location.Cast<IFunctionLikeDeclaration>().Body.ResolveUnionType())
                        {
                            // symbol lookup restrictions for function-like declarations
                            // - Type parameters of a function are in scope in the entire function declaration, including the parameter
                            //   list and return type. However, local types are only in scope in the function body.
                            // - parameters are only in the scope of function body
                            if ((meaning & result.Flags & SymbolFlags.Type) != SymbolFlags.None)
                            {
                                useResult = (result.Flags & SymbolFlags.TypeParameter) != SymbolFlags.None

                                    // type parameters are visible in parameter list, return type and type parameter list
                                    ? lastLocation.ResolveUnionType() == location.Cast<IFunctionLikeDeclaration>().Type.ResolveUnionType() ||
                                      lastLocation.Kind == SyntaxKind.Parameter ||
                                      lastLocation.Kind == SyntaxKind.TypeParameter

                                    // local types not visible outside the body
                                    : false;
                            }

                            if ((meaning & SymbolFlags.Value) != SymbolFlags.None &&
                                (result.Flags & SymbolFlags.FunctionScopedVariable) != SymbolFlags.None)
                            {
                                // parameters are visible only inside function body, parameter list and return type
                                // technically for parameter list case here we might mix parameters and variables declared in function,
                                // however it is detected separately when checking initializers of parameters
                                // to make sure that they reference no variables declared after them.
                                useResult =
                                    lastLocation.Kind == SyntaxKind.Parameter ||
                                    (lastLocation.ResolveUnionType() == location.Cast<IFunctionLikeDeclaration>().Type.ResolveUnionType() &&
                                     result.ValueDeclaration.Kind == SyntaxKind.Parameter);
                            }
                        }

                        if (useResult)
                        {
                            breakLoop = true;
                            break;
                        }
                        else
                        {
                            result = null;
                        }
                    }
                }

                switch (location.Kind)
                {
                    case SyntaxKind.SourceFile:
                    case SyntaxKind.ModuleDeclaration:
                        if (IsGlobalSourceFile(location))
                        {
                            break;
                        }

                        // DScript-specific. If the lookup reached the source file level and the source file is owned by a module with implicit
                        // reference semantics, the module exports that is used is the one that holds all internal values from the module. In that
                        // way all internal values will behave as implicitly imported. Otherwise, we preserve the original TS logic.
                        ModuleName moduleName;
                        ISymbolTable moduleExports = GetInternalModuleExports(location, out moduleName);

                        // TODO: revisit. Adding this to avoid a crash
                        if (moduleExports == null)
                        {
                            result = null;
                            breakLoop = true;
                            break;
                        }

                        if ((location.Kind == SyntaxKind.SourceFile) ||
                            (location.Kind == SyntaxKind.ModuleDeclaration &&
                             location.Cast<IModuleDeclaration>().Name.Kind == SyntaxKind.StringLiteral))
                        {
                            // It's an external module. First see if the module has an export default and if the local
                            // name of that export default matches.
                            result = moduleExports["default"];
                            if (result != null)
                            {
                                ISymbol localSymbol = GetLocalSymbolForExportDefault(result);
                                if ((localSymbol != null) &&
                                    (result.Flags & meaning) != SymbolFlags.None &&
                                    (localSymbol.Name == name))
                                {
                                    breakLoop = true;
                                    break;
                                }

                                result = null;
                            }

                            // Because of module/namespace merging, a module's exports are in scope,
                            // yet we never want to treat an export specifier as putting a member in scope.
                            // Therefore, if the name we find is purely an export specifier, it is not actually considered in scope.
                            // Two things to note about this:
                            //     1. We have to check this without calling getSymbol. The problem with calling getSymbol
                            //        on an export specifier is that it might find the export specifier itself, and try to
                            //        resolve it as an alias. This will cause the checker to consider the export specifier
                            //        a circular alias reference when it might not be.
                            //     2. We check == SymbolFlags.Alias in order to check that the symbol is *purely*
                            //        an alias. If we used &, we'd be throwing out symbols that have non alias aspects,
                            //        which is not the desired behavior.
                            if (HasProperty(moduleExports, name) &&
                                moduleExports[name].Flags == SymbolFlags.Alias)
                            {
                                var moduleExportDeclaration = GetDeclarationOfKind(moduleExports[name], SyntaxKind.ExportSpecifier);
                                if (moduleExportDeclaration != null)
                                {
                                    // DScript-specific. If the export specifier resides in a module with V2 semantics
                                    // the symbol *is* in scope for other specs in the same module
                                    // But this is the case only for other specs, in the same spec the symbol is not in scope, so we keep
                                    // the semantics of export specifiers (e.g. export {a as b} does not introduce b in the local scope)
                                    if (moduleName != ModuleName.Invalid && moduleExportDeclaration.GetSourceFile() != location)
                                    {
                                        result = moduleExports[name];
                                        breakLoop = true;
                                    }

                                    break;
                                }
                            }
                        }

                        result = GetSymbol(moduleExports, name, meaning & SymbolFlags.ModuleMember);
                        if (result != null)
                        {
                            breakLoop = true;
                            break;
                        }

                        break;
                    case SyntaxKind.EnumDeclaration:
                        result = GetSymbol(GetSymbolOfNode(location).Exports, name, meaning & SymbolFlags.EnumMember);
                        if (result != null)
                        {
                            breakLoop = true;
                            break;
                        }

                        break;
                    case SyntaxKind.PropertyDeclaration:
                    case SyntaxKind.PropertySignature:
                        // 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 ((IsClassLike(location.Parent) != null) &&
                            (location.Flags & NodeFlags.Static) == NodeFlags.None)
                        {
                            IConstructorDeclaration ctor = FindConstructorDeclaration(location.Parent.Cast<IClassLikeDeclaration>());
                            if (ctor != null && ctor.Locals != null)
                            {
                                if (GetSymbol(ctor.Locals, name, meaning & SymbolFlags.Value) != null)
                                {
                                    // Remember the property node, it will be used later to report appropriate error
                                    propertyWithInvalidInitializer = location;
                                }
                            }
                        }

                        break;
                    case SyntaxKind.ClassDeclaration:
                    case SyntaxKind.ClassExpression:
                    case SyntaxKind.InterfaceDeclaration:
                        result = GetSymbol(GetSymbolOfNode(location).Members, name, meaning & SymbolFlags.Type);
                        if (result != null)
                        {
                            if ((lastLocation != null) &&
                                (lastLocation.Flags & NodeFlags.Static) != NodeFlags.None)
                            {
                                // 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, Errors.Static_members_cannot_reference_class_type_parameters);
                                return null;
                            }

                            breakLoop = true;
                            break;
                        }

                        if ((location.Kind == SyntaxKind.ClassExpression) &&
                            (meaning & SymbolFlags.Class) != SymbolFlags.None)
                        {
                            var className = location.Cast<IClassExpression>().Name;
                            if (className != null && name.Equals(className.Text))
                            {
                                result = location.Symbol;
                                breakLoop = true;
                                break;
                            }
                        }

                        break;

                    // It is not legal to reference a class's own type parameters from a computed property name that
                    // belongs to the class. For example:
                    //
                    //   function foo<T>() { return '' }
                    //   class C<T> { // <-- Class's own type parameter T
                    //       [foo<T>()]() { } // <-- Reference to T from class's own computed property
                    //   }
                    case SyntaxKind.ComputedPropertyName:
                        grandparent = location.Parent.Parent;
                        if (IsClassLike(grandparent) != null ||
                            grandparent.Kind == SyntaxKind.InterfaceDeclaration)
                        {
                            // A reference to this grandparent's type parameters would be an error
                            result = GetSymbol(GetSymbolOfNode(grandparent).Members, name, meaning & SymbolFlags.Type);
                            if (result != null)
                            {
                                Error(errorLocation, Errors.A_computed_property_name_cannot_reference_a_type_parameter_from_its_containing_type);
                                return null;
                            }
                        }

                        break;
                    case SyntaxKind.MethodDeclaration:
                    case SyntaxKind.MethodSignature:
                    case SyntaxKind.Constructor:
                    case SyntaxKind.GetAccessor:
                    case SyntaxKind.SetAccessor:
                    case SyntaxKind.FunctionDeclaration:
                    case SyntaxKind.ArrowFunction:
                        if ((meaning & SymbolFlags.Variable) != SymbolFlags.None && name.Equals("arguments"))
                        {
                            result = m_argumentsSymbol;
                            breakLoop = true;
                            break;
                        }

                        break;
                    case SyntaxKind.FunctionExpression:
                        if ((meaning & SymbolFlags.Variable) != SymbolFlags.None && name.Equals("arguments"))
                        {
                            result = m_argumentsSymbol;
                            breakLoop = true;
                            break;
                        }

                        if ((meaning & SymbolFlags.Function) != SymbolFlags.None)
                        {
                            var functionName = location.Cast<IFunctionExpression>().Name;
                            if (functionName != null && name.Equals(functionName.Text))
                            {
                                result = location.Symbol;
                                breakLoop = true;
                                break;
                            }
                        }

                        break;
                    case SyntaxKind.Decorator:
                        // Decorators are resolved at the class declaration. Resolving at the parameter
                        // or member would result in looking up locals in the method.
                        //
                        //   function y() {}
                        //   class C {
                        //       method(@y x, y) {} // <-- decorator y should be resolved at the class declaration, not the parameter.
                        //   }
                        if (location.Parent != null && location.Parent.Kind == SyntaxKind.Parameter)
                        {
                            location = location.Parent;
                        }

                        // function y() {}
                        // class C {
                        //       @y method(x, y) {} // <-- decorator y should be resolved at the class declaration, not the method.
                        // }
                        if (location.Parent != null && IsClassElement(location.Parent))
                        {
                            location = location.Parent;
                        }

                        break;
                }

                lastLocation = location;
                location = location.Parent;
            }

            if (result == null)
            {
                result = GetSymbol(m_globals, name, meaning);
            }

            if (result == null)
            {
                if (nameNotFoundMessage != null)
                {
                    Error(errorLocation, nameNotFoundMessage, nameArgAsString ?? (nameArgAsString = DeclarationNameToString(nameArg)));
                }

                return null;
            }

            // Perform extra checks only if error reporting was requested
            if (nameNotFoundMessage != null)
            {
                if (propertyWithInvalidInitializer != null)
                {
                    // 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.

                    // TODO: Verify equivalence - const propertyName = (<PropertyDeclaration>propertyWithInvalidInitializer).Name;
                    var propertyName = propertyWithInvalidInitializer.Kind == SyntaxKind.PropertyDeclaration
                        ? propertyWithInvalidInitializer.Cast<IPropertyDeclaration>().Name
                        : propertyWithInvalidInitializer.Cast<IPropertySignature>().Name;

                    Error(
                        errorLocation,
                        Errors.Initializer_of_instance_member_variable_0_cannot_reference_identifier_1_declared_in_the_constructor,
                        DeclarationNameToString(propertyName),
                        nameArgAsString ?? (nameArgAsString = DeclarationNameToString(nameArg)));

                    return null;
                }

                // Only check for block-scoped variable if we are looking for the
                // name with variable meaning
                //      For example,
                //          declare module foo {
                //              interface bar {}
                //          }
                //      const foo/*1*/: foo/*2*/.bar;
                // The foo at /*1*/ and /*2*/ will share same symbol with two meaning
                // block - scope variable and namespace module. However, only when we
                // try to resolve name in /*1*/ which is used in variable position,
                // we want to check for block- scoped
                if ((meaning & SymbolFlags.BlockScopedVariable) != SymbolFlags.None)
                {
                    ISymbol exportOrLocalSymbol = GetExportSymbolOfValueSymbolIfExported(result);
                    if ((exportOrLocalSymbol.Flags & SymbolFlags.BlockScopedVariable) != SymbolFlags.None)
                    {
                        CheckResolvedBlockScopedVariable(exportOrLocalSymbol, errorLocation);
                    }
                }
            }

            return result;
        }