diff --git a/Src/Compilers/CSharp/Source/Compilation/CSharpCompilation.cs b/Src/Compilers/CSharp/Source/Compilation/CSharpCompilation.cs
index 94c71f6672b663388d1c5ab3c0e3f36474c14ede..59be0e7b5236b104490bd8694abe42d06219cdfa 100644
--- a/Src/Compilers/CSharp/Source/Compilation/CSharpCompilation.cs
+++ b/Src/Compilers/CSharp/Source/Compilation/CSharpCompilation.cs
@@ -14,6 +14,7 @@
using Microsoft.CodeAnalysis.CSharp.Emit;
using Microsoft.CodeAnalysis.CSharp.Symbols;
using Microsoft.CodeAnalysis.CSharp.Syntax;
+using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.Emit;
using Microsoft.CodeAnalysis.Instrumentation;
using Microsoft.CodeAnalysis.Text;
@@ -107,6 +108,11 @@ internal Conversions Conversions
///
private EntryPoint lazyEntryPoint;
+ ///
+ /// The set of trees for which a has been added to the queue.
+ ///
+ private HashSet lazyCompilationUnitCompletedTrees;
+
public override string Language
{
get
@@ -288,8 +294,9 @@ internal string Feature(string p)
Type hostObjectType,
bool isSubmission,
ReferenceManager referenceManager,
- bool reuseReferenceManager)
- : base(assemblyName, references, submissionReturnType, hostObjectType, isSubmission, syntaxTreeOrdinalMap)
+ bool reuseReferenceManager,
+ AsyncQueue eventQueue = null)
+ : base(assemblyName, references, submissionReturnType, hostObjectType, isSubmission, syntaxTreeOrdinalMap, eventQueue)
{
using (Logger.LogBlock(FunctionId.CSharp_Compilation_Create, message: assemblyName))
{
@@ -334,6 +341,7 @@ internal string Feature(string p)
}
Debug.Assert((object)this.lazyAssemblySymbol == null);
+ if (EventQueue != null) EventQueue.Enqueue(new CompilationEvent.CompilationStarted(this));
}
}
@@ -500,6 +508,28 @@ internal CSharpCompilation WithPreviousSubmission(CSharpCompilation newPreviousS
reuseReferenceManager: true);
}
+ ///
+ /// Returns a new compilation with a given event queue.
+ ///
+ internal CSharpCompilation WithEventQueue(AsyncQueue eventQueue)
+ {
+ return new CSharpCompilation(
+ this.AssemblyName,
+ this.options,
+ this.ExternalReferences,
+ this.SyntaxTrees,
+ this.syntaxTreeOrdinalMap,
+ this.rootNamespaces,
+ this.declarationTable,
+ this.previousSubmission,
+ this.SubmissionReturnType,
+ this.HostObjectType,
+ this.IsSubmission,
+ this.referenceManager,
+ reuseReferenceManager: true,
+ eventQueue: eventQueue);
+ }
+
#endregion
#region Submission
@@ -1700,24 +1730,73 @@ private AliasSymbol CreateGlobalNamespaceAlias()
return AliasSymbol.CreateGlobalNamespaceAlias(this.GlobalNamespace, new InContainerBinder(this.GlobalNamespace, new BuckStopsHereBinder(this)));
}
- internal void ReportUnusedImports(DiagnosticBag diagnostics, CancellationToken cancellationToken, SyntaxTree filterTree = null)
+ void CompleteTree(SyntaxTree tree)
{
- if (this.lazyImportInfos == null) return;
+ bool completedCompilationUnit = false;
+ bool completedCompilation = false;
- foreach (ImportInfo info in this.lazyImportInfos)
+ if (lazyCompilationUnitCompletedTrees == null) Interlocked.CompareExchange(ref lazyCompilationUnitCompletedTrees, new HashSet(), null);
+ lock (lazyCompilationUnitCompletedTrees)
{
- cancellationToken.ThrowIfCancellationRequested();
+ if (lazyCompilationUnitCompletedTrees.Add(tree))
+ {
+ completedCompilationUnit = true;
+ if (lazyCompilationUnitCompletedTrees.Count == SyntaxTrees.Length)
+ {
+ completedCompilation = true;
+ }
+ }
+ }
- SyntaxTree infoTree = info.Tree;
- if (filterTree == null || filterTree == infoTree)
+ if (completedCompilationUnit)
+ {
+ EventQueue.Enqueue(new CompilationEvent.CompilationUnitCompleted(this, null, tree));
+ }
+
+ if (completedCompilation)
+ {
+ EventQueue.Enqueue(new CompilationEvent.CompilationCompleted(this));
+ EventQueue.Complete(); // signal the end of compilation events
+ }
+ }
+
+ internal void ReportUnusedImports(DiagnosticBag diagnostics, CancellationToken cancellationToken, SyntaxTree filterTree = null)
+ {
+ if (this.lazyImportInfos != null)
+ {
+ foreach (ImportInfo info in this.lazyImportInfos)
{
- TextSpan infoSpan = info.Span;
- if (!this.IsImportDirectiveUsed(infoTree, infoSpan.Start))
+ cancellationToken.ThrowIfCancellationRequested();
+
+ SyntaxTree infoTree = info.Tree;
+ if (filterTree == null || filterTree == infoTree)
{
- ErrorCode code = info.Kind == SyntaxKind.ExternAliasDirective
- ? ErrorCode.INF_UnusedExternAlias
- : ErrorCode.INF_UnusedUsingDirective;
- diagnostics.Add(code, infoTree.GetLocation(infoSpan));
+ TextSpan infoSpan = info.Span;
+ if (!this.IsImportDirectiveUsed(infoTree, infoSpan.Start))
+ {
+ ErrorCode code = info.Kind == SyntaxKind.ExternAliasDirective
+ ? ErrorCode.INF_UnusedExternAlias
+ : ErrorCode.INF_UnusedUsingDirective;
+ diagnostics.Add(code, infoTree.GetLocation(infoSpan));
+ }
+ }
+ }
+ }
+
+ // By definition, a tree is complete when all of its compiler diagnostics have been reported.
+ // Since unused imports are the last thing we compute and report, a tree is complete when
+ // the unused imports have been reported.
+ if (EventQueue != null)
+ {
+ if (filterTree != null)
+ {
+ CompleteTree(filterTree);
+ }
+ else
+ {
+ foreach (var tree in SyntaxTrees)
+ {
+ CompleteTree(tree);
}
}
}
@@ -2982,5 +3061,10 @@ private bool IsMemberMissing(int member)
{
return lazyMakeMemberMissingMap != null && lazyMakeMemberMissingMap.ContainsKey(member);
}
+
+ internal void SymbolDeclaredEvent(Symbol symbol)
+ {
+ if (EventQueue != null) EventQueue.Enqueue(new CompilationEvent.SymbolDeclared(this, symbol));
+ }
}
}
diff --git a/Src/Compilers/CSharp/Source/Compiler/MethodBodyCompiler.cs b/Src/Compilers/CSharp/Source/Compiler/MethodBodyCompiler.cs
index 0ae2cd76cfd01186db383e84a94dc88825854940..71d1bc0922e64a248134c1b617c3f9b838c3e6e3 100644
--- a/Src/Compilers/CSharp/Source/Compiler/MethodBodyCompiler.cs
+++ b/Src/Compilers/CSharp/Source/Compiler/MethodBodyCompiler.cs
@@ -12,6 +12,7 @@
using Microsoft.CodeAnalysis.CSharp.Symbols;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Roslyn.Utilities;
+using Microsoft.CodeAnalysis.Diagnostics;
namespace Microsoft.CodeAnalysis.CSharp
{
@@ -674,6 +675,15 @@ public override object VisitField(FieldSymbol symbol, TypeCompilationState argum
if (methodSymbol.IsAbstract)
{
+ // TODO: ensure that we only send a single event for each method
+ if (methodSymbol.DeclaringSyntaxReferences.Length != 0)
+ {
+ compilation.SymbolDeclaredEvent(methodSymbol);
+ }
+ else
+ {
+ // what about source symbols with no declaration? How does that happen?
+ }
return;
}
@@ -779,7 +789,15 @@ public override object VisitField(FieldSymbol symbol, TypeCompilationState argum
DiagnosticsPass.IssueDiagnostics(compilation, body, diagsForCurrentMethod, methodSymbol);
}
- BoundBlock flowAnalyzedBody = (body == null) ? null : FlowAnalysisPass.Rewrite(methodSymbol, body, diagsForCurrentMethod);
+ BoundBlock flowAnalyzedBody = null;
+ if (body == null)
+ {
+ compilation.SymbolDeclaredEvent(methodSymbol);
+ }
+ else
+ {
+ flowAnalyzedBody = FlowAnalysisPass.Rewrite(methodSymbol, body, diagsForCurrentMethod);
+ }
bool hasErrors = hasDeclarationErrors || diagsForCurrentMethod.HasAnyErrors() || processedInitializers.HasErrors;
diff --git a/Src/Compilers/CSharp/Source/FlowAnalysis/FlowAnalysisPass.cs b/Src/Compilers/CSharp/Source/FlowAnalysis/FlowAnalysisPass.cs
index 4fb40428241c660ebca3a573860ed04a234bfd78..432311864a23060ea595e1e18a5dff277b6a6fec 100644
--- a/Src/Compilers/CSharp/Source/FlowAnalysis/FlowAnalysisPass.cs
+++ b/Src/Compilers/CSharp/Source/FlowAnalysis/FlowAnalysisPass.cs
@@ -5,6 +5,8 @@
using Microsoft.CodeAnalysis.CSharp.Symbols;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Text;
+using Microsoft.CodeAnalysis.Diagnostics;
+using System;
namespace Microsoft.CodeAnalysis.CSharp
{
@@ -19,7 +21,10 @@ internal class FlowAnalysisPass
/// the method's body
/// the receiver of the reported diagnostics
/// the rewritten block for the method (with a return statement possibly inserted)
- public static BoundBlock Rewrite(MethodSymbol method, BoundBlock block, DiagnosticBag diagnostics)
+ public static BoundBlock Rewrite(
+ MethodSymbol method,
+ BoundBlock block,
+ DiagnosticBag diagnostics)
{
var compilation = method.DeclaringCompilation;
@@ -90,10 +95,29 @@ internal static BoundBlock AppendImplicitReturn(BoundStatement node, MethodSymbo
}
}
- private static bool Analyze(CSharpCompilation compilation, MethodSymbol method, BoundBlock block, DiagnosticBag diagnostics)
+ private static bool Analyze(
+ CSharpCompilation compilation,
+ MethodSymbol method,
+ BoundBlock block,
+ DiagnosticBag diagnostics)
{
var result = ControlFlowPass.Analyze(compilation, method, block, diagnostics);
DataFlowPass.Analyze(compilation, method, block, diagnostics);
+ if (compilation.EventQueue != null)
+ {
+ var lazySemanticModel = new Lazy(() =>
+ {
+ var syntax = block.Syntax;
+ var semanticModel = (CSharpSemanticModel)compilation.GetSemanticModel(syntax.SyntaxTree);
+ var memberModel = semanticModel.GetMemberModel(syntax);
+ if (memberModel != null)
+ {
+ memberModel.AddBoundTreeForStandaloneSyntax(syntax, block);
+ }
+ return semanticModel;
+ });
+ compilation.EventQueue.Enqueue(new CompilationEvent.SymbolDeclared(compilation, method, lazySemanticModel));
+ }
DisposeCheckerPass.Analyze(compilation, method, block, diagnostics);
return result;
}
diff --git a/Src/Compilers/CSharp/Source/Symbols/Source/SourceEventSymbol.cs b/Src/Compilers/CSharp/Source/Symbols/Source/SourceEventSymbol.cs
index a29237000c2867ab04717a9e05123f9b3c0c3470..451e9f627dcd5a7336d6c6aefc243ef50cf901c9 100644
--- a/Src/Compilers/CSharp/Source/Symbols/Source/SourceEventSymbol.cs
+++ b/Src/Compilers/CSharp/Source/Symbols/Source/SourceEventSymbol.cs
@@ -9,6 +9,7 @@
using Microsoft.CodeAnalysis.Text;
using Roslyn.Utilities;
using System.Collections.Generic;
+using Microsoft.CodeAnalysis.Diagnostics;
namespace Microsoft.CodeAnalysis.CSharp.Symbols
{
@@ -177,7 +178,9 @@ private CustomAttributesBag GetAttributesBag()
if ((lazyCustomAttributesBag == null || !lazyCustomAttributesBag.IsSealed) &&
LoadAndValidateAttributes(OneOrMany.Create(this.AttributeDeclarationSyntaxList), ref lazyCustomAttributesBag))
{
- state.NotePartComplete(CompletionPart.Attributes);
+ var completed = state.NotePartComplete(CompletionPart.Attributes);
+ Debug.Assert(completed);
+ DeclaringCompilation.SymbolDeclaredEvent(this);
}
return lazyCustomAttributesBag;
diff --git a/Src/Compilers/CSharp/Source/Symbols/Source/SourceFieldSymbol.cs b/Src/Compilers/CSharp/Source/Symbols/Source/SourceFieldSymbol.cs
index 9f0d32edb42770749598f6f07fd67800e77524c0..3cf350174c79f852d83350f62469b31b4f108427 100644
--- a/Src/Compilers/CSharp/Source/Symbols/Source/SourceFieldSymbol.cs
+++ b/Src/Compilers/CSharp/Source/Symbols/Source/SourceFieldSymbol.cs
@@ -12,6 +12,7 @@
using Microsoft.CodeAnalysis.CSharp.Symbols;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Roslyn.Utilities;
+using Microsoft.CodeAnalysis.Diagnostics;
namespace Microsoft.CodeAnalysis.CSharp.Symbols
{
@@ -172,7 +173,9 @@ private CustomAttributesBag GetAttributesBag()
if (LoadAndValidateAttributes(OneOrMany.Create(this.AttributeDeclarationSyntaxList), ref lazyCustomAttributesBag))
{
- state.NotePartComplete(CompletionPart.Attributes);
+ var completed = state.NotePartComplete(CompletionPart.Attributes);
+ Debug.Assert(completed);
+ DeclaringCompilation.SymbolDeclaredEvent(this);
}
Debug.Assert(lazyCustomAttributesBag.IsSealed);
diff --git a/Src/Compilers/CSharp/Source/Symbols/Source/SourceMemberMethodSymbol.cs b/Src/Compilers/CSharp/Source/Symbols/Source/SourceMemberMethodSymbol.cs
index d04ba98beb1f1d82cf426d43e2164171a10d78c6..c3d35e3d83c3b8b99881dc6a1e78a9fd5a593063 100644
--- a/Src/Compilers/CSharp/Source/Symbols/Source/SourceMemberMethodSymbol.cs
+++ b/Src/Compilers/CSharp/Source/Symbols/Source/SourceMemberMethodSymbol.cs
@@ -9,6 +9,7 @@
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Text;
using Roslyn.Utilities;
+using Microsoft.CodeAnalysis.Diagnostics;
namespace Microsoft.CodeAnalysis.CSharp.Symbols
{
@@ -161,6 +162,11 @@ internal sealed class SourceMemberMethodSymbol : SourceMethodSymbol
this.constraintClauseBinder = null;
}
+
+ if (bodyOpt == null)
+ {
+ DeclaringCompilation.SymbolDeclaredEvent(this);
+ }
}
public override bool ReturnsVoid
diff --git a/Src/Compilers/CSharp/Source/Symbols/Source/SourceModuleSymbol.cs b/Src/Compilers/CSharp/Source/Symbols/Source/SourceModuleSymbol.cs
index 62f06b1e887cd23e4858b3e70d2f57ba2ed2cf8a..cba04adcdb015aa566e5fd53b8c0d9cea0add4b1 100644
--- a/Src/Compilers/CSharp/Source/Symbols/Source/SourceModuleSymbol.cs
+++ b/Src/Compilers/CSharp/Source/Symbols/Source/SourceModuleSymbol.cs
@@ -441,7 +441,8 @@ private CustomAttributesBag GetAttributesBag()
var mergedAttributes = ((SourceAssemblySymbol)this.ContainingAssembly).GetAttributeDeclarations();
if (LoadAndValidateAttributes(OneOrMany.Create(mergedAttributes), ref lazyCustomAttributesBag))
{
- state.NotePartComplete(CompletionPart.Attributes);
+ var completed = state.NotePartComplete(CompletionPart.Attributes);
+ Debug.Assert(completed);
}
}
diff --git a/Src/Compilers/CSharp/Source/Symbols/Source/SourceNamedTypeSymbol.cs b/Src/Compilers/CSharp/Source/Symbols/Source/SourceNamedTypeSymbol.cs
index eb371ef7e7dea657f4e829ac2daa80658855ea75..2665158be539183067346f08a09f5ba91f4e5023 100644
--- a/Src/Compilers/CSharp/Source/Symbols/Source/SourceNamedTypeSymbol.cs
+++ b/Src/Compilers/CSharp/Source/Symbols/Source/SourceNamedTypeSymbol.cs
@@ -13,6 +13,7 @@
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Text;
using Roslyn.Utilities;
+using Microsoft.CodeAnalysis.Diagnostics;
namespace Microsoft.CodeAnalysis.CSharp.Symbols
{
@@ -401,7 +402,9 @@ private CustomAttributesBag GetAttributesBag()
if (LoadAndValidateAttributes(OneOrMany.Create(this.GetAttributeDeclarations()), ref lazyCustomAttributesBag))
{
- state.NotePartComplete(CompletionPart.Attributes);
+ var completed = state.NotePartComplete(CompletionPart.Attributes);
+ Debug.Assert(completed);
+ DeclaringCompilation.SymbolDeclaredEvent(this);
}
Debug.Assert(lazyCustomAttributesBag.IsSealed);
diff --git a/Src/Compilers/CSharp/Source/Symbols/Source/SourcePropertySymbol.cs b/Src/Compilers/CSharp/Source/Symbols/Source/SourcePropertySymbol.cs
index 4b35149aac746ddd126ff2102497d35198e7b6da..9911d6c838dfdf890c66d786e7f5720d2780c8b9 100644
--- a/Src/Compilers/CSharp/Source/Symbols/Source/SourcePropertySymbol.cs
+++ b/Src/Compilers/CSharp/Source/Symbols/Source/SourcePropertySymbol.cs
@@ -2,6 +2,7 @@
using Microsoft.CodeAnalysis.CSharp.Symbols;
using Microsoft.CodeAnalysis.CSharp.Syntax;
+using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.Text;
using Roslyn.Utilities;
using System.Collections.Generic;
@@ -853,7 +854,9 @@ private CustomAttributesBag GetAttributesBag()
if (LoadAndValidateAttributes(OneOrMany.Create(this.CSharpSyntaxNode.AttributeLists), ref lazyCustomAttributesBag))
{
- state.NotePartComplete(CompletionPart.Attributes);
+ var completed = state.NotePartComplete(CompletionPart.Attributes);
+ Debug.Assert(completed);
+ DeclaringCompilation.SymbolDeclaredEvent(this);
}
Debug.Assert(lazyCustomAttributesBag.IsSealed);
diff --git a/Src/Compilers/CSharp/Test/Semantic/CSharpCompilerSemanticTest.csproj b/Src/Compilers/CSharp/Test/Semantic/CSharpCompilerSemanticTest.csproj
index 19262e07b867776bcfec00356a3e5aefefd2b754..fa1bab1f6e91ff9cbbf6279085ec3a992bde49b9 100644
--- a/Src/Compilers/CSharp/Test/Semantic/CSharpCompilerSemanticTest.csproj
+++ b/Src/Compilers/CSharp/Test/Semantic/CSharpCompilerSemanticTest.csproj
@@ -76,6 +76,7 @@
+
diff --git a/Src/Compilers/CSharp/Test/Semantic/Diagnostics/CompilationEventTests.cs b/Src/Compilers/CSharp/Test/Semantic/Diagnostics/CompilationEventTests.cs
new file mode 100644
index 0000000000000000000000000000000000000000..7a46b69e88c0bd13e0bd8a6eef6bd47353d90705
--- /dev/null
+++ b/Src/Compilers/CSharp/Test/Semantic/Diagnostics/CompilationEventTests.cs
@@ -0,0 +1,110 @@
+// Copyright (c) Microsoft Open Technologies, Inc. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System;
+using System.Collections.Generic;
+using System.Collections.Immutable;
+using System.Globalization;
+using System.Linq;
+using System.Runtime.Serialization;
+using System.Threading;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+using Microsoft.CodeAnalysis.CSharp.Test.Utilities;
+using Microsoft.CodeAnalysis.Diagnostics;
+using Roslyn.Test.Utilities;
+using Roslyn.Utilities;
+using Xunit;
+
+namespace Microsoft.CodeAnalysis.CSharp.UnitTests
+{
+ public partial class CompilationEventTests : CompilingTestBase
+ {
+ internal static void VerifyEvents(AsyncQueue queue, params string[] expectedEvents)
+ {
+ var expected = new HashSet();
+ foreach (var s in expectedEvents) expected.Add(s);
+ var actual = ArrayBuilder.GetInstance();
+ while (queue.Count != 0 || !queue.IsCompleted)
+ {
+ var te = queue.DequeueAsync();
+ Assert.True(te.IsCompleted);
+ actual.Add(te.Result);
+ }
+ bool unexpected = false;
+ foreach (var a in actual)
+ {
+ var eventString = a.ToString();
+ if (!expected.Remove(eventString))
+ {
+ if (!unexpected)
+ {
+ Console.WriteLine("UNEXPECTED EVENTS:");
+ unexpected = true;
+ }
+ Console.WriteLine(eventString);
+ }
+ }
+ if (expected.Count != 0)
+ {
+ Console.WriteLine("MISSING EVENTS:");
+ }
+ foreach (var e in expected)
+ {
+ Console.WriteLine(e);
+ }
+ if (unexpected || expected.Count != 0)
+ {
+ bool first = true;
+ Console.WriteLine("ACTUAL EVENTS:");
+ foreach (var e in actual)
+ {
+ if (!first)
+ {
+ Console.WriteLine(",");
+ }
+ first = false;
+ Console.Write("\"" + e.ToString() + "\"");
+ }
+ Console.WriteLine();
+ Assert.True(false);
+ }
+ }
+
+ [Fact]
+ public void TestQueuedSymbols()
+ {
+ var source =
+@"namespace N
+{
+ partial class C
+ {
+ partial void M(int x1);
+ internal int P { get; private set; }
+ int F = 12;
+ void N(int y = 12) { F = F + 1; }
+ }
+ partial class C
+ {
+ partial void M(int x2) {}
+ }
+}";
+ var q = new AsyncQueue();
+ CreateCompilationWithMscorlib45(source)
+ .WithEventQueue(q)
+ .VerifyDiagnostics() // force diagnostics twice
+ .VerifyDiagnostics();
+ VerifyEvents(q,
+ "CompilationStarted",
+ "SymbolDeclared(C N.C @ : (2,2)-(8,3))",
+ "SymbolDeclared(M N.C.M(int) @ : (4,4)-(4,27))",
+ "SymbolDeclared(P N.C.P @ : (5,4)-(5,40))",
+ "SymbolDeclared(F N.C.F @ : (6,8)-(6,14))",
+ "SymbolDeclared(M N.C.M(int) @ : (11,4)-(11,29))",
+ "SymbolDeclared(get_P N.C.P.get @ : (5,21)-(5,25))",
+ "SymbolDeclared(set_P N.C.P.set @ : (5,26)-(5,38))",
+ "SymbolDeclared(N N.C.N(int) @ : (7,4)-(7,41))",
+ "CompilationUnitCompleted()",
+ "CompilationCompleted"
+ );
+ }
+ }
+}
diff --git a/Src/Compilers/Core/Source/CodeAnalysis.csproj b/Src/Compilers/Core/Source/CodeAnalysis.csproj
index cf4dadc5e0c63d5b1254ac7d4620ac0be7788c2b..59c3e4a1e13cbaf96d942bad2151bd00d930622b 100644
--- a/Src/Compilers/Core/Source/CodeAnalysis.csproj
+++ b/Src/Compilers/Core/Source/CodeAnalysis.csproj
@@ -163,6 +163,7 @@
+
diff --git a/Src/Compilers/Core/Source/Compilation/Compilation.cs b/Src/Compilers/Core/Source/Compilation/Compilation.cs
index 32dc05d8874d584cf75042871999624c51cba119..fbb6e6e15c45478455a63b485669ebccd4466460 100644
--- a/Src/Compilers/Core/Source/Compilation/Compilation.cs
+++ b/Src/Compilers/Core/Source/Compilation/Compilation.cs
@@ -12,6 +12,7 @@
using System.Threading;
using Microsoft.CodeAnalysis.CodeGen;
using Microsoft.CodeAnalysis.Collections;
+using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.Emit;
using Microsoft.CodeAnalysis.Instrumentation;
using Microsoft.CodeAnalysis.Text;
@@ -46,13 +47,15 @@ public abstract partial class Compilation
Type submissionReturnType,
Type hostObjectType,
bool isSubmission,
- ImmutableDictionary syntaxTreeOrdinalMap)
+ ImmutableDictionary syntaxTreeOrdinalMap,
+ AsyncQueue eventQueue)
{
Debug.Assert(!references.IsDefault);
this.AssemblyName = name;
this.ExternalReferences = references;
this.syntaxTreeOrdinalMap = syntaxTreeOrdinalMap;
+ this.EventQueue = eventQueue;
if (isSubmission)
{
@@ -439,6 +442,11 @@ public bool ContainsSyntaxTree(SyntaxTree syntaxTree)
protected abstract bool CommonContainsSyntaxTree(SyntaxTree syntaxTree);
+ ///
+ /// The event queue that this compilation was created with.
+ ///
+ internal readonly AsyncQueue EventQueue;
+
#endregion
#region References
diff --git a/Src/Compilers/Core/Source/DiagnosticAnalyzer/AsyncQueue.cs b/Src/Compilers/Core/Source/DiagnosticAnalyzer/AsyncQueue.cs
index 51cd6ecc4dd8b301dd66326fa295157a74f52f47..53800aa522e25a7c69d4c8a3c628dd3d2600332e 100644
--- a/Src/Compilers/Core/Source/DiagnosticAnalyzer/AsyncQueue.cs
+++ b/Src/Compilers/Core/Source/DiagnosticAnalyzer/AsyncQueue.cs
@@ -7,6 +7,7 @@
using System.Diagnostics;
using System.Linq;
using System.Text;
+using System.Threading;
using System.Threading.Tasks;
namespace Microsoft.CodeAnalysis.Diagnostics
@@ -17,11 +18,11 @@ namespace Microsoft.CodeAnalysis.Diagnostics
/// The type of values kept by the queue.
public sealed class AsyncQueue
{
- private object syncObject = new object();
- private Queue data = new Queue();
- private Queue> waiters = new Queue>();
+ private readonly object syncObject = new object();
+ private readonly Queue data = new Queue();
+ private readonly Queue> waiters = new Queue>();
private bool completed = false;
- private TaskCompletionSource whenCompleted = new TaskCompletionSource();
+ private readonly TaskCompletionSource whenCompleted = new TaskCompletionSource();
private Exception thrown = null;
///
@@ -31,7 +32,10 @@ public int Count
{
get
{
- return data.Count;
+ lock(syncObject)
+ {
+ return data.Count;
+ }
}
}
@@ -49,21 +53,32 @@ public AsyncQueue()
public void Enqueue(TElement value)
{
TaskCompletionSource waiter;
- lock (syncObject)
+ lock(syncObject)
{
- if (completed) throw new InvalidOperationException("Add after Complete");
- if (thrown != null) return;
- if (waiters.Count == 0)
+ if (completed)
+ {
+ throw new InvalidOperationException("Enqueue after Complete");
+ }
+ else if (thrown != null)
+ {
+ return;
+ }
+ else if (waiters.Count == 0)
{
data.Enqueue(value);
return;
}
-
- waiter = waiters.Dequeue();
- Debug.Assert(data.Count == 0);
+ else
+ {
+ Debug.Assert(data.Count == 0);
+ waiter = waiters.Dequeue();
+ }
}
- waiter.SetResult(value);
+ Task.Run(() =>
+ {
+ waiter.SetResult(value);
+ });
}
///
@@ -77,19 +92,31 @@ public void SetException(Exception exception)
ImmutableArray> waitersArray;
lock (syncObject)
{
- if (completed) throw new InvalidOperationException("Thrown after Completed");
- if (thrown != null) throw new InvalidOperationException("Thrown after Thrown");
- thrown = exception;
- data.Clear();
- waitersArray = waiters.AsImmutable(); // TODO: move allocation out of the lock
- waiters.Clear();
+ if (completed)
+ {
+ throw new InvalidOperationException("Thrown after Completed");
+ }
+ else if (thrown != null)
+ {
+ throw new InvalidOperationException("Thrown after Thrown");
+ }
+ else
+ {
+ thrown = exception;
+ data.Clear();
+ waitersArray = waiters.AsImmutable();
+ waiters.Clear();
+ }
}
- whenCompleted.SetException(exception);
- foreach (var tcs in waitersArray)
+ Task.Run(() =>
{
- tcs.SetException(exception);
- }
+ whenCompleted.SetException(exception);
+ foreach (var tcs in waitersArray)
+ {
+ tcs.SetException(exception);
+ }
+ });
}
///
@@ -99,7 +126,10 @@ public bool IsCompleted
{
get
{
- return completed;
+ lock(syncObject)
+ {
+ return completed;
+ }
}
}
@@ -109,27 +139,43 @@ public bool IsCompleted
public void Complete()
{
ImmutableArray> waitersArray;
- lock (syncObject)
+ lock(syncObject)
{
- if (thrown != null) throw new InvalidOperationException("Done after Thrown");
- waitersArray = waiters.AsImmutable();
- waiters.Clear();
- completed = true;
+ if (completed)
+ {
+ throw new InvalidOperationException("Completed after Completed");
+ }
+ else if (thrown != null)
+ {
+ throw new InvalidOperationException("Completed after Thrown");
+ }
+ else
+ {
+ waitersArray = waiters.AsImmutable();
+ waiters.Clear();
+ completed = true;
+ }
}
- foreach (var tcs in waitersArray)
+ Task.Run(() =>
{
- tcs.SetCanceled();
- }
- whenCompleted.SetResult(true);
+ whenCompleted.SetResult(true);
+ foreach (var tcs in waitersArray)
+ {
+ tcs.SetCanceled();
+ }
+ });
}
///
/// Gets a task that transitions to a completed state when is called.
///
- public Task WhenCompleted
+ public Task WhenCompleted
{
- get { return whenCompleted.Task; }
+ get
+ {
+ return whenCompleted.Task;
+ }
}
///
@@ -141,20 +187,25 @@ public Task WhenCompleted
///
public Task DequeueAsync()
{
- lock (syncObject)
+ lock(syncObject)
{
if (thrown != null)
{
- throw thrown;
+ var waiter = new TaskCompletionSource();
+ waiter.SetException(thrown);
+ return waiter.Task;
}
else if (data.Count != 0)
{
Debug.Assert(waiters.Count == 0);
- return Task.FromResult(data.Dequeue());
+ var datum = data.Dequeue();
+ return Task.FromResult(datum);
}
else if (completed)
{
- throw new TaskCanceledException();
+ var waiter = new TaskCompletionSource();
+ waiter.SetCanceled();
+ return waiter.Task;
}
else
{
@@ -167,7 +218,7 @@ public Task DequeueAsync()
public override string ToString()
{
- return "AsyncQueue<" + typeof(TElement).Name + ">:" + (this.IsCompleted ? "Completed" : data.Count.ToString());
+ return "AsyncQueue<" + typeof(TElement).Name + ">:" + (IsCompleted ? "Completed" : Count.ToString());
}
}
}
diff --git a/Src/Compilers/Core/Source/DiagnosticAnalyzer/CompilationEvent.cs b/Src/Compilers/Core/Source/DiagnosticAnalyzer/CompilationEvent.cs
new file mode 100644
index 0000000000000000000000000000000000000000..c0f46875e067bb4d48edb72a110f8ca405bfb3ee
--- /dev/null
+++ b/Src/Compilers/Core/Source/DiagnosticAnalyzer/CompilationEvent.cs
@@ -0,0 +1,156 @@
+// Copyright (c) Microsoft Open Technologies, Inc. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Microsoft.CodeAnalysis.Diagnostics
+{
+ public abstract class CompilationEvent
+ {
+ protected CompilationEvent(Compilation compilation)
+ {
+ this.Compilation = compilation;
+ }
+
+ public Compilation Compilation { get; private set; }
+
+ ///
+ /// Flush any cached data in this to minimize space usage (at the possible expense of time later).
+ /// The principal effect of this is to free cached information on events that have a .
+ ///
+ public virtual void FlushCache() { }
+
+ ///
+ /// The first event placed into a compilation's event queue.
+ ///
+ public class CompilationStarted : CompilationEvent
+ {
+ public CompilationStarted(Compilation compilation) : base(compilation) { }
+ public override string ToString()
+ {
+ return "CompilationStarted";
+ }
+ }
+
+ ///
+ /// The last event placed into a compilation's event queue.
+ ///
+ public class CompilationCompleted : CompilationEvent
+ {
+ public CompilationCompleted(Compilation compilation) : base(compilation) { }
+ public override string ToString()
+ {
+ return "CompilationCompleted";
+ }
+ }
+
+ ///
+ /// An event for each declaration in the program (namespace, type, method, field, parameter, etc).
+ /// Note that some symbols may have multiple declarations (namespaces, partial types) and may therefore
+ /// have multiple events.
+ ///
+ public class SymbolDeclared : CompilationEvent
+ {
+ public SymbolDeclared(Compilation compilation, ISymbol symbol) : base(compilation)
+ {
+ this.Symbol = symbol;
+ }
+ public SymbolDeclared(Compilation compilation, ISymbol symbol, Lazy lazySemanticModel) : this(compilation, symbol)
+ {
+ this.lazySemanticModel = lazySemanticModel;
+ }
+ public ISymbol Symbol { get; private set; }
+
+ // At most one of these should be non-null.
+ private Lazy lazySemanticModel;
+ private SemanticModel semanticModel;
+ private WeakReference weakModel = null;
+ public SemanticModel SemanticModel(SyntaxReference reference)
+ {
+ lock (this)
+ {
+ var semanticModel = this.semanticModel;
+ if (semanticModel == null && this.lazySemanticModel != null)
+ {
+ this.semanticModel = semanticModel = this.lazySemanticModel.Value;
+ this.lazySemanticModel = null;
+ }
+ if (semanticModel == null && this.weakModel != null)
+ {
+ this.weakModel.TryGetTarget(out semanticModel);
+ }
+ if (semanticModel == null || semanticModel.SyntaxTree != reference.SyntaxTree)
+ {
+ semanticModel = Compilation.GetSemanticModel(reference.SyntaxTree);
+ this.weakModel = new WeakReference(semanticModel);
+ }
+
+ return semanticModel;
+ }
+ }
+ override public void FlushCache()
+ {
+ lock (this)
+ {
+ var semanticModel = this.semanticModel;
+ this.lazySemanticModel = null;
+ if (semanticModel == null) return;
+ this.weakModel = new WeakReference(semanticModel);
+ this.semanticModel = null;
+ }
+ }
+ public override string ToString()
+ {
+ var refs = Symbol.DeclaringSyntaxReferences;
+ var loc = refs.Length != 0 ? " @ " + refs[0].GetLocation().GetLineSpan() : null;
+ return "SymbolDeclared(" + this.Symbol.Name + " " + this.Symbol + loc + ")";
+ }
+ }
+
+ public class CompilationUnitCompleted : CompilationEvent
+ {
+ public CompilationUnitCompleted(Compilation compilation, SemanticModel semanticModel, SyntaxTree compilationUnit) : base(compilation)
+ {
+ this.SemanticModel = semanticModel;
+ this.CompilationUnit = compilationUnit;
+ }
+ private SemanticModel semanticModel;
+ private WeakReference weakModel = null;
+ public SemanticModel SemanticModel
+ {
+ get
+ {
+ var semanticModel = this.semanticModel;
+ var weakModel = this.weakModel;
+ if (semanticModel == null && weakModel == null || !weakModel.TryGetTarget(out semanticModel))
+ {
+ semanticModel = Compilation.GetSemanticModel(CompilationUnit);
+ this.weakModel = new WeakReference(semanticModel);
+ }
+ return semanticModel;
+ }
+ private set
+ {
+ this.semanticModel = value;
+ }
+ }
+ override public void FlushCache()
+ {
+ var semanticModel = this.semanticModel;
+ if (semanticModel == null) return;
+ this.weakModel = new WeakReference(semanticModel);
+ this.semanticModel = null;
+ }
+
+ public SyntaxTree CompilationUnit { get; private set; }
+ public override string ToString()
+ {
+ return "CompilationUnitCompleted(" + CompilationUnit.FilePath + ")";
+ }
+ }
+ }
+
+}
diff --git a/Src/Compilers/Core/Source/DiagnosticAnalyzer/ICodeBlockEndedAnalyzer.cs b/Src/Compilers/Core/Source/DiagnosticAnalyzer/ICodeBlockEndedAnalyzer.cs
index 52389dc06af51647d78c9200798ac8d812c55a3b..f6084271b1cf8051478c5a08682078af84de4fdd 100644
--- a/Src/Compilers/Core/Source/DiagnosticAnalyzer/ICodeBlockEndedAnalyzer.cs
+++ b/Src/Compilers/Core/Source/DiagnosticAnalyzer/ICodeBlockEndedAnalyzer.cs
@@ -1,4 +1,6 @@
-using System;
+// Copyright (c) Microsoft Open Technologies, Inc. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System;
using System.Collections.Generic;
using System.Threading;
diff --git a/Src/Compilers/Core/Source/DiagnosticAnalyzer/ICodeBlockStartedAnalyzer.cs b/Src/Compilers/Core/Source/DiagnosticAnalyzer/ICodeBlockStartedAnalyzer.cs
index a13eaf73c17a088e5ed5558fa76bd87f2a36708c..29ebeff682a4861959643a47b16886f5811a1efd 100644
--- a/Src/Compilers/Core/Source/DiagnosticAnalyzer/ICodeBlockStartedAnalyzer.cs
+++ b/Src/Compilers/Core/Source/DiagnosticAnalyzer/ICodeBlockStartedAnalyzer.cs
@@ -1,4 +1,6 @@
-using System;
+// Copyright (c) Microsoft Open Technologies, Inc. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System;
using System.Collections.Generic;
using System.Threading;
diff --git a/Src/Compilers/Core/Source/DiagnosticAnalyzer/ICompilationEndedAnalyzer.cs b/Src/Compilers/Core/Source/DiagnosticAnalyzer/ICompilationEndedAnalyzer.cs
index 2c908aec2acb5e051fbf0db66751c763063beae8..830cf08f74b59c3550e4de323070cd6375a1b272 100644
--- a/Src/Compilers/Core/Source/DiagnosticAnalyzer/ICompilationEndedAnalyzer.cs
+++ b/Src/Compilers/Core/Source/DiagnosticAnalyzer/ICompilationEndedAnalyzer.cs
@@ -1,4 +1,6 @@
-using System;
+// Copyright (c) Microsoft Open Technologies, Inc. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System;
using System.Collections.Generic;
using System.Threading;
diff --git a/Src/Compilers/Core/Source/DiagnosticAnalyzer/ICompilationStartedAnalyzer.cs b/Src/Compilers/Core/Source/DiagnosticAnalyzer/ICompilationStartedAnalyzer.cs
index b4b8977a80f81d54d171e12b34bbb31773c44b84..f0fd2f65c656bdb3899f7eb9bee78ff5987bc762 100644
--- a/Src/Compilers/Core/Source/DiagnosticAnalyzer/ICompilationStartedAnalyzer.cs
+++ b/Src/Compilers/Core/Source/DiagnosticAnalyzer/ICompilationStartedAnalyzer.cs
@@ -1,4 +1,6 @@
-using System;
+// Copyright (c) Microsoft Open Technologies, Inc. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System;
using System.Collections.Generic;
using System.Threading;
diff --git a/Src/Compilers/Core/Source/DiagnosticAnalyzer/ISemanticModelAnalyzer.cs b/Src/Compilers/Core/Source/DiagnosticAnalyzer/ISemanticModelAnalyzer.cs
index c1b72d21448df11e34d04fec3447e2775322c7b1..3d63183ff70830cf6db0ada678dad787c2a0a2d8 100644
--- a/Src/Compilers/Core/Source/DiagnosticAnalyzer/ISemanticModelAnalyzer.cs
+++ b/Src/Compilers/Core/Source/DiagnosticAnalyzer/ISemanticModelAnalyzer.cs
@@ -1,4 +1,6 @@
-using System;
+// Copyright (c) Microsoft Open Technologies, Inc. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System;
using System.Collections.Generic;
using System.Threading;
diff --git a/Src/Compilers/Core/Source/DiagnosticAnalyzer/ISymbolAnalyzer.cs b/Src/Compilers/Core/Source/DiagnosticAnalyzer/ISymbolAnalyzer.cs
index b962fd7611179d69d2bb69522a164324f67d5631..61ba2befb8881ffb2d6c502c7f0629b06efa977b 100644
--- a/Src/Compilers/Core/Source/DiagnosticAnalyzer/ISymbolAnalyzer.cs
+++ b/Src/Compilers/Core/Source/DiagnosticAnalyzer/ISymbolAnalyzer.cs
@@ -1,4 +1,6 @@
-using System;
+// Copyright (c) Microsoft Open Technologies, Inc. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Threading;
diff --git a/Src/Compilers/Core/Source/DiagnosticAnalyzer/ISyntaxNodeAnalyzer.cs b/Src/Compilers/Core/Source/DiagnosticAnalyzer/ISyntaxNodeAnalyzer.cs
index 94ff469f6e0550e3dafd59a4c10f526bb4d2c143..b17f8379379175d450b518105b4f820cf0af6649 100644
--- a/Src/Compilers/Core/Source/DiagnosticAnalyzer/ISyntaxNodeAnalyzer.cs
+++ b/Src/Compilers/Core/Source/DiagnosticAnalyzer/ISyntaxNodeAnalyzer.cs
@@ -1,4 +1,6 @@
-using System;
+// Copyright (c) Microsoft Open Technologies, Inc. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Threading;
diff --git a/Src/Compilers/Core/Source/DiagnosticAnalyzer/ISyntaxTreeAnalyzer.cs b/Src/Compilers/Core/Source/DiagnosticAnalyzer/ISyntaxTreeAnalyzer.cs
index 4618514549344802c072f12a5b38364731cac249..e680ff361d277427d72e1865d7fca17c4d016ae0 100644
--- a/Src/Compilers/Core/Source/DiagnosticAnalyzer/ISyntaxTreeAnalyzer.cs
+++ b/Src/Compilers/Core/Source/DiagnosticAnalyzer/ISyntaxTreeAnalyzer.cs
@@ -1,4 +1,6 @@
-using System;
+// Copyright (c) Microsoft Open Technologies, Inc. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System;
using System.Collections.Generic;
using System.Threading;
diff --git a/Src/Compilers/VisualBasic/Source/Compilation/VisualBasicCompilation.vb b/Src/Compilers/VisualBasic/Source/Compilation/VisualBasicCompilation.vb
index 80fad826a9d7dfcab62b159d2c07c83811eee7f7..c146a27d9f337c751ab80248ef361a8a090e0d3e 100644
--- a/Src/Compilers/VisualBasic/Source/Compilation/VisualBasicCompilation.vb
+++ b/Src/Compilers/VisualBasic/Source/Compilation/VisualBasicCompilation.vb
@@ -7,6 +7,7 @@ Imports System.Runtime.InteropServices
Imports System.Threading
Imports System.Threading.Tasks
Imports Microsoft.CodeAnalysis.CodeGen
+Imports Microsoft.CodeAnalysis.Diagnostics
Imports Microsoft.CodeAnalysis.Emit
Imports Microsoft.CodeAnalysis.Instrumentation
Imports Microsoft.CodeAnalysis.InternalUtilities
@@ -369,9 +370,10 @@ Namespace Microsoft.CodeAnalysis.VisualBasic
hostObjectType As Type,
isSubmission As Boolean,
referenceManager As ReferenceManager,
- reuseReferenceManager As Boolean
+ reuseReferenceManager As Boolean,
+ Optional eventQueue As AsyncQueue(Of CompilationEvent) = Nothing
)
- MyBase.New(assemblyName, references, submissionReturnType, hostObjectType, isSubmission, syntaxTreeOrdinalMap)
+ MyBase.New(assemblyName, references, submissionReturnType, hostObjectType, isSubmission, syntaxTreeOrdinalMap, eventQueue)
Using Logger.LogBlock(FunctionId.VisualBasic_Compilation_Create, message:=assemblyName)
Debug.Assert(rootNamespaces IsNot Nothing)
@@ -406,6 +408,9 @@ Namespace Microsoft.CodeAnalysis.VisualBasic
End If
Debug.Assert(m_lazyAssemblySymbol Is Nothing)
+ If Me.EventQueue IsNot Nothing Then
+ Me.EventQueue.Enqueue(New CompilationEvent.CompilationStarted(Me))
+ End If
End Using
End Sub
@@ -427,7 +432,8 @@ Namespace Microsoft.CodeAnalysis.VisualBasic
Me.HostObjectType,
Me.IsSubmission,
m_referenceManager,
- reuseReferenceManager:=True)
+ reuseReferenceManager:=True,
+ eventQueue:=Nothing) ' no event queue when cloning
End Function
Private Function UpdateSyntaxTrees(
@@ -603,6 +609,28 @@ Namespace Microsoft.CodeAnalysis.VisualBasic
reuseReferenceManager:=True)
End Function
+ '''
+ ''' Returns a new compilation with a given event queue.
+ '''
+ Friend Shadows Function WithEventQueue(eventQueue As AsyncQueue(Of CompilationEvent)) As VisualBasicCompilation
+ Return New VisualBasicCompilation(
+ Me.AssemblyName,
+ Me.Options,
+ Me.ExternalReferences,
+ m_syntaxTrees,
+ Me.syntaxTreeOrdinalMap,
+ m_rootNamespaces,
+ m_embeddedTrees,
+ m_declarationTable,
+ m_previousSubmission,
+ Me.SubmissionReturnType,
+ Me.HostObjectType,
+ Me.IsSubmission,
+ m_referenceManager,
+ reuseReferenceManager:=True,
+ eventQueue:=eventQueue)
+ End Function
+
#End Region
#Region "Submission"