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"