/
githubmirror
/
roslyn
Обзор
Документация
Войти
/
githubmirror
/
roslyn
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
src/EditorFeatures/CSharpTest/Semantics/SpeculationAnalyzerTests.cs
497 строк
16 KB
Cyrus Najmabadi
Cleanup test methods
24 июл 2025, 11:15
24 июл 2025, 11:15
9b3e554
Код
Авторство
О чём код?
// 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.Collections.Generic; using System.IO; using System.Linq; using System.Threading; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.CSharp.Test.Utilities; using Microsoft.CodeAnalysis.CSharp.Utilities; using Microsoft.CodeAnalysis.Editor.UnitTests.Semantics; using Roslyn.Test.Utilities; using Roslyn.Utilities; using Xunit; namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.Semantics; public sealed class SpeculationAnalyzerTests : SpeculationAnalyzerTestsBase { [Fact] public void SpeculationAnalyzerDifferentOverloads() => Test(""" class Program { void Vain(int arg = 3) { } void Vain(string arg) { } void Main() { [|Vain(5)|]; } } """, "Vain(string.Empty)", true); [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/672396")] public void SpeculationAnalyzerExtensionMethodExplicitInvocation() { // We consider a change here to be a change in semantics as an instance call became a static call. In // practice this is fine as the only thing that makes this change is complexification, and we don't test for // semantics changed after that as the purpose of complexification is to put us in a safe place to make // changes that won't break semantics. Test(""" static class Program { public static void Vain(this int arg) { } static void Main() { [|5.Vain()|]; } } """, "Vain(5)", semanticChanges: true); } [Fact] public void SpeculationAnalyzerImplicitBaseClassConversion() => Test(""" using System; class Program { void Main() { Exception ex = [|(Exception)new InvalidOperationException()|]; } } """, "new InvalidOperationException()", false); [Fact] public void SpeculationAnalyzerImplicitNumericConversion() => Test(""" class Program { void Main() { long i = [|(long)5|]; } } """, "5", false); [Fact] public void SpeculationAnalyzerImplicitUserConversion() => Test(""" class From { public static implicit operator To(From from) { return new To(); } } class To { } class Program { void Main() { To to = [|(To)new From()|]; } } """, "new From()", true); [Fact] public void SpeculationAnalyzerExplicitConversion() => Test(""" using System; class Program { void Main() { Exception ex1 = new InvalidOperationException(); var ex2 = [|(InvalidOperationException)ex1|]; } } """, "ex1", true); [Fact] public void SpeculationAnalyzerArrayImplementingNonGenericInterface() => Test(""" using System.Collections; class Program { void Main() { var a = new[] { 1, 2, 3 }; [|((IEnumerable)a).GetEnumerator()|]; } } """, "a.GetEnumerator()", false); [Fact] public void SpeculationAnalyzerVirtualMethodWithBaseConversion() => Test(""" using System; using System.IO; class Program { void Main() { var s = new MemoryStream(); [|((Stream)s).Flush()|]; } } """, "s.Flush()", false); [Fact] public void SpeculationAnalyzerNonVirtualMethodImplementingInterface() => Test(""" using System; class Class : IComparable { public int CompareTo(object other) { return 1; } } class Program { static void Main() { var c = new Class(); var d = new Class(); [|((IComparable)c).CompareTo(d)|]; } } """, "c.CompareTo(d)", true); [Fact] public void SpeculationAnalyzerSealedClassImplementingInterface() => Test(""" using System; sealed class Class : IComparable { public int CompareTo(object other) { return 1; } } class Program { static void Main() { var c = new Class(); var d = new Class(); [|((IComparable)c).CompareTo(d)|]; } } """, "((IComparable)c).CompareTo(d)", semanticChanges: false); [Fact] public void SpeculationAnalyzerValueTypeImplementingInterface() => Test(""" using System; class Program { void Main() { decimal d = 5; [|((IComparable<decimal>)d).CompareTo(6)|]; } } """, "d.CompareTo(6)", false); [Fact] public void SpeculationAnalyzerBinaryExpressionIntVsLong() => Test(""" class Program { void Main() { var r = [|1+1L|]; } } """, "1+1", true); [Fact] public void SpeculationAnalyzerQueryExpressionSelectType() => Test(""" using System.Linq; class Program { static void Main(string[] args) { var items = [|from i in Enumerable.Range(0, 3) select (long)i|]; } } """, "from i in Enumerable.Range(0, 3) select i", true); [Fact] public void SpeculationAnalyzerQueryExpressionFromType() => Test(""" using System.Linq; class Program { static void Main(string[] args) { var items = [|from i in new long[0] select i|]; } } """, "from i in new int[0] select i", true); [Fact] public void SpeculationAnalyzerQueryExpressionGroupByType() => Test(""" using System.Linq; class Program { static void Main(string[] args) { var items = [|from i in Enumerable.Range(0, 3) group (long)i by i|]; } } """, "from i in Enumerable.Range(0, 3) group i by i", true); [Fact] public void SpeculationAnalyzerQueryExpressionOrderByType() => Test(""" using System.Linq; class Program { static void Main(string[] args) { var items = from i in Enumerable.Range(0, 3) orderby [|(long)i|] select i; } } """, "i", true); [Fact] public void SpeculationAnalyzerDifferentAttributeConstructors() { Test(""" using System; class AnAttribute : Attribute { public AnAttribute(string a, long b) { } public AnAttribute(int a, int b) { } } class Program { [An([|"5"|], 6)] static void Main() { } } """, "5", false, "6"); // Note: the answer should have been that the replacement does change semantics (true), // however to have enough context one must analyze AttributeSyntax instead of separate ExpressionSyntaxes it contains, // which is not supported in SpeculationAnalyzer, but possible with GetSpeculativeSemanticModel API } [Fact] public void SpeculationAnalyzerCollectionInitializers() => Test(""" using System.Collections; class Collection : IEnumerable { public IEnumerator GetEnumerator() { throw new System.NotImplementedException(); } public void Add(string s) { } public void Add(int i) { } void Main() { var c = new Collection { [|"5"|] }; } } """, "5", true); [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/1088815")] public void SpeculationAnalyzerBrokenCode() => Test(""" public interface IRogueAction { public string Name { get; private set; } protected IRogueAction(string name) { [|this.Name|] = name; } } """, "Name", semanticChanges: false, isBrokenCode: true); [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/8111")] public void SpeculationAnalyzerAnonymousObjectMemberDeclaredWithNeededCast() => Test(""" class Program { static void Main(string[] args) { object thing = new { shouldBeAnInt = [|(int)Directions.South|] }; } public enum Directions { North, East, South, West } } """, "Directions.South", semanticChanges: true); [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/8111")] public void SpeculationAnalyzerAnonymousObjectMemberDeclaredWithUnneededCast() => Test(""" class Program { static void Main(string[] args) { object thing = new { shouldBeAnInt = [|(Directions)Directions.South|] }; } public enum Directions { North, East, South, West } } """, "Directions.South", semanticChanges: false); [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/19987")] public void SpeculationAnalyzerSwitchCaseWithRedundantCast() => Test(""" class Program { static void Main(string[] arts) { var x = 1f; switch (x) { case [|(float) 1|]: System.Console.WriteLine("one"); break; default: System.Console.WriteLine("not one"); break; } } } """, "1", semanticChanges: false); [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/19987")] public void SpeculationAnalyzerSwitchCaseWithRequiredCast() => Test(""" class Program { static void Main(string[] arts) { object x = 1f; switch (x) { case [|(float) 1|]: // without the case, object x does not match int 1 System.Console.WriteLine("one"); break; default: System.Console.WriteLine("not one"); break; } } } """, "1", semanticChanges: true); [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/28412")] public void SpeculationAnalyzerIndexerPropertyWithRedundantCast() => Test(code: """ class Indexer { public int this[int x] { get { return x; } } } class A { public Indexer Foo { get; } = new Indexer(); } class B : A { } class Program { static void Main(string[] args) { var b = new B(); var y = ([|(A)b|]).Foo[1]; } } """, replacementExpression: "b", semanticChanges: false); [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/28412")] public void SpeculationAnalyzerIndexerPropertyWithRequiredCast() => Test(code: """ class Indexer { public int this[int x] { get { return x; } } } class A { public Indexer Foo { get; } = new Indexer(); } class B : A { public new Indexer Foo { get; } = new Indexer(); } class Program { static void Main(string[] args) { var b = new B(); var y = ([|(A)b|]).Foo[1]; } } """, replacementExpression: "b", semanticChanges: true); [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/28412")] public void SpeculationAnalyzerDelegatePropertyWithRedundantCast() => Test(code: """ public delegate void MyDelegate(); class A { public MyDelegate Foo { get; } } class B : A { } class Program { static void Main(string[] args) { var b = new B(); ([|(A)b|]).Foo(); } } """, replacementExpression: "b", semanticChanges: false); [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/28412")] public void SpeculationAnalyzerDelegatePropertyWithRequiredCast() => Test(code: """ public delegate void MyDelegate(); class A { public MyDelegate Foo { get; } } class B : A { public new MyDelegate Foo { get; } } class Program { static void Main(string[] args) { var b = new B(); ([|(A)b|]).Foo(); } } """, replacementExpression: "b", semanticChanges: true); protected override SyntaxTree Parse(string text) => SyntaxFactory.ParseSyntaxTree(text); protected override bool IsExpressionNode(SyntaxNode node) => node is ExpressionSyntax; protected override Compilation CreateCompilation(SyntaxTree tree) { return CSharpCompilation.Create( CompilationName, [tree], References, TestOptions.ReleaseDll.WithSpecificDiagnosticOptions([KeyValuePair.Create("CS0219", ReportDiagnostic.Suppress)])); } protected override bool CompilationSucceeded(Compilation compilation, Stream temporaryStream) { var langCompilation = compilation; static bool isProblem(Diagnostic d) => d.Severity >= DiagnosticSeverity.Warning; return !langCompilation.GetDiagnostics().Any(isProblem) && !langCompilation.Emit(temporaryStream).Diagnostics.Any(isProblem); } protected override bool ReplacementChangesSemantics(SyntaxNode initialNode, SyntaxNode replacementNode, SemanticModel initialModel) => new SpeculationAnalyzer((ExpressionSyntax)initialNode, (ExpressionSyntax)replacementNode, initialModel, CancellationToken.None).ReplacementChangesSemantics(); }