public async Task <ImmutableArray <ValueTrackedItem> > TrackValueSourceAsync( Solution solution, ValueTrackedItem previousTrackedItem, CancellationToken cancellationToken) { using var logger = Logger.LogBlock(FunctionId.ValueTracking_TrackValueSource, cancellationToken, LogLevel.Information); var project = solution.GetRequiredProject(previousTrackedItem.DocumentId.ProjectId); var client = await RemoteHostClient.TryGetClientAsync(project, cancellationToken).ConfigureAwait(false); if (client != null) { var dehydratedItem = SerializableValueTrackedItem.Dehydrate(solution, previousTrackedItem, cancellationToken); var result = await client.TryInvokeAsync <IRemoteValueTrackingService, ImmutableArray <SerializableValueTrackedItem> >( solution, (service, solutionInfo, cancellationToken) => service.TrackValueSourceAsync(solutionInfo, dehydratedItem, cancellationToken), cancellationToken).ConfigureAwait(false); if (!result.HasValue) { return(ImmutableArray <ValueTrackedItem> .Empty); } return(await result.Value.SelectAsArrayAsync( (item, cancellationToken) => item.RehydrateAsync(solution, cancellationToken), cancellationToken).ConfigureAwait(false)); } var progressTracker = new ValueTrackingProgressCollector(); await ValueTracker.TrackValueSourceAsync(solution, previousTrackedItem, progressTracker, cancellationToken).ConfigureAwait(false); return(progressTracker.GetItems()); }
public static SerializableValueTrackedItem Dehydrate(Solution solution, ValueTrackedItem valueTrackedItem, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); var parent = valueTrackedItem.Parent is null ? null : Dehydrate(solution, valueTrackedItem.Parent, cancellationToken); return(new SerializableValueTrackedItem(valueTrackedItem.SymbolKey, valueTrackedItem.Span, valueTrackedItem.DocumentId, parent)); }
public async Task <ValueTrackedItem?> RehydrateAsync(Solution solution, CancellationToken cancellationToken) { var document = solution.GetRequiredDocument(DocumentId); var semanticModel = await document.GetRequiredSemanticModelAsync(cancellationToken).ConfigureAwait(false); var symbolResolution = SymbolKey.Resolve(semanticModel.Compilation, cancellationToken: cancellationToken); if (symbolResolution.Symbol is null) { return(null); } cancellationToken.ThrowIfCancellationRequested(); var parent = Parent is null ? null : await Parent.RehydrateAsync(solution, cancellationToken).ConfigureAwait(false); return(await ValueTrackedItem.TryCreateAsync(document, TextSpan, symbolResolution.Symbol, parent, cancellationToken).ConfigureAwait(false)); }
public async Task <ImmutableArray <ValueTrackedItem> > TrackValueSourceAsync( Solution solution, ValueTrackedItem previousTrackedItem, CancellationToken cancellationToken) { using var logger = Logger.LogBlock(FunctionId.ValueTracking_TrackValueSource, cancellationToken, LogLevel.Information); var project = solution.GetRequiredProject(previousTrackedItem.DocumentId.ProjectId); var client = await RemoteHostClient.TryGetClientAsync(project, cancellationToken).ConfigureAwait(false); if (client != null) { var dehydratedItem = SerializableValueTrackedItem.Dehydrate(solution, previousTrackedItem, cancellationToken); var result = await client.TryInvokeAsync <IRemoteValueTrackingService, ImmutableArray <SerializableValueTrackedItem> >( solution, (service, solutionInfo, cancellationToken) => service.TrackValueSourceAsync(solutionInfo, dehydratedItem, cancellationToken), cancellationToken).ConfigureAwait(false); if (!result.HasValue) { return(ImmutableArray <ValueTrackedItem> .Empty); } using var _ = PooledObjects.ArrayBuilder <ValueTrackedItem> .GetInstance(out var builder); foreach (var item in result.Value) { var rehydratedItem = await item.RehydrateAsync(solution, cancellationToken).ConfigureAwait(false); if (rehydratedItem is null) { throw new InvalidOperationException(); } builder.Add(rehydratedItem); } return(builder.ToImmutable()); } var progressTracker = new ValueTrackingProgressCollector(); await ValueTracker.TrackValueSourceAsync(solution, previousTrackedItem, progressTracker, cancellationToken).ConfigureAwait(false); return(progressTracker.GetItems()); }
public static async Task TrackValueSourceAsync( Solution solution, ValueTrackedItem previousTrackedItem, ValueTrackingProgressCollector progressCollector, CancellationToken cancellationToken) { progressCollector.Parent = previousTrackedItem; var operationCollector = new OperationCollector(progressCollector, solution); var symbol = await GetSymbolAsync(previousTrackedItem, solution, cancellationToken).ConfigureAwait(false); switch (symbol) { case ILocalSymbol: case IPropertySymbol: case IFieldSymbol: { // The "output" is a variable assignment, track places where it gets assigned and defined await TrackVariableDefinitionsAsync(symbol, operationCollector, cancellationToken).ConfigureAwait(false); await TrackVariableReferencesAsync(symbol, operationCollector, cancellationToken).ConfigureAwait(false); } break; case IParameterSymbol parameterSymbol: { var previousSymbol = await GetSymbolAsync(previousTrackedItem.Parent, solution, cancellationToken).ConfigureAwait(false); // If the current parameter is a parameter symbol for the previous tracked method it should be treated differently. // For example: // string PrependString(string pre, string s) => pre + s; // ^--- previously tracked ^---- current parameter being tracked // // In this case, s is being tracked because it contributed to the return of the method. We only // want to track assignments to s that could impact the return rather than tracking the same method // twice. var isParameterForPreviousTrackedMethod = previousSymbol?.Equals(parameterSymbol.ContainingSymbol, SymbolEqualityComparer.Default) == true; // For Ref or Out parameters, they contribute data across method calls through assignments // within the method. No need to track returns // Ex: TryGetValue("mykey", out var [|v|]) // [|v|] is the interesting part, we don't care what the method returns var isRefOrOut = parameterSymbol.IsRefOrOut(); // Always track the parameter assignments as variables, in case they are assigned anywhere in the method await TrackVariableReferencesAsync(parameterSymbol, operationCollector, cancellationToken).ConfigureAwait(false); var trackMethod = !(isParameterForPreviousTrackedMethod || isRefOrOut); if (trackMethod) { await TrackParameterSymbolAsync(parameterSymbol, operationCollector, cancellationToken).ConfigureAwait(false); } } break; case IMethodSymbol methodSymbol: { // The "output" is from a method, meaning it has a return or out param that is used. Track those await TrackMethodSymbolAsync(methodSymbol, operationCollector, cancellationToken).ConfigureAwait(false); } break; } }