private void AddTypeParameters(TypeSyntax typeSyntax, MultiDictionary<string, TypeParameterSymbol> map)
 {
     switch (typeSyntax.Kind())
     {
         case SyntaxKind.AliasQualifiedName:
             AddTypeParameters(((AliasQualifiedNameSyntax)typeSyntax).Name, map);
             break;
         case SyntaxKind.QualifiedName:
             // NOTE: Dev11 does not warn about duplication, it just matches parameter types to the
             // *last* type parameter with the same name.  That's why we're iterating backwards.
             QualifiedNameSyntax qualifiedNameSyntax = (QualifiedNameSyntax)typeSyntax;
             AddTypeParameters(qualifiedNameSyntax.Right, map);
             AddTypeParameters(qualifiedNameSyntax.Left, map);
             break;
         case SyntaxKind.GenericName:
             AddTypeParameters((GenericNameSyntax)typeSyntax, map);
             break;
         case SyntaxKind.IdentifierName:
         case SyntaxKind.PredefinedType:
             break;
         default:
             Debug.Assert(false, "Unexpected type syntax kind " + typeSyntax.Kind());
             break;
     }
 }
            private static void AppendTypeName(StringBuilder builder, TypeSyntax type)
            {
                if (type is NameSyntax)
                {
                    AppendName(builder, (NameSyntax)type);
                }
                else
                {
                    switch (type.Kind())
                    {
                        case SyntaxKind.PredefinedType:
                            builder.Append(((PredefinedTypeSyntax)type).Keyword.ValueText);
                            break;

                        case SyntaxKind.ArrayType:
                            var arrayType = (ArrayTypeSyntax)type;
                            AppendTypeName(builder, arrayType.ElementType);

                            var specifiers = arrayType.RankSpecifiers;
                            for (int i = 0; i < specifiers.Count; i++)
                            {
                                builder.Append('[');

                                var specifier = specifiers[i];
                                if (specifier.Rank > 1)
                                {
                                    builder.Append(',', specifier.Rank - 1);
                                }

                                builder.Append(']');
                            }

                            break;

                        case SyntaxKind.PointerType:
                            AppendTypeName(builder, ((PointerTypeSyntax)type).ElementType);
                            builder.Append('*');
                            break;

                        case SyntaxKind.NullableType:
                            AppendTypeName(builder, ((NullableTypeSyntax)type).ElementType);
                            builder.Append('?');
                            break;
                    }
                }
            }
            private bool CompareTypes(TypeSyntax oldType, TypeSyntax newType)
            {
                // Type nodes can be NULL for ctor/dtor/operators ...
                if (oldType == null || newType == null)
                {
                    return oldType == newType;
                }

                if (oldType.Kind() != newType.Kind())
                {
                    return false;
                }

                switch (oldType.Kind())
                {
                    case SyntaxKind.PredefinedType:
                        var oldPredefinedType = (PredefinedTypeSyntax)oldType;
                        var newPredefinedType = (PredefinedTypeSyntax)newType;

                        return oldPredefinedType.Keyword.RawKind == newPredefinedType.Keyword.RawKind;

                    case SyntaxKind.ArrayType:
                        var oldArrayType = (ArrayTypeSyntax)oldType;
                        var newArrayType = (ArrayTypeSyntax)newType;

                        return (oldArrayType.RankSpecifiers.Count == newArrayType.RankSpecifiers.Count)
                            && CompareTypes(oldArrayType.ElementType, newArrayType.ElementType);

                    case SyntaxKind.PointerType:
                        var oldPointerType = (PointerTypeSyntax)oldType;
                        var newPointerType = (PointerTypeSyntax)newType;

                        return CompareTypes(oldPointerType.ElementType, newPointerType.ElementType);

                    case SyntaxKind.NullableType:
                        var oldNullableType = (NullableTypeSyntax)oldType;
                        var newNullableType = (NullableTypeSyntax)newType;

                        return CompareTypes(oldNullableType.ElementType, newNullableType.ElementType);

                    case SyntaxKind.IdentifierName:
                    case SyntaxKind.QualifiedName:
                    case SyntaxKind.AliasQualifiedName:
                    case SyntaxKind.GenericName:
                        var oldName = (NameSyntax)oldType;
                        var newName = (NameSyntax)newType;

                        return CompareNames(oldName, newName);
                }

                Debug.Fail("Unknown kind: " + oldType.Kind());
                return false;
            }
        private static bool IsNotNullableReplaceable(this NameSyntax name, TypeSyntax reducedName)
        {
            var isNotNullableReplaceable = false;
            var isLeftSideOfDot = name.IsLeftSideOfDot();
            var isRightSideOfDot = name.IsRightSideOfDot();

            if (reducedName.Kind() == SyntaxKind.NullableType)
            {
                if (((NullableTypeSyntax)reducedName).ElementType.Kind() == SyntaxKind.OmittedTypeArgument)
                {
                    isNotNullableReplaceable = true;
                }
                else
                {
                    isNotNullableReplaceable = name.IsLeftSideOfDot() || name.IsRightSideOfDot();
                }
            }

            return isNotNullableReplaceable;
        }
        /// <summary>
        /// Make a local variable symbol for an element of a deconstruction,
        /// which can be inferred (if necessary) by binding the enclosing statement.
        /// </summary>
        /// <param name="containingSymbol"></param>
        /// <param name="scopeBinder">
        /// Binder that owns the scope for the local, the one that returns it in its <see cref="Binder.Locals"/> array.
        /// </param>
        /// <param name="nodeBinder">
        /// Enclosing binder for the location where the local is declared.
        /// It should be used to bind something at that location.
        /// </param>
        /// <param name="closestTypeSyntax"></param>
        /// <param name="identifierToken"></param>
        /// <param name="kind"></param>
        /// <param name="deconstruction"></param>
        /// <returns></returns>
        public static SourceLocalSymbol MakeDeconstructionLocal(
            Symbol containingSymbol,
            Binder scopeBinder,
            Binder nodeBinder,
            TypeSyntax closestTypeSyntax,
            SyntaxToken identifierToken,
            LocalDeclarationKind kind,
            SyntaxNode deconstruction)
        {
            Debug.Assert(closestTypeSyntax != null);
            Debug.Assert(nodeBinder != null);

            Debug.Assert(closestTypeSyntax.Kind() != SyntaxKind.RefType);
            return closestTypeSyntax.IsVar
                ? new DeconstructionLocalSymbol(containingSymbol, scopeBinder, nodeBinder, closestTypeSyntax, identifierToken, kind, deconstruction)
                : new SourceLocalSymbol(containingSymbol, scopeBinder, false, closestTypeSyntax, identifierToken, kind);
        }
        private static void ExpandTypeName(TypeSyntax type, StringBuilder builder)
        {
            switch (type.Kind())
            {
                case SyntaxKind.AliasQualifiedName:
                    var alias = (AliasQualifiedNameSyntax)type;
                    builder.Append(alias.Alias.Identifier.ValueText);
                    break;
                case SyntaxKind.ArrayType:
                    var array = (ArrayTypeSyntax)type;
                    ExpandTypeName(array.ElementType, builder);
                    for (int i = 0; i < array.RankSpecifiers.Count; i++)
                    {
                        var rankSpecifier = array.RankSpecifiers[i];
                        builder.Append(rankSpecifier.OpenBracketToken.Text);
                        for (int j = 1; j < rankSpecifier.Sizes.Count; j++)
                        {
                            builder.Append(',');
                        }

                        builder.Append(rankSpecifier.CloseBracketToken.Text);
                    }

                    break;
                case SyntaxKind.GenericName:
                    var generic = (GenericNameSyntax)type;
                    builder.Append(generic.Identifier.ValueText);
                    if (generic.TypeArgumentList != null)
                    {
                        var arguments = generic.TypeArgumentList.Arguments;
                        builder.Append(generic.TypeArgumentList.LessThanToken.Text);
                        for (int i = 0; i < arguments.Count; i++)
                        {
                            if (i != 0)
                            {
                                builder.Append(',');
                            }

                            ExpandTypeName(arguments[i], builder);
                        }

                        builder.Append(generic.TypeArgumentList.GreaterThanToken.Text);
                    }

                    break;
                case SyntaxKind.IdentifierName:
                    var identifierName = (IdentifierNameSyntax)type;
                    builder.Append(identifierName.Identifier.ValueText);
                    break;
                case SyntaxKind.NullableType:
                    var nullable = (NullableTypeSyntax)type;
                    ExpandTypeName(nullable.ElementType, builder);
                    builder.Append(nullable.QuestionToken.Text);
                    break;
                case SyntaxKind.OmittedTypeArgument:
                    // do nothing since it was omitted, but don't reach the default block
                    break;
                case SyntaxKind.PointerType:
                    var pointer = (PointerTypeSyntax)type;
                    ExpandTypeName(pointer.ElementType, builder);
                    builder.Append(pointer.AsteriskToken.Text);
                    break;
                case SyntaxKind.PredefinedType:
                    var predefined = (PredefinedTypeSyntax)type;
                    builder.Append(predefined.Keyword.Text);
                    break;
                case SyntaxKind.QualifiedName:
                    var qualified = (QualifiedNameSyntax)type;
                    ExpandTypeName(qualified.Left, builder);
                    builder.Append(qualified.DotToken.Text);
                    ExpandTypeName(qualified.Right, builder);
                    break;
                default:
                    Debug.Assert(false, "Unexpected type syntax " + type.Kind());
                    break;
            }
        }
        private static void TestGetSpeculativeSemanticModelForTypeSyntax_Common(
            SemanticModel model,
            int position,
            TypeSyntax speculatedTypeSyntax,
            SpeculativeBindingOption bindingOption,
            SymbolKind expectedSymbolKind,
            string expectedTypeDisplayString)
        {
            Assert.False(model.IsSpeculativeSemanticModel);
            Assert.Null(model.ParentModel);
            Assert.Equal(0, model.OriginalPositionForSpeculation);

            SemanticModel speculativeModel;
            var success = model.TryGetSpeculativeSemanticModel(position, speculatedTypeSyntax, out speculativeModel, bindingOption);
            Assert.True(success);
            Assert.NotNull(speculativeModel);

            Assert.True(speculativeModel.IsSpeculativeSemanticModel);
            Assert.Equal(model, speculativeModel.ParentModel);
            Assert.NotNull(speculativeModel);
            Assert.Equal(position, speculativeModel.OriginalPositionForSpeculation);

            var symbol = speculativeModel.GetSymbolInfo(speculatedTypeSyntax).Symbol;
            Assert.NotNull(symbol);
            Assert.Equal(expectedSymbolKind, symbol.Kind);
            Assert.Equal(expectedTypeDisplayString, symbol.ToDisplayString());

            var typeSymbol = speculativeModel.GetTypeInfo(speculatedTypeSyntax).Type;
            Assert.NotNull(symbol);
            Assert.Equal(expectedSymbolKind, symbol.Kind);
            Assert.Equal(expectedTypeDisplayString, symbol.ToDisplayString());

            if (speculatedTypeSyntax.Kind() == SyntaxKind.QualifiedName)
            {
                var right = ((QualifiedNameSyntax)speculatedTypeSyntax).Right;

                symbol = speculativeModel.GetSymbolInfo(right).Symbol;
                Assert.NotNull(symbol);
                Assert.Equal(expectedSymbolKind, symbol.Kind);
                Assert.Equal(expectedTypeDisplayString, symbol.ToDisplayString());

                typeSymbol = speculativeModel.GetTypeInfo(right).Type;
                Assert.NotNull(symbol);
                Assert.Equal(expectedSymbolKind, symbol.Kind);
                Assert.Equal(expectedTypeDisplayString, symbol.ToDisplayString());
            }
        }