/
githubmirror
/
roslyn
Обзор
Документация
Войти
/
githubmirror
/
roslyn
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
src/Compilers/CSharp/Test/Symbol/Symbols/Source/CompletionTests.cs
215 строк
8 KB
AlekseyTs
SourceMemberMethodSymbol.IsMetadataVirtual should force complete declaring type when queried by a different module (#84013)
05 июн 2026, 17:03
Не верифицирован
05 июн 2026, 17:03
04c548a
Код
Авторство
О чём код?
// 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); } [Fact] [WorkItem("https://github.com/dotnet/roslyn/issues/83978")] public void Repro_IsMetadataVirtualLocked_Assertion() { var source1 = """ public struct U : I1 { public object Value => throw null!; } public interface I1 { object Value { get; } } """; var source2 = """ class Program { object M1(U u) { return u.Value; } } """; var comp0 = CreateCompilation(source1); var comp = CreateCompilation(source2, references: [comp0.ToMetadataReference()]); comp.VerifyEmitDiagnostics(); comp0.VerifyEmitDiagnostics(); } } }