Inheritance: Exception
Ejemplo n.º 1
0
        public async Task <Deltas> EmitProjectDeltaAsync(Project project, EmitBaseline baseline, CancellationToken cancellationToken)
        {
            try
            {
                Debug.Assert(!_stoppedAtException);

                var projectChanges = await GetProjectChangesAsync(project, cancellationToken).ConfigureAwait(false);

                var currentCompilation = await project.GetCompilationAsync(cancellationToken).ConfigureAwait(false);

                var allAddedSymbols = await GetAllAddedSymbolsAsync(project, cancellationToken).ConfigureAwait(false);

                var baseActiveStatements = await BaseActiveStatements.GetValueAsync(cancellationToken).ConfigureAwait(false);

                var baseActiveExceptionRegions = await BaseActiveExceptionRegions.GetValueAsync(cancellationToken).ConfigureAwait(false);

                var pdbStream      = new MemoryStream();
                var updatedMethods = new List <MethodDefinitionHandle>();

                using (var metadataStream = SerializableBytes.CreateWritableStream())
                    using (var ilStream = SerializableBytes.CreateWritableStream())
                    {
                        EmitDifferenceResult result = currentCompilation.EmitDifference(
                            baseline,
                            projectChanges.SemanticEdits,
                            s => allAddedSymbols?.Contains(s) ?? false,
                            metadataStream,
                            ilStream,
                            pdbStream,
                            updatedMethods,
                            cancellationToken);

                        int[] updatedMethodTokens = updatedMethods.Select(h => MetadataTokens.GetToken(h)).ToArray();

                        // Determine all active statements whose span changed and exception region span deltas.

                        GetActiveStatementAndExceptionRegionSpans(
                            baseline.OriginalMetadata.GetModuleVersionId(),
                            baseActiveStatements,
                            baseActiveExceptionRegions,
                            updatedMethodTokens,
                            _nonRemappableRegions,
                            projectChanges.NewActiveStatements,
                            out var activeStatementsInUpdatedMethods,
                            out var nonRemappableRegions);

                        return(new Deltas(
                                   ilStream.ToArray(),
                                   metadataStream.ToArray(),
                                   pdbStream,
                                   updatedMethodTokens,
                                   projectChanges.LineChanges,
                                   nonRemappableRegions,
                                   activeStatementsInUpdatedMethods,
                                   result));
                    }
            }
            catch (Exception e) when(FatalError.ReportWithoutCrashAndPropagate(e))
            {
                throw ExceptionUtilities.Unreachable;
            }
        }
Ejemplo n.º 2
0
 private static void Exit(string message)
 {
     FatalError.Report(new Exception(message));
 }
Ejemplo n.º 3
0
        /// <summary>
        /// Get the text changes between this document and a prior version of the same document.
        /// The changes, when applied to the text of the old document, will produce the text of the current document.
        /// </summary>
        public async Task <IEnumerable <TextChange> > GetTextChangesAsync(Document oldDocument, CancellationToken cancellationToken = default)
        {
            try
            {
                using (Logger.LogBlock(FunctionId.Workspace_Document_GetTextChanges, this.Name, cancellationToken))
                {
                    if (oldDocument == this)
                    {
                        // no changes
                        return(SpecializedCollections.EmptyEnumerable <TextChange>());
                    }

                    if (this.Id != oldDocument.Id)
                    {
                        throw new ArgumentException(WorkspacesResources.The_specified_document_is_not_a_version_of_this_document);
                    }

                    // first try to see if text already knows its changes
                    IList <TextChange> textChanges = null;
                    if (this.TryGetText(out var text) && oldDocument.TryGetText(out var oldText))
                    {
                        if (text == oldText)
                        {
                            return(SpecializedCollections.EmptyEnumerable <TextChange>());
                        }

                        var container = text.Container;
                        if (container != null)
                        {
                            textChanges = text.GetTextChanges(oldText).ToList();

                            // if changes are significant (not the whole document being replaced) then use these changes
                            if (textChanges.Count > 1 || (textChanges.Count == 1 && textChanges[0].Span != new TextSpan(0, oldText.Length)))
                            {
                                return(textChanges);
                            }
                        }
                    }

                    // get changes by diffing the trees
                    if (this.SupportsSyntaxTree)
                    {
                        var tree = await this.GetSyntaxTreeAsync(cancellationToken).ConfigureAwait(false);

                        var oldTree = await oldDocument.GetSyntaxTreeAsync(cancellationToken).ConfigureAwait(false);

                        return(tree.GetChanges(oldTree));
                    }

                    text = await this.GetTextAsync(cancellationToken).ConfigureAwait(false);

                    oldText = await oldDocument.GetTextAsync(cancellationToken).ConfigureAwait(false);

                    return(text.GetTextChanges(oldText).ToList());
                }
            }
            catch (Exception e) when(FatalError.ReportUnlessCanceled(e))
            {
                throw ExceptionUtilities.Unreachable;
            }
        }
                    private async Task ProcessProjectAsync(ImmutableArray <IIncrementalAnalyzer> analyzers, WorkItem workItem, CancellationToken cancellationToken)
                    {
                        if (CancellationToken.IsCancellationRequested)
                        {
                            return;
                        }

                        // we do have work item for this project
                        var projectId           = workItem.ProjectId;
                        var processedEverything = false;
                        var processingSolution  = Processor.CurrentSolution;

                        try
                        {
                            using (Logger.LogBlock(FunctionId.WorkCoordinator_ProcessProjectAsync, w => w.ToString(), workItem, cancellationToken))
                            {
                                var project = processingSolution.GetProject(projectId);
                                if (project != null)
                                {
                                    var reasons          = workItem.InvocationReasons;
                                    var semanticsChanged = reasons.Contains(PredefinedInvocationReasons.SemanticChanged) ||
                                                           reasons.Contains(PredefinedInvocationReasons.SolutionRemoved);

                                    using (Processor.EnableCaching(project.Id))
                                    {
                                        await Processor.RunAnalyzersAsync(analyzers, project, workItem, (a, p, c) => a.AnalyzeProjectAsync(p, semanticsChanged, reasons, c), cancellationToken).ConfigureAwait(false);
                                    }
                                }
                                else
                                {
                                    SolutionCrawlerLogger.LogProcessProjectNotExist(Processor._logAggregator);

                                    await RemoveProjectAsync(projectId, cancellationToken).ConfigureAwait(false);
                                }

                                if (!cancellationToken.IsCancellationRequested)
                                {
                                    processedEverything = true;
                                }
                            }
                        }
                        catch (Exception e) when(FatalError.ReportUnlessCanceled(e))
                        {
                            throw ExceptionUtilities.Unreachable;
                        }
                        finally
                        {
                            // we got cancelled in the middle of processing the project.
                            // let's make sure newly enqueued work item has all the flag needed.
                            // Avoid retry attempts after cancellation is requested, since work will not be processed
                            // after that point.
                            if (!processedEverything && !CancellationToken.IsCancellationRequested)
                            {
                                _workItemQueue.AddOrReplace(workItem.Retry(Listener.BeginAsyncOperation("ReenqueueWorkItem")));
                            }

                            SolutionCrawlerLogger.LogProcessProject(Processor._logAggregator, projectId.Id, processedEverything);

                            // remove one that is finished running
                            _workItemQueue.MarkWorkItemDoneFor(projectId);
                        }
                    }
Ejemplo n.º 5
0
        public async ValueTask GetAssetsAsync(PipeWriter pipeWriter, int scopeId, Checksum[] checksums, CancellationToken cancellationToken)
        {
            var assetStorage = _services.GetRequiredService <ISolutionAssetStorageProvider>().AssetStorage;
            var serializer   = _services.GetRequiredService <ISerializerService>();

            SolutionAsset?singleAsset = null;
            IReadOnlyDictionary <Checksum, SolutionAsset>?assetMap = null;

            if (checksums.Length == 1)
            {
                singleAsset = (await assetStorage.GetAssetAsync(scopeId, checksums[0], cancellationToken).ConfigureAwait(false)) ?? SolutionAsset.Null;
            }
            else
            {
                assetMap = await assetStorage.GetAssetsAsync(scopeId, checksums, cancellationToken).ConfigureAwait(false);
            }

            // We can cancel early, but once the pipe operations are scheduled we rely on both operations running to
            // avoid deadlocks (the exception handler in 'task1' ensures progress is made in 'task2').
            cancellationToken.ThrowIfCancellationRequested();
            var mustNotCancelToken = CancellationToken.None;

            // Work around the lack of async stream writing in ObjectWriter, which is required when writing to the RPC pipe.
            // Run two tasks - the first synchronously writes to a local pipe and the second asynchronosly transfers the data to the RPC pipe.
            //
            // Configure the pipe to never block on write (waiting for the reader to read). This prevents deadlocks but might result in more
            // (non-contiguous) memory allocated for the underlying buffers. The amount of memory is bounded by the total size of the serialized assets.
            var localPipe = new Pipe(RemoteHostAssetSerialization.PipeOptionsWithUnlimitedWriterBuffer);

            var task1 = Task.Run(() =>
            {
                try
                {
                    var stream       = localPipe.Writer.AsStream(leaveOpen: false);
                    using var writer = new ObjectWriter(stream, leaveOpen: false, cancellationToken);
                    RemoteHostAssetSerialization.WriteData(writer, singleAsset, assetMap, serializer, scopeId, checksums, cancellationToken);
                }
                catch (Exception e) when(FatalError.ReportWithoutCrashUnlessCanceled(e, cancellationToken))
                {
                    // no-op
                }
            }, mustNotCancelToken);

            // Complete RPC once we send the initial piece of data and start waiting for the writer to send more,
            // so the client can start reading from the stream. Once CopyPipeDataAsync completes the pipeWriter
            // the corresponding client-side pipeReader will complete and the data transfer will be finished.
            var task2 = CopyPipeDataAsync();

            await Task.WhenAll(task1, task2).ConfigureAwait(false);

            async Task CopyPipeDataAsync()
            {
                Exception?exception = null;

                try
                {
                    await localPipe.Reader.CopyToAsync(pipeWriter, cancellationToken).ConfigureAwait(false);
                }
                catch (Exception e)
                {
                    FatalError.ReportWithoutCrashUnlessCanceled(e, cancellationToken);
                    exception = e;
                }
                finally
                {
                    await localPipe.Reader.CompleteAsync(exception).ConfigureAwait(false);

                    await pipeWriter.CompleteAsync(exception).ConfigureAwait(false);
                }
            }
        }
Ejemplo n.º 6
0
        public override Compilation?CreateCompilation(
            TextWriter consoleOutput,
            TouchedFileLogger touchedFilesLogger,
            ErrorLogger errorLogger,
            ImmutableArray <AnalyzerConfigOptionsResult> analyzerConfigOptions)
        {
            var parseOptions = Arguments.ParseOptions;

            // We compute script parse options once so we don't have to do it repeatedly in
            // case there are many script files.
            var scriptParseOptions = parseOptions.WithKind(SourceCodeKind.Script);

            bool hadErrors = false;

            var sourceFiles         = Arguments.SourceFiles;
            var trees               = new SyntaxTree?[sourceFiles.Length];
            var normalizedFilePaths = new string[sourceFiles.Length];
            var diagnosticBag       = DiagnosticBag.GetInstance();

            if (Arguments.CompilationOptions.ConcurrentBuild)
            {
                Parallel.For(0, sourceFiles.Length, UICultureUtilities.WithCurrentUICulture <int>(i =>
                {
                    try
                    {
                        //NOTE: order of trees is important!!
                        trees[i] = ParseFile(
                            parseOptions,
                            scriptParseOptions,
                            ref hadErrors,
                            sourceFiles[i],
                            diagnosticBag,
                            out normalizedFilePaths[i]);
                    }
                    catch (Exception e) when(FatalError.Report(e))
                    {
                        throw ExceptionUtilities.Unreachable;
                    }
                }));
            }
            else
            {
                for (int i = 0; i < sourceFiles.Length; i++)
                {
                    //NOTE: order of trees is important!!
                    trees[i] = ParseFile(
                        parseOptions,
                        scriptParseOptions,
                        ref hadErrors,
                        sourceFiles[i],
                        diagnosticBag,
                        out normalizedFilePaths[i]);
                }
            }

            // If errors had been reported in ParseFile, while trying to read files, then we should simply exit.
            if (ReportDiagnostics(diagnosticBag.ToReadOnlyAndFree(), consoleOutput, errorLogger))
            {
                Debug.Assert(hadErrors);
                return(null);
            }

            var diagnostics     = new List <DiagnosticInfo>();
            var uniqueFilePaths = new HashSet <string>(StringComparer.OrdinalIgnoreCase);

            for (int i = 0; i < sourceFiles.Length; i++)
            {
                var normalizedFilePath = normalizedFilePaths[i];
                Debug.Assert(normalizedFilePath != null);
                Debug.Assert(sourceFiles[i].IsInputRedirected || PathUtilities.IsAbsolute(normalizedFilePath));

                if (!uniqueFilePaths.Add(normalizedFilePath))
                {
                    // warning CS2002: Source file '{0}' specified multiple times
                    diagnostics.Add(new DiagnosticInfo(MessageProvider, (int)ErrorCode.WRN_FileAlreadyIncluded,
                                                       Arguments.PrintFullPaths ? normalizedFilePath : _diagnosticFormatter.RelativizeNormalizedPath(normalizedFilePath)));

                    trees[i] = null;
                }
            }

            if (Arguments.TouchedFilesPath != null)
            {
                foreach (var path in uniqueFilePaths)
                {
                    touchedFilesLogger.AddRead(path);
                }
            }

            var assemblyIdentityComparer = DesktopAssemblyIdentityComparer.Default;
            var appConfigPath            = this.Arguments.AppConfigPath;

            if (appConfigPath != null)
            {
                try
                {
                    using (var appConfigStream = new FileStream(appConfigPath, FileMode.Open, FileAccess.Read))
                    {
                        assemblyIdentityComparer = DesktopAssemblyIdentityComparer.LoadFromXml(appConfigStream);
                    }

                    if (touchedFilesLogger != null)
                    {
                        touchedFilesLogger.AddRead(appConfigPath);
                    }
                }
                catch (Exception ex)
                {
                    diagnostics.Add(new DiagnosticInfo(MessageProvider, (int)ErrorCode.ERR_CantReadConfigFile, appConfigPath, ex.Message));
                }
            }

            var xmlFileResolver    = new LoggingXmlFileResolver(Arguments.BaseDirectory, touchedFilesLogger);
            var sourceFileResolver = new LoggingSourceFileResolver(ImmutableArray <string> .Empty, Arguments.BaseDirectory, Arguments.PathMap, touchedFilesLogger);

            MetadataReferenceResolver referenceDirectiveResolver;
            var resolvedReferences = ResolveMetadataReferences(diagnostics, touchedFilesLogger, out referenceDirectiveResolver);

            if (ReportDiagnostics(diagnostics, consoleOutput, errorLogger))
            {
                return(null);
            }

            var loggingFileSystem = new LoggingStrongNameFileSystem(touchedFilesLogger, _tempDirectory);
            var optionsProvider   = new CompilerSyntaxTreeOptionsProvider(trees, analyzerConfigOptions);

            return(CSharpCompilation.Create(
                       Arguments.CompilationName,
                       trees.WhereNotNull(),
                       resolvedReferences,
                       Arguments.CompilationOptions
                       .WithMetadataReferenceResolver(referenceDirectiveResolver)
                       .WithAssemblyIdentityComparer(assemblyIdentityComparer)
                       .WithXmlReferenceResolver(xmlFileResolver)
                       .WithStrongNameProvider(Arguments.GetStrongNameProvider(loggingFileSystem))
                       .WithSourceReferenceResolver(sourceFileResolver)
                       .WithSyntaxTreeOptionsProvider(optionsProvider)));
        }
Ejemplo n.º 7
0
        private void InitializeOpenBuffers(SnapshotSpan triggerSpan)
        {
            using (
                Logger.LogBlock(
                    FunctionId.Rename_CreateOpenTextBufferManagerForAllOpenDocs,
                    CancellationToken.None
                    )
                )
            {
                var openBuffers = new HashSet <ITextBuffer>();
                foreach (var d in _workspace.GetOpenDocumentIds())
                {
                    var document = _baseSolution.GetDocument(d);
                    if (document == null)
                    {
                        continue;
                    }

                    Contract.ThrowIfFalse(document.TryGetText(out var text));
                    Contract.ThrowIfNull(text);

                    var textSnapshot = text.FindCorrespondingEditorTextSnapshot();
                    if (textSnapshot == null)
                    {
                        FatalError.ReportAndCatch(new NullTextBufferException(document, text));
                        continue;
                    }
                    Contract.ThrowIfNull(textSnapshot.TextBuffer);

                    openBuffers.Add(textSnapshot.TextBuffer);
                }

                foreach (var buffer in openBuffers)
                {
                    TryPopulateOpenTextBufferManagerForBuffer(buffer);
                }
            }

            var startingSpan = triggerSpan.Span;

            // Select this span if we didn't already have something selected
            var selections = _triggerView.Selection.GetSnapshotSpansOnBuffer(
                triggerSpan.Snapshot.TextBuffer
                );

            if (
                !selections.Any() ||
                selections.First().IsEmpty ||
                !startingSpan.Contains(selections.First())
                )
            {
                _triggerView.SetSelection(new SnapshotSpan(triggerSpan.Snapshot, startingSpan));
            }

            this.UndoManager.CreateInitialState(
                this.ReplacementText,
                _triggerView.Selection,
                new SnapshotSpan(triggerSpan.Snapshot, startingSpan)
                );
            _openTextBuffers[triggerSpan.Snapshot.TextBuffer].SetReferenceSpans(
                SpecializedCollections.SingletonEnumerable(startingSpan.ToTextSpan())
                );

            UpdateReferenceLocationsTask(
                ThreadingContext.JoinableTaskFactory.RunAsync(
                    () =>
                    _renameInfo.FindRenameLocationsAsync(
                        _optionSet,
                        _cancellationTokenSource.Token
                        )
                    )
                );

            RenameTrackingDismisser.DismissRenameTracking(
                _workspace,
                _workspace.GetOpenDocumentIds()
                );
        }
Ejemplo n.º 8
0
            /// <summary>
            /// Find conflicts in the new solution
            /// </summary>
            private async Task <bool> IdentifyConflictsAsync(
                HashSet <DocumentId> documentIdsForConflictResolution,
                IEnumerable <DocumentId> allDocumentIdsInProject,
                ProjectId projectId,
                ConflictResolution conflictResolution)
            {
                try
                {
                    _documentOfRenameSymbolHasBeenRenamed |= documentIdsForConflictResolution.Contains(_documentIdOfRenameSymbolDeclaration);

                    // Get the renamed symbol in complexified new solution
                    ISymbol renamedSymbolInNewSolution = await GetRenamedSymbolInCurrentSolutionAsync(conflictResolution).ConfigureAwait(false);

                    // if the text replacement is invalid, we just did a simple token replacement.
                    // Therefore we don't need more mapping information and can skip the rest of
                    // the loop body.
                    if (!IsRenameValid(conflictResolution, renamedSymbolInNewSolution))
                    {
                        foreach (var documentId in documentIdsForConflictResolution)
                        {
                            var newDocument = conflictResolution.NewSolution.GetDocument(documentId);
                            var syntaxRoot  = await newDocument.GetSyntaxRootAsync(_cancellationToken).ConfigureAwait(false);

                            var nodesOrTokensWithConflictCheckAnnotations = GetNodesOrTokensToCheckForConflicts(documentId, syntaxRoot);
                            foreach (var nodeOrToken in nodesOrTokensWithConflictCheckAnnotations)
                            {
                                if (nodeOrToken.annotation.IsRenameLocation)
                                {
                                    conflictResolution.AddRelatedLocation(new RelatedLocation(
                                                                              nodeOrToken.annotation.OriginalSpan, documentId, RelatedLocationType.UnresolvedConflict));
                                }
                            }
                        }

                        return(false);
                    }

                    Dictionary <Location, Location> reverseMappedLocations = new Dictionary <Location, Location>();

                    foreach (var documentId in documentIdsForConflictResolution)
                    {
                        var newDocument = conflictResolution.NewSolution.GetDocument(documentId);
                        var syntaxRoot  = await newDocument.GetSyntaxRootAsync(_cancellationToken).ConfigureAwait(false);

                        var baseDocument   = conflictResolution.OldSolution.GetDocument(documentId);
                        var baseSyntaxTree = await baseDocument.GetSyntaxTreeAsync(_cancellationToken).ConfigureAwait(false);

                        var baseRoot = await baseDocument.GetSyntaxRootAsync(_cancellationToken).ConfigureAwait(false);

                        SemanticModel newDocumentSemanticModel = null;
                        var           syntaxFactsService       = newDocument.Project.LanguageServices.GetService <ISyntaxFactsService>();

                        // Get all tokens that need conflict check
                        var nodesOrTokensWithConflictCheckAnnotations = GetNodesOrTokensToCheckForConflicts(documentId, syntaxRoot);

                        var complexifiedLocationSpanForThisDocument =
                            _conflictLocations
                            .Where(t => t.DocumentId == documentId)
                            .Select(t => t.OriginalIdentifierSpan).ToSet();

                        foreach (var nodeAndAnnotation in nodesOrTokensWithConflictCheckAnnotations)
                        {
                            var tokenOrNode        = nodeAndAnnotation.syntax;
                            var conflictAnnotation = nodeAndAnnotation.annotation;
                            reverseMappedLocations[tokenOrNode.GetLocation()] = baseSyntaxTree.GetLocation(conflictAnnotation.OriginalSpan);
                            var originalLocation = conflictAnnotation.OriginalSpan;
                            IEnumerable <ISymbol> newReferencedSymbols = null;

                            var hasConflict = _renameAnnotations.HasAnnotation(tokenOrNode, RenameInvalidIdentifierAnnotation.Instance);
                            if (!hasConflict)
                            {
                                newDocumentSemanticModel = newDocumentSemanticModel ?? await newDocument.GetSemanticModelAsync(_cancellationToken).ConfigureAwait(false);

                                newReferencedSymbols = GetSymbolsInNewSolution(newDocument, newDocumentSemanticModel, conflictAnnotation, tokenOrNode);

                                // The semantic correctness, after rename, for each token of interest in the
                                // rename context is performed by getting the symbol pointed by each token
                                // and obtain the Symbol's First Ordered Location's  Span-Start and check to
                                // see if it is the same as before from the base solution. During rename,
                                // the spans would have been modified and so we need to adjust the old position
                                // to the new position for which we use the renameSpanTracker, which was tracking
                                // & mapping the old span -> new span during rename
                                hasConflict = _hasConflictCallback?.Invoke(newReferencedSymbols) ??
                                              await CheckForConflictAsync(conflictResolution, renamedSymbolInNewSolution, newDocument, conflictAnnotation, newReferencedSymbols).ConfigureAwait(false);
                            }

                            if (!hasConflict && !conflictAnnotation.IsInvocationExpression)
                            {
                                hasConflict = LocalVariableConflictPerLanguage((SyntaxToken)tokenOrNode, newDocument, newReferencedSymbols);
                            }

                            if (!hasConflict)
                            {
                                if (conflictAnnotation.IsRenameLocation)
                                {
                                    conflictResolution.AddRelatedLocation(
                                        new RelatedLocation(originalLocation,
                                                            documentId,
                                                            complexifiedLocationSpanForThisDocument.Contains(originalLocation) ? RelatedLocationType.ResolvedReferenceConflict : RelatedLocationType.NoConflict,
                                                            isReference: true));
                                }
                                else
                                {
                                    // if a complexified location was not a reference location, then it was a resolved conflict of a non reference location
                                    if (!conflictAnnotation.IsOriginalTextLocation && complexifiedLocationSpanForThisDocument.Contains(originalLocation))
                                    {
                                        conflictResolution.AddRelatedLocation(
                                            new RelatedLocation(originalLocation,
                                                                documentId,
                                                                RelatedLocationType.ResolvedNonReferenceConflict,
                                                                isReference: false));
                                    }
                                }
                            }
                            else
                            {
                                var baseToken          = baseRoot.FindToken(conflictAnnotation.OriginalSpan.Start, true);
                                var complexifiedTarget = GetExpansionTargetForLocationPerLanguage(baseToken, baseDocument);
                                conflictResolution.AddRelatedLocation(new RelatedLocation(
                                                                          originalLocation,
                                                                          documentId,
                                                                          complexifiedTarget != null ? RelatedLocationType.PossiblyResolvableConflict : RelatedLocationType.UnresolvableConflict,
                                                                          isReference: conflictAnnotation.IsRenameLocation,
                                                                          complexifiedTargetSpan: complexifiedTarget != null ? complexifiedTarget.Span : default(TextSpan)));
                            }
                        }
                    }

                    // there are more conflicts that cannot be identified by checking if the tokens still reference the same
                    // symbol. These conflicts are mostly language specific. A good example is a member with the same name
                    // as the parent (yes I know, this is a simplification).
                    if (_documentIdOfRenameSymbolDeclaration.ProjectId == projectId)
                    {
                        // Calculating declaration conflicts may require location mapping in documents
                        // that were not otherwise being processed in the current rename phase, so add
                        // the annotated spans in these documents to reverseMappedLocations.
                        foreach (var unprocessedDocumentIdWithPotentialDeclarationConflicts in allDocumentIdsInProject.Where(d => !documentIdsForConflictResolution.Contains(d)))
                        {
                            var newDocument = conflictResolution.NewSolution.GetDocument(unprocessedDocumentIdWithPotentialDeclarationConflicts);
                            var syntaxRoot  = await newDocument.GetSyntaxRootAsync(_cancellationToken).ConfigureAwait(false);

                            var baseDocument   = conflictResolution.OldSolution.GetDocument(unprocessedDocumentIdWithPotentialDeclarationConflicts);
                            var baseSyntaxTree = await baseDocument.GetSyntaxTreeAsync(_cancellationToken).ConfigureAwait(false);

                            var nodesOrTokensWithConflictCheckAnnotations = GetNodesOrTokensToCheckForConflicts(unprocessedDocumentIdWithPotentialDeclarationConflicts, syntaxRoot);
                            foreach (var nodeAndAnnotation in nodesOrTokensWithConflictCheckAnnotations)
                            {
                                var tokenOrNode        = nodeAndAnnotation.syntax;
                                var conflictAnnotation = nodeAndAnnotation.annotation;
                                reverseMappedLocations[tokenOrNode.GetLocation()] = baseSyntaxTree.GetLocation(conflictAnnotation.OriginalSpan);
                            }
                        }

                        var referencedSymbols = _renameLocationSet.ReferencedSymbols;
                        var renameSymbol      = _renameLocationSet.Symbol;
                        await AddDeclarationConflictsAsync(
                            renamedSymbolInNewSolution, renameSymbol, referencedSymbols, conflictResolution, reverseMappedLocations, _cancellationToken).ConfigureAwait(false);
                    }

                    return(conflictResolution.RelatedLocations.Any(r => r.Type == RelatedLocationType.PossiblyResolvableConflict));
                }
                catch (Exception e) when(FatalError.ReportUnlessCanceled(e))
                {
                    throw ExceptionUtilities.Unreachable;
                }
            }
Ejemplo n.º 9
0
            private async Task <bool> CheckForConflictAsync(
                ConflictResolution conflictResolution,
                ISymbol renamedSymbolInNewSolution,
                Document newDocument,
                RenameActionAnnotation conflictAnnotation,
                IEnumerable <ISymbol> newReferencedSymbols)
            {
                try
                {
                    bool hasConflict;
                    var  solution = conflictResolution.NewSolution;

                    if (conflictAnnotation.IsNamespaceDeclarationReference)
                    {
                        hasConflict = false;
                    }
                    else if (conflictAnnotation.IsMemberGroupReference)
                    {
                        if (!conflictAnnotation.RenameDeclarationLocationReferences.Any())
                        {
                            hasConflict = false;
                        }
                        else
                        {
                            // Ensure newReferencedSymbols contains at least one of the original referenced
                            // symbols, and allow any new symbols to be added to the set of references.

                            hasConflict = true;

                            var newLocationTasks = newReferencedSymbols.Select(async symbol => await GetSymbolLocationAsync(solution, symbol, _cancellationToken).ConfigureAwait(false));
                            var newLocations     = (await Task.WhenAll(newLocationTasks).ConfigureAwait(false)).Where(loc => loc != null && loc.IsInSource);
                            foreach (var originalReference in conflictAnnotation.RenameDeclarationLocationReferences.Where(loc => loc.IsSourceLocation))
                            {
                                var adjustedStartPosition = conflictResolution.GetAdjustedTokenStartingPosition(originalReference.TextSpan.Start, originalReference.DocumentId);
                                if (newLocations.Any(loc => loc.SourceSpan.Start == adjustedStartPosition))
                                {
                                    hasConflict = false;
                                    break;
                                }
                            }
                        }
                    }
                    else if (!conflictAnnotation.IsRenameLocation && conflictAnnotation.IsOriginalTextLocation && conflictAnnotation.RenameDeclarationLocationReferences.Length > 1 && newReferencedSymbols.Count() == 1)
                    {
                        // an ambiguous situation was resolved through rename in non reference locations
                        hasConflict = false;
                    }
                    else if (newReferencedSymbols.Count() != conflictAnnotation.RenameDeclarationLocationReferences.Length)
                    {
                        // Don't show conflicts for errors in the old solution that now bind in the new solution.
                        if (newReferencedSymbols.Count() != 0 && conflictAnnotation.RenameDeclarationLocationReferences.Length == 0)
                        {
                            hasConflict = false;
                        }
                        else
                        {
                            hasConflict = true;
                        }
                    }
                    else
                    {
                        hasConflict = false;
                        int symbolIndex = 0;
                        foreach (var symbol in newReferencedSymbols)
                        {
                            if (conflictAnnotation.RenameDeclarationLocationReferences[symbolIndex].SymbolLocationsCount != symbol.Locations.Count())
                            {
                                hasConflict = true;
                                break;
                            }

                            var newLocation = await GetSymbolLocationAsync(solution, symbol, _cancellationToken).ConfigureAwait(false);

                            if (newLocation != null && conflictAnnotation.RenameDeclarationLocationReferences[symbolIndex].IsSourceLocation)
                            {
                                // location was in source before, but not after rename
                                if (!newLocation.IsInSource)
                                {
                                    hasConflict = true;
                                    break;
                                }

                                var renameDeclarationLocationReference = conflictAnnotation.RenameDeclarationLocationReferences[symbolIndex];
                                var newAdjustedStartPosition           = conflictResolution.GetAdjustedTokenStartingPosition(renameDeclarationLocationReference.TextSpan.Start, renameDeclarationLocationReference.DocumentId);
                                if (newAdjustedStartPosition != newLocation.SourceSpan.Start)
                                {
                                    hasConflict = true;
                                    break;
                                }

                                if (conflictAnnotation.RenameDeclarationLocationReferences[symbolIndex].IsOverriddenFromMetadata)
                                {
                                    var overridingSymbol = await SymbolFinder.FindSymbolAtPositionAsync(solution.GetDocument(newLocation.SourceTree), newLocation.SourceSpan.Start, cancellationToken : _cancellationToken).ConfigureAwait(false);

                                    if (overridingSymbol != null && renamedSymbolInNewSolution != overridingSymbol)
                                    {
                                        if (!overridingSymbol.IsOverride)
                                        {
                                            hasConflict = true;
                                            break;
                                        }
                                        else
                                        {
                                            var overriddenSymbol = overridingSymbol.OverriddenMember();
                                            if (overriddenSymbol == null || !overriddenSymbol.Locations.All(loc => loc.IsInMetadata))
                                            {
                                                hasConflict = true;
                                                break;
                                            }
                                        }
                                    }
                                }
                            }
                            else
                            {
                                var newMetadataName = symbol.ToDisplayString(s_metadataSymbolDisplayFormat);
                                var oldMetadataName = conflictAnnotation.RenameDeclarationLocationReferences[symbolIndex].Name;
                                if (newLocation == null ||
                                    newLocation.IsInSource ||
                                    !HeuristicMetadataNameEquivalenceCheck(
                                        oldMetadataName,
                                        newMetadataName,
                                        _originalText,
                                        _replacementText))
                                {
                                    hasConflict = true;
                                    break;
                                }
                            }

                            symbolIndex++;
                        }
                    }

                    return(hasConflict);
                }
                catch (Exception e) when(FatalError.ReportUnlessCanceled(e))
                {
                    throw ExceptionUtilities.Unreachable;
                }
            }
Ejemplo n.º 10
0
        /// <summary>
        /// The workspace is designed to be stateless. If someone asks for a solution (through solution checksum),
        /// it will create one and return the solution. The engine takes care of syncing required data and creating a solution
        /// corresponding to the given checksum.
        ///
        /// but doing that from scratch all the time will be expansive in terms of syncing data, compilation being cached, file being parsed
        /// and etc. so even if the service itself is stateless, internally it has several caches to improve perf of various parts.
        ///
        /// first, it holds onto last solution got built. this will take care of common cases where multiple services running off same solution.
        /// second, it uses assets cache to hold onto data just synched (within 3 min) so that if it requires to build new solution,
        ///         it can save some time to re-sync data which might just used by other solution.
        /// third, it holds onto solution from primary branch from Host. and it will try to see whether it can build new solution off the
        ///        primary solution it is holding onto. this will make many solution level cache to be re-used.
        ///
        /// the primary solution can be updated in 2 ways.
        /// first, host will keep track of primary solution changes in host, and call OOP to synch to latest time to time.
        /// second, engine keeps track of whether a certain request is for primary solution or not, and if it is,
        ///         it let that request to update primary solution cache to latest.
        ///
        /// these 2 are complimentary to each other. #1 makes OOP's primary solution to be ready for next call (push), #2 makes OOP's primary
        /// solution be not stale as much as possible. (pull)
        /// </summary>
        private async Task <Solution> CreateSolution_NoLockAsync(
            AssetProvider assetProvider,
            Checksum solutionChecksum,
            bool fromPrimaryBranch,
            int workspaceVersion,
            Solution baseSolution,
            CancellationToken cancellationToken
            )
        {
            try
            {
                var updater = new SolutionCreator(
                    Services.HostServices,
                    assetProvider,
                    baseSolution,
                    cancellationToken
                    );

                // check whether solution is update to the given base solution
                if (await updater.IsIncrementalUpdateAsync(solutionChecksum).ConfigureAwait(false))
                {
                    // create updated solution off the baseSolution
                    var solution = await updater
                                   .CreateSolutionAsync(solutionChecksum)
                                   .ConfigureAwait(false);

                    if (fromPrimaryBranch)
                    {
                        // if the solutionChecksum is for primary branch, update primary workspace cache with the solution
                        return(UpdateSolutionIfPossible(solution, workspaceVersion));
                    }

                    // otherwise, just return the solution
                    return(solution);
                }

                // we need new solution. bulk sync all asset for the solution first.
                await assetProvider
                .SynchronizeSolutionAssetsAsync(solutionChecksum, cancellationToken)
                .ConfigureAwait(false);

                // get new solution info and options
                var(solutionInfo, options) = await assetProvider
                                             .CreateSolutionInfoAndOptionsAsync(solutionChecksum, cancellationToken)
                                             .ConfigureAwait(false);

                if (fromPrimaryBranch)
                {
                    // if the solutionChecksum is for primary branch, update primary workspace cache with new solution
                    if (
                        TrySetCurrentSolution(
                            solutionInfo,
                            workspaceVersion,
                            options,
                            out var solution
                            )
                        )
                    {
                        return(solution);
                    }
                }

                // otherwise, just return new solution
                var workspace = new TemporaryWorkspace(
                    Services.HostServices,
                    WorkspaceKind.RemoteTemporaryWorkspace,
                    solutionInfo,
                    options
                    );
                return(workspace.CurrentSolution);
            }
            catch (Exception e)
                when(FatalError.ReportAndPropagateUnlessCanceled(e, cancellationToken))
                {
                    throw ExceptionUtilities.Unreachable;
                }
        }
Ejemplo n.º 11
0
            // The method which performs rename, resolves the conflict locations and returns the result of the rename operation
            public async Task <ConflictResolution> ResolveConflictsAsync()
            {
                try
                {
                    await FindDocumentsAndPossibleNameConflicts().ConfigureAwait(false);

                    var baseSolution = _renameLocationSet.Solution;

                    // Process rename one project at a time to improve caching and reduce syntax tree serialization.
                    var documentsGroupedByTopologicallySortedProjectId = _documentsIdsToBeCheckedForConflict
                                                                         .GroupBy(d => d.ProjectId)
                                                                         .OrderBy(g => _topologicallySortedProjects.IndexOf(g.Key));

                    _replacementTextValid = IsIdentifierValid_Worker(baseSolution, _replacementText, documentsGroupedByTopologicallySortedProjectId.Select(g => g.Key), _cancellationToken);
                    var renamedSpansTracker = new RenamedSpansTracker();
                    var conflictResolution  = new ConflictResolution(baseSolution, renamedSpansTracker, _replacementText, _replacementTextValid);

                    foreach (var documentsByProject in documentsGroupedByTopologicallySortedProjectId)
                    {
                        var documentIdsThatGetsAnnotatedAndRenamed = new HashSet <DocumentId>(documentsByProject);
                        using (baseSolution.Services.CacheService?.EnableCaching(documentsByProject.Key))
                        {
                            // Rename is going to be in 4 phases.
                            // 1st phase - Does a simple token replacement
                            // If the 1st phase results in conflict then we perform then:
                            //      2nd phase is to expand and simplify only the reference locations with conflicts
                            //      3rd phase is to expand and simplify all the conflict locations (both reference and non-reference)
                            // If there are unresolved Conflicts after the 3rd phase then in 4th phase,
                            //      We complexify and resolve locations that were resolvable and for the other locations we perform the normal token replacement like the first the phase.
                            for (int phase = 0; phase < 4; phase++)
                            {
                                // Step 1:
                                // The rename process and annotation for the bookkeeping is performed in one-step
                                // The Process in short is,
                                // 1. If renaming a token which is no conflict then replace the token and make a map of the oldspan to the newspan
                                // 2. If we encounter a node that has to be expanded( because there was a conflict in previous phase), we expand it.
                                //    If the node happens to contain a token that needs to be renamed then we annotate it and rename it after expansion else just expand and proceed
                                // 3. Through the whole process we maintain a map of the oldspan to newspan. In case of expansion & rename, we map the expanded node and the renamed token
                                conflictResolution.UpdateCurrentSolution(await AnnotateAndRename_WorkerAsync(
                                                                             baseSolution,
                                                                             conflictResolution.NewSolution,
                                                                             documentIdsThatGetsAnnotatedAndRenamed,
                                                                             _renameLocationSet.Locations,
                                                                             renamedSpansTracker,
                                                                             _replacementTextValid).ConfigureAwait(false));

                                // Step 2: Check for conflicts in the renamed solution
                                bool foundResolvableConflicts = await IdentifyConflictsAsync(
                                    documentIdsForConflictResolution : documentIdsThatGetsAnnotatedAndRenamed,
                                    allDocumentIdsInProject : documentsByProject,
                                    projectId : documentsByProject.Key,
                                    conflictResolution : conflictResolution).ConfigureAwait(false);

                                if (!foundResolvableConflicts || phase == 3)
                                {
                                    break;
                                }

                                if (phase == 0)
                                {
                                    _conflictLocations = conflictResolution.RelatedLocations
                                                         .Where(loc => (documentIdsThatGetsAnnotatedAndRenamed.Contains(loc.DocumentId) && loc.Type == RelatedLocationType.PossiblyResolvableConflict && loc.IsReference))
                                                         .Select(loc => new ConflictLocationInfo(loc))
                                                         .ToSet();

                                    // If there were no conflicting locations in references, then the first conflict phase has to be skipped.
                                    if (_conflictLocations.Count == 0)
                                    {
                                        phase++;
                                    }
                                }

                                if (phase == 1)
                                {
                                    _conflictLocations = _conflictLocations.Concat(conflictResolution.RelatedLocations
                                                                                   .Where(loc => documentIdsThatGetsAnnotatedAndRenamed.Contains(loc.DocumentId) && loc.Type == RelatedLocationType.PossiblyResolvableConflict)
                                                                                   .Select(loc => new ConflictLocationInfo(loc)))
                                                         .ToSet();
                                }

                                // Set the documents with conflicts that need to be processed in the next phase.
                                // Note that we need to get the conflictLocations here since we're going to remove some locations below if phase == 2
                                documentIdsThatGetsAnnotatedAndRenamed = new HashSet <DocumentId>(_conflictLocations.Select(l => l.DocumentId));

                                if (phase == 2)
                                {
                                    // After phase 2, if there are still conflicts then remove the conflict locations from being expanded
                                    var unresolvedLocations = conflictResolution.RelatedLocations
                                                              .Where(l => (l.Type & RelatedLocationType.UnresolvedConflict) != 0)
                                                              .Select(l => Tuple.Create(l.ComplexifiedTargetSpan, l.DocumentId)).Distinct();

                                    _conflictLocations = _conflictLocations.Where(l => !unresolvedLocations.Any(c => c.Item2 == l.DocumentId && c.Item1.Contains(l.OriginalIdentifierSpan))).ToSet();
                                }

                                // Clean up side effects from rename before entering the next phase
                                conflictResolution.ClearDocuments(documentIdsThatGetsAnnotatedAndRenamed);
                            }

                            // Step 3: Simplify the project
                            conflictResolution.UpdateCurrentSolution(await renamedSpansTracker.SimplifyAsync(conflictResolution.NewSolution, documentsByProject, _replacementTextValid, _renameAnnotations, _cancellationToken).ConfigureAwait(false));
                            await conflictResolution.RemoveAllRenameAnnotationsAsync(documentsByProject, _renameAnnotations, _cancellationToken).ConfigureAwait(false);
                        }
                    }

                    // This rename could break implicit references of this symbol (e.g. rename MoveNext on a collection like type in a
                    // foreach/for each statement
                    ISymbol renamedSymbolInNewSolution = await GetRenamedSymbolInCurrentSolutionAsync(conflictResolution).ConfigureAwait(false);

                    if (IsRenameValid(conflictResolution, renamedSymbolInNewSolution))
                    {
                        await AddImplicitConflictsAsync(
                            renamedSymbolInNewSolution,
                            _renameLocationSet.Symbol,
                            _renameLocationSet.ImplicitLocations,
                            await conflictResolution.NewSolution.GetDocument(_documentIdOfRenameSymbolDeclaration).GetSemanticModelAsync(_cancellationToken).ConfigureAwait(false),
                            _renameSymbolDeclarationLocation,
                            renamedSpansTracker.GetAdjustedPosition(_renameSymbolDeclarationLocation.SourceSpan.Start, _documentIdOfRenameSymbolDeclaration),
                            conflictResolution,
                            _cancellationToken).ConfigureAwait(false);
                    }

                    foreach (var relatedLocation in conflictResolution.RelatedLocations)
                    {
                        if (relatedLocation.Type == RelatedLocationType.PossiblyResolvableConflict)
                        {
                            relatedLocation.Type = RelatedLocationType.UnresolvedConflict;
                        }
                    }
#if DEBUG
                    await DebugVerifyNoErrorsAsync(conflictResolution, _documentsIdsToBeCheckedForConflict).ConfigureAwait(false);
#endif
                    return(conflictResolution);
                }
                catch (Exception e) when(FatalError.ReportUnlessCanceled(e))
                {
                    throw ExceptionUtilities.Unreachable;
                }
            }
Ejemplo n.º 12
0
        /// <summary>
        /// Closes the invisible editor and saves the underlying document as appropriate.
        /// </summary>
        public void Dispose()
        {
            AssertIsForeground();

            _buffer      = null;
            _vsTextLines = null !;

            try
            {
                if (_needsSave)
                {
                    // We need to tell this document to save before we get rid of the invisible editor. Otherwise,
                    // the invisible editor never actually makes the document go away. Check out CLockHolder::ReleaseEditLock
                    // in env\msenv\core\editmgr.cpp for details. We choose this particular technique for saving files
                    // since it's what the old cslangsvc.dll used.
                    var runningDocumentTable4 =
                        (IVsRunningDocumentTable4)_serviceProvider.GetService(
                            typeof(SVsRunningDocumentTable)
                            );

                    if (runningDocumentTable4.IsMonikerValid(_filePath))
                    {
                        var cookie = runningDocumentTable4.GetDocumentCookie(_filePath);
                        var runningDocumentTable = (IVsRunningDocumentTable)runningDocumentTable4;

                        // Old cslangsvc.dll requested not to add to MRU for, and I quote, "performance!". Makes sense not
                        // to include it in the MRU anyways.
                        ErrorHandler.ThrowOnFailure(
                            runningDocumentTable.ModifyDocumentFlags(
                                cookie,
                                (uint)_VSRDTFLAGS.RDT_DontAddToMRU,
                                fSet: 1
                                )
                            );

                        runningDocumentTable.SaveDocuments(
                            (uint)__VSRDTSAVEOPTIONS.RDTSAVEOPT_SaveIfDirty,
                            pHier: null,
                            itemid: 0,
                            docCookie: cookie
                            );
                    }
                }

                if (_needsUndoRestored && _manager != null)
                {
                    _manager.Enable(1);
                    _manager = null;
                }

                // Clean up our RCW. This RCW is a unique RCW, so this is actually safe to do!
                Marshal.ReleaseComObject(_invisibleEditor);
                _invisibleEditor = null !;

                GC.SuppressFinalize(this);
            }
            catch (Exception ex) when(FatalError.ReportAndPropagate(ex))
            {
                throw ExceptionUtilities.Unreachable;
            }
        }
Ejemplo n.º 13
0
            private async Task <Model> ComputeModelInBackgroundAsync(
                Model currentModel,
                IList <ISignatureHelpProvider> matchedProviders,
                IList <ISignatureHelpProvider> unmatchedProviders,
                SnapshotPoint caretPosition,
                DisconnectedBufferGraph disconnectedBufferGraph,
                SignatureHelpTriggerInfo triggerInfo,
                CancellationToken cancellationToken)
            {
                try
                {
                    using (Logger.LogBlock(FunctionId.SignatureHelp_ModelComputation_ComputeModelInBackground, cancellationToken))
                    {
                        AssertIsBackground();
                        cancellationToken.ThrowIfCancellationRequested();

                        var document = await Controller.DocumentProvider.GetDocumentAsync(caretPosition.Snapshot, cancellationToken).ConfigureAwait(false);

                        if (document == null)
                        {
                            return(currentModel);
                        }

                        if (triggerInfo.TriggerReason == SignatureHelpTriggerReason.RetriggerCommand)
                        {
                            if (currentModel == null ||
                                (triggerInfo.TriggerCharacter.HasValue && !currentModel.Provider.IsRetriggerCharacter(triggerInfo.TriggerCharacter.Value)))
                            {
                                return(currentModel);
                            }
                        }

                        // first try to query the providers that can trigger on the specified character
                        var result = await ComputeItemsAsync(matchedProviders, caretPosition, triggerInfo, document, cancellationToken).ConfigureAwait(false);

                        var provider = result.Item1;
                        var items    = result.Item2;

                        if (provider == null)
                        {
                            // no match, so now query the other providers
                            result = await ComputeItemsAsync(unmatchedProviders, caretPosition, triggerInfo, document, cancellationToken).ConfigureAwait(false);

                            provider = result.Item1;
                            items    = result.Item2;

                            if (provider == null)
                            {
                                // the other providers didn't produce items either, so we don't produce a model
                                return(null);
                            }
                        }

                        if (currentModel != null &&
                            currentModel.Provider == provider &&
                            currentModel.GetCurrentSpanInSubjectBuffer(disconnectedBufferGraph.SubjectBufferSnapshot).Span.Start == items.ApplicableSpan.Start &&
                            currentModel.ArgumentIndex == items.ArgumentIndex &&
                            currentModel.ArgumentCount == items.ArgumentCount &&
                            currentModel.ArgumentName == items.ArgumentName)
                        {
                            // The new model is the same as the current model.  Return the currentModel
                            // so we keep the active selection.
                            return(currentModel);
                        }

                        var selectedItem = GetSelectedItem(currentModel, items, provider);
                        var model        = new Model(disconnectedBufferGraph, items.ApplicableSpan, provider,
                                                     items.Items, selectedItem, items.ArgumentIndex, items.ArgumentCount, items.ArgumentName,
                                                     selectedParameter: 0);

                        var syntaxFactsService = document.GetLanguageService <ISyntaxFactsService>();
                        var isCaseSensitive    = syntaxFactsService == null || syntaxFactsService.IsCaseSensitive;
                        var selection          = DefaultSignatureHelpSelector.GetSelection(model.Items,
                                                                                           model.SelectedItem, model.ArgumentIndex, model.ArgumentCount, model.ArgumentName, isCaseSensitive);

                        return(model.WithSelectedItem(selection.SelectedItem)
                               .WithSelectedParameter(selection.SelectedParameter));
                    }
                }
                catch (Exception e) when(FatalError.ReportUnlessCanceled(e))
                {
                    throw ExceptionUtilities.Unreachable;
                }
            }
        private async Task FindImplementingMembersAsync(
            Document document, int caretPosition, IStreamingFindUsagesPresenter presenter, CancellationToken cancellationToken)
        {
            try
            {
                using var token = _asyncListener.BeginAsyncOperation(nameof(FindImplementingMembersAsync));

                // Let the presented know we're starting a search.  We pass in no cancellation token here as this
                // operation itself is fire-and-forget and the user won't cancel the operation through us (though
                // the window itself can cancel the operation if it is taken over for another find operation.
                var context = presenter.StartSearch(EditorFeaturesResources.Navigating, supportsReferences: true, cancellationToken);

                using (Logger.LogBlock(
                           FunctionId.CommandHandler_FindAllReference,
                           KeyValueLogMessage.Create(LogType.UserAction, m => m["type"] = "streaming"),
                           context.CancellationToken))
                {
                    try
                    {
#pragma warning disable CA2007 // Consider calling ConfigureAwait on the awaited task
                        var relevantSymbol = await FindUsagesHelpers.GetRelevantSymbolAndProjectAtPositionAsync(document, caretPosition, context.CancellationToken);

#pragma warning restore CA2007 // Consider calling ConfigureAwait on the awaited task

                        var interfaceSymbol = relevantSymbol?.symbol as INamedTypeSymbol;

                        if (interfaceSymbol == null || interfaceSymbol.TypeKind != TypeKind.Interface)
                        {
                            //looks like it's not a relevant symbol
                            return;
                        }

                        // we now need to find the class that implements this particular interface, at the
                        // caret position, or somewhere around it
                        SyntaxNode nodeRoot;
                        if (!document.TryGetSyntaxRoot(out nodeRoot))
                        {
                            return;
                        }

#pragma warning disable CA2007 // Consider calling ConfigureAwait on the awaited task
                        var syntaxTree = await document.GetSyntaxTreeAsync(cancellationToken);

#pragma warning restore CA2007 // Consider calling ConfigureAwait on the awaited task
                        var documentToken = nodeRoot.FindToken(caretPosition);

                        if (!documentToken.Span.IntersectsWith(caretPosition))
                        {
                            return; // looks like it's not relevant
                        }
                        // the parents should bring us to the class definition
                        var parentTypeNode = documentToken.Parent?.Parent?.Parent?.Parent;
#pragma warning disable CA2007 // Consider calling ConfigureAwait on the awaited task
                        var compilation = await document.Project.GetCompilationAsync(cancellationToken);

#pragma warning restore CA2007 // Consider calling ConfigureAwait on the awaited task

                        // let's finally get our implementing type
                        var namedTypeSymbol = compilation.GetSemanticModel(syntaxTree).GetDeclaredSymbol(parentTypeNode, cancellationToken: cancellationToken) as INamedTypeSymbol;
                        // unless something went wrong, and we got an empty symbol,
                        if (namedTypeSymbol == null)
                        {
                            return;
                        }

                        // we can search for implementations of the interface, within this type
#pragma warning disable CA2007 // Consider calling ConfigureAwait on the awaited task
                        await InspectInterfaceAsync(context, interfaceSymbol, namedTypeSymbol, document.Project);

#pragma warning restore CA2007 // Consider calling ConfigureAwait on the awaited task

                        // now, we iterate on interfaces of our interfaces
                        foreach (var iFace in interfaceSymbol.AllInterfaces)
                        {
#pragma warning disable CA2007 // Consider calling ConfigureAwait on the awaited task
                            await InspectInterfaceAsync(context, iFace, namedTypeSymbol, document.Project);

#pragma warning restore CA2007 // Consider calling ConfigureAwait on the awaited task
                        }
                    }
                    finally
                    {
                        await context.OnCompletedAsync().ConfigureAwait(false);
                    }
                }
            }
            catch (OperationCanceledException)
            {
            }
            catch (Exception e) when(FatalError.ReportAndCatch(e))
            {
            }
        }
Ejemplo n.º 15
0
        private static async Task <SyntaxTreeIndex> CreateIndexAsync(
            Document document, Checksum checksum, CancellationToken cancellationToken)
        {
            var syntaxFacts     = document.GetLanguageService <ISyntaxFactsService>();
            var ignoreCase      = syntaxFacts != null && !syntaxFacts.IsCaseSensitive;
            var isCaseSensitive = !ignoreCase;

            GetIdentifierSet(ignoreCase, out var identifiers, out var escapedIdentifiers);

            var stringLiterals = StringLiteralHashSetPool.Allocate();
            var longLiterals   = LongLiteralHashSetPool.Allocate();

            try
            {
                var containsForEachStatement           = false;
                var containsLockStatement              = false;
                var containsUsingStatement             = false;
                var containsQueryExpression            = false;
                var containsThisConstructorInitializer = false;
                var containsBaseConstructorInitializer = false;
                var containsElementAccess              = false;
                var containsIndexerMemberCref          = false;

                var predefinedTypes     = (int)PredefinedType.None;
                var predefinedOperators = (int)PredefinedOperator.None;

                var declaredSymbolInfos = ArrayBuilder <DeclaredSymbolInfo> .GetInstance();

                if (syntaxFacts != null)
                {
                    var root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false);

                    foreach (var current in root.DescendantNodesAndTokensAndSelf(descendIntoTrivia: true))
                    {
                        if (current.IsNode)
                        {
                            var node = (SyntaxNode)current;

                            containsForEachStatement  = containsForEachStatement || syntaxFacts.IsForEachStatement(node);
                            containsLockStatement     = containsLockStatement || syntaxFacts.IsLockStatement(node);
                            containsUsingStatement    = containsUsingStatement || syntaxFacts.IsUsingStatement(node);
                            containsQueryExpression   = containsQueryExpression || syntaxFacts.IsQueryExpression(node);
                            containsElementAccess     = containsElementAccess || syntaxFacts.IsElementAccessExpression(node);
                            containsIndexerMemberCref = containsIndexerMemberCref || syntaxFacts.IsIndexerMemberCRef(node);
                            // We've received a number of error reports where DeclaredSymbolInfo.GetSymbolAsync() will
                            // crash because the document's syntax root doesn't contain the span of the node returned
                            // by TryGetDeclaredSymbolInfo().  There are two possibilities for this crash:
                            //   1) syntaxFacts.TryGetDeclaredSymbolInfo() is returning a bad span, or
                            //   2) Document.GetSyntaxRootAsync() (called from DeclaredSymbolInfo.GetSymbolAsync) is
                            //      returning a bad syntax root that doesn't represent the original parsed document.
                            // By adding the `root.FullSpan.Contains()` check below, if we get similar crash reports in
                            // the future then we know the problem lies in (2).  If, however, the problem is really in
                            // TryGetDeclaredSymbolInfo, then this will at least prevent us from returning bad spans
                            // and will prevent the crash from occurring.
                            if (syntaxFacts.TryGetDeclaredSymbolInfo(node, out var declaredSymbolInfo))
                            {
                                if (root.FullSpan.Contains(declaredSymbolInfo.Span))
                                {
                                    declaredSymbolInfos.Add(declaredSymbolInfo);
                                }
                                else
                                {
                                    var message =
                                        $@"Invalid span in {nameof(declaredSymbolInfo)}.
{nameof(declaredSymbolInfo.Span)} = {declaredSymbolInfo.Span}
{nameof(root.FullSpan)} = {root.FullSpan}";

                                    FatalError.ReportWithoutCrash(new InvalidOperationException(message));
                                }
                            }
                        }
                        else
                        {
                            var token = (SyntaxToken)current;

                            containsThisConstructorInitializer = containsThisConstructorInitializer || syntaxFacts.IsThisConstructorInitializer(token);
                            containsBaseConstructorInitializer = containsBaseConstructorInitializer || syntaxFacts.IsBaseConstructorInitializer(token);

                            if (syntaxFacts.IsIdentifier(token) ||
                                syntaxFacts.IsGlobalNamespaceKeyword(token))
                            {
                                var valueText = token.ValueText;

                                identifiers.Add(valueText);
                                if (valueText.Length != token.Width())
                                {
                                    escapedIdentifiers.Add(valueText);
                                }
                            }

                            if (syntaxFacts.TryGetPredefinedType(token, out var predefinedType))
                            {
                                predefinedTypes |= (int)predefinedType;
                            }

                            if (syntaxFacts.TryGetPredefinedOperator(token, out var predefinedOperator))
                            {
                                predefinedOperators |= (int)predefinedOperator;
                            }

                            if (syntaxFacts.IsStringLiteral(token))
                            {
                                stringLiterals.Add(token.ValueText);
                            }

                            if (syntaxFacts.IsCharacterLiteral(token))
                            {
                                longLiterals.Add((char)token.Value);
                            }

                            if (syntaxFacts.IsNumericLiteral(token))
                            {
                                var value = token.Value;
                                switch (value)
                                {
                                case decimal d:
                                    // not supported for now.
                                    break;

                                case double d:
                                    longLiterals.Add(BitConverter.DoubleToInt64Bits(d));
                                    break;

                                case float f:
                                    longLiterals.Add(BitConverter.DoubleToInt64Bits(f));
                                    break;

                                default:
                                    longLiterals.Add(IntegerUtilities.ToInt64(token.Value));
                                    break;
                                }
                            }
                        }
                    }
                }

                return(new SyntaxTreeIndex(
                           checksum,
                           new LiteralInfo(
                               new BloomFilter(FalsePositiveProbability, stringLiterals, longLiterals)),
                           new IdentifierInfo(
                               new BloomFilter(FalsePositiveProbability, isCaseSensitive, identifiers),
                               new BloomFilter(FalsePositiveProbability, isCaseSensitive, escapedIdentifiers)),
                           new ContextInfo(
                               predefinedTypes,
                               predefinedOperators,
                               containsForEachStatement,
                               containsLockStatement,
                               containsUsingStatement,
                               containsQueryExpression,
                               containsThisConstructorInitializer,
                               containsBaseConstructorInitializer,
                               containsElementAccess,
                               containsIndexerMemberCref),
                           new DeclarationInfo(
                               declaredSymbolInfos.ToImmutableAndFree())));
            }
            finally
            {
                Free(ignoreCase, identifiers, escapedIdentifiers);
                StringLiteralHashSetPool.ClearAndFree(stringLiterals);
                LongLiteralHashSetPool.ClearAndFree(longLiterals);
            }
        }
Ejemplo n.º 16
0
            // The rename process and annotation for the bookkeeping is performed in one-step
            private async Task <Solution> AnnotateAndRename_WorkerAsync(
                Solution originalSolution,
                Solution partiallyRenamedSolution,
                HashSet <DocumentId> documentIdsToRename,
                ISet <RenameLocation> renameLocations,
                RenamedSpansTracker renameSpansTracker,
                bool replacementTextValid)
            {
                try
                {
                    foreach (var documentId in documentIdsToRename.ToList())
                    {
                        _cancellationToken.ThrowIfCancellationRequested();

                        var document      = originalSolution.GetDocument(documentId);
                        var semanticModel = await document.GetSemanticModelAsync(_cancellationToken).ConfigureAwait(false);

                        var originalSyntaxRoot = await semanticModel.SyntaxTree.GetRootAsync(_cancellationToken).ConfigureAwait(false);

                        // Get all rename locations for the current document.
                        var allTextSpansInSingleSourceTree = renameLocations
                                                             .Where(l => l.DocumentId == documentId && ShouldIncludeLocation(renameLocations, l))
                                                             .ToDictionary(l => l.Location.SourceSpan);

                        // All textspan in the document documentId, that requires rename in String or Comment
                        var stringAndCommentTextSpansInSingleSourceTree = renameLocations
                                                                          .Where(l => l.DocumentId == documentId && l.IsRenameInStringOrComment)
                                                                          .GroupBy(l => l.ContainingLocationForStringOrComment)
                                                                          .Select(g => g.Key)
                                                                          .ToSet();

                        var conflictLocationSpans = _conflictLocations
                                                    .Where(t => t.DocumentId == documentId)
                                                    .Select(t => t.ComplexifiedSpan).ToSet();

                        // Annotate all nodes with a RenameLocation annotations to record old locations & old referenced symbols.
                        // Also annotate nodes that should get complexified (nodes for rename locations + conflict locations)
                        var parameters = new RenameRewriterParameters(
                            _renamedSymbolDeclarationAnnotation,
                            document,
                            semanticModel,
                            originalSyntaxRoot,
                            _replacementText,
                            _originalText,
                            _possibleNameConflicts,
                            allTextSpansInSingleSourceTree,
                            stringAndCommentTextSpansInSingleSourceTree,
                            conflictLocationSpans,
                            originalSolution,
                            _renameLocationSet.Symbol,
                            replacementTextValid,
                            renameSpansTracker,
                            _optionSet,
                            _renameAnnotations,
                            _cancellationToken);

                        var renameRewriterLanguageService = document.Project.LanguageServices.GetService <IRenameRewriterLanguageService>();
                        var newRoot = renameRewriterLanguageService.AnnotateAndRename(parameters);

                        if (newRoot == originalSyntaxRoot)
                        {
                            // Update the list for the current phase, some files with strings containing the original or replacement
                            // text may have been filtered out.
                            documentIdsToRename.Remove(documentId);
                        }
                        else
                        {
                            partiallyRenamedSolution = partiallyRenamedSolution.WithDocumentSyntaxRoot(documentId, newRoot, PreservationMode.PreserveIdentity);
                        }
                    }

                    return(partiallyRenamedSolution);
                }
                catch (Exception e) when(FatalError.ReportUnlessCanceled(e))
                {
                    throw ExceptionUtilities.Unreachable;
                }
            }
Ejemplo n.º 17
0
        protected override async Task <IEnumerable <CompletionItem> > GetItemsWorkerAsync(
            Document document, int position,
            CompletionTrigger trigger, CancellationToken cancellationToken)
        {
            try
            {
                var tree = await document.GetSyntaxTreeAsync(cancellationToken).ConfigureAwait(false);

                var token        = tree.FindTokenOnLeftOfPosition(position, cancellationToken);
                var parentTrivia = token.GetAncestor <DocumentationCommentTriviaSyntax>();

                if (parentTrivia == null)
                {
                    return(null);
                }

                var attachedToken = parentTrivia.ParentTrivia.Token;
                if (attachedToken.Kind() == SyntaxKind.None)
                {
                    return(null);
                }

                var semanticModel = await document.GetSemanticModelForNodeAsync(attachedToken.Parent, cancellationToken).ConfigureAwait(false);

                ISymbol declaredSymbol    = null;
                var     memberDeclaration = attachedToken.GetAncestor <MemberDeclarationSyntax>();
                if (memberDeclaration != null)
                {
                    declaredSymbol = semanticModel.GetDeclaredSymbol(memberDeclaration, cancellationToken);
                }
                else
                {
                    var typeDeclaration = attachedToken.GetAncestor <TypeDeclarationSyntax>();
                    if (typeDeclaration != null)
                    {
                        declaredSymbol = semanticModel.GetDeclaredSymbol(typeDeclaration, cancellationToken);
                    }
                }

                if (IsAttributeNameContext(token, position, out var elementName, out var existingAttributes))
                {
                    return(GetAttributeItems(elementName, existingAttributes));
                }

                var wasTriggeredAfterSpace = trigger.Kind == CompletionTriggerKind.Insertion && trigger.Character == ' ';
                if (wasTriggeredAfterSpace)
                {
                    // Nothing below this point should triggered by a space character
                    // (only attribute names should be triggered by <SPACE>)
                    return(null);
                }

                if (IsAttributeValueContext(token, out elementName, out var attributeName))
                {
                    return(GetAttributeValueItems(declaredSymbol, elementName, attributeName));
                }

                if (trigger.Kind == CompletionTriggerKind.Insertion && trigger.Character != '<')
                {
                    // With the use of IsTriggerAfterSpaceOrStartOfWordCharacter, the code below is much
                    // too aggressive at suggesting tags, so exit early before degrading the experience
                    return(null);
                }

                var items = new List <CompletionItem>();

                if (token.Parent.Kind() == SyntaxKind.XmlEmptyElement || token.Parent.Kind() == SyntaxKind.XmlText ||
                    (token.Parent.IsKind(SyntaxKind.XmlElementEndTag) && token.IsKind(SyntaxKind.GreaterThanToken)) ||
                    (token.Parent.IsKind(SyntaxKind.XmlName) && token.Parent.IsParentKind(SyntaxKind.XmlEmptyElement)))
                {
                    // The user is typing inside an XmlElement
                    if (token.Parent.Parent.Kind() == SyntaxKind.XmlElement ||
                        token.Parent.Parent.IsParentKind(SyntaxKind.XmlElement))
                    {
                        // Avoid including language keywords when following < or <text, since these cases should only be
                        // attempting to complete the XML name (which for language keywords is 'see'). While the parser
                        // treats the 'name' in '< name' as an XML name, we don't treat it like that here so the completion
                        // experience is consistent for '< ' and '< n'.
                        var xmlNameOnly = token.IsKind(SyntaxKind.LessThanToken) ||
                                          (token.Parent.IsKind(SyntaxKind.XmlName) && !token.HasLeadingTrivia);
                        var includeKeywords = !xmlNameOnly;

                        items.AddRange(GetNestedItems(declaredSymbol, includeKeywords));
                    }

                    if (token.Parent.Parent is XmlElementSyntax xmlElement)
                    {
                        AddXmlElementItems(items, xmlElement.StartTag);
                    }

                    if (token.Parent.IsParentKind(SyntaxKind.XmlEmptyElement) &&
                        token.Parent.Parent.Parent is XmlElementSyntax nestedXmlElement)
                    {
                        AddXmlElementItems(items, nestedXmlElement.StartTag);
                    }

                    if (token.Parent.Parent is DocumentationCommentTriviaSyntax ||
                        (token.Parent.Parent.IsKind(SyntaxKind.XmlEmptyElement) && token.Parent.Parent.Parent is DocumentationCommentTriviaSyntax))
                    {
                        items.AddRange(GetTopLevelItems(declaredSymbol, parentTrivia));
                    }
                }

                if (token.Parent is XmlElementStartTagSyntax startTag &&
                    token == startTag.GreaterThanToken)
                {
                    AddXmlElementItems(items, startTag);
                }

                items.AddRange(GetAlwaysVisibleItems());
                return(items);
            }
            catch (Exception e) when(FatalError.ReportWithoutCrashUnlessCanceled(e))
            {
                return(SpecializedCollections.EmptyEnumerable <CompletionItem>());
            }
        }
        public override async Task ProvideCompletionsAsync(CompletionContext context)
        {
            try
            {
                var document          = context.Document;
                var position          = context.Position;
                var cancellationToken = context.CancellationToken;

                var syntaxTree = await document
                                 .GetRequiredSyntaxTreeAsync(cancellationToken)
                                 .ConfigureAwait(false);

                if (syntaxTree.IsInNonUserCode(position, cancellationToken))
                {
                    return;
                }

                var token = syntaxTree
                            .FindTokenOnLeftOfPosition(position, cancellationToken)
                            .GetPreviousTokenIfTouchingWord(position);

                if (!token.IsKind(SyntaxKind.OpenBracketToken, SyntaxKind.CommaToken))
                {
                    return;
                }

                if (
                    token.Parent
                    is not FunctionPointerUnmanagedCallingConventionListSyntax callingConventionList
                    )
                {
                    return;
                }

                var contextPosition = token.SpanStart;
                var semanticModel   = await document
                                      .ReuseExistingSpeculativeModelAsync(callingConventionList, cancellationToken)
                                      .ConfigureAwait(false);

                var completionItems = new HashSet <CompletionItem>(CompletionItemComparer.Instance);
                AddTypes(completionItems, contextPosition, semanticModel, cancellationToken);

                // Even if we didn't have types, there are four magic calling conventions recognized regardless.
                // We add these after doing the type lookup so if we had types we can show that instead
                foreach (var callingConvention in s_predefinedCallingConventions)
                {
                    completionItems.Add(
                        CompletionItem.Create(
                            callingConvention,
                            tags: GlyphTags.GetTags(Glyph.Keyword)
                            )
                        );
                }

                context.AddItems(completionItems);
            }
            catch (Exception e) when(FatalError.ReportAndCatchUnlessCanceled(e))
            {
                // nop
            }
        }
Ejemplo n.º 19
0
            /// <summary>
            /// Add all appropriate references to the compilation and set it as our final compilation
            /// state.
            /// </summary>
            private async Task <CompilationInfo> FinalizeCompilationAsync(
                SolutionState 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.GetProjectState(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);

                    this.WriteState(new FinalState(State.CreateValueSource(compilation, solution.Services), hasSuccessfullyLoaded), solution);

                    return(new CompilationInfo(compilation, hasSuccessfullyLoaded));
                }
                catch (Exception e) when(FatalError.ReportUnlessCanceled(e))
                {
                    throw ExceptionUtilities.Unreachable;
                }
            }
Ejemplo n.º 20
0
                    private async Task ProcessDocumentAsync(ImmutableArray <IIncrementalAnalyzer> analyzers, WorkItem workItem, CancellationToken cancellationToken)
                    {
                        Contract.ThrowIfNull(workItem.DocumentId);

                        if (CancellationToken.IsCancellationRequested)
                        {
                            return;
                        }

                        var processedEverything = false;
                        var documentId          = workItem.DocumentId;

                        // we should always use solution snapshot after workitem is removed from the queue.
                        // otherwise, we can have a race such as below.
                        //
                        // 1.solution crawler picked up a solution
                        // 2.before processing the solution, an workitem got changed
                        // 3.and then the work item got picked up from the queue
                        // 4.and use the work item with the solution that got picked up in step 1
                        //
                        // step 2 is happening because solution has changed, but step 4 used old solution from step 1
                        // that doesn't have effects of the solution changes.
                        //
                        // solution crawler must remove the work item from the queue first and then pick up the soluton,
                        // so that the queue gets new work item if there is any solution changes after the work item is removed
                        // from the queue
                        //
                        // using later version of solution is always fine since, as long as there is new work item in the queue,
                        // solution crawler will eventually call the last workitem with the lastest solution
                        // making everything to catch up
                        var solution = Processor._registration.GetSolutionToAnalyze();

                        try
                        {
                            using (Logger.LogBlock(FunctionId.WorkCoordinator_ProcessDocumentAsync, w => w.ToString(), workItem, cancellationToken))
                            {
                                var textDocument = solution.GetTextDocument(documentId) ?? await solution.GetSourceGeneratedDocumentAsync(documentId, cancellationToken).ConfigureAwait(false);

                                if (textDocument != null)
                                {
                                    // if we are called because a document is opened, we invalidate the document so that
                                    // it can be re-analyzed. otherwise, since newly opened document has same version as before
                                    // analyzer will simply return same data back
                                    if (workItem.MustRefresh && !workItem.IsRetry)
                                    {
                                        var isOpen = textDocument.IsOpen();

                                        await ProcessOpenDocumentIfNeededAsync(analyzers, workItem, textDocument, isOpen, cancellationToken).ConfigureAwait(false);
                                        await ProcessCloseDocumentIfNeededAsync(analyzers, workItem, textDocument, isOpen, cancellationToken).ConfigureAwait(false);
                                    }

                                    // check whether we are having special reanalyze request
                                    await ProcessReanalyzeDocumentAsync(workItem, textDocument, cancellationToken).ConfigureAwait(false);

                                    await Processor.ProcessDocumentAnalyzersAsync(textDocument, analyzers, workItem, cancellationToken).ConfigureAwait(false);
                                }
                                else
                                {
                                    SolutionCrawlerLogger.LogProcessDocumentNotExist(Processor._logAggregator);

                                    await RemoveDocumentAsync(documentId, cancellationToken).ConfigureAwait(false);
                                }

                                if (!cancellationToken.IsCancellationRequested)
                                {
                                    processedEverything = true;
                                }
                            }
                        }
                        catch (Exception e) when(FatalError.ReportAndPropagateUnlessCanceled(e, cancellationToken))
                        {
                            throw ExceptionUtilities.Unreachable;
                        }
                        finally
                        {
                            // we got cancelled in the middle of processing the document.
                            // let's make sure newly enqueued work item has all the flag needed.
                            // Avoid retry attempts after cancellation is requested, since work will not be processed
                            // after that point.
                            if (!processedEverything && !CancellationToken.IsCancellationRequested)
                            {
                                _workItemQueue.AddOrReplace(workItem.Retry(Listener.BeginAsyncOperation("ReenqueueWorkItem")));
                            }

                            SolutionCrawlerLogger.LogProcessDocument(Processor._logAggregator, documentId.Id, processedEverything);

                            // remove one that is finished running
                            _workItemQueue.MarkWorkItemDoneFor(workItem.DocumentId);
                        }
                    }
Ejemplo n.º 21
0
        public async Task <MetadataAsSourceFile> GetGeneratedFileAsync(Project project, ISymbol symbol, bool allowDecompilation, CancellationToken cancellationToken = default)
        {
            if (project == null)
            {
                throw new ArgumentNullException(nameof(project));
            }

            if (symbol == null)
            {
                throw new ArgumentNullException(nameof(symbol));
            }

            if (symbol.Kind == SymbolKind.Namespace)
            {
                throw new ArgumentException(EditorFeaturesResources.symbol_cannot_be_a_namespace, nameof(symbol));
            }

            symbol = symbol.GetOriginalUnreducedDefinition();

            MetadataAsSourceGeneratedFileInfo fileInfo;
            Location navigateLocation  = null;
            var      topLevelNamedType = MetadataAsSourceHelpers.GetTopLevelContainingNamedType(symbol);
            var      symbolId          = SymbolKey.Create(symbol, cancellationToken);
            var      compilation       = await project.GetCompilationAsync(cancellationToken).ConfigureAwait(false);

            using (await _gate.DisposableWaitAsync(cancellationToken).ConfigureAwait(false))
            {
                InitializeWorkspace(project);

                var infoKey = await GetUniqueDocumentKey(project, topLevelNamedType, allowDecompilation, cancellationToken).ConfigureAwait(false);

                fileInfo = _keyToInformation.GetOrAdd(infoKey, _ => new MetadataAsSourceGeneratedFileInfo(GetRootPathWithGuid_NoLock(), project, topLevelNamedType, allowDecompilation));

                _generatedFilenameToInformation[fileInfo.TemporaryFilePath] = fileInfo;

                if (!File.Exists(fileInfo.TemporaryFilePath))
                {
                    // We need to generate this. First, we'll need a temporary project to do the generation into. We
                    // avoid loading the actual file from disk since it doesn't exist yet.
                    var temporaryProjectInfoAndDocumentId = fileInfo.GetProjectInfoAndDocumentId(_workspace, loadFileFromDisk: false);
                    var temporaryDocument = _workspace.CurrentSolution.AddProject(temporaryProjectInfoAndDocumentId.Item1)
                                            .GetDocument(temporaryProjectInfoAndDocumentId.Item2);

                    var useDecompiler = allowDecompilation;
                    if (useDecompiler)
                    {
                        useDecompiler = !symbol.ContainingAssembly.GetAttributes().Any(attribute => attribute.AttributeClass.Name == nameof(SuppressIldasmAttribute) &&
                                                                                       attribute.AttributeClass.ToNameDisplayString() == typeof(SuppressIldasmAttribute).FullName);
                    }

                    if (useDecompiler)
                    {
                        try
                        {
                            var decompiledSourceService = temporaryDocument.GetLanguageService <IDecompiledSourceService>();
                            if (decompiledSourceService != null)
                            {
                                temporaryDocument = await decompiledSourceService.AddSourceToAsync(temporaryDocument, compilation, symbol, cancellationToken).ConfigureAwait(false);
                            }
                            else
                            {
                                useDecompiler = false;
                            }
                        }
                        catch (Exception e) when(FatalError.ReportWithoutCrashUnlessCanceled(e))
                        {
                            useDecompiler = false;
                        }
                    }

                    if (!useDecompiler)
                    {
                        var sourceFromMetadataService = temporaryDocument.Project.LanguageServices.GetService <IMetadataAsSourceService>();
                        temporaryDocument = await sourceFromMetadataService.AddSourceToAsync(temporaryDocument, compilation, symbol, cancellationToken).ConfigureAwait(false);
                    }

                    // We have the content, so write it out to disk
                    var text = await temporaryDocument.GetTextAsync(cancellationToken).ConfigureAwait(false);

                    // Create the directory. It's possible a parallel deletion is happening in another process, so we may have
                    // to retry this a few times.
                    var directoryToCreate = Path.GetDirectoryName(fileInfo.TemporaryFilePath);
                    while (!Directory.Exists(directoryToCreate))
                    {
                        try
                        {
                            Directory.CreateDirectory(directoryToCreate);
                        }
                        catch (DirectoryNotFoundException)
                        {
                        }
                        catch (UnauthorizedAccessException)
                        {
                        }
                    }

                    using (var textWriter = new StreamWriter(fileInfo.TemporaryFilePath, append: false, encoding: fileInfo.Encoding))
                    {
                        text.Write(textWriter);
                    }

                    // Mark read-only
                    new FileInfo(fileInfo.TemporaryFilePath).IsReadOnly = true;

                    // Locate the target in the thing we just created
                    navigateLocation = await MetadataAsSourceHelpers.GetLocationInGeneratedSourceAsync(symbolId, temporaryDocument, cancellationToken).ConfigureAwait(false);
                }

                // If we don't have a location yet, then that means we're re-using an existing file. In this case, we'll want to relocate the symbol.
                if (navigateLocation == null)
                {
                    navigateLocation = await RelocateSymbol_NoLock(fileInfo, symbolId, cancellationToken).ConfigureAwait(false);
                }
            }

            var documentName = string.Format(
                "{0} [{1}]",
                topLevelNamedType.Name,
                EditorFeaturesResources.from_metadata);

            var documentTooltip = topLevelNamedType.ToDisplayString(new SymbolDisplayFormat(typeQualificationStyle: SymbolDisplayTypeQualificationStyle.NameAndContainingTypesAndNamespaces));

            return(new MetadataAsSourceFile(fileInfo.TemporaryFilePath, navigateLocation, documentName, documentTooltip));
        }
Ejemplo n.º 22
0
                    private async Task ResetStatesAsync()
                    {
                        try
                        {
                            if (!IsSolutionChanged())
                            {
                                return;
                            }

                            await Processor.RunAnalyzersAsync(
                                Analyzers,
                                Processor._registration.GetSolutionToAnalyze(),
                                workItem : new WorkItem(), (a, s, c) => a.NewSolutionSnapshotAsync(s, c), CancellationToken).ConfigureAwait(false);

                            foreach (var id in Processor.GetOpenDocumentIds())
                            {
                                AddHigherPriorityDocument(id);
                            }

                            SolutionCrawlerLogger.LogResetStates(Processor._logAggregator);
                        }
                        catch (Exception e) when(FatalError.ReportAndPropagateUnlessCanceled(e))
                        {
                            throw ExceptionUtilities.Unreachable;
                        }

                        bool IsSolutionChanged()
                        {
                            var currentSolution = Processor._registration.GetSolutionToAnalyze();
                            var oldSolution     = _lastSolution;

                            if (currentSolution == oldSolution)
                            {
                                return(false);
                            }

                            _lastSolution = currentSolution;

                            ResetLogAggregatorIfNeeded(currentSolution, oldSolution);

                            return(true);
                        }

                        void ResetLogAggregatorIfNeeded(Solution currentSolution, Solution?oldSolution)
                        {
                            if (oldSolution == null || currentSolution.Id == oldSolution.Id)
                            {
                                // we log aggregated info when solution is changed such as
                                // new solution is opened or solution is closed
                                return;
                            }

                            // this log things like how many time we analyzed active files, how many times other files are analyzed,
                            // avg time to analyze files, how many solution snapshot got analyzed and etc.
                            // all accumultation is done in VS side and we only send statistics to VS telemetry otherwise, it is too much
                            // data to send
                            SolutionCrawlerLogger.LogIncrementalAnalyzerProcessorStatistics(
                                Processor._registration.CorrelationId, oldSolution, Processor._logAggregator, Analyzers);

                            Processor.ResetLogAggregator();
                        }
                    }
Ejemplo n.º 23
0
        private void ProcessProjectChange(Solution solution, ProjectId projectId)
        {
            this.AssertIsForeground();

            // Remove anything we have associated with this project.
            _projectToInstalledPackageAndVersion.TryRemove(projectId, out var projectState);

            var project = solution.GetProject(projectId);

            if (project == null)
            {
                // Project was removed.  Nothing needs to be done.
                return;
            }

            // We really only need to know the NuGet status for managed language projects.
            // Also, the NuGet APIs may throw on some projects that don't implement the
            // full set of DTE APIs they expect.  So we filter down to just C# and VB here
            // as we know these languages are safe to build up this index for.
            if (project.Language != LanguageNames.CSharp &&
                project.Language != LanguageNames.VisualBasic)
            {
                return;
            }

            // Project was changed in some way.  Let's go find the set of installed packages for it.
            var dteProject = _workspace.TryGetDTEProject(projectId);

            if (dteProject == null)
            {
                // Don't have a DTE project for this project ID.  not something we can query NuGet for.
                return;
            }

            var installedPackages = new MultiDictionary <string, string>();
            var isEnabled         = false;

            // Calling into NuGet.  Assume they may fail for any reason.
            try
            {
                var installedPackageMetadata = _packageServices.GetInstalledPackages(dteProject);
                foreach (var metadata in installedPackageMetadata)
                {
                    if (metadata.VersionString != null)
                    {
                        installedPackages.Add(metadata.Id, metadata.VersionString);
                    }
                }

                isEnabled = true;
            }
            catch (ArgumentException e) when(IsKnownNugetIssue(e))
            {
                // Nuget may throw an ArgumentException when there is something about the project
                // they do not like/support.
            }
            catch (InvalidOperationException e) when(e.StackTrace.Contains("NuGet.PackageManagement.VisualStudio.NetCorePackageReferenceProject.GetPackageSpecsAsync"))
            {
                // NuGet throws an InvalidOperationException if details
                // for the project fail to load. We don't need to report
                // these, and can assume that this will work on a future
                // project change
                // This should be removed with https://github.com/dotnet/roslyn/issues/33187
            }
            catch (Exception e) when(FatalError.ReportWithoutCrash(e))
            {
            }

            var state = new ProjectState(isEnabled, installedPackages);

            _projectToInstalledPackageAndVersion.AddOrUpdate(
                projectId, state, (_1, _2) => state);
        }
Ejemplo n.º 24
0
        /// <summary>
        /// Computes an adds conflicts relating to declarations, which are independent of
        /// location-based checks. Examples of these types of conflicts include renaming a member to
        /// the same name as another member of a type: binding doesn't change (at least from the
        /// perspective of find all references), but we still need to track it.
        /// </summary>
        internal static async Task AddDeclarationConflictsAsync(
            ISymbol renamedSymbol,
            ISymbol renameSymbol,
            IEnumerable <SymbolAndProjectId> referencedSymbols,
            ConflictResolution conflictResolution,
            IDictionary <Location, Location> reverseMappedLocations,
            CancellationToken cancellationToken)
        {
            try
            {
                var project = conflictResolution.NewSolution.GetProject(renamedSymbol.ContainingAssembly, cancellationToken);

                if (renamedSymbol.ContainingSymbol.IsKind(SymbolKind.NamedType))
                {
                    var otherThingsNamedTheSame = renamedSymbol.ContainingType.GetMembers(renamedSymbol.Name)
                                                  .Where(s => !s.Equals(renamedSymbol) &&
                                                         string.Equals(s.MetadataName, renamedSymbol.MetadataName, StringComparison.Ordinal));

                    IEnumerable <ISymbol> otherThingsNamedTheSameExcludeMethodAndParameterizedProperty;

                    // Possibly overloaded symbols are excluded here and handled elsewhere
                    var semanticFactsService = project.LanguageServices.GetService <ISemanticFactsService>();
                    if (semanticFactsService.SupportsParameterizedProperties)
                    {
                        otherThingsNamedTheSameExcludeMethodAndParameterizedProperty = otherThingsNamedTheSame
                                                                                       .Where(s => !s.MatchesKind(SymbolKind.Method, SymbolKind.Property) ||
                                                                                              !renamedSymbol.MatchesKind(SymbolKind.Method, SymbolKind.Property));
                    }
                    else
                    {
                        otherThingsNamedTheSameExcludeMethodAndParameterizedProperty = otherThingsNamedTheSame
                                                                                       .Where(s => s.Kind != SymbolKind.Method || renamedSymbol.Kind != SymbolKind.Method);
                    }

                    AddConflictingSymbolLocations(otherThingsNamedTheSameExcludeMethodAndParameterizedProperty, conflictResolution, reverseMappedLocations);
                }


                if (renamedSymbol.IsKind(SymbolKind.Namespace) && renamedSymbol.ContainingSymbol.IsKind(SymbolKind.Namespace))
                {
                    var otherThingsNamedTheSame = ((INamespaceSymbol)renamedSymbol.ContainingSymbol).GetMembers(renamedSymbol.Name)
                                                  .Where(s => !s.Equals(renamedSymbol) &&
                                                         !s.IsKind(SymbolKind.Namespace) &&
                                                         string.Equals(s.MetadataName, renamedSymbol.MetadataName, StringComparison.Ordinal));

                    AddConflictingSymbolLocations(otherThingsNamedTheSame, conflictResolution, reverseMappedLocations);
                }

                if (renamedSymbol.IsKind(SymbolKind.NamedType) && renamedSymbol.ContainingSymbol is INamespaceOrTypeSymbol)
                {
                    var otherThingsNamedTheSame = ((INamespaceOrTypeSymbol)renamedSymbol.ContainingSymbol).GetMembers(renamedSymbol.Name)
                                                  .Where(s => !s.Equals(renamedSymbol) &&
                                                         string.Equals(s.MetadataName, renamedSymbol.MetadataName, StringComparison.Ordinal));

                    var conflictingSymbolLocations = otherThingsNamedTheSame.Where(s => !s.IsKind(SymbolKind.Namespace));
                    if (otherThingsNamedTheSame.Any(s => s.IsKind(SymbolKind.Namespace)))
                    {
                        conflictingSymbolLocations = conflictingSymbolLocations.Concat(renamedSymbol);
                    }

                    AddConflictingSymbolLocations(conflictingSymbolLocations, conflictResolution, reverseMappedLocations);
                }

                // Some types of symbols (namespaces, cref stuff, etc) might not have ContainingAssemblies
                if (renamedSymbol.ContainingAssembly != null)
                {
                    // There also might be language specific rules we need to include
                    var languageRenameService = project.LanguageServices.GetService <IRenameRewriterLanguageService>();
                    var languageConflicts     = await languageRenameService.ComputeDeclarationConflictsAsync(
                        conflictResolution.ReplacementText,
                        renamedSymbol,
                        renameSymbol,
                        referencedSymbols,
                        conflictResolution.OldSolution,
                        conflictResolution.NewSolution,
                        reverseMappedLocations,
                        cancellationToken).ConfigureAwait(false);

                    foreach (var languageConflict in languageConflicts)
                    {
                        conflictResolution.AddOrReplaceRelatedLocation(new RelatedLocation(languageConflict.SourceSpan, conflictResolution.OldSolution.GetDocument(languageConflict.SourceTree).Id, RelatedLocationType.UnresolvableConflict));
                    }
                }
            }
            catch (Exception e) when(FatalError.ReportUnlessCanceled(e))
            {
                // A NullReferenceException is happening in this method, but the dumps do not
                // contain information about this stack frame because this method is async and
                // therefore the exception filter in IdentifyConflictsAsync is insufficient.
                // See https://devdiv.visualstudio.com/DevDiv/_workitems?_a=edit&id=378642

                throw ExceptionUtilities.Unreachable;
            }
        }
Ejemplo n.º 25
0
        public async Task <ImmutableArray <Diagnostic> > GetDocumentDiagnosticsAsync(Document document, CancellationToken cancellationToken)
        {
            try
            {
                var debuggingSession = _debuggingSession;
                if (debuggingSession == null)
                {
                    return(ImmutableArray <Diagnostic> .Empty);
                }

                // Not a C# or VB project.
                var project = document.Project;
                if (!SupportsEditAndContinue(project))
                {
                    return(ImmutableArray <Diagnostic> .Empty);
                }

                // Document does not compile to the assembly (e.g. cshtml files, .g.cs files generated for completion only)
                if (IsDesignTimeOnlyDocument(document) || !document.SupportsSyntaxTree)
                {
                    return(ImmutableArray <Diagnostic> .Empty);
                }

                // Do not analyze documents (and report diagnostics) of projects that have not been built.
                // Allow user to make any changes in these documents, they won't be applied within the current debugging session.
                // Do not report the file read error - it might be an intermittent issue. The error will be reported when the
                // change is attempted to be applied.
                var(mvid, _) = await debuggingSession.GetProjectModuleIdAsync(project.Id, cancellationToken).ConfigureAwait(false);

                if (mvid == Guid.Empty)
                {
                    return(ImmutableArray <Diagnostic> .Empty);
                }

                var(oldDocument, oldDocumentState) = await debuggingSession.LastCommittedSolution.GetDocumentAndStateAsync(document.Id, cancellationToken).ConfigureAwait(false);

                if (oldDocumentState == CommittedSolution.DocumentState.OutOfSync ||
                    oldDocumentState == CommittedSolution.DocumentState.Indeterminate ||
                    oldDocumentState == CommittedSolution.DocumentState.DesignTimeOnly)
                {
                    // Do not report diagnostics for existing out-of-sync documents or design-time-only documents.
                    return(ImmutableArray <Diagnostic> .Empty);
                }

                // The document has not changed while the application is running since the last changes were committed:
                var editSession = _editSession;

                if (editSession == null)
                {
                    if (document == oldDocument)
                    {
                        return(ImmutableArray <Diagnostic> .Empty);
                    }

                    // Any changes made in loaded, built projects outside of edit session are rude edits (the application is running):
                    var newSyntaxTree = await document.GetSyntaxTreeAsync(cancellationToken).ConfigureAwait(false);

                    Contract.ThrowIfNull(newSyntaxTree);

                    var changedSpans = await GetChangedSpansAsync(oldDocument, newSyntaxTree, cancellationToken).ConfigureAwait(false);

                    return(GetRunModeDocumentDiagnostics(document, newSyntaxTree, changedSpans));
                }

                var analysis = await editSession.GetDocumentAnalysis(oldDocument, document).GetValueAsync(cancellationToken).ConfigureAwait(false);

                if (analysis.HasChanges)
                {
                    // Once we detected a change in a document let the debugger know that the corresponding loaded module
                    // is about to be updated, so that it can start initializing it for EnC update, reducing the amount of time applying
                    // the change blocks the UI when the user "continues".
                    debuggingSession.PrepareModuleForUpdate(mvid);
                }

                if (analysis.RudeEditErrors.IsEmpty)
                {
                    return(ImmutableArray <Diagnostic> .Empty);
                }

                editSession.Telemetry.LogRudeEditDiagnostics(analysis.RudeEditErrors);

                // track the document, so that we can refresh or clean diagnostics at the end of edit session:
                editSession.TrackDocumentWithReportedDiagnostics(document.Id);

                var tree = await document.GetSyntaxTreeAsync(cancellationToken).ConfigureAwait(false);

                return(analysis.RudeEditErrors.SelectAsArray((e, t) => e.ToDiagnostic(t), tree));
            }
            catch (Exception e) when(FatalError.ReportWithoutCrashUnlessCanceled(e))
            {
                return(ImmutableArray <Diagnostic> .Empty);
            }
        }
Ejemplo n.º 26
0
            private async Task <Model> ComputeModelInBackgroundAsync(
                int localRetriggerId,
                Model currentModel,
                ImmutableArray <ISignatureHelpProvider> providers,
                SnapshotPoint caretPosition,
                DisconnectedBufferGraph disconnectedBufferGraph,
                SignatureHelpTriggerInfo triggerInfo,
                CancellationToken cancellationToken)
            {
                try
                {
                    using (Logger.LogBlock(FunctionId.SignatureHelp_ModelComputation_ComputeModelInBackground, cancellationToken))
                    {
                        AssertIsBackground();
                        cancellationToken.ThrowIfCancellationRequested();

                        var document = await Controller.DocumentProvider.GetDocumentAsync(caretPosition.Snapshot, cancellationToken).ConfigureAwait(false);

                        if (document == null)
                        {
                            return(currentModel);
                        }

                        if (triggerInfo.TriggerReason == SignatureHelpTriggerReason.RetriggerCommand)
                        {
                            if (currentModel == null)
                            {
                                return(null);
                            }

                            if (triggerInfo.TriggerCharacter.HasValue &&
                                !currentModel.Provider.IsRetriggerCharacter(triggerInfo.TriggerCharacter.Value))
                            {
                                return(currentModel);
                            }
                        }

                        // first try to query the providers that can trigger on the specified character
                        var providerAndItemsOpt = await ComputeItemsAsync(
                            localRetriggerId, providers, caretPosition,
                            triggerInfo, document, cancellationToken).ConfigureAwait(false);

                        if (providerAndItemsOpt == null)
                        {
                            // Another retrigger was enqueued while we were inflight.  Just
                            // stop all work and return the last computed model.  We'll compute
                            // the correct model when we process the other retrigger task.
                            return(currentModel);
                        }

                        var(provider, items) = providerAndItemsOpt.Value;
                        if (provider == null)
                        {
                            // No provider produced items. So we can't produce a model
                            return(null);
                        }

                        if (currentModel != null &&
                            currentModel.Provider == provider &&
                            currentModel.GetCurrentSpanInSubjectBuffer(disconnectedBufferGraph.SubjectBufferSnapshot).Span.Start == items.ApplicableSpan.Start &&
                            currentModel.ArgumentIndex == items.ArgumentIndex &&
                            currentModel.ArgumentCount == items.ArgumentCount &&
                            currentModel.ArgumentName == items.ArgumentName)
                        {
                            // The new model is the same as the current model.  Return the currentModel
                            // so we keep the active selection.
                            return(currentModel);
                        }

                        var selectedItem = GetSelectedItem(currentModel, items, provider);
                        var model        = new Model(disconnectedBufferGraph, items.ApplicableSpan, provider,
                                                     items.Items, selectedItem, items.ArgumentIndex, items.ArgumentCount, items.ArgumentName,
                                                     selectedParameter: 0);

                        var syntaxFactsService = document.GetLanguageService <ISyntaxFactsService>();
                        var isCaseSensitive    = syntaxFactsService == null || syntaxFactsService.IsCaseSensitive;
                        var selection          = DefaultSignatureHelpSelector.GetSelection(model.Items,
                                                                                           model.SelectedItem, model.ArgumentIndex, model.ArgumentCount, model.ArgumentName, isCaseSensitive);

                        return(model.WithSelectedItem(selection.SelectedItem)
                               .WithSelectedParameter(selection.SelectedParameter));
                    }
                }
                catch (Exception e) when(FatalError.ReportUnlessCanceled(e))
                {
                    throw ExceptionUtilities.Unreachable;
                }
            }
Ejemplo n.º 27
0
        private static async Task <ImmutableDictionary <DiagnosticAnalyzer, DiagnosticAnalysisResult> > MergeProjectDiagnosticAnalyzerDiagnosticsAsync(
            Project project,
            ImmutableArray <DiagnosticAnalyzer> ideAnalyzers,
            Compilation?compilation,
            ImmutableDictionary <DiagnosticAnalyzer, DiagnosticAnalysisResult> result,
            CancellationToken cancellationToken)
        {
            try
            {
                var version = await GetDiagnosticVersionAsync(project, cancellationToken).ConfigureAwait(false);

                var(fileLoadAnalysisResult, failedDocuments) = await GetDocumentLoadFailuresAsync(project, version, cancellationToken).ConfigureAwait(false);

                result = result.SetItem(FileContentLoadAnalyzer.Instance, fileLoadAnalysisResult);

                foreach (var analyzer in ideAnalyzers)
                {
                    var builder = new DiagnosticAnalysisResultBuilder(project, version);

                    switch (analyzer)
                    {
                    case DocumentDiagnosticAnalyzer documentAnalyzer:
                        foreach (var document in project.Documents)
                        {
                            // don't analyze documents whose content failed to load
                            if (failedDocuments == null || !failedDocuments.Contains(document))
                            {
                                var tree = await document.GetSyntaxTreeAsync(cancellationToken).ConfigureAwait(false);

                                if (tree != null)
                                {
                                    builder.AddSyntaxDiagnostics(tree, await AnalyzerHelper.ComputeDocumentDiagnosticAnalyzerDiagnosticsAsync(documentAnalyzer, document, AnalysisKind.Syntax, compilation, cancellationToken).ConfigureAwait(false));
                                    builder.AddSemanticDiagnostics(tree, await AnalyzerHelper.ComputeDocumentDiagnosticAnalyzerDiagnosticsAsync(documentAnalyzer, document, AnalysisKind.Semantic, compilation, cancellationToken).ConfigureAwait(false));
                                }
                                else
                                {
                                    builder.AddExternalSyntaxDiagnostics(document.Id, await AnalyzerHelper.ComputeDocumentDiagnosticAnalyzerDiagnosticsAsync(documentAnalyzer, document, AnalysisKind.Syntax, compilation, cancellationToken).ConfigureAwait(false));
                                    builder.AddExternalSemanticDiagnostics(document.Id, await AnalyzerHelper.ComputeDocumentDiagnosticAnalyzerDiagnosticsAsync(documentAnalyzer, document, AnalysisKind.Semantic, compilation, cancellationToken).ConfigureAwait(false));
                                }
                            }
                        }

                        break;

                    case ProjectDiagnosticAnalyzer projectAnalyzer:
                        builder.AddCompilationDiagnostics(await AnalyzerHelper.ComputeProjectDiagnosticAnalyzerDiagnosticsAsync(projectAnalyzer, project, compilation, cancellationToken).ConfigureAwait(false));
                        break;
                    }

                    // merge the result to existing one.
                    // there can be existing one from compiler driver with empty set. overwrite it with
                    // ide one.
                    result = result.SetItem(analyzer, DiagnosticAnalysisResult.CreateFromBuilder(builder));
                }

                return(result);
            }
            catch (Exception e) when(FatalError.ReportAndPropagateUnlessCanceled(e, cancellationToken))
            {
                throw ExceptionUtilities.Unreachable;
            }
        }
Ejemplo n.º 28
0
        public async Task <bool> ApplyAsync(
            Workspace workspace, Document?fromDocument,
            ImmutableArray <CodeActionOperation> operations,
            string title, IProgressTracker progressTracker,
            CancellationToken cancellationToken)
        {
            // Much of the work we're going to do will be on the UI thread, so switch there preemptively.
            // When we get to the expensive parts we can do in the BG then we'll switch over to relinquish
            // the UI thread.
            await this.ThreadingContext.JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken);

            if (operations.IsDefaultOrEmpty)
            {
                return(_renameService.ActiveSession is null);
            }

            if (_renameService.ActiveSession != null)
            {
                workspace.Services.GetService <INotificationService>()?.SendNotification(
                    EditorFeaturesResources.Cannot_apply_operation_while_a_rename_session_is_active,
                    severity: NotificationSeverity.Error);
                return(false);
            }

            var oldSolution = workspace.CurrentSolution;

            var applied = false;

            // Determine if we're making a simple text edit to a single file or not.
            // If we're not, then we need to make a linked global undo to wrap the
            // application of these operations.  This way we should be able to undo
            // them all with one user action.
            //
            // The reason we don't always create a global undo is that a global undo
            // forces all files to save.  And that's rather a heavyweight and
            // unexpected experience for users (for the common case where a single
            // file got edited).
            var singleChangedDocument = TryGetSingleChangedText(oldSolution, operations);

            if (singleChangedDocument != null)
            {
                var text = await singleChangedDocument.GetTextAsync(cancellationToken).ConfigureAwait(true);

                using (workspace.Services.GetRequiredService <ISourceTextUndoService>().RegisterUndoTransaction(text, title))
                {
                    try
                    {
                        this.AssertIsForeground();

                        applied = await operations.Single().TryApplyAsync(
                            workspace, progressTracker, cancellationToken).ConfigureAwait(true);
                    }
                    catch (Exception ex) when(FatalError.ReportAndPropagateUnlessCanceled(ex, cancellationToken))
                    {
                        throw ExceptionUtilities.Unreachable;
                    }
                }
            }
            else
            {
                // More than just a single document changed.  Make a global undo to run
                // all the changes under.
                using var transaction = workspace.OpenGlobalUndoTransaction(title);

                // link current file in the global undo transaction
                // Do this before processing operations, since that can change
                // documentIds.
                if (fromDocument != null)
                {
                    transaction.AddDocument(fromDocument.Id);
                }

                try
                {
                    // Come back to the UI thread after processing the operations so we can commit the transaction
                    applied = await ProcessOperationsAsync(
                        workspace, operations, progressTracker,
                        cancellationToken).ConfigureAwait(true);
                }
                catch (Exception ex) when(FatalError.ReportAndPropagateUnlessCanceled(ex, cancellationToken))
                {
                    throw ExceptionUtilities.Unreachable;
                }

                transaction.Commit();
            }

            var updatedSolution = operations.OfType <ApplyChangesOperation>().FirstOrDefault()?.ChangedSolution ?? oldSolution;

            await TryNavigateToLocationOrStartRenameSessionAsync(
                workspace, oldSolution, updatedSolution, cancellationToken).ConfigureAwait(false);

            return(applied);
        }
        public override async Task ProvideCompletionsAsync(CompletionContext context)
        {
            try
            {
                var document          = context.Document;
                var position          = context.Position;
                var options           = context.Options;
                var cancellationToken = context.CancellationToken;

                var span          = new TextSpan(position, length: 0);
                var semanticModel = await document.GetSemanticModelForSpanAsync(span, cancellationToken).ConfigureAwait(false);

                var syntaxTree = semanticModel.SyntaxTree;

                var syntaxFacts   = document.GetLanguageService <ISyntaxFactsService>();
                var semanticFacts = document.GetLanguageService <ISemanticFactsService>();

                if (syntaxFacts.IsInNonUserCode(syntaxTree, position, cancellationToken) ||
                    semanticFacts.IsPreProcessorDirectiveContext(semanticModel, position, cancellationToken))
                {
                    return;
                }

                var targetToken = syntaxTree.FindTokenOnLeftOfPosition(position, cancellationToken)
                                  .GetPreviousTokenIfTouchingWord(position);

                if (!syntaxTree.IsRightOfDotOrArrowOrColonColon(position, targetToken, cancellationToken))
                {
                    return;
                }

                var node = targetToken.Parent;

                if (node.Kind() != SyntaxKind.ExplicitInterfaceSpecifier)
                {
                    return;
                }

                // Bind the interface name which is to the left of the dot
                var name = ((ExplicitInterfaceSpecifierSyntax)node).Name;

                var symbol = semanticModel.GetSymbolInfo(name, cancellationToken).Symbol as ITypeSymbol;
                if (symbol?.TypeKind != TypeKind.Interface)
                {
                    return;
                }

                var members = symbol.GetMembers();

                // We're going to create a entry for each one, including the signature
                var namePosition = name.SpanStart;

                var text = await syntaxTree.GetTextAsync(cancellationToken).ConfigureAwait(false);

                foreach (var member in members)
                {
                    if (member.IsAccessor() || member.Kind == SymbolKind.NamedType || !(member.IsAbstract || member.IsVirtual) ||
                        !semanticModel.IsAccessible(node.SpanStart, member))
                    {
                        continue;
                    }

                    var displayText = member.ToMinimalDisplayString(
                        semanticModel, namePosition, s_signatureDisplayFormat);
                    var insertionText = displayText;

                    var item = SymbolCompletionItem.CreateWithSymbolId(
                        displayText,
                        displayTextSuffix: "",
                        insertionText: insertionText,
                        symbols: ImmutableArray.Create(member),
                        contextPosition: position,
                        rules: CompletionItemRules.Default);
                    item = item.AddProperty(InsertionTextOnOpenParen, member.Name);

                    context.AddItem(item);
                }
            }
            catch (Exception e) when(FatalError.ReportWithoutCrashUnlessCanceled(e))
            {
                // nop
            }
        }
Ejemplo n.º 30
0
        public async Task <ProjectAnalysisSummary> GetProjectAnalysisSummaryAsync(Project project, CancellationToken cancellationToken)
        {
            try
            {
                var baseProject = _baseSolution.GetProject(project.Id);

                // TODO (https://github.com/dotnet/roslyn/issues/1204):
                if (baseProject == null)
                {
                    return(ProjectAnalysisSummary.NoChanges);
                }

                var documentAnalyses = GetChangedDocumentsAnalyses(baseProject, project);
                if (documentAnalyses.Count == 0)
                {
                    return(ProjectAnalysisSummary.NoChanges);
                }

                bool hasChanges            = false;
                bool hasSignificantChanges = false;

                foreach (var analysis in documentAnalyses)
                {
                    var result = await analysis.Item2.GetValueAsync(cancellationToken).ConfigureAwait(false);

                    // skip documents that actually were not changed:
                    if (!result.HasChanges)
                    {
                        continue;
                    }

                    // rude edit detection wasn't completed due to errors in compilation:
                    if (result.HasChangesAndCompilationErrors)
                    {
                        return(ProjectAnalysisSummary.CompilationErrors);
                    }

                    // rude edits detected:
                    if (result.RudeEditErrors.Length != 0)
                    {
                        return(ProjectAnalysisSummary.RudeEdits);
                    }

                    hasChanges             = true;
                    hasSignificantChanges |= result.HasSignificantChanges;
                }

                if (!hasChanges)
                {
                    // we get here if a document is closed and reopen without any actual change:
                    return(ProjectAnalysisSummary.NoChanges);
                }

                if (_stoppedAtException)
                {
                    // all edits are disallowed when stopped at exception:
                    return(ProjectAnalysisSummary.RudeEdits);
                }

                return(hasSignificantChanges ?
                       ProjectAnalysisSummary.ValidChanges :
                       ProjectAnalysisSummary.ValidInsignificantChanges);
            }
            catch (Exception e) when(FatalError.ReportUnlessCanceled(e))
            {
                throw ExceptionUtilities.Unreachable;
            }
        }