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());
        }
示例#2
0
        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));
        }
示例#3
0
        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());
        }
示例#5
0
        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;
            }
        }