File: Symbols\Source\CompletionTests.cs
Web Access
Project: src\src\Compilers\CSharp\Test\Symbol\Microsoft.CodeAnalysis.CSharp.Symbol.UnitTests.csproj (Microsoft.CodeAnalysis.CSharp.Symbol.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.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.CSharp.Symbols;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.CSharp.Test.Utilities;
using Microsoft.CodeAnalysis.Text;
using Roslyn.Test.Utilities;
using Xunit;
 
namespace Microsoft.CodeAnalysis.CSharp.UnitTests
{
    public class CompletionTests : CSharpTestBase
    {
        [Fact]
        public void FieldSymbolsAreLazy()
        {
            var text =
@"
class A {
    int x;
    NotFound y;
    public int F(int x, int y) {}
}
";
            var comp = CreateEmptyCompilation(text, skipUsesIsNullable: true);
            var global = comp.GlobalNamespace;
 
            var a = global.GetMember<NamedTypeSymbol>("A");
            Assert.False(a.HasComplete(CompletionPart.StartBaseType));
            Assert.False(a.HasComplete(CompletionPart.Members));
 
            var x = a.GetMember<FieldSymbol>("x");
            Assert.True(a.HasComplete(CompletionPart.Members)); // getting one member completes the whole set
            Assert.False(a.HasComplete(CompletionPart.StartBaseType));
            Assert.False(x.HasComplete(CompletionPart.Type));
 
            var xType = x.TypeWithAnnotations;
            Assert.True(x.HasComplete(CompletionPart.Type));
            Assert.False(a.HasComplete(CompletionPart.StartBaseType));
 
            var y = a.GetMember<FieldSymbol>("y");
            Assert.False(a.HasComplete(CompletionPart.StartBaseType));
            Assert.False(y.HasComplete(CompletionPart.Type));
 
            var yType = y.TypeWithAnnotations;
            Assert.True(y.HasComplete(CompletionPart.Type));
            Assert.False(a.HasComplete(CompletionPart.StartBaseType)); // needed to look in A's base for y's type
 
            var f = a.GetMember<MethodSymbol>("F");
            Assert.False(f.HasComplete(CompletionPart.StartMethodChecks));
            Assert.False(f.ReturnsVoid);
            Assert.True(f.HasComplete(CompletionPart.StartMethodChecks));
            Assert.True(f.HasComplete(CompletionPart.FinishMethodChecks));
        }
 
        [Fact]
        public void PropertySymbolsAreLazy()
        {
            var text =
@"class A
{
    object P { get; set; }
    object this[object o] { get { return null; } set { } }
}";
            var comp = CreateCompilation(text, skipUsesIsNullable: true);
            var global = comp.GlobalNamespace;
 
            var a = global.GetMember<NamedTypeSymbol>("A");
            Assert.False(a.HasComplete(CompletionPart.StartBaseType));
            Assert.False(a.HasComplete(CompletionPart.Members));
 
            var p = a.GetMember<PropertySymbol>("P");
            Assert.True(a.HasComplete(CompletionPart.Members)); // getting one member completes the whole set
            Assert.False(a.HasComplete(CompletionPart.StartBaseType));
 
            var pType = p.TypeWithAnnotations;
            var pParameters = p.Parameters;
            Assert.False(p.HasComplete(CompletionPart.Type));
            Assert.False(p.HasComplete(CompletionPart.Parameters));
 
            p = a.GetMember<PropertySymbol>("this[]");
            pType = p.TypeWithAnnotations;
            pParameters = p.Parameters;
            Assert.False(p.HasComplete(CompletionPart.Type));
            Assert.False(p.HasComplete(CompletionPart.Parameters));
 
            a.ForceComplete(null, filter: null, CancellationToken.None);
            Assert.True(p.HasComplete(CompletionPart.Type));
            Assert.True(p.HasComplete(CompletionPart.Parameters));
        }
 
        /// <summary>
        /// We used to have a problem where Symbol.NextIncompletePart read from
        /// Symbol.incompleteParts twice, rather than copying the field value
        /// into the temp.  If the value changed in between the reads, NextIncompletePart
        /// would return more than one part, which caused a deadlock in
        /// SourceNamedTypeSymbol.ForceComplete.  This test sometimes, but not always,
        /// failed before the fix was applied.  Now it documents the former problem
        /// and gives us some level of confidence in the fix.
        /// </summary>
        [Fact, WorkItem(546196, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/546196"), WorkItem(546604, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/546604")]
        public void TestNextCompletionPart()
        {
            SymbolCompletionState state = new SymbolCompletionState();
 
            Action reader = () =>
            {
                while (state.IncompleteParts != 0)
                {
                    Assert.True(SymbolCompletionState.HasAtMostOneBitSet((int)state.NextIncompletePart));
                }
            };
 
            Action writers = () =>
            {
                Parallel.For(0, Math.Max(1, Environment.ProcessorCount - 1), t =>
                {
                    Random r = new Random(t);
                    while (state.IncompleteParts != 0)
                    {
                        CompletionPart part = (CompletionPart)(1 << r.Next(8 * sizeof(CompletionPart)));
                        state.NotePartComplete(part);
                    }
                });
            };
 
            for (int i = 0; i < 1000; i++)
            {
                Parallel.Invoke(reader, writers);
            }
        }
 
        /// <summary>
        /// This test demonstrates the correctness of <see cref="SymbolCompletionState.HasAtMostOneBitSet"/>.
        /// </summary>
        [Fact]
        public void TestHasAtMostOneBitSet()
        {
            for (int i = sbyte.MinValue; i <= sbyte.MaxValue; i++)
            {
                sbyte b = (sbyte)i;
                Assert.Equal(HasAtMostOneBitSetSafe(b), HasAtMostOneBitSetFast(b));
            }
        }
 
        /// <summary>
        /// This is the simple implementation of the sbyte version of <see cref="SymbolCompletionState.HasAtMostOneBitSet"/>.
        /// Hopefully, it is obviously correct.
        /// </summary>
        private static bool HasAtMostOneBitSetSafe(sbyte bits)
        {
            bool seenOne = false;
            for (int i = 0; i < sizeof(sbyte) * 8; i++)
            {
                if ((bits & (1 << i)) != 0)
                {
                    if (seenOne)
                    {
                        return false;
                    }
                    seenOne = true;
                }
            }
            return true;
        }
 
        /// <summary>
        /// This is the sbyte version of <see cref="SymbolCompletionState.HasAtMostOneBitSet"/>.
        /// It can be exhaustively tested more quickly than the full version.
        /// </summary>
        private static bool HasAtMostOneBitSetFast(sbyte bits)
        {
            return unchecked((bits & (sbyte)(bits - 1)) == 0);
        }
    }
}