/
githubmirror
/
roslyn
Обзор
Документация
Войти
/
githubmirror
/
roslyn
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
src/EditorFeatures/CSharpTest/Completion/CompletionProviders/OperatorCompletionProviderTests.cs
883 строки
29 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. using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Microsoft.CodeAnalysis.CSharp.Completion.Providers; using Microsoft.CodeAnalysis.Editor.Implementation.IntelliSense.AsyncCompletion; using Microsoft.CodeAnalysis.Test.Utilities; using Roslyn.Test.Utilities; using Xunit; namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.Completion.CompletionProviders; [Trait(Traits.Feature, Traits.Features.Completion)] public sealed class OperatorCompletionProviderTests : AbstractCSharpCompletionProviderTests { internal override Type GetCompletionProviderType() => typeof(UnnamedSymbolCompletionProvider); // The suggestion is e.g. "+". If the user actually types "+" the completion list is closed. Operators therefore do not support partially written items. protected override string? ItemPartiallyWritten(string? expectedItemOrNull) => ""; public static IEnumerable<object[]> BinaryArithmeticAndLogicalOperators() { yield return new[] { "+" }; yield return new[] { "&" }; yield return new[] { "|" }; yield return new[] { "/" }; yield return new[] { "^" }; yield return new[] { "%" }; yield return new[] { "*" }; yield return new[] { ">>" }; yield return new[] { ">>>" }; yield return new[] { "<<" }; yield return new[] { "-" }; } public static IEnumerable<object[]> BinaryEqualityAndRelationalOperators() { yield return new[] { "==" }; yield return new[] { ">" }; yield return new[] { ">=" }; yield return new[] { "!=" }; yield return new[] { "<" }; yield return new[] { "<=" }; } public static IEnumerable<object[]> PostfixOperators() { yield return new[] { "++" }; yield return new[] { "--" }; } public static IEnumerable<object[]> PrefixOperators() { yield return new[] { "!" }; yield return new[] { "~" }; yield return new[] { "-" }; yield return new[] { "+" }; } public static IEnumerable<object[]> BinaryOperators() => BinaryArithmeticAndLogicalOperators().Union(BinaryEqualityAndRelationalOperators()); public static IEnumerable<object[]> UnaryOperators() => PostfixOperators().Union(PrefixOperators()); [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/47511")] public Task OperatorIsNotOfferedAfterNumberLiteral() => VerifyNoItemsExistAsync(""" public class C { public static C operator +(C a, C b) => default; } public class Program { public static void Main() { 1.$$ } } """, SourceCodeKind.Regular); [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/47511")] public Task OperatorIsSuggestedAfterDot() => VerifyItemExistsAsync(""" public class C { public static C operator +(C a, C b) => default; } public class Program { public static void Main() { var c = new C(); c.$$; } } """, "+", inlineDescription: "x + y", glyph: Glyph.OperatorPublic, matchingFilters: [FilterSet.OperatorFilter]); [Theory, WorkItem("https://github.com/dotnet/roslyn/issues/47511")] [InlineData("c.$$", true)] [InlineData("c.$$;", true)] [InlineData("c.a$$", true)] [InlineData("c.ab$$", true)] [InlineData("c.abc$$", true)] [InlineData("c.abcd$$", true)] [InlineData("c. a$$", true)] [InlineData("c.$$a", true)] [InlineData("c.$$ a", true)] [InlineData("c?.$$", true)] public async Task OperatorSuggestionOnPartiallyWrittenMember(string expression, bool isOffered) { var verifyAction = isOffered ? new Func<string, Task>(markup => VerifyItemExistsAsync(markup, "+", inlineDescription: "x + y")) : new Func<string, Task>(markup => VerifyNoItemsExistAsync(markup)); await verifyAction($$""" public class C { public static C operator +(C a, C b) => default; } public class Program { public static void Main() { var c = new C(); {{expression}} } } """); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/47511")] public Task OperatorIsNotSuggestedOnStaticAccess() => VerifyNoItemsExistAsync(""" public class C { public static C operator +(C a, C b) => default; } public class Program { public static void Main() { C.$$ } } """); [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/47511")] public Task OperatorIsNotSuggestedInNameoOfContext() => VerifyNoItemsExistAsync(""" public class C { public static C operator +(C a, C b) => default; } public class Program { public static void Main() { var c = new C(); var name = nameof(c.$$ } } """); [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/47511")] public async Task OperatorsAreSortedByImporttanceAndGroupedByTopic() { var items = await GetCompletionItemsAsync(""" public class C { public static C operator +(C a, C b) => null; public static C operator -(C a, C b) => null; public static C operator *(C a, C b) => null; public static C operator /(C a, C b) => null; public static C operator %(C a, C b) => null; public static bool operator ==(C a, C b) => true; public static bool operator !=(C a, C b) => false; public static bool operator <(C a, C b) => true; public static bool operator >(C a, C b) => false; public static bool operator <=(C a, C b) => true; public static bool operator >=(C a, C b) => false; public static C operator +(C a) => null; public static C operator -(C a) => null; public static C operator ++(C a) => null; public static C operator --(C a) => null; public static bool operator true(C w) => true; public static bool operator false(C w) => false; public static bool operator &(C a, C b) => true; public static bool operator |(C a, C b) => true; public static C operator !(C a) => null; public static C operator ^(C a, C b) => null; public static C operator <<(C a, int b) => null; public static C operator >>(C a, int b) => null; public static C operator >>>(C a, int b) => null; public static C operator ~(C a) => null; } public class Program { public static void Main() { var c = new C(); c.$$; } } """, SourceCodeKind.Regular); // true and false operators are not listed Assert.Collection(items, i => Assert.Equal("==", i.DisplayText), i => Assert.Equal("!=", i.DisplayText), i => Assert.Equal(">", i.DisplayText), i => Assert.Equal(">=", i.DisplayText), i => Assert.Equal("<", i.DisplayText), i => Assert.Equal("<=", i.DisplayText), i => Assert.Equal("!", i.DisplayText), i => Assert.Equal("+", i.DisplayText), // Addition a+b i => Assert.Equal("-", i.DisplayText), // Subtraction a-b i => Assert.Equal("*", i.DisplayText), i => Assert.Equal("/", i.DisplayText), i => Assert.Equal("%", i.DisplayText), i => Assert.Equal("++", i.DisplayText), i => Assert.Equal("--", i.DisplayText), i => Assert.Equal("+", i.DisplayText), // Unary plus +a i => Assert.Equal("-", i.DisplayText), // Unary minus -a i => Assert.Equal("&", i.DisplayText), i => Assert.Equal("|", i.DisplayText), i => Assert.Equal("^", i.DisplayText), i => Assert.Equal("<<", i.DisplayText), i => Assert.Equal(">>", i.DisplayText), i => Assert.Equal(">>>", i.DisplayText), i => Assert.Equal("~", i.DisplayText) ); } [Theory] [CombinatorialData] public async Task InstanceIncrementOperators([CombinatorialValues("++", "--")] string op) { var items = await GetCompletionItemsAsync($$$""" public class C { public void operator {{{op}}}(int x) {} } public class Program { public static void Main() { var c = new C(); c.$$; } } """, SourceCodeKind.Regular); Assert.Collection(items, i => Assert.Equal(op, i.DisplayText) ); } [Theory] [CombinatorialData] public async Task InstanceCompoundAssignmentOperators([CombinatorialValues("+=", "-=", "*=", "/=", "%=", "&=", "|=", "^=", "<<=", ">>=", ">>>=")] string op) { var items = await GetCompletionItemsAsync($$$""" public class C { public void operator {{{op}}}(int x) {} } public class Program { public static void Main() { var c = new C(); c.$$; } } """, SourceCodeKind.Regular); // Compound forms of operators are never offered, see OperatorsAreSortedByImporttanceAndGroupedByTopic unit-test Assert.Empty(items); } [Theory, WorkItem("https://github.com/dotnet/roslyn/issues/47511")] [InlineData("bool", 0)] [InlineData("System.Boolean", 0)] [InlineData("char", 0)] [InlineData("System.Char", 0)] [InlineData("string", 0)] [InlineData("System.String", 0)] [InlineData("sbyte", 0)] [InlineData("System.SByte", 0)] [InlineData("byte", 0)] [InlineData("System.Byte", 0)] [InlineData("short", 0)] [InlineData("System.Int16", 0)] [InlineData("ushort", 0)] [InlineData("System.UInt16", 0)] [InlineData("int", 0)] [InlineData("System.Int32", 0)] [InlineData("uint", 0)] [InlineData("System.UInt32", 0)] [InlineData("long", 0)] [InlineData("System.Int64", 0)] [InlineData("ulong", 0)] [InlineData("System.UInt64", 0)] [InlineData("float", 0)] [InlineData("System.Single", 0)] [InlineData("double", 0)] [InlineData("System.Double", 0)] [InlineData("decimal", 0)] [InlineData("System.Decimal", 0)] [InlineData("nint", 0)] [InlineData("System.IntPtr", 0)] [InlineData("nuint", 0)] [InlineData("System.UIntPtr", 0)] [InlineData("System.DateTime", 8)] [InlineData("System.TimeSpan", 10)] [InlineData("System.DateTimeOffset", 8)] [InlineData("System.Guid", 2)] public async Task OperatorSuggestionForSpecialTypes(string specialType, int numberOfSuggestions) { var completionItems = await GetCompletionItemsAsync($$""" public class Program { public static void Main() { {{specialType}} i = default({{specialType}}); i.$$ } } """, SourceCodeKind.Regular); Assert.Equal( numberOfSuggestions, completionItems.Count(c => c.GetProperty(UnnamedSymbolCompletionProvider.KindName) == UnnamedSymbolCompletionProvider.OperatorKindName)); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/47511")] public Task OperatorNoSuggestionForTrueAndFalse() => VerifyNoItemsExistAsync(""" public class C { public static bool operator true(C _) => true; public static bool operator false(C _) => true; } public class Program { public static void Main() { var c = new C(); c.$$ } } """); [WpfTheory, WorkItem("https://github.com/dotnet/roslyn/issues/47511")] [MemberData(nameof(BinaryOperators))] public Task OperatorBinaryIsCompleted(string binaryOperator) => VerifyCustomCommitProviderAsync($$""" public class C { public static C operator {{binaryOperator}}(C a, C b) => default; } public class Program { public static void Main() { var c = new C(); c.$$ } } """, binaryOperator, $$""" public class C { public static C operator {{binaryOperator}}(C a, C b) => default; } public class Program { public static void Main() { var c = new C(); c {{binaryOperator}} $$ } } """); [WpfTheory, WorkItem("https://github.com/dotnet/roslyn/issues/47511")] [MemberData(nameof(PostfixOperators))] public Task OperatorPostfixIsCompleted(string postfixOperator) => VerifyCustomCommitProviderAsync($$""" public class C { public static C operator {{postfixOperator}}(C _) => default; } public class Program { public static void Main() { var c = new C(); c.$$ } } """, postfixOperator, $$""" public class C { public static C operator {{postfixOperator}}(C _) => default; } public class Program { public static void Main() { var c = new C(); c{{postfixOperator}} $$ } } """); [WpfTheory, WorkItem("https://github.com/dotnet/roslyn/issues/47511")] [MemberData(nameof(PrefixOperators))] public Task OperatorPrefixIsCompleted(string prefixOperator) => VerifyCustomCommitProviderAsync($$""" public class C { public static C operator {{prefixOperator}}(C _) => default; } public class Program { public static void Main() { var c = new C(); c.$$ } } """, prefixOperator, $$""" public class C { public static C operator {{prefixOperator}}(C _) => default; } public class Program { public static void Main() { var c = new C(); {{prefixOperator}}c$$ } } """); [WpfFact, WorkItem("https://github.com/dotnet/roslyn/issues/47511")] public async Task OperatorDuplicateOperatorsAreListedBoth() { var items = await GetCompletionItemsAsync($$""" public class C { public static C operator +(C a, C b) => default; public static C operator +(C _) => default; } public class Program { public static void Main() { var c = new C(); c.$$ } } """, SourceCodeKind.Regular); Assert.Collection(items, i => { Assert.Equal("+", i.DisplayText); Assert.EndsWith("002_007", i.SortText); // Addition }, i => { Assert.Equal("+", i.DisplayText); Assert.EndsWith("002_014", i.SortText); // unary plus }); } [WpfFact, WorkItem("https://github.com/dotnet/roslyn/issues/47511")] public Task OperatorDuplicateOperatorsAreCompleted() => VerifyCustomCommitProviderAsync($$""" public class C { public static C operator +(C a, C b) => default; public static C operator +(C _) => default; } public class Program { public static void Main() { var c = new C(); c.$$ } } """, "+", $$""" public class C { public static C operator +(C a, C b) => default; public static C operator +(C _) => default; } public class Program { public static void Main() { var c = new C(); c + $$ } } """); [WpfTheory, WorkItem("https://github.com/dotnet/roslyn/issues/47511")] [InlineData("c.$$", "c + $$")] [InlineData("c. $$", "c + $$ ")] [InlineData("c .$$", "c + $$")] [InlineData("c.$$;", "c + $$;")] [InlineData("c.abc$$", "c + $$")] [InlineData("c.a$$bc", "c + $$")] [InlineData("c.$$abc", "c + $$abc")] [InlineData("c.$$ abc", "c + $$ abc")] [InlineData("(true ? c : c).$$", "(true ? c : c) + $$")] [InlineData("c?.$$", "c + $$")] [InlineData("(true ? c : c)?.$$", "(true ? c : c) + $$")] [InlineData("c? .$$", "c + $$")] [InlineData("c ? .$$", "c + $$")] [InlineData("c?.CProp.$$", "c?.CProp + $$")] [InlineData("c?.CProp?.$$", "c?.CProp + $$")] [InlineData("c.CProp.CProp?.$$", "c.CProp.CProp + $$")] [InlineData("c?.CProp.CProp.$$", "c?.CProp.CProp + $$")] [InlineData("c[0].$$", "c[0] + $$")] [InlineData("c[0]?.$$", "c[0] + $$")] [InlineData("c?.CProp[0].$$", "c?.CProp[0] + $$")] [InlineData("c.CProp[0].CProp?.$$", "c.CProp[0].CProp + $$")] public Task OperatorInfixOfferingsAndCompletions(string expression, string completion) => VerifyCustomCommitProviderAsync($$""" public class C { public static C operator +(C a, C b) => default; public C CProp { get; } public C this[int _] => default; } public class Program { public static void Main() { var c = new C(); {{expression}} } } """, "+", $$""" public class C { public static C operator +(C a, C b) => default; public C CProp { get; } public C this[int _] => default; } public class Program { public static void Main() { var c = new C(); {{completion}} } } """); [WpfTheory, WorkItem("https://github.com/dotnet/roslyn/issues/47511")] [MemberData(nameof(UnaryOperators))] public async Task OperatorLiftingUnary(string operatorSign) { const string template = """ public struct S {{ {0} => default; }} public class Program {{ public static void Main() {{ S? s = null; s.$$ }} }} """; var inlineDescription = operatorSign.Length == 1 ? $"{operatorSign}x" : $"x{operatorSign}"; await VerifyItemExistsAsync(string.Format(template, $"public static S operator {operatorSign}(S _)"), operatorSign, inlineDescription: inlineDescription); await VerifyItemExistsAsync(string.Format(template, $"public static bool operator {operatorSign}(S _)"), operatorSign, inlineDescription: inlineDescription); await VerifyNoItemsExistAsync(string.Format(template, $"public static object operator {operatorSign}(S _)")); await VerifyNoItemsExistAsync(string.Format(template, $"public static S operator {operatorSign}(S a, S b, S c)")); } [WpfTheory, WorkItem("https://github.com/dotnet/roslyn/issues/47511")] [MemberData(nameof(BinaryArithmeticAndLogicalOperators))] public async Task OperatorLiftingBinary(string operatorSign) { const string template = """ public struct S {{ {0} => default; }} public class Program {{ public static void Main() {{ S? s = null; s.$$ }} }} """; var inlineDescription = $"x {operatorSign} y"; await VerifyItemExistsAsync(string.Format(template, $"public static S operator {operatorSign}(S a, S b)"), operatorSign, inlineDescription: inlineDescription); await VerifyItemExistsAsync(string.Format(template, $"public static int operator {operatorSign}(S a, S b)"), operatorSign, inlineDescription: inlineDescription); await VerifyNoItemsExistAsync(string.Format(template, $"public static object operator {operatorSign}(S a, S b)")); await VerifyNoItemsExistAsync(string.Format(template, $"public static S operator {operatorSign}(S a, object b)")); await VerifyNoItemsExistAsync(string.Format(template, $"public static S operator {operatorSign}(S a, S b, S c)")); } [WpfTheory, WorkItem("https://github.com/dotnet/roslyn/issues/47511")] [MemberData(nameof(BinaryEqualityAndRelationalOperators))] public async Task OperatorLiftingEqualityRelational(string operatorSign) { const string template = """ public struct S {{ {0} => default; }} public class Program {{ public static void Main() {{ S? s = null; s.$$ }} }} """; await VerifyItemExistsAsync(string.Format(template, $"public static bool operator {operatorSign}(S a, S b)"), operatorSign, inlineDescription: $"x {operatorSign} y"); await VerifyNoItemsExistAsync(string.Format(template, $"public static int operator {operatorSign}(S a, S b)")); await VerifyNoItemsExistAsync(string.Format(template, $"public static bool operator {operatorSign}(S a, S b, S c)")); await VerifyNoItemsExistAsync(string.Format(template, $"public static bool operator {operatorSign}(S a, object b)")); } [WpfFact, WorkItem("https://github.com/dotnet/roslyn/issues/47511")] public Task OperatorLiftingIsApplied() => VerifyCustomCommitProviderAsync(""" public struct S { public static bool operator ==(S a, S b) => default; } public class Program { public static void Main() { S? s = null; s.$$ } } """, "==", """ public struct S { public static bool operator ==(S a, S b) => default; } public class Program { public static void Main() { S? s = null; s == $$ } } """); [WpfFact, WorkItem("https://github.com/dotnet/roslyn/issues/47511")] public Task OperatorOfBaseTypeIsSuggested() => VerifyItemExistsAsync(""" public class Base { public static int operator +(Base b, int a)=>0; } public class Derived: Base { } public class Program { public static void Main() { var d = new Derived(); d.$$ } } """, "+", inlineDescription: "x + y", glyph: Glyph.OperatorPublic, matchingFilters: [FilterSet.OperatorFilter]); [WpfFact, WorkItem("https://github.com/dotnet/roslyn/issues/47511")] public Task OperatorForRecordsAreSuggested() => VerifyItemExistsAsync(""" public record R { } public class Program { public static void Main() { var r = new R(); r.$$ } } """, "==", inlineDescription: "x == y", glyph: Glyph.OperatorPublic, matchingFilters: [FilterSet.OperatorFilter]); [WpfFact, WorkItem("https://github.com/dotnet/roslyn/issues/47511")] public async Task TestEditorBrowsableOnOperatorIsRespected_EditorBrowsableStateNever() { var markup = """ namespace N { public class Program { public static void Main() { var c = new C(); c.$$ } } } """; var referencedCode = """ using System.ComponentModel; namespace N { public class C { [EditorBrowsable(EditorBrowsableState.Never)] public static C operator -(C a, C b) => default; } } """; await VerifyItemInEditorBrowsableContextsAsync( markup: markup, referencedCode: referencedCode, item: "-", expectedSymbolsSameSolution: 1, expectedSymbolsMetadataReference: 0, sourceLanguage: LanguageNames.CSharp, referencedLanguage: LanguageNames.CSharp); } [WpfFact, WorkItem("https://github.com/dotnet/roslyn/issues/47511")] public async Task TestEditorBrowsableOnOperatorIsRespected_EditorBrowsableStateAdvanced() { var markup = """ namespace N { public class Program { public static void Main() { var c = new C(); c.$$ } } } """; var referencedCode = """ using System.ComponentModel; namespace N { public class C { [EditorBrowsable(EditorBrowsableState.Advanced)] public static C operator -(C a, C b) => default; } } """; HideAdvancedMembers = false; await VerifyItemInEditorBrowsableContextsAsync( markup: markup, referencedCode: referencedCode, item: "-", expectedSymbolsSameSolution: 1, expectedSymbolsMetadataReference: 1, sourceLanguage: LanguageNames.CSharp, referencedLanguage: LanguageNames.CSharp); HideAdvancedMembers = true; await VerifyItemInEditorBrowsableContextsAsync( markup: markup, referencedCode: referencedCode, item: "-", expectedSymbolsSameSolution: 1, expectedSymbolsMetadataReference: 0, sourceLanguage: LanguageNames.CSharp, referencedLanguage: LanguageNames.CSharp); } [WpfFact, WorkItem("https://github.com/dotnet/roslyn/issues/47511")] public Task OperatorBinaryNullForgivingHandling() => VerifyCustomCommitProviderAsync(""" #nullable enable public class C { public static C operator +(C a, C b) => default; } public class Program { public static void Main() { C? c = null; var _ = c!.$$ } } """, "+", """ #nullable enable public class C { public static C operator +(C a, C b) => default; } public class Program { public static void Main() { C? c = null; var _ = c! + $$ } } """); }