Skip to content

Commit

Permalink
Merge pull request #51898 from ryzngard/feature/value_tracking/service
Browse files Browse the repository at this point in the history
Add value tracking service
  • Loading branch information
msftbot[bot] committed Mar 19, 2021
2 parents 78287fa + cdfb8dc commit 307e7c6
Show file tree
Hide file tree
Showing 7 changed files with 566 additions and 0 deletions.
20 changes: 20 additions & 0 deletions src/EditorFeatures/Core/ValueTracking/IValueTrackingService.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// 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.Immutable;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.Host;

namespace Microsoft.CodeAnalysis.ValueTracking
{
internal interface IValueTrackingService : IWorkspaceService
{
Task<ImmutableArray<ValueTrackedItem>> TrackValueSourceAsync(Solution solution, ISymbol symbol, CancellationToken cancellationToken);
Task TrackValueSourceAsync(Solution solution, ISymbol symbol, ValueTrackingProgressCollector progressCollector, CancellationToken cancellationToken);

Task<ImmutableArray<ValueTrackedItem>> TrackValueSourceAsync(Solution solution, ValueTrackedItem previousTrackedItem, CancellationToken cancellationToken);
Task TrackValueSourceAsync(Solution solution, ValueTrackedItem previousTrackedItem, ValueTrackingProgressCollector progressCollector, CancellationToken cancellationToken);
}
}
20 changes: 20 additions & 0 deletions src/EditorFeatures/Core/ValueTracking/ValueTrackedItem.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// 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.

namespace Microsoft.CodeAnalysis.ValueTracking
{
internal class ValueTrackedItem
{
public Location Location { get; }
public ISymbol Symbol { get; }

public ValueTrackedItem(
Location location,
ISymbol symbol)
{
Location = location;
Symbol = symbol;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
// 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.Generic;
using System.Collections.Immutable;

namespace Microsoft.CodeAnalysis.ValueTracking
{
internal class ValueTrackingProgressCollector : IProgress<ValueTrackedItem>
{
private readonly object _lock = new();
private readonly Stack<ValueTrackedItem> _items = new();

public event EventHandler<ValueTrackedItem>? OnNewItem;

public void Report(ValueTrackedItem item)
{
lock (_lock)
{
_items.Push(item);
}

OnNewItem?.Invoke(null, item);
}

public ImmutableArray<ValueTrackedItem> GetItems()
{
lock (_lock)
{
return _items.ToImmutableArray();
}
}
}
}
116 changes: 116 additions & 0 deletions src/EditorFeatures/Core/ValueTracking/ValueTrackingService.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
// 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.FindSymbols;
using Microsoft.CodeAnalysis.Host.Mef;
using Microsoft.CodeAnalysis.Shared.Utilities;

namespace Microsoft.CodeAnalysis.ValueTracking
{
[ExportWorkspaceService(typeof(IValueTrackingService)), Shared]
internal class ValueTrackingService : IValueTrackingService
{
[ImportingConstructor]
[Obsolete(MefConstruction.ImportingConstructorMessage, error: true)]
public ValueTrackingService()
{
}

public async Task<ImmutableArray<ValueTrackedItem>> TrackValueSourceAsync(
Solution solution,
ISymbol symbol,
CancellationToken cancellationToken)
{
var progressTracker = new ValueTrackingProgressCollector();
await TrackValueSourceAsync(solution, symbol, progressTracker, cancellationToken).ConfigureAwait(false);
return progressTracker.GetItems();
}

public async Task TrackValueSourceAsync(
Solution solution,
ISymbol symbol,
ValueTrackingProgressCollector progressCollector,
CancellationToken cancellationToken)
{
if (symbol
is IPropertySymbol
or IFieldSymbol
or ILocalSymbol
or IParameterSymbol)
{
// 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 symbol.DeclaringSyntaxReferences)
{
var location = Location.Create(syntaxRef.SyntaxTree, syntaxRef.Span);
progressCollector.Report(new ValueTrackedItem(location, symbol));
}

var findReferenceProgressCollector = new FindReferencesProgress(progressCollector);
await SymbolFinder.FindReferencesAsync(
symbol, solution, findReferenceProgressCollector,
documents: null, FindReferencesSearchOptions.Default, cancellationToken).ConfigureAwait(false);
}
}

public async Task<ImmutableArray<ValueTrackedItem>> TrackValueSourceAsync(
Solution solution,
ValueTrackedItem previousTrackedItem,
CancellationToken cancellationToken)
{
var progressTracker = new ValueTrackingProgressCollector();
await TrackValueSourceAsync(solution, previousTrackedItem, progressTracker, cancellationToken).ConfigureAwait(false);
return progressTracker.GetItems();
}

public Task TrackValueSourceAsync(
Solution solution,
ValueTrackedItem previousTrackedItem,
ValueTrackingProgressCollector progressCollector,
CancellationToken cancellationToken)
{
throw new NotImplementedException();
}

private class FindReferencesProgress : IStreamingFindReferencesProgress, IStreamingProgressTracker
{
private readonly ValueTrackingProgressCollector _valueTrackingProgressCollector;
public FindReferencesProgress(ValueTrackingProgressCollector valueTrackingProgressCollector)
{
_valueTrackingProgressCollector = valueTrackingProgressCollector;
}

public IStreamingProgressTracker ProgressTracker => this;

public ValueTask AddItemsAsync(int count) => new();

public ValueTask ItemCompletedAsync() => new();

public ValueTask OnCompletedAsync() => new();

public ValueTask OnDefinitionFoundAsync(ISymbol symbol) => new();

public ValueTask OnFindInDocumentCompletedAsync(Document document) => new();

public ValueTask OnFindInDocumentStartedAsync(Document document) => new();

public ValueTask OnReferenceFoundAsync(ISymbol symbol, ReferenceLocation location)
{
if (location.IsWrittenTo)
{
_valueTrackingProgressCollector.Report(new ValueTrackedItem(location.Location, symbol));
}

return new();
}

public ValueTask OnStartedAsync() => new();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
// 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.Immutable;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.ValueTracking;
using Microsoft.CodeAnalysis.Editor.UnitTests.Workspaces;
using Microsoft.CodeAnalysis.Shared.Extensions;
using System.Threading;
using Microsoft.CodeAnalysis.Text;
using Xunit;

namespace Microsoft.CodeAnalysis.Editor.UnitTests.ValueTracking
{
public abstract class AbstractBaseValueTrackingTests
{
internal static async Task<ImmutableArray<ValueTrackedItem>> GetTrackedItemsAsync(TestWorkspace testWorkspace, CancellationToken cancellationToken = default)
{
var cursorDocument = testWorkspace.DocumentWithCursor;
var document = testWorkspace.CurrentSolution.GetRequiredDocument(cursorDocument.Id);
var textSpan = new TextSpan(cursorDocument.CursorPosition!.Value, 0);
var symbol = await GetSelectedSymbolAsync(textSpan, document, cancellationToken);
var service = testWorkspace.Services.GetRequiredService<IValueTrackingService>();
return await service.TrackValueSourceAsync(testWorkspace.CurrentSolution, symbol, cancellationToken);

}

internal static async Task<ISymbol> GetSelectedSymbolAsync(TextSpan textSpan, Document document, CancellationToken cancellationToken)
{
var root = await document.GetRequiredSyntaxRootAsync(cancellationToken).ConfigureAwait(false);
var selectedNode = root.FindNode(textSpan);

Assert.NotNull(selectedNode);

var semanticModel = await document.GetRequiredSemanticModelAsync(cancellationToken).ConfigureAwait(false);
var selectedSymbol =
semanticModel.GetSymbolInfo(selectedNode, cancellationToken).Symbol
?? semanticModel.GetDeclaredSymbol(selectedNode, cancellationToken);

Assert.NotNull(selectedSymbol);
return selectedSymbol!;
}

internal static void ValidateItem(ValueTrackedItem item, int line)
{
var lineSpan = item.Location.GetLineSpan();
Assert.Equal(line, lineSpan.StartLinePosition.Line);
}
}
}
Loading

0 comments on commit 307e7c6

Please sign in to comment.