Skip to content

Commit

Permalink
Merge pull request #33836 from dotnet/merges/master-to-features/reado…
Browse files Browse the repository at this point in the history
…nly-members

Merge master to features/readonly-members
  • Loading branch information
dotnet-automerge-bot authored Mar 3, 2019
2 parents b5ec68d + 880d35a commit d02d0a5
Show file tree
Hide file tree
Showing 16 changed files with 223 additions and 19 deletions.
2 changes: 2 additions & 0 deletions src/Compilers/CSharp/Portable/Syntax/SyntaxKindFacts.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1630,6 +1630,8 @@ public static string GetText(SyntaxKind kind)
return "$\"";
case SyntaxKind.InterpolatedStringEndToken:
return "\"";
case SyntaxKind.InterpolatedVerbatimStringStartToken:
return "$@\"";
case SyntaxKind.UnderscoreToken:
return "_";
case SyntaxKind.VarKeyword:
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,51 @@
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System.Linq;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.CSharp.Test.Utilities;
using Roslyn.Test.Utilities;
using Xunit;

namespace Microsoft.CodeAnalysis.CSharp.UnitTests.CodeGen
{
public class InterpolatedStringTests : CSharpTestBase
{
[Fact, WorkItem(33713, "https://github.com/dotnet/roslyn/issues/33713")]
public void AlternateVerbatimString()
{
var source = @"
class C
{
static void Main()
{
int i = 42;
var s = @$""{i}
{i}"";
System.Console.Write(s);
var s2 = $@"""";
}
}";
var comp = CreateCompilation(source, options: TestOptions.ReleaseExe);
CompileAndVerify(comp, expectedOutput: @"42
42");

var tree = comp.SyntaxTrees.Single();
var interpolatedStrings = tree.GetRoot().DescendantNodes().OfType<InterpolatedStringExpressionSyntax>().ToArray();
var token1 = interpolatedStrings[0].StringStartToken;
Assert.Equal("@$\"", token1.Text);
Assert.Equal("@$\"", token1.ValueText);

var token2 = interpolatedStrings[1].StringStartToken;
Assert.Equal("$@\"", token2.Text);
Assert.Equal("$@\"", token2.ValueText);

foreach (var token in tree.GetRoot().DescendantTokens().Where(t => t.Kind() != SyntaxKind.EndOfFileToken))
{
Assert.False(string.IsNullOrEmpty(token.Text));
Assert.False(string.IsNullOrEmpty(token.ValueText));
}
}

[Fact]
public void ConstInterpolations()
{
Expand Down
8 changes: 8 additions & 0 deletions src/Compilers/CSharp/Test/Syntax/Syntax/SyntaxFactoryTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,14 @@ namespace Microsoft.CodeAnalysis.CSharp.UnitTests
{
public class SyntaxFactoryTests : CSharpTestBase
{
[Fact, WorkItem(33713, "https://github.com/dotnet/roslyn/issues/33713")]
public void AlternateVerbatimString()
{
var token = SyntaxFactory.Token(SyntaxKind.InterpolatedVerbatimStringStartToken);
Assert.Equal("$@\"", token.Text);
Assert.Equal("$@\"", token.ValueText);
}

[Fact]
public void SyntaxTree()
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ public EditAndContinueDiagnosticUpdateSource(IDiagnosticUpdateSourceRegistration
public bool SupportGetDiagnostics => false;

public event EventHandler<DiagnosticsUpdatedArgs> DiagnosticsUpdated;
public event EventHandler DiagnosticsCleared { add { } remove { } }

public ImmutableArray<DiagnosticData> GetDiagnostics(Workspace workspace, ProjectId projectId, DocumentId documentId, object id, bool includeSuppressedDiagnostics = false, CancellationToken cancellationToken = default)
{
Expand Down
92 changes: 80 additions & 12 deletions src/EditorFeatures/Test/Diagnostics/DiagnosticServiceTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,17 +20,17 @@ public void TestGetDiagnostics1()
{
using (var workspace = new TestWorkspace(TestExportProvider.ExportProviderWithCSharpAndVisualBasic))
{
var set = new ManualResetEvent(false);
var mutex = new ManualResetEvent(false);
var document = workspace.CurrentSolution.AddProject("TestProject", "TestProject", LanguageNames.CSharp).AddDocument("TestDocument", string.Empty);

var source = new TestDiagnosticUpdateSource(false, null);
var diagnosticService = new DiagnosticService(AsynchronousOperationListenerProvider.NullProvider);
diagnosticService.Register(source);

diagnosticService.DiagnosticsUpdated += (s, o) => { set.Set(); };
diagnosticService.DiagnosticsUpdated += (s, o) => { mutex.Set(); };

var id = Tuple.Create(workspace, document);
var diagnostic = RaiseDiagnosticEvent(set, source, workspace, document.Project.Id, document.Id, id);
var diagnostic = RaiseDiagnosticEvent(mutex, source, workspace, document.Project.Id, document.Id, id);

var data1 = diagnosticService.GetDiagnostics(workspace, null, null, null, false, CancellationToken.None);
Assert.Equal(diagnostic, data1.Single());
Expand All @@ -51,27 +51,27 @@ public void TestGetDiagnostics2()
{
using (var workspace = new TestWorkspace(TestExportProvider.ExportProviderWithCSharpAndVisualBasic))
{
var set = new ManualResetEvent(false);
var mutex = new ManualResetEvent(false);
var document = workspace.CurrentSolution.AddProject("TestProject", "TestProject", LanguageNames.CSharp).AddDocument("TestDocument", string.Empty);
var document2 = document.Project.AddDocument("TestDocument2", string.Empty);

var source = new TestDiagnosticUpdateSource(false, null);
var diagnosticService = new DiagnosticService(AsynchronousOperationListenerProvider.NullProvider);
diagnosticService.Register(source);

diagnosticService.DiagnosticsUpdated += (s, o) => { set.Set(); };
diagnosticService.DiagnosticsUpdated += (s, o) => { mutex.Set(); };

var id = Tuple.Create(workspace, document);
RaiseDiagnosticEvent(set, source, workspace, document.Project.Id, document.Id, id);
RaiseDiagnosticEvent(mutex, source, workspace, document.Project.Id, document.Id, id);

var id2 = Tuple.Create(workspace, document.Project, document);
RaiseDiagnosticEvent(set, source, workspace, document.Project.Id, document.Id, id2);
RaiseDiagnosticEvent(mutex, source, workspace, document.Project.Id, document.Id, id2);

RaiseDiagnosticEvent(set, source, workspace, document2.Project.Id, document2.Id, Tuple.Create(workspace, document2));
RaiseDiagnosticEvent(mutex, source, workspace, document2.Project.Id, document2.Id, Tuple.Create(workspace, document2));

var id3 = Tuple.Create(workspace, document.Project);
RaiseDiagnosticEvent(set, source, workspace, document.Project.Id, null, id3);
RaiseDiagnosticEvent(set, source, workspace, null, null, Tuple.Create(workspace));
RaiseDiagnosticEvent(mutex, source, workspace, document.Project.Id, null, id3);
RaiseDiagnosticEvent(mutex, source, workspace, null, null, Tuple.Create(workspace));

var data1 = diagnosticService.GetDiagnostics(workspace, null, null, null, false, CancellationToken.None);
Assert.Equal(5, data1.Count());
Expand All @@ -90,13 +90,75 @@ public void TestGetDiagnostics2()
}
}

[Fact, Trait(Traits.Feature, Traits.Features.Diagnostics)]
public void TestCleared()
{
using (var workspace = new TestWorkspace(TestExportProvider.ExportProviderWithCSharpAndVisualBasic))
{
var mutex = new ManualResetEvent(false);
var document = workspace.CurrentSolution.AddProject("TestProject", "TestProject", LanguageNames.CSharp).AddDocument("TestDocument", string.Empty);
var document2 = document.Project.AddDocument("TestDocument2", string.Empty);

var diagnosticService = new DiagnosticService(AsynchronousOperationListenerProvider.NullProvider);

var source1 = new TestDiagnosticUpdateSource(support: false, diagnosticData: null);
diagnosticService.Register(source1);

var source2 = new TestDiagnosticUpdateSource(support: false, diagnosticData: null);
diagnosticService.Register(source2);

diagnosticService.DiagnosticsUpdated += MarkSet;

// add bunch of data to the service for both sources
RaiseDiagnosticEvent(mutex, source1, workspace, document.Project.Id, document.Id, Tuple.Create(workspace, document));
RaiseDiagnosticEvent(mutex, source1, workspace, document.Project.Id, document.Id, Tuple.Create(workspace, document.Project, document));
RaiseDiagnosticEvent(mutex, source1, workspace, document2.Project.Id, document2.Id, Tuple.Create(workspace, document2));

RaiseDiagnosticEvent(mutex, source2, workspace, document.Project.Id, null, Tuple.Create(workspace, document.Project));
RaiseDiagnosticEvent(mutex, source2, workspace, null, null, Tuple.Create(workspace));

// confirm data is there.
var data1 = diagnosticService.GetDiagnostics(workspace, null, null, null, false, CancellationToken.None);
Assert.Equal(5, data1.Count());

diagnosticService.DiagnosticsUpdated -= MarkSet;

// confirm clear for a source
mutex.Reset();
var count = 0;
diagnosticService.DiagnosticsUpdated += MarkCalled;

source1.RaiseDiagnosticsClearedEvent();

mutex.WaitOne();

// confirm there are 2 data left
var data2 = diagnosticService.GetDiagnostics(workspace, null, null, null, false, CancellationToken.None);
Assert.Equal(2, data2.Count());

void MarkCalled(object sender, DiagnosticsUpdatedArgs args)
{
// event is serialized. no concurrent call
if (++count == 3)
{
mutex.Set();
}
}

void MarkSet(object sender, DiagnosticsUpdatedArgs args)
{
mutex.Set();
}
}
}

private static DiagnosticData RaiseDiagnosticEvent(ManualResetEvent set, TestDiagnosticUpdateSource source, TestWorkspace workspace, ProjectId project, DocumentId document, object id)
{
set.Reset();

var diagnostic = CreateDiagnosticData(workspace, project, document);

source.RaiseUpdateEvent(
source.RaiseDiagnosticsUpdatedEvent(
DiagnosticsUpdatedArgs.DiagnosticsCreated(id, workspace, workspace.CurrentSolution, project, document, ImmutableArray.Create(diagnostic)));

set.WaitOne();
Expand Down Expand Up @@ -126,16 +188,22 @@ public TestDiagnosticUpdateSource(bool support, DiagnosticData[] diagnosticData)

public bool SupportGetDiagnostics { get { return _support; } }
public event EventHandler<DiagnosticsUpdatedArgs> DiagnosticsUpdated;
public event EventHandler DiagnosticsCleared;

public ImmutableArray<DiagnosticData> GetDiagnostics(Workspace workspace, ProjectId projectId, DocumentId documentId, object id, bool includeSuppressedDiagnostics = false, CancellationToken cancellationToken = default)
{
return _support ? _diagnosticData : ImmutableArray<DiagnosticData>.Empty;
}

public void RaiseUpdateEvent(DiagnosticsUpdatedArgs args)
public void RaiseDiagnosticsUpdatedEvent(DiagnosticsUpdatedArgs args)
{
DiagnosticsUpdated?.Invoke(this, args);
}

public void RaiseDiagnosticsClearedEvent()
{
DiagnosticsCleared?.Invoke(this, EventArgs.Empty);
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ public void RaiseDiagnosticsUpdated(DiagnosticsUpdatedArgs args)
}

public event EventHandler<DiagnosticsUpdatedArgs> DiagnosticsUpdated;
public event EventHandler DiagnosticsCleared { add { } remove { } }

public bool SupportGetDiagnostics => false;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ public ImmutableArray<DiagnosticData> GetDiagnostics(Workspace workspace, Projec
}

public event EventHandler<DiagnosticsUpdatedArgs> DiagnosticsUpdated;
public event EventHandler DiagnosticsCleared { add { } remove { } }

public void RaiseDiagnosticsUpdated(DiagnosticsUpdatedArgs args)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ public IIncrementalAnalyzer CreateIncrementalAnalyzer(Workspace workspace)
}

public event EventHandler<DiagnosticsUpdatedArgs> DiagnosticsUpdated;
public event EventHandler DiagnosticsCleared { add { } remove { } }

// this only support push model, pull model will be provided by DiagnosticService by caching everything this one pushed
public bool SupportGetDiagnostics => false;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,19 @@ public event EventHandler<DiagnosticsUpdatedArgs> DiagnosticsUpdated
}
}

public event EventHandler DiagnosticsCleared
{
add
{
// don't do anything. this update source doesn't use cleared event
}

remove
{
// don't do anything. this update source doesn't use cleared event
}
}

internal void RaiseDiagnosticsUpdated(DiagnosticsUpdatedArgs args)
{
// all diagnostics events are serialized.
Expand Down
73 changes: 67 additions & 6 deletions src/Features/Core/Portable/Diagnostics/DiagnosticService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -55,11 +55,8 @@ public event EventHandler<DiagnosticsUpdatedArgs> DiagnosticsUpdated
}
}

private void RaiseDiagnosticsUpdated(object sender, DiagnosticsUpdatedArgs args)
private void RaiseDiagnosticsUpdated(IDiagnosticUpdateSource source, DiagnosticsUpdatedArgs args)
{
Contract.ThrowIfNull(sender);
var source = (IDiagnosticUpdateSource)sender;

var ev = _eventMap.GetEventHandlers<EventHandler<DiagnosticsUpdatedArgs>>(DiagnosticsUpdatedEventName);
if (!RequireRunningEventTasks(source, ev))
{
Expand All @@ -75,7 +72,35 @@ private void RaiseDiagnosticsUpdated(object sender, DiagnosticsUpdatedArgs args)
return;
}

ev.RaiseEvent(handler => handler(sender, args));
ev.RaiseEvent(handler => handler(source, args));
}).CompletesAsyncOperation(eventToken);
}

private void RaiseDiagnosticsCleared(IDiagnosticUpdateSource source)
{
var ev = _eventMap.GetEventHandlers<EventHandler<DiagnosticsUpdatedArgs>>(DiagnosticsUpdatedEventName);
if (!RequireRunningEventTasks(source, ev))
{
return;
}

var eventToken = _listener.BeginAsyncOperation(DiagnosticsUpdatedEventName);
_eventQueue.ScheduleTask(() =>
{
using (var pooledObject = SharedPools.Default<List<DiagnosticsUpdatedArgs>>().GetPooledObject())
{
var removed = pooledObject.Object;
if (!ClearDiagnosticsReportedBySource(source, removed))
{
// there is no change, nothing to raise events for.
return;
}

foreach (var args in removed)
{
ev.RaiseEvent(handler => handler(source, args));
}
}
}).CompletesAsyncOperation(eventToken);
}

Expand Down Expand Up @@ -150,10 +175,46 @@ private bool UpdateDataMap(IDiagnosticUpdateSource source, DiagnosticsUpdatedArg
}
}

private bool ClearDiagnosticsReportedBySource(IDiagnosticUpdateSource source, List<DiagnosticsUpdatedArgs> removed)
{
// we expect source who uses this ability to have small number of diagnostics.
lock (_gate)
{
Debug.Assert(_updateSources.Contains(source));

// 2 different workspaces (ex, PreviewWorkspaces) can return same Args.Id, we need to
// distinguish them. so we separate diagnostics per workspace map.
if (!_map.TryGetValue(source, out var workspaceMap))
{
return false;
}

foreach (var (workspace, map) in workspaceMap)
{
foreach (var (id, data) in map)
{
removed.Add(DiagnosticsUpdatedArgs.DiagnosticsRemoved(id, data.Workspace, solution: null, data.ProjectId, data.DocumentId));
}
}

// all diagnostics from the source is cleared
_map.Remove(source);
return true;
}
}

private void OnDiagnosticsUpdated(object sender, DiagnosticsUpdatedArgs e)
{
AssertIfNull(e.Diagnostics);
RaiseDiagnosticsUpdated(sender, e);

// all events are serialized by async event handler
RaiseDiagnosticsUpdated((IDiagnosticUpdateSource)sender, e);
}

private void OnCleared(object sender, EventArgs e)
{
// all events are serialized by async event handler
RaiseDiagnosticsCleared((IDiagnosticUpdateSource)sender);
}

public IEnumerable<DiagnosticData> GetDiagnostics(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,9 @@ public void Register(IDiagnosticUpdateSource source)
}

_updateSources = _updateSources.Add(source);

source.DiagnosticsUpdated += OnDiagnosticsUpdated;
source.DiagnosticsCleared += OnCleared;
}
}
}
Expand Down
Loading

0 comments on commit d02d0a5

Please sign in to comment.