/
githubmirror
/
roslyn
Обзор
Документация
Войти
/
githubmirror
/
roslyn
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
src/Workspaces/CoreTest/SemanticModelReuse/SemanticModelReuseTests.cs
596 строк
27 KB
Cyrus Najmabadi
Use raw strings in tests
09 июл 2025, 08:52
09 июл 2025, 08:52
f265dce
Код
Авторство
О чём код?
// 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.Threading; using System.Threading.Tasks; using Basic.Reference.Assemblies; using Microsoft.CodeAnalysis.Collections; using Microsoft.CodeAnalysis.CSharp.Extensions.ContextQuery; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Test.Utilities; using Microsoft.CodeAnalysis.Text; using Microsoft.CodeAnalysis.VisualBasic.Extensions.ContextQuery; using Roslyn.Test.Utilities; using Xunit; namespace Microsoft.CodeAnalysis.UnitTests.SemanticModelReuse; [UseExportProvider] public sealed class SemanticModelReuseTests { private static Document CreateDocument(string code, string language) { var solution = new AdhocWorkspace().CurrentSolution; var projectId = ProjectId.CreateNewId(); var project = solution.AddProject(projectId, "Project", "Project.dll", language).GetProject(projectId); return project.AddMetadataReference(Net40.References.mscorlib) .AddDocument("Document", SourceText.From(code)); } #region C# tests [Fact] public async Task NullBodyReturnsNormalSemanticModel1_CSharp() { var document = CreateDocument("", LanguageNames.CSharp); // trying to get a model for null should return a non-speculative model var model = await document.ReuseExistingSpeculativeModelAsync(null, CancellationToken.None); Assert.False(model.IsSpeculativeSemanticModel); } [Fact] public async Task NullBodyReturnsNormalSemanticModel2_CSharp() { var source = "class C { void M() { return; } }"; var document = CreateDocument(source, LanguageNames.CSharp); // Even if we've primed things with a real location, getting a semantic model for null should return a // non-speculative model. var model1 = await document.ReuseExistingSpeculativeModelAsync(source.IndexOf("return"), CancellationToken.None); var model2 = await document.ReuseExistingSpeculativeModelAsync(null, CancellationToken.None); Assert.False(model1.IsSpeculativeSemanticModel); Assert.False(model2.IsSpeculativeSemanticModel); } [Fact] public async Task SameSyntaxTreeReturnsNonSpeculativeModel_CSharp() { var source = "class C { void M() { return; } }"; var document = CreateDocument(source, LanguageNames.CSharp); // First call will prime the cache to point at the real semantic model. The next call will also use the // same syntax tree, so it should get the same semantic model. var model1 = await document.ReuseExistingSpeculativeModelAsync(source.IndexOf("return"), CancellationToken.None); var model2 = await document.ReuseExistingSpeculativeModelAsync(source.IndexOf("return"), CancellationToken.None); Assert.False(model1.IsSpeculativeSemanticModel); Assert.False(model2.IsSpeculativeSemanticModel); // Should be the same models. Assert.Equal(model1, model2); // Which also should be the normal model the document provides. var actualModel = await document.GetSemanticModelAsync(); Assert.Equal(model1, actualModel); } [Fact] public async Task InBodyEditShouldProduceCachedModel_CSharp() { var source = "class C { void M() { return; } }"; var document1 = CreateDocument(source, LanguageNames.CSharp); // First call will prime the cache to point at the real semantic model. var model1 = await document1.ReuseExistingSpeculativeModelAsync(source.IndexOf("return"), CancellationToken.None); Assert.False(model1.IsSpeculativeSemanticModel); var document2 = document1.WithText(SourceText.From("class C { void M() { return null; } }")); // This should be able to get a speculative model using the original model we primed the cache with. var model2 = await document2.ReuseExistingSpeculativeModelAsync(source.IndexOf("return"), CancellationToken.None); Assert.True(model2.IsSpeculativeSemanticModel); } [Fact] public async Task OutOfBodyEditShouldProduceFreshModel_CSharp() { var source = "class C { void M() { return; } }"; var document1 = CreateDocument(source, LanguageNames.CSharp); // First call will prime the cache to point at the real semantic model. var model1 = await document1.ReuseExistingSpeculativeModelAsync(source.IndexOf("return"), CancellationToken.None); Assert.False(model1.IsSpeculativeSemanticModel); var document2 = document1.WithText(SourceText.From("class C { long M() { return; } }")); // We changed the return type, so we can't reuse the previous model. var model2 = await document2.ReuseExistingSpeculativeModelAsync(source.IndexOf("return"), CancellationToken.None); Assert.False(model2.IsSpeculativeSemanticModel); var document3 = document2.WithText(SourceText.From("class C { long M() { return 0; } }")); // We are now again only editing a method body so we should be able to get a speculative model. var model3 = await document3.ReuseExistingSpeculativeModelAsync(source.IndexOf("return"), CancellationToken.None); Assert.True(model3.IsSpeculativeSemanticModel); } [Fact] public async Task MultipleBodyEditsShouldProduceFreshModel_CSharp() { var source = "class C { void M() { return; } }"; var document1 = CreateDocument(source, LanguageNames.CSharp); // First call will prime the cache to point at the real semantic model. var model1 = await document1.ReuseExistingSpeculativeModelAsync(source.IndexOf("return"), CancellationToken.None); Assert.False(model1.IsSpeculativeSemanticModel); var document2 = document1.WithText(SourceText.From("class C { void M() { return 0; } }")); // This should be able to get a speculative model using the original model we primed the cache with. var model2 = await document2.ReuseExistingSpeculativeModelAsync(source.IndexOf("return"), CancellationToken.None); Assert.True(model2.IsSpeculativeSemanticModel); var document3 = document1.WithText(SourceText.From("class C { void M() { return 1; } }")); // This should be able to get a speculative model using the original model we primed the cache with. var model3 = await document3.ReuseExistingSpeculativeModelAsync(source.IndexOf("return"), CancellationToken.None); Assert.True(model3.IsSpeculativeSemanticModel); } [Fact, WorkItem("https://devdiv.visualstudio.com/DevDiv/_workitems/edit/1167540")] public async Task MultipleBodyEditsShouldProduceFreshModel_Accessor_Property_CSharp() { var source = "class C { int M { get { return 0; } } }"; var document1 = CreateDocument(source, LanguageNames.CSharp); // First call will prime the cache to point at the real semantic model. var model1 = await document1.ReuseExistingSpeculativeModelAsync(source.IndexOf("return"), CancellationToken.None); Assert.False(model1.IsSpeculativeSemanticModel); var document2 = document1.WithText(SourceText.From("class C { int M { get { return 1; } } }")); // This should be able to get a speculative model using the original model we primed the cache with. var model2 = await document2.ReuseExistingSpeculativeModelAsync(source.IndexOf("return"), CancellationToken.None); Assert.True(model2.IsSpeculativeSemanticModel); var document3 = document1.WithText(SourceText.From("class C { int M { get { return 2; } } }")); // This should be able to get a speculative model using the original model we primed the cache with. var model3 = await document3.ReuseExistingSpeculativeModelAsync(source.IndexOf("return"), CancellationToken.None); Assert.True(model3.IsSpeculativeSemanticModel); } [Fact, WorkItem("https://devdiv.visualstudio.com/DevDiv/_workitems/edit/1167540")] public async Task MultipleBodyEditsShouldProduceFreshModel_Accessor_Event_CSharp() { var source = "class C { event System.Action E { add { return 0; } } }"; var document1 = CreateDocument(source, LanguageNames.CSharp); // First call will prime the cache to point at the real semantic model. var model1 = await document1.ReuseExistingSpeculativeModelAsync(source.IndexOf("return"), CancellationToken.None); Assert.False(model1.IsSpeculativeSemanticModel); var document2 = document1.WithText(SourceText.From("class C { event System.Action E { add { return 1; } } }")); // This should be able to get a speculative model using the original model we primed the cache with. var model2 = await document2.ReuseExistingSpeculativeModelAsync(source.IndexOf("return"), CancellationToken.None); Assert.True(model2.IsSpeculativeSemanticModel); var document3 = document1.WithText(SourceText.From("class C { event System.Action E { add { return 2; } } }")); // This should be able to get a speculative model using the original model we primed the cache with. var model3 = await document3.ReuseExistingSpeculativeModelAsync(source.IndexOf("return"), CancellationToken.None); Assert.True(model3.IsSpeculativeSemanticModel); } [Fact, WorkItem("https://devdiv.visualstudio.com/DevDiv/_workitems/edit/1167540")] public async Task MultipleBodyEditsShouldProduceFreshModel_Accessor_Indexer_CSharp() { var source = "class C { int this[int i] { get { return 0; } } }"; var document1 = CreateDocument(source, LanguageNames.CSharp); // First call will prime the cache to point at the real semantic model. var model1 = await document1.ReuseExistingSpeculativeModelAsync(source.IndexOf("return"), CancellationToken.None); Assert.False(model1.IsSpeculativeSemanticModel); var document2 = document1.WithText(SourceText.From("class C { int this[int i] { get { return 1; } } }")); // This should be able to get a speculative model using the original model we primed the cache with. var model2 = await document2.ReuseExistingSpeculativeModelAsync(source.IndexOf("return"), CancellationToken.None); Assert.True(model2.IsSpeculativeSemanticModel); var document3 = document1.WithText(SourceText.From("class C { int this[int i] { get { return 2; } } }")); // This should be able to get a speculative model using the original model we primed the cache with. var model3 = await document3.ReuseExistingSpeculativeModelAsync(source.IndexOf("return"), CancellationToken.None); Assert.True(model3.IsSpeculativeSemanticModel); } [Fact, WorkItem("https://devdiv.visualstudio.com/DevDiv/_workitems/edit/1541001")] [WorkItem("https://devdiv.visualstudio.com/DevDiv/_workitems/edit/1587699")] public async Task TestOutOfBoundsInSyntaxContext1_CSharp() { var source = "class C { void M() { return; } }"; var document1 = CreateDocument(source, LanguageNames.CSharp); // First call will prime the cache to point at the real semantic model. var model1 = await document1.ReuseExistingSpeculativeModelAsync(source.IndexOf("return"), CancellationToken.None); Assert.False(model1.IsSpeculativeSemanticModel); var document2 = document1.WithText(SourceText.From("class C { void M() { return null; } }")); // This should be able to get a speculative model using the original model we primed the cache with. var model2 = await document2.ReuseExistingSpeculativeModelAsync(source.IndexOf("return"), CancellationToken.None); Assert.True(model2.IsSpeculativeSemanticModel); // ensure this doesn't crash. CSharpSyntaxContext.CreateContext(document2, model2, source.IndexOf("void"), CancellationToken.None); } [Fact, WorkItem("https://devdiv.visualstudio.com/DevDiv/_workitems/edit/1541001")] [WorkItem("https://devdiv.visualstudio.com/DevDiv/_workitems/edit/1587699")] public async Task TestOutOfBoundsInSyntaxContext2_CSharp() { // These two tree are considered equavilent at top level, but the change in trivia around the method // makes it tricky to decide whether it's safe to use the speculative model at a given position. var source1 = "class C { void M() { return; } }"; // ^ this is the position used to set OriginalPositionForSpeculation when creating the speculative model. var source2 = "class C { void M() { return null; } }"; // ^ it's unsafe to use the speculative model at this position, even though it's after OriginalPositionForSpeculation // First call will prime the cache to point at the real semantic model. var document1 = CreateDocument(source1, LanguageNames.CSharp); var model1 = await document1.ReuseExistingSpeculativeModelAsync(source1.IndexOf("return"), CancellationToken.None); Assert.False(model1.IsSpeculativeSemanticModel); var document2 = document1.WithText(SourceText.From(source2)); // Because the change in trivia shifted the method definition, we are not able to get a speculative model based on previous model var model2 = await document2.ReuseExistingSpeculativeModelAsync(source2.IndexOf("{ return"), CancellationToken.None); Assert.False(model2.IsSpeculativeSemanticModel); // ensure this doesn't crash. CSharpSyntaxContext.CreateContext(document2, model2, source2.IndexOf("{ return"), CancellationToken.None); } #endregion #region Visual Basic tests [Fact] public async Task NullBodyReturnsNormalSemanticModel1_VisualBasic() { var document = CreateDocument("", LanguageNames.VisualBasic); // trying to get a model for null should return a non-speculative model var model = await document.ReuseExistingSpeculativeModelAsync(null, CancellationToken.None); Assert.False(model.IsSpeculativeSemanticModel); } [Fact] public async Task NullBodyReturnsNormalSemanticModel2_VisualBasic() { var source = """ class C sub M() return end sub end class """; var document = CreateDocument(source, LanguageNames.VisualBasic); // Even if we've primed things with a real location, getting a semantic model for null should return a // non-speculative model. var model1 = await document.ReuseExistingSpeculativeModelAsync(source.IndexOf("return"), CancellationToken.None); var model2 = await document.ReuseExistingSpeculativeModelAsync(null, CancellationToken.None); Assert.False(model1.IsSpeculativeSemanticModel); Assert.False(model2.IsSpeculativeSemanticModel); } [Fact] public async Task SameSyntaxTreeReturnsNonSpeculativeModel_VisualBasic() { var source = """ class C sub M() return end sub end class """; var document = CreateDocument(source, LanguageNames.VisualBasic); // First call will prime the cache to point at the real semantic model. The next call will also use the // same syntax tree, so it should get the same semantic model. var model1 = await document.ReuseExistingSpeculativeModelAsync(source.IndexOf("return"), CancellationToken.None); var model2 = await document.ReuseExistingSpeculativeModelAsync(source.IndexOf("return"), CancellationToken.None); Assert.False(model1.IsSpeculativeSemanticModel); Assert.False(model2.IsSpeculativeSemanticModel); // Should be the same models. Assert.Equal(model1, model2); // Which also should be the normal model the document provides. var actualModel = await document.GetSemanticModelAsync(); Assert.Equal(model1, actualModel); } [Fact] public async Task InBodyEditShouldProduceCachedModel_VisualBasic() { var source = """ class C sub M() return end sub end class """; var document1 = CreateDocument(source, LanguageNames.VisualBasic); // First call will prime the cache to point at the real semantic model. var model1 = await document1.ReuseExistingSpeculativeModelAsync(source.IndexOf("return"), CancellationToken.None); Assert.False(model1.IsSpeculativeSemanticModel); var document2 = document1.WithText(SourceText.From(""" class C sub M() return nothing end sub end class """)); // This should be able to get a speculative model using the original model we primed the cache with. var model2 = await document2.ReuseExistingSpeculativeModelAsync(source.IndexOf("return"), CancellationToken.None); Assert.True(model2.IsSpeculativeSemanticModel); } [Fact] public async Task OutOfBodyEditShouldProduceFreshModel_VisualBasic() { var source1 = """ class C sub M() return end sub end class """; var document1 = CreateDocument(source1, LanguageNames.VisualBasic); // First call will prime the cache to point at the real semantic model. var model1 = await document1.ReuseExistingSpeculativeModelAsync(source1.IndexOf("return"), CancellationToken.None); Assert.False(model1.IsSpeculativeSemanticModel); var source2 = """ class C function M() as long return end function end class """; var document2 = document1.WithText(SourceText.From(source2)); // We changed the return type, so we can't reuse the previous model. var model2 = await document2.ReuseExistingSpeculativeModelAsync(source2.IndexOf("return"), CancellationToken.None); Assert.False(model2.IsSpeculativeSemanticModel); var document3 = document2.WithText(SourceText.From(""" class C function M() as long return 0 end function end class """)); // We are now again only editing a method body so we should be able to get a speculative model. var model3 = await document3.ReuseExistingSpeculativeModelAsync(source2.IndexOf("return"), CancellationToken.None); Assert.True(model3.IsSpeculativeSemanticModel); } [Fact] public async Task MultipleBodyEditsShouldProduceFreshModel_VisualBasic() { var source = """ class C sub M() return end sub end class """; var document1 = CreateDocument(source, LanguageNames.VisualBasic); // First call will prime the cache to point at the real semantic model. var model1 = await document1.ReuseExistingSpeculativeModelAsync(source.IndexOf("return"), CancellationToken.None); Assert.False(model1.IsSpeculativeSemanticModel); var document2 = document1.WithText(SourceText.From(""" class C sub M() return 0 end sub end class """)); // This should be able to get a speculative model using the original model we primed the cache with. var model2 = await document2.ReuseExistingSpeculativeModelAsync(source.IndexOf("return"), CancellationToken.None); Assert.True(model2.IsSpeculativeSemanticModel); var document3 = document1.WithText(SourceText.From(""" class C sub M() return 1 end sub end class """)); // This should be able to get a speculative model using the original model we primed the cache with. var model3 = await document3.ReuseExistingSpeculativeModelAsync(source.IndexOf("return"), CancellationToken.None); Assert.True(model3.IsSpeculativeSemanticModel); } [Fact] public async Task MultipleBodyEditsShouldProduceFreshModel_Accessor_Property_VisualBasic() { var source = """ class C readonly property M as integer get return 0 end get end property end class """; var document1 = CreateDocument(source, LanguageNames.VisualBasic); // First call will prime the cache to point at the real semantic model. var model1 = await document1.ReuseExistingSpeculativeModelAsync(source.IndexOf("return"), CancellationToken.None); Assert.False(model1.IsSpeculativeSemanticModel); var document2 = document1.WithText(SourceText.From(""" class C readonly property M as integer get return 1 end get end property end class """)); // This should be able to get a speculative model using the original model we primed the cache with. var model2 = await document2.ReuseExistingSpeculativeModelAsync(source.IndexOf("return"), CancellationToken.None); Assert.True(model2.IsSpeculativeSemanticModel); var document3 = document1.WithText(SourceText.From(""" class C readonly property M as integer get return 2 end get end property end class """)); // This should be able to get a speculative model using the original model we primed the cache with. var model3 = await document3.ReuseExistingSpeculativeModelAsync(source.IndexOf("return"), CancellationToken.None); Assert.True(model3.IsSpeculativeSemanticModel); } [Fact] public async Task MultipleBodyEditsShouldProduceFreshModel_Accessor_Event_VisualBasic() { var source = """ class C public custom event E as System.Action addhandler(value as System.Action) return 0 end addhandler end event end class """; var document1 = CreateDocument(source, LanguageNames.VisualBasic); // First call will prime the cache to point at the real semantic model. var model1 = await document1.ReuseExistingSpeculativeModelAsync(source.IndexOf("return"), CancellationToken.None); Assert.False(model1.IsSpeculativeSemanticModel); var document2 = document1.WithText(SourceText.From(""" class C public custom event E as System.Action addhandler(value as System.Action) return 1 end addhandler end event end class """)); // This should be able to get a speculative model using the original model we primed the cache with. var model2 = await document2.ReuseExistingSpeculativeModelAsync(source.IndexOf("return"), CancellationToken.None); Assert.True(model2.IsSpeculativeSemanticModel); var document3 = document1.WithText(SourceText.From(""" class C public custom event E as System.Action addhandler(value as System.Action) return 2 end addhandler end event end class """)); // This should be able to get a speculative model using the original model we primed the cache with. var model3 = await document3.ReuseExistingSpeculativeModelAsync(source.IndexOf("return"), CancellationToken.None); Assert.True(model3.IsSpeculativeSemanticModel); } [Fact, WorkItem("https://devdiv.visualstudio.com/DevDiv/_workitems/edit/1541001")] [WorkItem("https://devdiv.visualstudio.com/DevDiv/_workitems/edit/1587699")] public async Task TestOutOfBoundsInSyntaxContext1_VisualBasic() { var source = """ class C sub M() return end sub end class """; var document1 = CreateDocument(source, LanguageNames.VisualBasic); // First call will prime the cache to point at the real semantic model. var model1 = await document1.ReuseExistingSpeculativeModelAsync(source.IndexOf("return"), CancellationToken.None); Assert.False(model1.IsSpeculativeSemanticModel); var document2 = document1.WithText(SourceText.From(""" class C sub M() return nothing end sub end class """)); // This should be able to get a speculative model using the original model we primed the cache with. var model2 = await document2.ReuseExistingSpeculativeModelAsync(source.IndexOf("return"), CancellationToken.None); Assert.True(model2.IsSpeculativeSemanticModel); // Ensure this doesn't crash. VisualBasicSyntaxContext.CreateContext(document2, model2, source.IndexOf("sub"), CancellationToken.None); } [Fact, WorkItem("https://devdiv.visualstudio.com/DevDiv/_workitems/edit/1541001")] [WorkItem("https://devdiv.visualstudio.com/DevDiv/_workitems/edit/1587699")] public async Task TestOutOfBoundsInSyntaxContext2_VisualBasic() { var source = """ class C sub M() return end sub end class """; var document1 = CreateDocument(source, LanguageNames.VisualBasic); // First call will prime the cache to point at the real semantic model. var model1 = await document1.ReuseExistingSpeculativeModelAsync(source.IndexOf("return"), CancellationToken.None); Assert.False(model1.IsSpeculativeSemanticModel); var document2 = document1.WithText(SourceText.From(""" class C class C sub M() return nothing end sub end class """)); // Because the change in trivia shifted the method definition, we are not able to get a speculative model based on previous model var model2 = await document2.ReuseExistingSpeculativeModelAsync(source.IndexOf("return"), CancellationToken.None); Assert.False(model2.IsSpeculativeSemanticModel); // Ensure this doesn't crash. VisualBasicSyntaxContext.CreateContext(document2, model2, source.IndexOf("return"), CancellationToken.None); } #endregion }