Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -43,11 +43,5 @@ public ValueTask OnUserCodeExceptionAsync(UserCodeExceptionInfo exception, Cance
OnUserCodeExceptionImpl?.Invoke(exception);
return ValueTaskFactory.CompletedTask;
}

public ValueTask OnCompilationFailureAsync(ImmutableArray<QueryCompilationError> errors, CancellationToken cancellationToken)
{
OnCompilationFailureImpl?.Invoke(errors);
return ValueTaskFactory.CompletedTask;
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -120,13 +120,11 @@ public async Task<ExecuteQueryResult> ExecuteQueryAsync(
}
}

await observer.OnCompilationFailureAsync(
emitResult.Diagnostics.SelectAsArray(
var errors = emitResult.Diagnostics.SelectAsArray(
d => d.Severity == DiagnosticSeverity.Error,
d => new QueryCompilationError(d.Id, d.GetMessage(), (d.Location.SourceTree == queryTree) ? d.Location.SourceSpan : default)),
cancellationToken).ConfigureAwait(false);
d => new QueryCompilationError(d.Id, d.GetMessage(), (d.Location.SourceTree == queryTree) ? d.Location.SourceSpan : default));

return CreateResult(FeaturesResources.Semantic_search_query_failed_to_compile);
return CreateResult(errors, FeaturesResources.Semantic_search_query_failed_to_compile);
}

peStream.Position = 0;
Expand All @@ -145,7 +143,7 @@ await observer.OnCompilationFailureAsync(
if (!TryGetFindMethod(queryAssembly, out var findMethod, out var queryKind, out var errorMessage, out var errorMessageArgs))
{
traceSource.TraceInformation($"Semantic search failed: {errorMessage}");
return CreateResult(errorMessage, errorMessageArgs);
return CreateResult(compilationErrors: [], errorMessage, errorMessageArgs);
}

var invocationContext = new QueryExecutionContext(queryText, findMethod, observer, classificationOptions, traceSource);
Expand All @@ -155,7 +153,7 @@ await observer.OnCompilationFailureAsync(

if (invocationContext.TerminatedWithException)
{
return CreateResult(FeaturesResources.Semantic_search_query_terminated_with_exception);
return CreateResult(compilationErrors: [], FeaturesResources.Semantic_search_query_terminated_with_exception);
}
}
finally
Expand All @@ -175,10 +173,10 @@ await observer.OnCompilationFailureAsync(
}
}

return CreateResult(errorMessage: null);
return CreateResult(compilationErrors: [], errorMessage: null);

ExecuteQueryResult CreateResult(string? errorMessage, params string[]? args)
=> new(errorMessage, args, emitTime, executionTime);
ExecuteQueryResult CreateResult(ImmutableArray<QueryCompilationError> compilationErrors, string? errorMessage, params string[]? args)
=> new(compilationErrors, errorMessage, args, emitTime, executionTime);
}
catch (Exception e) when (FatalError.ReportAndPropagateUnlessCanceled(e, cancellationToken, ErrorSeverity.Critical))
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,15 @@
// See the LICENSE file in the project root for more information.

using System;
using System.Collections.Immutable;
using System.Runtime.Serialization;

namespace Microsoft.CodeAnalysis.SemanticSearch;

/// <summary>
/// The result of Semantic Search query execution.
/// </summary>
/// <param name="compilationErrors">Compilation errors.</param>
/// <param name="ErrorMessage">An error message if the execution failed.</param>
/// <param name="ErrorMessageArgs">
/// Arguments to be substituted to <paramref name="ErrorMessage"/>.
Expand All @@ -20,7 +22,8 @@ namespace Microsoft.CodeAnalysis.SemanticSearch;
/// <param name="ExecutionTime">Time it took to execute the query.</param>
[DataContract]
internal readonly record struct ExecuteQueryResult(
[property: DataMember(Order = 0)] string? ErrorMessage,
[property: DataMember(Order = 1)] string[]? ErrorMessageArgs = null,
[property: DataMember(Order = 2)] TimeSpan EmitTime = default,
[property: DataMember(Order = 3)] TimeSpan ExecutionTime = default);
[property: DataMember(Order = 0)] ImmutableArray<QueryCompilationError> compilationErrors,
[property: DataMember(Order = 1)] string? ErrorMessage,
[property: DataMember(Order = 2)] string[]? ErrorMessageArgs = null,
[property: DataMember(Order = 3)] TimeSpan EmitTime = default,
[property: DataMember(Order = 4)] TimeSpan ExecutionTime = default);
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ internal interface ICallback
{
ValueTask OnDefinitionFoundAsync(RemoteServiceCallbackId callbackId, SerializableDefinitionItem definition, CancellationToken cancellationToken);
ValueTask OnUserCodeExceptionAsync(RemoteServiceCallbackId callbackId, UserCodeExceptionInfo exception, CancellationToken cancellationToken);
ValueTask OnCompilationFailureAsync(RemoteServiceCallbackId callbackId, ImmutableArray<QueryCompilationError> errors, CancellationToken cancellationToken);
ValueTask<ClassificationOptions> GetClassificationOptionsAsync(RemoteServiceCallbackId callbackId, string language, CancellationToken cancellationToken);
ValueTask AddItemsAsync(RemoteServiceCallbackId callbackId, int itemCount, CancellationToken cancellationToken);
ValueTask ItemsCompletedAsync(RemoteServiceCallbackId callbackId, int itemCount, CancellationToken cancellationToken);
Expand All @@ -43,9 +42,6 @@ public ValueTask OnDefinitionFoundAsync(RemoteServiceCallbackId callbackId, Seri
public ValueTask OnUserCodeExceptionAsync(RemoteServiceCallbackId callbackId, UserCodeExceptionInfo exception, CancellationToken cancellationToken)
=> ((ServerCallback)GetCallback(callbackId)).OnUserCodeExceptionAsync(exception, cancellationToken);

public ValueTask OnCompilationFailureAsync(RemoteServiceCallbackId callbackId, ImmutableArray<QueryCompilationError> errors, CancellationToken cancellationToken)
=> ((ServerCallback)GetCallback(callbackId)).OnCompilationFailureAsync(errors, cancellationToken);

public ValueTask AddItemsAsync(RemoteServiceCallbackId callbackId, int itemCount, CancellationToken cancellationToken)
=> ((ServerCallback)GetCallback(callbackId)).AddItemsAsync(itemCount, cancellationToken);

Expand Down Expand Up @@ -81,17 +77,6 @@ public async ValueTask OnUserCodeExceptionAsync(UserCodeExceptionInfo exception,
}
}

public async ValueTask OnCompilationFailureAsync(ImmutableArray<QueryCompilationError> errors, CancellationToken cancellationToken)
{
try
{
await observer.OnCompilationFailureAsync(errors, cancellationToken).ConfigureAwait(false);
}
catch (Exception e) when (FatalError.ReportAndCatchUnlessCanceled(e, cancellationToken))
{
}
}

public async ValueTask AddItemsAsync(int itemCount, CancellationToken cancellationToken)
{
try
Expand Down Expand Up @@ -132,7 +117,7 @@ public static async ValueTask<ExecuteQueryResult> ExecuteQueryAsync(Solution sol
var client = await RemoteHostClient.TryGetClientAsync(solution.Services, cancellationToken).ConfigureAwait(false);
if (client == null)
{
return new ExecuteQueryResult(FeaturesResources.Semantic_search_only_supported_on_net_core);
return new ExecuteQueryResult(compilationErrors: [], FeaturesResources.Semantic_search_only_supported_on_net_core);
}

var serverCallback = new ServerCallback(solution, results, classificationOptions);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ namespace Microsoft.CodeAnalysis.SemanticSearch;
internal interface ISemanticSearchResultsObserver
{
ValueTask OnUserCodeExceptionAsync(UserCodeExceptionInfo exception, CancellationToken cancellationToken);
ValueTask OnCompilationFailureAsync(ImmutableArray<QueryCompilationError> errors, CancellationToken cancellationToken);
ValueTask OnDefinitionFoundAsync(DefinitionItem definition, CancellationToken cancellationToken);
ValueTask AddItemsAsync(int itemCount, CancellationToken cancellationToken);
ValueTask ItemsCompletedAsync(int itemCount, CancellationToken cancellationToken);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@

namespace Microsoft.CodeAnalysis.SemanticSearch;

internal sealed class SearchCompilationFailureDefinitionItem(QueryCompilationError error, Document queryDocument)
internal sealed class SearchCompilationFailureDefinitionItem(QueryCompilationError error, Document? queryDocument)
: DefinitionItem(
tags:
[
Expand All @@ -24,10 +24,7 @@ internal sealed class SearchCompilationFailureDefinitionItem(QueryCompilationErr
new TaggedText(TextTags.Text, error.Message)
],
nameDisplayParts: [],
sourceSpans:
[
new DocumentSpan(queryDocument, error.Span)
],
sourceSpans: queryDocument != null ? [new DocumentSpan(queryDocument, error.Span)] : [],
classifiedSpans: [],
metadataLocations: [],
properties: null,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ internal sealed class SearchExceptionDefinitionItem(string message, ImmutableArr
.. stackTrace
],
nameDisplayParts: exceptionTypeName,
sourceSpans: [documentSpan],
sourceSpans: documentSpan.SourceSpan.IsEmpty ? [] : [documentSpan],
classifiedSpans: [],
metadataLocations: [],
properties: null,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
// 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.Composition;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.Classification;
using Microsoft.CodeAnalysis.ExternalAccess.Copilot.SemanticSearch;
using Microsoft.CodeAnalysis.FindUsages;
using Microsoft.CodeAnalysis.Host;
using Microsoft.CodeAnalysis.Host.Mef;
using Microsoft.CodeAnalysis.SemanticSearch;
using Roslyn.Utilities;

namespace Microsoft.CodeAnalysis.ExternalAccess.Copilot.Internal.SemanticSearch;

[Export(typeof(ICopilotSemanticSearchQueryExecutor)), Shared]
[method: ImportingConstructor]
[method: Obsolete(MefConstruction.ImportingConstructorMessage, error: true)]
internal sealed class CopilotSemanticSearchQueryExecutor(IHostWorkspaceProvider workspaceProvider) : ICopilotSemanticSearchQueryExecutor
{
private sealed class ResultsObserver(CancellationTokenSource cancellationSource, int resultCountLimit) : ISemanticSearchResultsObserver
{
private ImmutableList<string> _results = [];
public string? RuntimeException { get; private set; }
public bool LimitReached { get; private set; }

public ImmutableList<string> Results => _results;

public ValueTask AddItemsAsync(int itemCount, CancellationToken cancellationToken)
=> ValueTaskFactory.CompletedTask;

public ValueTask ItemsCompletedAsync(int itemCount, CancellationToken cancellationToken)
=> ValueTaskFactory.CompletedTask;

public ValueTask OnUserCodeExceptionAsync(UserCodeExceptionInfo exception, CancellationToken cancellationToken)
{
RuntimeException ??= $"{exception.TypeName.ToVisibleDisplayString(includeLeftToRightMarker: false)}: {exception.Message}{Environment.NewLine}{exception.StackTrace.ToVisibleDisplayString(includeLeftToRightMarker: false)}";
cancellationSource.Cancel();
return ValueTaskFactory.CompletedTask;
}

public ValueTask OnDefinitionFoundAsync(DefinitionItem definition, CancellationToken cancellationToken)
{
if (!ImmutableInterlocked.Update(ref _results,
list => list.Count == resultCountLimit ? list : list.Add(definition.NameDisplayParts.ToVisibleDisplayString(includeLeftToRightMarker: false))))
{
LimitReached = true;
cancellationSource.Cancel();
}

return ValueTaskFactory.CompletedTask;
}
}

/// <summary>
/// We only use symbol display names, classification is not relevant.
/// </summary>
private sealed class DefaultClassificationOptionsProvider : OptionsProvider<ClassificationOptions>
{
public static readonly DefaultClassificationOptionsProvider Instance = new();

public ValueTask<ClassificationOptions> GetOptionsAsync(LanguageServices languageServices, CancellationToken cancellationToken)
=> new(ClassificationOptions.Default);
}

private readonly Workspace _workspace = workspaceProvider.Workspace;

public async Task<CopilotSemanticSearchQueryResults> ExecuteAsync(string query, int resultCountLimit, CancellationToken cancellationToken)
{
Contract.ThrowIfFalse(resultCountLimit > 0);

using var cancellationSource = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken);
var observer = new ResultsObserver(cancellationSource, resultCountLimit);

try
{
var result = await RemoteSemanticSearchServiceProxy.ExecuteQueryAsync(
_workspace.CurrentSolution,
LanguageNames.CSharp,
query,
SemanticSearchUtilities.ReferenceAssembliesDirectory,
observer,
DefaultClassificationOptionsProvider.Instance,
cancellationSource.Token).ConfigureAwait(false);

return new CopilotSemanticSearchQueryResults()
{
Symbols = observer.Results,
CompilationErrors = result.compilationErrors.SelectAsArray(e => (e.Id, e.Message)),
Error = (result.ErrorMessage != null) ? string.Format(result.ErrorMessage, result.ErrorMessageArgs ?? []) : null,
LimitReached = false,
};
}
catch (OperationCanceledException) when (cancellationSource.IsCancellationRequested)
{
return new CopilotSemanticSearchQueryResults()
{
Symbols = observer.Results,
CompilationErrors = [],
Error = observer.RuntimeException != null ? $"The query failed with an exception: {observer.RuntimeException}" : null,
LimitReached = observer.LimitReached,
};
}
}
}
12 changes: 12 additions & 0 deletions src/Features/ExternalAccess/Copilot/InternalAPI.Unshipped.txt
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,18 @@ Microsoft.CodeAnalysis.ExternalAccess.Copilot.IExternalCSharpOnTheFlyDocsService
Microsoft.CodeAnalysis.ExternalAccess.Copilot.IExternalCSharpCopilotGenerateImplementationService
Microsoft.CodeAnalysis.ExternalAccess.Copilot.RelatedDocuments.ICopilotRelatedDocumentsService
Microsoft.CodeAnalysis.ExternalAccess.Copilot.RelatedDocuments.ICopilotRelatedDocumentsService.GetRelatedDocumentIdsAsync(Microsoft.CodeAnalysis.Document! document, int position, System.Func<System.Collections.Immutable.ImmutableArray<Microsoft.CodeAnalysis.DocumentId!>, System.Threading.CancellationToken, System.Threading.Tasks.ValueTask>! callbackAsync, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.ValueTask
Microsoft.CodeAnalysis.ExternalAccess.Copilot.SemanticSearch.CopilotSemanticSearchQueryResults
Microsoft.CodeAnalysis.ExternalAccess.Copilot.SemanticSearch.CopilotSemanticSearchQueryResults.CompilationErrors.get -> System.Collections.Generic.IReadOnlyList<(string! id, string! message)>!
Microsoft.CodeAnalysis.ExternalAccess.Copilot.SemanticSearch.CopilotSemanticSearchQueryResults.CompilationErrors.init -> void
Microsoft.CodeAnalysis.ExternalAccess.Copilot.SemanticSearch.CopilotSemanticSearchQueryResults.CopilotSemanticSearchQueryResults() -> void
Microsoft.CodeAnalysis.ExternalAccess.Copilot.SemanticSearch.CopilotSemanticSearchQueryResults.Error.get -> string?
Microsoft.CodeAnalysis.ExternalAccess.Copilot.SemanticSearch.CopilotSemanticSearchQueryResults.Error.init -> void
Microsoft.CodeAnalysis.ExternalAccess.Copilot.SemanticSearch.CopilotSemanticSearchQueryResults.LimitReached.get -> bool
Microsoft.CodeAnalysis.ExternalAccess.Copilot.SemanticSearch.CopilotSemanticSearchQueryResults.LimitReached.init -> void
Microsoft.CodeAnalysis.ExternalAccess.Copilot.SemanticSearch.CopilotSemanticSearchQueryResults.Symbols.get -> System.Collections.Generic.IReadOnlyList<string!>!
Microsoft.CodeAnalysis.ExternalAccess.Copilot.SemanticSearch.CopilotSemanticSearchQueryResults.Symbols.init -> void
Microsoft.CodeAnalysis.ExternalAccess.Copilot.SemanticSearch.ICopilotSemanticSearchQueryExecutor
Microsoft.CodeAnalysis.ExternalAccess.Copilot.SemanticSearch.ICopilotSemanticSearchQueryExecutor.ExecuteAsync(string! query, int resultCountLimit, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task<Microsoft.CodeAnalysis.ExternalAccess.Copilot.SemanticSearch.CopilotSemanticSearchQueryResults>!
Microsoft.CodeAnalysis.ExternalAccess.Copilot.SemanticSearch.ISemanticSearchCopilotServiceImpl
Microsoft.CodeAnalysis.ExternalAccess.Copilot.SemanticSearch.ISemanticSearchCopilotServiceImpl.TryGetQueryAsync(string! text, Microsoft.CodeAnalysis.ExternalAccess.Copilot.SemanticSearch.SemanticSearchCopilotContextImpl! context, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.ValueTask<Microsoft.CodeAnalysis.ExternalAccess.Copilot.SemanticSearch.SemanticSearchCopilotGeneratedQueryImpl>
Microsoft.CodeAnalysis.ExternalAccess.Copilot.SemanticSearch.SemanticSearchCopilotContextImpl
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// 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.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;

namespace Microsoft.CodeAnalysis.ExternalAccess.Copilot.SemanticSearch;

internal interface ICopilotSemanticSearchQueryExecutor
{
Task<CopilotSemanticSearchQueryResults> ExecuteAsync(string query, int resultCountLimit, CancellationToken cancellationToken);
}

internal readonly struct CopilotSemanticSearchQueryResults
{
public required IReadOnlyList<string> Symbols { get; init; }
public required IReadOnlyList<(string id, string message)> CompilationErrors { get; init; }
public string? Error { get; init; }
public required bool LimitReached { get; init; }
}
Loading
Loading