// Add all appropriate references to the compilation and set it as our final compilation // state. private async Task <Compilation> FinalizeCompilationAsync( Solution solution, Compilation compilation, CancellationToken cancellationToken) { try { var newReferences = new List <MetadataReference>(); newReferences.AddRange(this.ProjectState.MetadataReferences); foreach (var projectReference in this.ProjectState.ProjectReferences) { var referencedProject = solution.GetProject(projectReference.ProjectId); // Even though we're creating a final compilation (vs. an in progress compilation), // it's possible that the target project has been removed. if (referencedProject != null) { // If both projects are submissions, we'll count this as a previous submission link // instead of a regular metadata reference if (referencedProject.IsSubmission) { // if the referenced project is a submission project must be a submission as well: Debug.Assert(this.ProjectState.IsSubmission); var previousSubmissionCompilation = await solution.GetCompilationAsync(projectReference.ProjectId, cancellationToken).ConfigureAwait(false); compilation = compilation.WithPreviousSubmission(previousSubmissionCompilation); } else { var metadataReference = await solution.GetMetadataReferenceAsync( projectReference, this.ProjectState, cancellationToken).ConfigureAwait(false); // The compilation doesn't want to receive a null entry in the set // of references it is constructed with. A reference can fail to be // created if a skeleton assembly could not be constructed. if (metadataReference != null) { newReferences.Add(metadataReference); } } } } if (!Enumerable.SequenceEqual(compilation.References, newReferences)) { compilation = compilation.WithReferences(newReferences); } this.WriteState(new FinalState(this.Retain(solution, compilation))); return(compilation); } catch (Exception e) if (ExceptionHelpers.CrashUnlessCanceled(e)) { throw ExceptionUtilities.Unreachable; } }
// Add all appropriate references to the compilation and set it as our final compilation // state. private async Task<Compilation> FinalizeCompilationAsync( Solution solution, Compilation compilation, CancellationToken cancellationToken) { var newReferences = new List<MetadataReference>(); newReferences.AddRange(this.ProjectState.MetadataReferences); foreach (var projectReference in this.ProjectState.ProjectReferences) { var referencedProject = solution.GetProject(projectReference.ProjectId); // Even though we're creating a final compilation (vs. an in progress compilation), // it's possible that the target project has been removed. if (referencedProject != null) { // If both projects are submissions, we'll count this as a previous submission link // instead of a regular metadata reference if (referencedProject.IsSubmission) { // if the referenced project is a submission project must be a submission as well: Debug.Assert(this.ProjectState.IsSubmission); var previousSubmissionCompilation = await solution.GetCompilationAsync(projectReference.ProjectId, cancellationToken).ConfigureAwait(false); compilation = compilation.WithPreviousSubmission(previousSubmissionCompilation); } else { var metadataReference = await solution.GetMetadataReferenceAsync( projectReference, this.ProjectState, cancellationToken).ConfigureAwait(false); // The compilation doesn't want to receive a null entry in the set // of references it is constructed with. A reference can fail to be // created if a skeleton assembly could not be constructed. if (metadataReference != null) { newReferences.Add(metadataReference); } } } } if (!Enumerable.SequenceEqual(compilation.References, newReferences)) { compilation = compilation.WithReferences(newReferences); } this.WriteState(new FinalState(this.Retain(solution, compilation))); return compilation; }
/// <summary> /// Add all appropriate references to the compilation and set it as our final compilation /// state. /// </summary> private async Task <CompilationInfo> FinalizeCompilationAsync( Solution solution, Compilation compilation, CancellationToken cancellationToken) { try { bool hasCompleteReferences = true; var newReferences = new List <MetadataReference>(); newReferences.AddRange(this.ProjectState.MetadataReferences); foreach (var projectReference in this.ProjectState.ProjectReferences) { var referencedProject = solution.GetProject(projectReference.ProjectId); // Even though we're creating a final compilation (vs. an in progress compilation), // it's possible that the target project has been removed. if (referencedProject != null) { // If both projects are submissions, we'll count this as a previous submission link // instead of a regular metadata reference if (referencedProject.IsSubmission) { // if the referenced project is a submission project must be a submission as well: Debug.Assert(this.ProjectState.IsSubmission); var previousSubmissionCompilation = await solution.GetCompilationAsync(projectReference.ProjectId, cancellationToken).ConfigureAwait(false); compilation = compilation.WithScriptCompilationInfo( compilation.ScriptCompilationInfo.WithPreviousScriptCompilation(previousSubmissionCompilation)); } else { var metadataReference = await solution.GetMetadataReferenceAsync( projectReference, this.ProjectState, cancellationToken).ConfigureAwait(false); // A reference can fail to be created if a skeleton assembly could not be constructed. if (metadataReference != null) { newReferences.Add(metadataReference); } else { hasCompleteReferences = false; } } } } if (!Enumerable.SequenceEqual(compilation.References, newReferences)) { compilation = compilation.WithReferences(newReferences); } this.WriteState(new FinalState(State.CreateValueSource(compilation, solution.Services), hasCompleteReferences), solution); return(new CompilationInfo(compilation, hasCompleteReferences)); } catch (Exception e) when(FatalError.ReportUnlessCanceled(e)) { throw ExceptionUtilities.Unreachable; } }
private async Task<Compilation> GetOrBuildDeclarationCompilationAsync(Solution solution, CancellationToken cancellationToken) { try { cancellationToken.ThrowIfCancellationRequested(); using (await this.buildLock.DisposableWaitAsync(cancellationToken).ConfigureAwait(false)) { var state = this.ReadState(); // we are already in the final stage. just return it. var compilation = state.FinalCompilation.GetValue(cancellationToken); if (compilation != null) { return compilation; } compilation = state.Compilation.GetValue(cancellationToken); if (compilation == null) { // let's see whether we have declaration only compilation if (state.DeclarationOnlyCompilation != null) { // okay, move to full declaration state. do this so that declaration only compilation never // realize symbols. this.WriteState(new FullDeclarationState(this.Retain(solution, state.DeclarationOnlyCompilation))); return state.DeclarationOnlyCompilation; } // We've got nothing. Build it from scratch :( return await BuildDeclarationCompilationFromScratchAsync(solution, cancellationToken).ConfigureAwait(false); } else if (state is FullDeclarationState) { // we have full declaration, just use it. return state.Compilation.GetValue(cancellationToken); } else if (state is InProgressState) { // We have an in progress compilation. Build off of that. return await BuildDeclarationCompilationFromInProgressAsync(solution, state as InProgressState, compilation, cancellationToken).ConfigureAwait(false); } else { throw Contract.Unreachable; } } } catch (Exception e) if (ExceptionHelpers.CrashUnlessCanceled(e)) { throw ExceptionUtilities.Unreachable; } } private async Task<Compilation> GetOrBuildCompilationAsync( Solution solution, bool lockGate, CancellationToken cancellationToken) { try { using (Logger.LogBlock(FunctionId.Workspace_Project_CompilationTracker_BuildCompilationAsync, logBuildCompilationAsync, this.ProjectState, cancellationToken)) { cancellationToken.ThrowIfCancellationRequested(); var state = this.ReadState(); // Try to get the built compilation. If it exists, then we can just return that. var finalCompilation = state.FinalCompilation.GetValue(cancellationToken); if (finalCompilation != null) { return finalCompilation; } // Otherwise, we actually have to build it. Ensure that only one thread is trying to // build this compilation at a time. if (lockGate) { using (await this.buildLock.DisposableWaitAsync(cancellationToken).ConfigureAwait(false)) { return await BuildCompilationAsync(solution, cancellationToken).ConfigureAwait(false); } } else { return await BuildCompilationAsync(solution, cancellationToken).ConfigureAwait(false); } } } catch (Exception e) if (ExceptionHelpers.CrashUnlessCanceled(e)) { throw ExceptionUtilities.Unreachable; } } /// <summary> /// Builds the compilation matching the project state. In the process of building, also /// produce in progress snapshots that can be accessed from other threads. /// </summary> private Task<Compilation> BuildCompilationAsync( Solution solution, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); var state = this.ReadState(); // if we already have a compilation, we must be already done! This can happen if two // threads were waiting to build, and we came in after the other succeeded. var compilation = state.FinalCompilation.GetValue(cancellationToken); if (compilation != null) { return SpecializedTasks.FromResult(compilation); } compilation = state.Compilation.GetValue(cancellationToken); if (compilation == null) { // this can happen if compilation is already kicked out from the cache. // check whether the state we have support declaration only compilation if (state.DeclarationOnlyCompilation != null) { // we have declaration only compilation. build final one from it. return FinalizeCompilationAsync(solution, state.DeclarationOnlyCompilation, cancellationToken); } // We've got nothing. Build it from scratch :( return BuildCompilationFromScratchAsync(solution, state, cancellationToken); } else if (state is FullDeclarationState) { // We have a declaration compilation, use it to reconstruct the final compilation return this.FinalizeCompilationAsync(solution, compilation, cancellationToken); } else if (state is InProgressState) { // We have an in progress compilation. Build off of that. return BuildFinalStateFromInProgressStateAsync(solution, state as InProgressState, compilation, cancellationToken); } else { throw Contract.Unreachable; } } private async Task<Compilation> BuildCompilationFromScratchAsync( Solution solution, State state, CancellationToken cancellationToken) { try { var compilation = await BuildDeclarationCompilationFromScratchAsync(solution, cancellationToken).ConfigureAwait(false); return await FinalizeCompilationAsync(solution, compilation, cancellationToken).ConfigureAwait(false); } catch (Exception e) if (ExceptionHelpers.CrashUnlessCanceled(e)) { throw ExceptionUtilities.Unreachable; } } private async Task<Compilation> BuildDeclarationCompilationFromScratchAsync( Solution solution, CancellationToken cancellationToken) { try { var compilation = CreateEmptyCompilation(solution); foreach (var document in this.ProjectState.OrderedDocumentStates) { cancellationToken.ThrowIfCancellationRequested(); compilation = compilation.AddSyntaxTrees(await document.GetSyntaxTreeAsync(cancellationToken).ConfigureAwait(false)); } this.WriteState(new FullDeclarationState(this.Retain(solution, compilation))); return compilation; } catch (Exception e) if (ExceptionHelpers.CrashUnlessCanceled(e)) { throw ExceptionUtilities.Unreachable; } } private Compilation CreateEmptyCompilation(Solution solution) { var compilationFactory = this.ProjectState.LanguageServices.GetService<ICompilationFactoryService>(); if (this.ProjectState.IsSubmission) { return compilationFactory.CreateSubmissionCompilation( this.ProjectState.AssemblyName, this.ProjectState.CompilationOptions, this.ProjectState.HostObjectType); } else { return compilationFactory.CreateCompilation( this.ProjectState.AssemblyName, this.ProjectState.CompilationOptions); } } private async Task<Compilation> BuildFinalStateFromInProgressStateAsync( Solution solution, InProgressState state, Compilation inProgressCompilation, CancellationToken cancellationToken) { try { var compilation = await BuildDeclarationCompilationFromInProgressAsync(solution, state, inProgressCompilation, cancellationToken).ConfigureAwait(false); return await FinalizeCompilationAsync(solution, compilation, cancellationToken).ConfigureAwait(false); } catch (Exception e) if (ExceptionHelpers.CrashUnlessCanceled(e)) { throw ExceptionUtilities.Unreachable; } } private async Task<Compilation> BuildDeclarationCompilationFromInProgressAsync( Solution solution, InProgressState state, Compilation inProgressCompilation, CancellationToken cancellationToken) { try { Contract.Requires(inProgressCompilation != null); var intermediateProjects = state.IntermediateProjects; while (intermediateProjects.Length > 0) { cancellationToken.ThrowIfCancellationRequested(); var intermediateProject = intermediateProjects[0]; var inProgressProject = intermediateProject.Item1; var action = intermediateProject.Item2; inProgressCompilation = await action.InvokeAsync(inProgressCompilation, cancellationToken).ConfigureAwait(false); intermediateProjects = intermediateProjects.RemoveAt(0); this.WriteState(State.Create( this.Retain(solution, inProgressCompilation), intermediateProjects)); } return inProgressCompilation; } catch (Exception e) if (ExceptionHelpers.CrashUnlessCanceled(e)) { throw ExceptionUtilities.Unreachable; } } // Add all appropriate references to the compilation and set it as our final compilation // state. private async Task<Compilation> FinalizeCompilationAsync( Solution solution, Compilation compilation, CancellationToken cancellationToken) { try { var newReferences = new List<MetadataReference>(); newReferences.AddRange(this.ProjectState.MetadataReferences); foreach (var projectReference in this.ProjectState.ProjectReferences) { var referencedProject = solution.GetProject(projectReference.ProjectId); // Even though we're creating a final compilation (vs. an in progress compilation), // it's possible that the target project has been removed. if (referencedProject != null) { // If both projects are submissions, we'll count this as a previous submission link // instead of a regular metadata reference if (referencedProject.IsSubmission) { // if the referenced project is a submission project must be a submission as well: Debug.Assert(this.ProjectState.IsSubmission); var previousSubmissionCompilation = await solution.GetCompilationAsync(projectReference.ProjectId, cancellationToken).ConfigureAwait(false); compilation = compilation.WithPreviousSubmission(previousSubmissionCompilation); } else { var metadataReference = await solution.GetMetadataReferenceAsync( projectReference, this.ProjectState, cancellationToken).ConfigureAwait(false); // The compilation doesn't want to receive a null entry in the set // of references it is constructed with. A reference can fail to be // created if a skeleton assembly could not be constructed. if (metadataReference != null) { newReferences.Add(metadataReference); } } } } if (!Enumerable.SequenceEqual(compilation.References, newReferences)) { compilation = compilation.WithReferences(newReferences); } this.WriteState(new FinalState(this.Retain(solution, compilation))); return compilation; } catch (Exception e) if (ExceptionHelpers.CrashUnlessCanceled(e)) { throw ExceptionUtilities.Unreachable; } } /// <summary> /// Get a metadata reference to this compilation info's compilation with respect to /// another project. For cross language references produce a skeletal assembly. If the /// compilation is not available, it is built. If a skeletal assembly reference is /// needed and does not exist, it is also built. /// </summary> internal async Task<MetadataReference> GetMetadataReferenceAsync( Solution solution, ProjectState fromProject, ProjectReference projectReference, CancellationToken cancellationToken) { try { Compilation compilation; // if we already have the compilation and its right kind then use it. if (this.ProjectState.LanguageServices == fromProject.LanguageServices && this.TryGetCompilation(out compilation)) { return compilation.ToMetadataReference(projectReference.Aliases, projectReference.EmbedInteropTypes); } // If same language then we can wrap the other project's compilation into a compilation reference if (this.ProjectState.LanguageServices == fromProject.LanguageServices) { // otherwise, base it off the compilation by building it first. compilation = await this.GetCompilationAsync(solution, cancellationToken).ConfigureAwait(false); return compilation.ToMetadataReference(projectReference.Aliases, projectReference.EmbedInteropTypes); } else { // otherwise get a metadata only image reference that is built by emitting the metadata from the referenced project's compilation and re-importing it. return await this.GetMetadataOnlyImageReferenceAsync(solution, projectReference, cancellationToken).ConfigureAwait(false); } } catch (Exception e) if (ExceptionHelpers.CrashUnlessCanceled(e)) { throw ExceptionUtilities.Unreachable; } } /// <summary> /// Attempts to get (without waiting) a metadata reference to a possibly in progress /// compilation. Actual compilation references are preferred over skeletal assembly /// references. Could potentially return null if nothing can be provided. /// </summary> internal MetadataReference GetPartialMetadataReference(Solution solution, ProjectState fromProject, ProjectReference projectReference, CancellationToken cancellationToken) { var state = this.ReadState(); // get compilation in any state it happens to be in right now. Compilation compilation; if (state.Compilation.TryGetValue(out compilation) && compilation != null && this.ProjectState.LanguageServices == fromProject.LanguageServices) { // if we have a compilation and its the correct language, use a simple compilation reference return compilation.ToMetadataReference(projectReference.Aliases, projectReference.EmbedInteropTypes); } return null; } /// <summary> /// Gets a metadata reference to the metadata-only-image corresponding to the compilation. /// </summary> private async Task<MetadataReference> GetMetadataOnlyImageReferenceAsync( Solution solution, ProjectReference projectReference, CancellationToken cancellationToken) { try { using (Logger.LogBlock(FunctionId.Workspace_SkeletonAssembly_GetMetadataOnlyImage, cancellationToken)) { var projectId = this.ProjectState.Id; var version = await this.GetDependentSemanticVersionAsync(solution, cancellationToken).ConfigureAwait(false); // get or build compilation up to decleration state. this compilation will be used to provide live xml doc comment var declarationCompilation = await this.GetOrBuildDeclarationCompilationAsync(solution, cancellationToken: cancellationToken).ConfigureAwait(false); MetadataReference reference; if (!MetadataOnlyReference.TryGetReference(solution, projectReference, declarationCompilation, version, out reference)) { // using async build lock so we don't get multiple consumers attempting to build metadata-only images for the same compilation. using (await this.buildLock.DisposableWaitAsync(cancellationToken).ConfigureAwait(false)) { // okay, we still don't have one. bring the compilation to final state since we are going to use it to create skeleton assembly var compilation = await this.GetOrBuildCompilationAsync(solution, lockGate: false, cancellationToken: cancellationToken).ConfigureAwait(false); reference = MetadataOnlyReference.GetOrBuildReference(solution, projectReference, compilation, version, cancellationToken); } } return reference; } } catch (Exception e) if (ExceptionHelpers.CrashUnlessCanceled(e)) { throw ExceptionUtilities.Unreachable; } } /// <summary> /// Converts a compilation into a ValueSource employing an external compilation retention policy. /// </summary> private ValueSource<Compilation> Retain(Solution solution, Compilation compilation) { var cache = solution.Services.CompilationCacheService; if (cache != null) { return new CachedObjectSource<Compilation>(compilation, cache); } else { return new ConstantValueSource<Compilation>(compilation); } } #region Versions // Dependent Versions are stored on compilation tracker so they are more likely to survive when unrelated solution branching occurs. private AsyncLazy<VersionStamp> lazyDependentVersion; private AsyncLazy<VersionStamp> lazyDependentSemanticVersion; internal async Task<VersionStamp> GetDependentVersionAsync(Solution solution, CancellationToken cancellationToken) { if (this.lazyDependentVersion == null) { // note: solution is captured here, but it will go away once GetValueAsync executes. Interlocked.CompareExchange(ref this.lazyDependentVersion, new AsyncLazy<VersionStamp>(c => ComputeDependentVersionAsync(solution, c), cacheResult: true), null); } return await this.lazyDependentVersion.GetValueAsync(cancellationToken).ConfigureAwait(false); }
private void ValidateSolutionAndCompilations(Solution solution) { foreach (var project in solution.Projects) { Assert.True(solution.ContainsProject(project.Id), "Solution was expected to have project " + project.Id); Assert.Equal(project, solution.GetProject(project.Id)); // these won't always be unique in real-world but should be for these tests Assert.Equal(project, solution.GetProjectsByName(project.Name).FirstOrDefault()); var compilation = project.GetCompilationAsync().Result; Assert.NotNull(compilation); // check that the options are the same Assert.Equal(project.CompilationOptions, compilation.Options); // check that all known metadata references are present in the compilation foreach (var meta in project.MetadataReferences) { Assert.True(compilation.References.Contains(meta), "Compilation references were expected to contain " + meta); } // check that all project-to-project reference metadata is present in the compilation foreach (var referenced in project.ProjectReferences) { if (solution.ContainsProject(referenced.ProjectId)) { var referencedMetadata = solution.GetMetadataReferenceAsync(referenced, solution.GetProjectState(project.Id), CancellationToken.None).Result; Assert.NotNull(referencedMetadata); var compilationReference = referencedMetadata as CompilationReference; if (compilationReference != null) { compilation.References.Single(r => { var cr = r as CompilationReference; return cr != null && cr.Compilation == compilationReference.Compilation; }); } } } // check that the syntax trees are the same var docs = project.Documents.ToList(); var trees = compilation.SyntaxTrees.ToList(); Assert.Equal(docs.Count, trees.Count); foreach (var doc in docs) { Assert.True(trees.Contains(doc.GetSyntaxTreeAsync().Result), "trees list was expected to contain the syntax tree of doc"); } } }
/// <summary> /// Add all appropriate references to the compilation and set it as our final compilation /// state. /// </summary> private async Task<CompilationInfo> FinalizeCompilationAsync( Solution solution, Compilation compilation, CancellationToken cancellationToken) { try { // if HasAllInformation is false, then this project is always not completed. bool hasSuccessfullyLoaded = this.ProjectState.HasAllInformation; var newReferences = new List<MetadataReference>(); var metadataReferenceToProjectId = new Dictionary<MetadataReference, ProjectId>(); newReferences.AddRange(this.ProjectState.MetadataReferences); foreach (var projectReference in this.ProjectState.ProjectReferences) { var referencedProject = solution.GetProject(projectReference.ProjectId); // Even though we're creating a final compilation (vs. an in progress compilation), // it's possible that the target project has been removed. if (referencedProject != null) { // If both projects are submissions, we'll count this as a previous submission link // instead of a regular metadata reference if (referencedProject.IsSubmission) { // if the referenced project is a submission project must be a submission as well: Debug.Assert(this.ProjectState.IsSubmission); var previousSubmissionCompilation = await solution.GetCompilationAsync(projectReference.ProjectId, cancellationToken).ConfigureAwait(false); compilation = compilation.WithScriptCompilationInfo( compilation.ScriptCompilationInfo.WithPreviousScriptCompilation(previousSubmissionCompilation)); } else { var metadataReference = await solution.GetMetadataReferenceAsync( projectReference, this.ProjectState, cancellationToken).ConfigureAwait(false); // A reference can fail to be created if a skeleton assembly could not be constructed. if (metadataReference != null) { newReferences.Add(metadataReference); metadataReferenceToProjectId.Add(metadataReference, projectReference.ProjectId); } else { hasSuccessfullyLoaded = false; } } } } compilation = UpdateCompilationWithNewReferencesAndRecordAssemblySymbols(compilation, newReferences, metadataReferenceToProjectId); bool hasSuccessfullyLoadedTransitively = !HasMissingReferences(compilation, this.ProjectState.MetadataReferences) && await ComputeHasSuccessfullyLoadedTransitivelyAsync(solution, hasSuccessfullyLoaded, cancellationToken).ConfigureAwait(false); this.WriteState(new FinalState(State.CreateValueSource(compilation, solution.Services), hasSuccessfullyLoadedTransitively), solution); return new CompilationInfo(compilation, hasSuccessfullyLoadedTransitively); } catch (Exception e) when (FatalError.ReportUnlessCanceled(e)) { throw ExceptionUtilities.Unreachable; } }
/// <summary> /// Add all appropriate references to the compilation and set it as our final compilation /// state. /// </summary> private async Task <CompilationInfo> FinalizeCompilationAsync( Solution solution, Compilation compilation, CancellationToken cancellationToken) { try { // if HasAllInformation is false, then this project is always not completed. bool hasSuccessfullyLoaded = this.ProjectState.HasAllInformation; var newReferences = new List <MetadataReference>(); var metadataReferenceToProjectId = new Dictionary <MetadataReference, ProjectId>(); newReferences.AddRange(this.ProjectState.MetadataReferences); foreach (var projectReference in this.ProjectState.ProjectReferences) { var referencedProject = solution.GetProject(projectReference.ProjectId); // Even though we're creating a final compilation (vs. an in progress compilation), // it's possible that the target project has been removed. if (referencedProject != null) { // If both projects are submissions, we'll count this as a previous submission link // instead of a regular metadata reference if (referencedProject.IsSubmission) { // if the referenced project is a submission project must be a submission as well: Debug.Assert(this.ProjectState.IsSubmission); var previousSubmissionCompilation = await solution.GetCompilationAsync(projectReference.ProjectId, cancellationToken).ConfigureAwait(false); compilation = compilation.WithScriptCompilationInfo( compilation.ScriptCompilationInfo.WithPreviousScriptCompilation(previousSubmissionCompilation)); } else { var metadataReference = await solution.GetMetadataReferenceAsync( projectReference, this.ProjectState, cancellationToken).ConfigureAwait(false); // A reference can fail to be created if a skeleton assembly could not be constructed. if (metadataReference != null) { newReferences.Add(metadataReference); metadataReferenceToProjectId.Add(metadataReference, projectReference.ProjectId); } else { hasSuccessfullyLoaded = false; } } } } compilation = UpdateCompilationWithNewReferencesAndRecordAssemblySymbols(compilation, newReferences, metadataReferenceToProjectId); bool hasSuccessfullyLoadedTransitively = !HasMissingReferences(compilation, this.ProjectState.MetadataReferences) && await ComputeHasSuccessfullyLoadedTransitivelyAsync(solution, hasSuccessfullyLoaded, cancellationToken).ConfigureAwait(false); this.WriteState(new FinalState(State.CreateValueSource(compilation, solution.Services), hasSuccessfullyLoadedTransitively), solution); return(new CompilationInfo(compilation, hasSuccessfullyLoadedTransitively)); } catch (Exception e) when(FatalError.ReportUnlessCanceled(e)) { throw ExceptionUtilities.Unreachable; } }
/// <summary> /// Add all appropriate references to the compilation and set it as our final compilation /// state. /// </summary> private async Task<CompilationInfo> FinalizeCompilationAsync( Solution solution, Compilation compilation, CancellationToken cancellationToken) { try { bool hasCompleteReferences = true; var newReferences = new List<MetadataReference>(); newReferences.AddRange(this.ProjectState.MetadataReferences); foreach (var projectReference in this.ProjectState.ProjectReferences) { var referencedProject = solution.GetProject(projectReference.ProjectId); // Even though we're creating a final compilation (vs. an in progress compilation), // it's possible that the target project has been removed. if (referencedProject != null) { // If both projects are submissions, we'll count this as a previous submission link // instead of a regular metadata reference if (referencedProject.IsSubmission) { // if the referenced project is a submission project must be a submission as well: Debug.Assert(this.ProjectState.IsSubmission); var previousSubmissionCompilation = await solution.GetCompilationAsync(projectReference.ProjectId, cancellationToken).ConfigureAwait(false); compilation = compilation.WithScriptCompilationInfo( compilation.ScriptCompilationInfo.WithPreviousScriptCompilation(previousSubmissionCompilation)); } else { var metadataReference = await solution.GetMetadataReferenceAsync( projectReference, this.ProjectState, cancellationToken).ConfigureAwait(false); // A reference can fail to be created if a skeleton assembly could not be constructed. if (metadataReference != null) { newReferences.Add(metadataReference); } else { hasCompleteReferences = false; } } } } if (!Enumerable.SequenceEqual(compilation.ExternalReferences, newReferences)) { compilation = compilation.WithReferences(newReferences); } this.WriteState(new FinalState(State.CreateValueSource(compilation, solution.Services), hasCompleteReferences), solution); return new CompilationInfo(compilation, hasCompleteReferences); } catch (Exception e) when (FatalError.ReportUnlessCanceled(e)) { throw ExceptionUtilities.Unreachable; } }