/
githubmirror
/
roslyn
Обзор
Документация
Войти
/
githubmirror
/
roslyn
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
src/EditorFeatures/CSharpTest2/Recommendations/RecommenderTests.cs
277 строк
12 KB
Fred Silberberg
Add C# 15 language version (#84799)
11 авг 2026, 23:51
Не верифицирован
11 авг 2026, 23:51
1284a4a
Код
Авторство
О чём код?
// 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.Immutable; using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Completion.Providers; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Extensions; using Microsoft.CodeAnalysis.CSharp.Extensions.ContextQuery; using Microsoft.CodeAnalysis.CSharp.Shared.Extensions; using Microsoft.CodeAnalysis.Test.Utilities; using Roslyn.Test.Utilities; using Xunit; namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.Recommendations; [UseExportProvider] public abstract class RecommenderTests : TestBase { protected static readonly CSharpParseOptions CSharp9ParseOptions = CSharpParseOptions.Default.WithLanguageVersion(LanguageVersion.CSharp9); protected static readonly CSharpParseOptions CSharp14ParseOptions = CSharpParseOptions.Default.WithLanguageVersion(LanguageVersion.CSharp14); protected static readonly CSharpParseOptions CSharp15ParseOptions = CSharpParseOptions.Default.WithLanguageVersion(LanguageVersion.CSharp15); protected static readonly CSharpParseOptions CSharp15ScriptParseOptions = Options.Script.WithLanguageVersion(LanguageVersion.CSharp15); protected static readonly CSharpParseOptions CSharpNextParseOptions = CSharpParseOptions.Default.WithLanguageVersion(LanguageVersionExtensions.CSharpNext); protected static readonly CSharpParseOptions CSharpNextScriptParseOptions = Options.Script.WithLanguageVersion(LanguageVersionExtensions.CSharpNext); // If no language version is specified, then we want to test against the implicit language version (ie. preview) protected static readonly CSharpParseOptions CSharpImplicitParseOptions = CSharpParseOptions.Default.WithLanguageVersion(LanguageVersion.Preview); protected static readonly CSharpParseOptions CSharpImplicitScriptParseOptions = Options.Script.WithLanguageVersion(LanguageVersion.Preview); protected abstract string KeywordText { get; } internal Func<int, CSharpSyntaxContext, Task<ImmutableArray<RecommendedKeyword>>>? RecommendKeywordsAsync; internal async Task VerifyWorkerAsync(string markup, bool absent, CSharpParseOptions? options = null, int? matchPriority = null) { options ??= CSharpImplicitParseOptions; Testing.TestFileMarkupParser.GetPosition(markup, out var code, out var position); await VerifyAtPositionAsync(code, position, absent, options: options, matchPriority: matchPriority); await VerifyInFrontOfCommentAsync(code, position, absent, options: options, matchPriority: matchPriority); await VerifyAtEndOfFileAsync(code, position, absent, options: options, matchPriority: matchPriority); await VerifyAtPosition_KeywordPartiallyWrittenAsync(code, position, absent, options: options, matchPriority: matchPriority); await VerifyInFrontOfComment_KeywordPartiallyWrittenAsync(code, position, absent, options: options, matchPriority: matchPriority); await VerifyAtEndOfFile_KeywordPartiallyWrittenAsync(code, position, absent, options: options, matchPriority: matchPriority); } private Task VerifyInFrontOfCommentAsync( string text, int position, bool absent, string insertText, CSharpParseOptions? options, int? matchPriority) { text = text[..position] + insertText + "/**/" + text[position..]; position += insertText.Length; return CheckResultAsync(text, position, absent, options, matchPriority); } private Task CheckResultAsync(string text, int position, bool absent, CSharpParseOptions? options, int? matchPriority) { using var workspace = new TestWorkspace(composition: FeaturesTestCompositions.Features); var solution = workspace.CurrentSolution; var project = solution.AddProject("test", "test", LanguageNames.CSharp); var document = project.AddDocument("test.cs", text); var tree = SyntaxFactory.ParseSyntaxTree(text, options: options); var compilation = CSharpCompilation.Create( "test", syntaxTrees: [tree], references: [NetFramework.mscorlib]); if (tree.IsInNonUserCode(position, CancellationToken.None) && !absent) { Assert.Fail("Wanted keyword, but in non-user code position: " + KeywordText); } var semanticModel = compilation.GetSemanticModel(tree); var context = CSharpSyntaxContext.CreateContext(document, semanticModel, position, CancellationToken.None); return CheckResultAsync(absent, position, context, matchPriority); } private async Task CheckResultAsync(bool absent, int position, CSharpSyntaxContext context, int? matchPriority) { if (absent) { if (RecommendKeywordsAsync != null) { var keywords = await RecommendKeywordsAsync(position, context); Assert.True(keywords == null || !keywords.Any(), "Keywords must be null or empty."); } } else { if (RecommendKeywordsAsync == null) { Assert.Fail("No recommender for: " + KeywordText); } else { var result = (await RecommendKeywordsAsync(position, context)).SingleOrDefault(); Assert.NotNull(result); Assert.Equal(KeywordText, result.Keyword); if (matchPriority != null) { Assert.Equal(matchPriority.Value, result.MatchPriority); } } } } private Task VerifyInFrontOfCommentAsync(string text, int cursorPosition, bool absent, CSharpParseOptions? options, int? matchPriority) => VerifyInFrontOfCommentAsync(text, cursorPosition, absent, string.Empty, options: options, matchPriority: matchPriority); private Task VerifyInFrontOfComment_KeywordPartiallyWrittenAsync(string text, int position, bool absent, CSharpParseOptions? options, int? matchPriority) => VerifyInFrontOfCommentAsync(text, position, absent, KeywordText[..1], options: options, matchPriority: matchPriority); private Task VerifyAtPositionAsync( string text, int position, bool absent, string insertText, CSharpParseOptions? options, int? matchPriority) { text = text[..position] + insertText + text[position..]; position += insertText.Length; return CheckResultAsync(text, position, absent, options, matchPriority); } private Task VerifyAtPositionAsync(string text, int position, bool absent, CSharpParseOptions? options, int? matchPriority) => VerifyAtPositionAsync(text, position, absent, string.Empty, options: options, matchPriority: matchPriority); private Task VerifyAtPosition_KeywordPartiallyWrittenAsync(string text, int position, bool absent, CSharpParseOptions? options, int? matchPriority) => VerifyAtPositionAsync(text, position, absent, KeywordText[..1], options: options, matchPriority: matchPriority); private async Task VerifyAtEndOfFileAsync( string text, int position, bool absent, string insertText, CSharpParseOptions? options, int? matchPriority) { // only do this if the placeholder was at the end of the text. if (text.Length != position) { return; } text = text[..position] + insertText; position += insertText.Length; await CheckResultAsync(text, position, absent, options, matchPriority); } private Task VerifyAtEndOfFileAsync(string text, int position, bool absent, CSharpParseOptions? options, int? matchPriority) => VerifyAtEndOfFileAsync(text, position, absent, string.Empty, options: options, matchPriority: matchPriority); private Task VerifyAtEndOfFile_KeywordPartiallyWrittenAsync(string text, int position, bool absent, CSharpParseOptions? options, int? matchPriority) => VerifyAtEndOfFileAsync(text, position, absent, KeywordText[..1], options: options, matchPriority: matchPriority); internal async Task VerifyKeywordAsync( [StringSyntax(PredefinedEmbeddedLanguageNames.CSharpTest)] string text, CSharpParseOptions? options = null, CSharpParseOptions? scriptOptions = null) { // run the verification in both context(normal and script) options ??= CSharpImplicitParseOptions; scriptOptions ??= CSharpImplicitScriptParseOptions; await VerifyWorkerAsync(text, absent: false, options: options); await VerifyWorkerAsync(text, absent: false, options: scriptOptions); } protected async Task VerifyKeywordAsync( SourceCodeKind kind, [StringSyntax(PredefinedEmbeddedLanguageNames.CSharpTest)] string text) { switch (kind) { case SourceCodeKind.Regular: await VerifyWorkerAsync(text, absent: false); break; case SourceCodeKind.Script: await VerifyWorkerAsync(text, absent: false, options: CSharpImplicitScriptParseOptions); break; } } protected async Task VerifyAbsenceAsync( [StringSyntax(PredefinedEmbeddedLanguageNames.CSharpTest)] string text, CSharpParseOptions? options = null, CSharpParseOptions? scriptOptions = null) { // run the verification in both context(normal and script) options ??= CSharpImplicitParseOptions; scriptOptions ??= CSharpImplicitScriptParseOptions; await VerifyWorkerAsync(text, absent: true, options: options); await VerifyWorkerAsync(text, absent: true, options: scriptOptions); } protected async Task VerifyAbsenceAsync( SourceCodeKind kind, [StringSyntax(PredefinedEmbeddedLanguageNames.CSharpTest)] string text) { switch (kind) { case SourceCodeKind.Regular: await VerifyWorkerAsync(text, absent: true); break; case SourceCodeKind.Script: await VerifyWorkerAsync(text, absent: true, options: CSharpImplicitScriptParseOptions); break; } } protected static string AddInsideMethod( [StringSyntax(PredefinedEmbeddedLanguageNames.CSharpTest)] string text, bool isAsync = false, string returnType = "void", bool topLevelStatement = false) { if (topLevelStatement) { return returnType switch { "void" => text, "int" => text, _ => throw new ArgumentException("Unsupported return type", nameof(returnType)), }; } var builder = new StringBuilder(); if (isAsync && returnType != "void") { builder.AppendLine("using System.Threading.Tasks;"); } builder.AppendLine("class C"); builder.AppendLine("{"); builder.Append(" "); if (isAsync) { builder.Append("async "); if (returnType == "void") { builder.Append("Task"); } else { builder.Append($"Task<{returnType}>"); } } else { builder.Append(returnType); } builder.AppendLine(" F()"); builder.AppendLine(" {"); builder.Append(" ").Append(text); builder.AppendLine(" }"); builder.Append('}'); return builder.ToString(); } }