예제 #1
0
        public static async Task TrackValueSourceAsync(
            TextSpan selection,
            Document document,
            ValueTrackingProgressCollector progressCollector,
            CancellationToken cancellationToken)
        {
            var(symbol, node) = await GetSelectedSymbolAsync(selection, document, cancellationToken).ConfigureAwait(false);

            var operationCollector = new OperationCollector(progressCollector, document.Project.Solution);

            if (symbol
                is IPropertySymbol
                or IFieldSymbol
                or ILocalSymbol
                or IParameterSymbol)
            {
                RoslynDebug.AssertNotNull(node);

                var solution = document.Project.Solution;
                var declaringSyntaxReferences = symbol.DeclaringSyntaxReferences;
                var syntaxFacts = document.GetRequiredLanguageService <ISyntaxFactsService>();

                // If the selection is within a declaration of the symbol, we want to include
                // all declarations and assignments of the symbol
                if (declaringSyntaxReferences.Any(static (r, selection) => r.Span.IntersectsWith(selection), selection))
예제 #2
0
 private static async Task TrackVariableReferencesAsync(ISymbol symbol, OperationCollector collector, CancellationToken cancellationToken)
 {
     var findReferenceProgressCollector = new FindReferencesProgress(collector);
     await SymbolFinder.FindReferencesAsync(
         symbol,
         collector.Solution,
         findReferenceProgressCollector,
         documents : null, FindReferencesSearchOptions.Default, cancellationToken).ConfigureAwait(false);
 }
예제 #3
0
        private static async Task TrackMethodSymbolAsync(IMethodSymbol methodSymbol, OperationCollector collector, CancellationToken cancellationToken)
        {
            var hasAnyOutData = HasAValueReturn(methodSymbol) || HasAnOutOrRefParam(methodSymbol);

            if (!hasAnyOutData)
            {
                // With no out data, there's nothing to do here
                return;
            }

            // TODO: Use DFA to find meaningful returns? https://github.com/dotnet/roslyn-analyzers/blob/9e5f533cbafcc5579e4d758bc9bde27b7611ca54/docs/Writing%20dataflow%20analysis%20based%20analyzers.md
            if (HasAValueReturn(methodSymbol))
            {
                foreach (var location in methodSymbol.GetDefinitionLocationsToShow())
                {
                    if (location.SourceTree is null)
                    {
                        continue;
                    }

                    var node          = location.FindNode(cancellationToken);
                    var sourceDoc     = collector.Solution.GetRequiredDocument(location.SourceTree);
                    var syntaxFacts   = sourceDoc.GetRequiredLanguageService <ISyntaxFactsService>();
                    var semanticModel = await sourceDoc.GetRequiredSemanticModelAsync(cancellationToken).ConfigureAwait(false);

                    var operation = semanticModel.GetOperation(node, cancellationToken);

                    // In VB the parent node contains the operation (IBlockOperation) instead of the one returned
                    // by the symbol location.
                    if (operation is null && node.Parent is not null)
                    {
                        operation = semanticModel.GetOperation(node.Parent, cancellationToken);
                    }

                    if (operation is null)
                    {
                        continue;
                    }

                    await collector.VisitAsync(operation, cancellationToken).ConfigureAwait(false);
                }
            }

            if (HasAnOutOrRefParam(methodSymbol))
            {
                foreach (var outOrRefParam in methodSymbol.Parameters.Where(p => p.IsRefOrOut()))
                {
                    if (!outOrRefParam.IsFromSource())
                    {
                        continue;
                    }

                    await TrackVariableReferencesAsync(outOrRefParam, collector, cancellationToken).ConfigureAwait(false);
                }
            }
예제 #4
0
 public static IEnumerable<IOperation> DescendantsAndSelf(this IOperation operation)
 {
     if (operation == null)
     {
         return SpecializedCollections.EmptyEnumerable<IOperation>();
     }
     var list = new List<IOperation>();
     var collector = new OperationCollector(list);
     collector.Visit(operation);
     return list;
 }
예제 #5
0
        public static async Task TrackValueSourceAsync(
            TextSpan selection,
            Document document,
            ValueTrackingProgressCollector progressCollector,
            CancellationToken cancellationToken)
        {
            var(symbol, node) = await GetSelectedSymbolAsync(selection, document, cancellationToken).ConfigureAwait(false);

            var operationCollector = new OperationCollector(progressCollector, document.Project.Solution);

            if (symbol
                is IPropertySymbol
                or IFieldSymbol
                or ILocalSymbol
                or IParameterSymbol)
            {
                RoslynDebug.AssertNotNull(node);

                var solution = document.Project.Solution;
                var declaringSyntaxReferences = symbol.DeclaringSyntaxReferences;
                var syntaxFacts = document.GetRequiredLanguageService <ISyntaxFactsService>();

                // If the selection is within a declaration of the symbol, we want to include
                // all declarations and assignments of the symbol
                if (declaringSyntaxReferences.Any(r => r.Span.IntersectsWith(selection)))
                {
                    // Add all initializations of the symbol. Those are not caught in
                    // the reference finder but should still show up in the tree
                    foreach (var syntaxRef in declaringSyntaxReferences)
                    {
                        var location = Location.Create(syntaxRef.SyntaxTree, syntaxRef.Span);
                        await progressCollector.TryReportAsync(solution, location, symbol, cancellationToken).ConfigureAwait(false);
                    }

                    await TrackVariableReferencesAsync(symbol, operationCollector, cancellationToken).ConfigureAwait(false);
                }
                // The selection is not on a declaration, check that the node
                // is on the left side of an assignment. If so, populate so we can
                // track the RHS values that contribute to this value
                else if (syntaxFacts.IsLeftSideOfAnyAssignment(node))
                {
                    await AddItemsFromAssignmentAsync(document, node, operationCollector, cancellationToken).ConfigureAwait(false);
                }
                // Not on the left part of an assignment? Then just add an item with the statement
                // and the symbol. It should be the top item, and children will find the sources
                // of the value. A good example is a return statement, such as "return $$x",
                // where $$ is the cursor position. The top item should have the return statement for
                // context, and the remaining items should expand into the assignments of x
                else
                {
                    await progressCollector.TryReportAsync(document.Project.Solution, node.GetLocation(), symbol, cancellationToken).ConfigureAwait(false);
                }
            }
        }
        public static IEnumerable <IOperation> DescendantsAndSelf(this IOperation operation)
        {
            if (operation == null)
            {
                return(SpecializedCollections.EmptyEnumerable <IOperation>());
            }
            List <IOperation>  list      = new List <IOperation>();
            OperationCollector collector = new OperationCollector(list);

            collector.Visit(operation);
            return(list);
        }
예제 #7
0
        public static IEnumerable <IOperation> Descendants(this IOperation operation)
        {
            if (operation == null)
            {
                return(SpecializedCollections.EmptyEnumerable <IOperation>());
            }
            var list      = new List <IOperation>();
            var collector = new OperationCollector(list);

            collector.Visit(operation);
            list.RemoveAt(0);
            return(list);
        }
예제 #8
0
 private static async Task TrackParameterSymbolAsync(
     IParameterSymbol parameterSymbol,
     OperationCollector collector,
     CancellationToken cancellationToken)
 {
     var containingSymbol = parameterSymbol.ContainingSymbol;
     var findReferenceProgressCollector = new FindReferencesProgress(collector);
     await SymbolFinder.FindReferencesAsync(
         containingSymbol,
         collector.Solution,
         findReferenceProgressCollector,
         documents : null, FindReferencesSearchOptions.Default, cancellationToken).ConfigureAwait(false);
 }
 public FindReferencesProgress(OperationCollector valueTrackingProgressCollector)
 {
     _operationCollector = valueTrackingProgressCollector;
 }
예제 #10
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;
            }
        }
예제 #11
0
        private static async Task AddItemsFromAssignmentAsync(Document document, SyntaxNode lhsNode, OperationCollector collector, CancellationToken cancellationToken)
        {
            var semanticModel = await document.GetRequiredSemanticModelAsync(cancellationToken).ConfigureAwait(false);

            var operation = semanticModel.GetOperation(lhsNode, cancellationToken);

            if (operation is null)
            {
                return;
            }

            IAssignmentOperation?assignmentOperation = null;

            while (assignmentOperation is null &&
                   operation is not null)
            {
                assignmentOperation = operation as IAssignmentOperation;
                operation           = operation.Parent;
            }

            if (assignmentOperation is null)
            {
                return;
            }

            await collector.VisitAsync(assignmentOperation, cancellationToken).ConfigureAwait(false);
        }