File: Semantics\InteractiveSemanticModelTests.cs
Web Access
Project: src\src\Compilers\CSharp\Test\Semantic\Microsoft.CodeAnalysis.CSharp.Semantic.UnitTests.csproj (Microsoft.CodeAnalysis.CSharp.Semantic.UnitTests)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
 
#nullable disable
 
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.CodeAnalysis.CSharp.Symbols;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.CSharp.Test.Utilities;
using Microsoft.CodeAnalysis.Test.Utilities;
using Microsoft.CodeAnalysis.Text;
using Roslyn.Test.Utilities;
using Xunit;
 
namespace Microsoft.CodeAnalysis.CSharp.UnitTests
{
    public partial class SemanticModelTests : CSharpTestBase
    {
        [Fact]
        public void NamespaceBindingInInteractiveCode()
        {
            var compilation = CreateCompilation(@"
using Z = Goo.Bar.Script.C;
 
class C { }
 
namespace Goo.Bar
{
    class B : Z { }
}
",
                parseOptions: TestOptions.Script,
                options: TestOptions.ReleaseExe.WithScriptClassName("Goo.Bar.Script")
            );
 
            var tree = compilation.SyntaxTrees[0];
            var root = tree.GetCompilationUnitRoot();
            var classB = (root.Members[1] as NamespaceDeclarationSyntax).Members[0] as TypeDeclarationSyntax;
            var model = compilation.GetSemanticModel(tree);
            var symbol = model.GetDeclaredSymbol(classB);
            var baseType = symbol?.BaseType;
            Assert.NotNull(baseType);
            Assert.Equal(TypeKind.Error, baseType.TypeKind);
            Assert.Equal(LookupResultKind.Inaccessible, baseType.GetSymbol<ErrorTypeSymbol>().ResultKind); // Script class members are private.
        }
 
        [Fact]
        public void CompilationChain_OverloadsWithParams()
        {
            CompileAndVerifyBindInfo(@"
public static string[] str = null;
public static void Goo(string[] r, string i) { str = r;}
public static void Goo(params string[] r) { str = r;}
/*<bind>*/ Goo(""1"", ""2"") /*</bind>*/;",
"Goo(params string[])");
        }
 
        [Fact]
        public void CompilationChain_NestedTypesClass()
        {
            CompileAndVerifyBindInfo(@"
class InnerClass
{
   public string innerStr = null;
   public string Goo() { return innerStr;}       
}
InnerClass iC = new InnerClass();
/*<bind>*/ iC.Goo(); /*</bind>*/",
"InnerClass.Goo()");
        }
 
        [Fact]
        public void MethodCallBinding()
        {
            var testSrc = @"
void Goo() {};
/*<bind>*/Goo()/*</bind>*/;
";
            // Get the bind info for the text identified within the commented <bind> </bind> tags
            var bindInfo = GetBindInfoForTest(testSrc);
 
            Assert.NotNull(bindInfo.Type);
            Assert.Equal("System.Void", bindInfo.Type.ToTestDisplayString());
        }
 
        [Fact]
        public void BindNullLiteral()
        {
            var testSrc = @"string s = /*<bind>*/null/*</bind>*/;";
            // Get the bind info for the text identified within the commented <bind> </bind> tags
            var bindInfo = GetBindInfoForTest(testSrc);
            Assert.Null(bindInfo.Type);
        }
 
        [Fact]
        public void BindBooleanField()
        {
            var testSrc = @"
bool result = true ;
/*<bind>*/ result /*</bind>*/= false;
";
            // Get the bind info for the text identified within the commented <bind> </bind> tags
            var bindInfo = GetBindInfoForTest(testSrc);
            Assert.NotNull(bindInfo.Type);
            Assert.Equal("System.Boolean", bindInfo.Type.ToTestDisplayString());
        }
 
        [Fact]
        public void BindLocals()
        {
            var testSrc = @"
const int constantField = 1;
int field = constantField;
{
    int local1 = field;
    int local2 = /*<bind>*/local1/*</bind>*/;
}
{
    int local2 = constantField;
}";
            // Get the bind info for the text identified within the commented <bind> </bind> tags
            var bindInfo = GetBindInfoForTest(testSrc);
            Assert.Equal(SpecialType.System_Int32, bindInfo.Type.SpecialType);
            var symbol = bindInfo.Symbol;
            Assert.Equal("System.Int32 local1", symbol.ToTestDisplayString());
            Assert.IsAssignableFrom<SourceLocalSymbol>(symbol.GetSymbol());
        }
 
        [WorkItem(540513, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/540513")]
        [Fact]
        public void BindVariableInGlobalStatement()
        {
            var testSrc = @"
int i = 2;
++/*<bind>*/i/*</bind>*/;";
            // Get the bind info for the text identified within the commented <bind> </bind> tags
            var bindInfo = GetBindInfoForTest(testSrc);
            Assert.Equal(SpecialType.System_Int32, bindInfo.Type.SpecialType);
            var symbol = bindInfo.Symbol;
            Assert.Equal("System.Int32 Script.i", symbol.ToTestDisplayString());
            Assert.Equal(SymbolKind.Field, symbol.Kind);
        }
 
        [WorkItem(543860, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/543860")]
        [Fact]
        public void BindVarKeyword()
        {
            var testSrc = @"
/*<bind>*/var/*</bind>*/ rand = new System.Random();";
 
            // Get the bind info for the text identified within the commented <bind> </bind> tags
            var semanticInfo = GetBindInfoForTest(testSrc);
 
            Assert.Equal("System.Random", semanticInfo.Type.ToTestDisplayString());
            Assert.Equal(TypeKind.Class, semanticInfo.Type.TypeKind);
            Assert.Equal("System.Random", semanticInfo.ConvertedType.ToTestDisplayString());
            Assert.Equal(TypeKind.Class, semanticInfo.ConvertedType.TypeKind);
            Assert.Equal(ConversionKind.Identity, semanticInfo.ImplicitConversion.Kind);
 
            Assert.Equal("System.Random", semanticInfo.Symbol.ToTestDisplayString());
            Assert.Equal(SymbolKind.NamedType, semanticInfo.Symbol.Kind);
            Assert.Equal(0, semanticInfo.CandidateSymbols.Length);
 
            Assert.Equal(0, semanticInfo.MethodGroup.Length);
 
            Assert.False(semanticInfo.IsCompileTimeConstant);
        }
 
        [WorkItem(543860, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/543860")]
        [Fact]
        public void BindVarKeyword_MultipleDeclarators()
        {
            string testSrc = @"
/*<bind>*/var/*</bind>*/ i = new int(), j = new char();
";
            // Get the bind info for the text identified within the commented <bind> </bind> tags
            var semanticInfo = GetBindInfoForTest(testSrc);
 
            Assert.Equal("var", semanticInfo.Type.ToTestDisplayString());
            Assert.Equal(TypeKind.Error, semanticInfo.Type.TypeKind);
            Assert.Equal("var", semanticInfo.ConvertedType.ToTestDisplayString());
            Assert.Equal(TypeKind.Error, semanticInfo.ConvertedType.TypeKind);
            Assert.Equal(ConversionKind.Identity, semanticInfo.ImplicitConversion.Kind);
 
            Assert.Null(semanticInfo.Symbol);
            Assert.Equal(CandidateReason.None, semanticInfo.CandidateReason);
            Assert.Equal(0, semanticInfo.CandidateSymbols.Length);
 
            Assert.Equal(0, semanticInfo.MethodGroup.Length);
 
            Assert.False(semanticInfo.IsCompileTimeConstant);
        }
 
        [WorkItem(543860, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/543860")]
        [Fact]
        public void BindVarNamedType()
        {
            string testSrc = @"
public class var { }
/*<bind>*/var/*</bind>*/ x = new var();
";
            // Get the bind info for the text identified within the commented <bind> </bind> tags
            var semanticInfo = GetBindInfoForTest(testSrc);
 
            Assert.Equal("Script.var", semanticInfo.Type.ToTestDisplayString());
            Assert.Equal(TypeKind.Class, semanticInfo.Type.TypeKind);
            Assert.Equal("Script.var", semanticInfo.ConvertedType.ToTestDisplayString());
            Assert.Equal(TypeKind.Class, semanticInfo.ConvertedType.TypeKind);
            Assert.Equal(ConversionKind.Identity, semanticInfo.ImplicitConversion.Kind);
 
            Assert.Equal("Script.var", semanticInfo.Symbol.ToTestDisplayString());
            Assert.Equal(SymbolKind.NamedType, semanticInfo.Symbol.Kind);
            Assert.Equal(0, semanticInfo.CandidateSymbols.Length);
 
            Assert.Equal(0, semanticInfo.MethodGroup.Length);
 
            Assert.False(semanticInfo.IsCompileTimeConstant);
        }
 
        [WorkItem(543860, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/543860")]
        [Fact]
        public void BindVarNamedType_Ambiguous()
        {
            string testSrc = @"
using System;
public class var { }
public struct var { }
/*<bind>*/var/*</bind>*/ x = new var();
";
            // Get the bind info for the text identified within the commented <bind> </bind> tags
            var semanticInfo = GetBindInfoForTest(testSrc);
 
            Assert.Equal("Script.var", semanticInfo.Type.ToTestDisplayString());
            Assert.Equal(TypeKind.Error, semanticInfo.Type.TypeKind);
            Assert.Equal("Script.var", semanticInfo.ConvertedType.ToTestDisplayString());
            Assert.Equal(TypeKind.Error, semanticInfo.ConvertedType.TypeKind);
            Assert.Equal(ConversionKind.Identity, semanticInfo.ImplicitConversion.Kind);
 
            Assert.Null(semanticInfo.Symbol);
            Assert.Equal(CandidateReason.Ambiguous, semanticInfo.CandidateReason);
            Assert.Equal(2, semanticInfo.CandidateSymbols.Length);
            var sortedCandidates = semanticInfo.CandidateSymbols.OrderBy(s => s.ToTestDisplayString()).ToArray();
            Assert.Equal("Script.var", sortedCandidates[0].ToTestDisplayString());
            Assert.Equal(SymbolKind.NamedType, sortedCandidates[0].Kind);
            Assert.Equal("Script.var", sortedCandidates[1].ToTestDisplayString());
            Assert.Equal(SymbolKind.NamedType, sortedCandidates[1].Kind);
 
            Assert.Equal(0, semanticInfo.MethodGroup.Length);
 
            Assert.False(semanticInfo.IsCompileTimeConstant);
        }
 
        [WorkItem(543864, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/543864")]
        [Fact]
        public void BindQueryVariable()
        {
            string testSrc = @"
using System.Linq;
 
var x = from c in ""goo"" select /*<bind>*/c/*</bind>*/";
            // Get the bind info for the text identified within the commented <bind> </bind> tags
            var semanticInfo = GetBindInfoForTest(testSrc);
            Assert.Equal("c", semanticInfo.Symbol.Name);
            Assert.Equal(SymbolKind.RangeVariable, semanticInfo.Symbol.Kind);
            Assert.Equal(SpecialType.System_Char, semanticInfo.Type.SpecialType);
        }
 
        #region helpers
 
        private List<ExpressionSyntax> GetExprSyntaxList(SyntaxTree syntaxTree)
        {
            return GetExprSyntaxList(syntaxTree.GetCompilationUnitRoot(), null);
        }
 
        private List<ExpressionSyntax> GetExprSyntaxList(SyntaxNode node, List<ExpressionSyntax> exprSynList)
        {
            if (exprSynList == null)
                exprSynList = new List<ExpressionSyntax>();
 
            if (node is ExpressionSyntax)
            {
                exprSynList.Add(node as ExpressionSyntax);
            }
 
            foreach (var child in node.ChildNodesAndTokens())
            {
                if (child.IsNode)
                    exprSynList = GetExprSyntaxList(child.AsNode(), exprSynList);
            }
 
            return exprSynList;
        }
 
        private ExpressionSyntax GetExprSyntaxForBinding(List<ExpressionSyntax> exprSynList)
        {
            foreach (var exprSyntax in exprSynList)
            {
                string exprFullText = exprSyntax.ToFullString();
                exprFullText = exprFullText.Trim();
 
                if (exprFullText.StartsWith("/*<bind>*/", StringComparison.Ordinal))
                {
                    if (exprFullText.Contains("/*</bind>*/"))
                    {
                        if (exprFullText.EndsWith("/*</bind>*/", StringComparison.Ordinal))
                        {
                            return exprSyntax;
                        }
                        else
                        {
                            continue;
                        }
                    }
                    else
                    {
                        return exprSyntax;
                    }
                }
 
                if (exprFullText.EndsWith("/*</bind>*/", StringComparison.Ordinal))
                {
                    if (exprFullText.Contains("/*<bind>*/"))
                    {
                        if (exprFullText.StartsWith("/*<bind>*/", StringComparison.Ordinal))
                        {
                            return exprSyntax;
                        }
                        else
                        {
                            continue;
                        }
                    }
                    else
                    {
                        return exprSyntax;
                    }
                }
            }
 
            return null;
        }
 
        private void CompileAndVerifyBindInfo(string testSrc, string expected)
        {
            // Get the bind info for the text identified within the commented <bind> </bind> tags
            var bindInfo = GetBindInfoForTest(testSrc);
            Assert.NotNull(bindInfo.Type);
            var method = bindInfo.Symbol.ToDisplayString();
            Assert.Equal(expected, method);
        }
 
        private CompilationUtils.SemanticInfoSummary GetBindInfoForTest(string testSrc)
        {
            var compilation = CreateCompilation(testSrc, parseOptions: TestOptions.Script);
            var tree = compilation.SyntaxTrees[0];
            var model = compilation.GetSemanticModel(tree);
            var exprSyntaxToBind = GetExprSyntaxForBinding(GetExprSyntaxList(tree));
 
            return model.GetSemanticInfoSummary(exprSyntaxToBind);
        }
 
        #endregion helpers
    }
}