private IEnumerable <DiagnosticData> ConvertToLocalDiagnosticsWithCompilation(Document targetDocument, IEnumerable <Diagnostic> diagnostics, TextSpan?span = null) { var project = targetDocument.Project; Contract.ThrowIfFalse(project.SupportsCompilation); foreach (var diagnostic in diagnostics) { var document = project.GetDocument(diagnostic.Location.SourceTree); if (document == null || document != targetDocument) { continue; } if (span.HasValue && !span.Value.Contains(diagnostic.Location.SourceSpan)) { continue; } yield return(DiagnosticData.Create(document, diagnostic)); } }
internal DiagnosticData GetPrimaryDiagnosticData() { var diagnostic = PrimaryDiagnostic; if (diagnostic.Location.IsInSource) { var document = Project.GetDocument(diagnostic.Location.SourceTree); if (document != null) { return(DiagnosticData.Create(diagnostic, document)); } } else if (diagnostic.Location.Kind == LocationKind.ExternalFile) { var document = Project.Documents.FirstOrDefault(d => d.FilePath == diagnostic.Location.GetLineSpan().Path); if (document != null) { return(DiagnosticData.Create(diagnostic, document)); } } return(DiagnosticData.Create(diagnostic, Project)); }
private void AddDiagnostics( ref Dictionary <DocumentId, List <DiagnosticData> > lazyLocals, SyntaxTree tree, IEnumerable <Diagnostic> diagnostics) { foreach (var diagnostic in diagnostics) { // REVIEW: what is our plan for additional locations? switch (diagnostic.Location.Kind) { case LocationKind.ExternalFile: { // TODO: currently additional file location is not supported. break; } case LocationKind.None: { _lazyOthers = _lazyOthers ?? new List <DiagnosticData>(); _lazyOthers.Add(DiagnosticData.Create(_project, diagnostic)); break; } case LocationKind.SourceFile: { if (tree != null && diagnostic.Location.SourceTree == tree) { var document = GetDocument(diagnostic); if (document != null) { // local diagnostics to a file lazyLocals = lazyLocals ?? new Dictionary <DocumentId, List <DiagnosticData> >(); lazyLocals.GetOrAdd(document.Id, _ => new List <DiagnosticData>()).Add(DiagnosticData.Create(document, diagnostic)); AddDocumentToSet(document); } } else if (diagnostic.Location.SourceTree != null) { var document = _project.GetDocument(diagnostic.Location.SourceTree); if (document != null) { // non local diagnostics to a file _lazyNonLocals = _lazyNonLocals ?? new Dictionary <DocumentId, List <DiagnosticData> >(); _lazyNonLocals.GetOrAdd(document.Id, _ => new List <DiagnosticData>()).Add(DiagnosticData.Create(document, diagnostic)); AddDocumentToSet(document); } } else { // non local diagnostics without location _lazyOthers = _lazyOthers ?? new List <DiagnosticData>(); _lazyOthers.Add(DiagnosticData.Create(_project, diagnostic)); } break; } case LocationKind.MetadataFile: case LocationKind.XmlFile: { // something we don't care continue; } default: { Contract.Fail("should not reach"); break; } } } }
private void AddExternalDiagnostics( ref Dictionary <DocumentId, List <DiagnosticData> > lazyLocals, DocumentId documentId, IEnumerable <Diagnostic> diagnostics) { Contract.ThrowIfTrue(_project.SupportsCompilation); foreach (var diagnostic in diagnostics) { // REVIEW: what is our plan for additional locations? switch (diagnostic.Location.Kind) { case LocationKind.ExternalFile: { var diagnosticDocumentId = GetExternalDocumentId(diagnostic); if (documentId == diagnosticDocumentId) { var document = _project.GetDocument(diagnosticDocumentId); if (document != null) { // local diagnostics to a file lazyLocals = lazyLocals ?? new Dictionary <DocumentId, List <DiagnosticData> >(); lazyLocals.GetOrAdd(document.Id, _ => new List <DiagnosticData>()).Add(DiagnosticData.Create(document, diagnostic)); AddDocumentToSet(document); } } else if (diagnosticDocumentId != null) { var document = _project.GetDocument(diagnosticDocumentId); if (document != null) { // non local diagnostics to a file _lazyNonLocals = _lazyNonLocals ?? new Dictionary <DocumentId, List <DiagnosticData> >(); _lazyNonLocals.GetOrAdd(document.Id, _ => new List <DiagnosticData>()).Add(DiagnosticData.Create(document, diagnostic)); AddDocumentToSet(document); } } else { // non local diagnostics without location _lazyOthers = _lazyOthers ?? new List <DiagnosticData>(); _lazyOthers.Add(DiagnosticData.Create(_project, diagnostic)); } break; } case LocationKind.None: { _lazyOthers = _lazyOthers ?? new List <DiagnosticData>(); _lazyOthers.Add(DiagnosticData.Create(_project, diagnostic)); break; } case LocationKind.SourceFile: case LocationKind.MetadataFile: case LocationKind.XmlFile: { // something we don't care continue; } default: { Contract.Fail("should not reach"); break; } } } }
private static IEnumerable <DiagnosticData> GetDiagnosticData(Document document, SyntaxTree tree, TextSpan?span, IEnumerable <Diagnostic> diagnostics) { return(diagnostics != null?diagnostics.Where(dx => ShouldIncludeDiagnostic(dx, tree, span)).Select(d => DiagnosticData.Create(document, d)) : null); }
public async Task AnalyzeSyntaxAsync(Document document, CancellationToken cancellationToken) { // if closed file diagnostic is off and document is not opened, then don't do anything if (!_workspace.Options.GetOption(ServiceFeatureOnOffOptions.ClosedFileDiagnostic, document.Project.Language) && !document.IsOpen()) { return; } var tree = await document.GetSyntaxTreeAsync(cancellationToken).ConfigureAwait(false); var diagnostics = tree.GetDiagnostics(cancellationToken); Contract.Requires(document.Project.Solution.Workspace == _workspace); var diagnosticData = diagnostics == null ? ImmutableArray <DiagnosticData> .Empty : diagnostics.Select(d => DiagnosticData.Create(document, d)).ToImmutableArrayOrEmpty(); _service.RaiseDiagnosticsUpdated( new DiagnosticsUpdatedArgs(new MiscUpdateArgsId(document.Id), _workspace, document.Project.Solution, document.Project.Id, document.Id, diagnosticData)); }
DiagnosticData?syntaxError)> EmitSolutionUpdateAsync( Solution solution, ActiveStatementSpanProvider activeStatementSpanProvider, IDiagnosticAnalyzerService diagnosticService, EditAndContinueDiagnosticUpdateSource diagnosticUpdateSource, CancellationToken cancellationToken) { ManagedModuleUpdates moduleUpdates; ImmutableArray <DiagnosticData> diagnosticData; ImmutableArray <(DocumentId DocumentId, ImmutableArray <RudeEditDiagnostic> Diagnostics)> rudeEdits; DiagnosticData?syntaxError; try { var client = await RemoteHostClient.TryGetClientAsync(_workspace, cancellationToken).ConfigureAwait(false); if (client == null) { var results = await GetLocalService().EmitSolutionUpdateAsync(_sessionId, solution, activeStatementSpanProvider, cancellationToken).ConfigureAwait(false); moduleUpdates = results.ModuleUpdates; diagnosticData = results.GetDiagnosticData(solution); rudeEdits = results.RudeEdits; syntaxError = results.GetSyntaxErrorData(solution); } else { var result = await client.TryInvokeAsync <IRemoteEditAndContinueService, EmitSolutionUpdateResults.Data>( solution, (service, solutionInfo, callbackId, cancellationToken) => service.EmitSolutionUpdateAsync(solutionInfo, callbackId, _sessionId, cancellationToken), callbackTarget : new ActiveStatementSpanProviderCallback(activeStatementSpanProvider), cancellationToken).ConfigureAwait(false); if (result.HasValue) { moduleUpdates = result.Value.ModuleUpdates; diagnosticData = result.Value.Diagnostics; rudeEdits = result.Value.RudeEdits; syntaxError = result.Value.SyntaxError; } else { moduleUpdates = new ManagedModuleUpdates(ManagedModuleUpdateStatus.RestartRequired, ImmutableArray <ManagedModuleUpdate> .Empty); diagnosticData = ImmutableArray <DiagnosticData> .Empty; rudeEdits = ImmutableArray <(DocumentId DocumentId, ImmutableArray <RudeEditDiagnostic> Diagnostics)> .Empty; syntaxError = null; } } } catch (Exception e) when(FatalError.ReportAndCatchUnlessCanceled(e, cancellationToken)) { var descriptor = EditAndContinueDiagnosticDescriptors.GetDescriptor(RudeEditKind.InternalError); var diagnostic = Diagnostic.Create( descriptor, Location.None, string.Format(descriptor.MessageFormat.ToString(), "", e.Message)); diagnosticData = ImmutableArray.Create(DiagnosticData.Create(diagnostic, project: null)); rudeEdits = ImmutableArray <(DocumentId DocumentId, ImmutableArray <RudeEditDiagnostic> Diagnostics)> .Empty; moduleUpdates = new ManagedModuleUpdates(ManagedModuleUpdateStatus.RestartRequired, ImmutableArray <ManagedModuleUpdate> .Empty); syntaxError = null; } // clear emit/apply diagnostics reported previously: diagnosticUpdateSource.ClearDiagnostics(isSessionEnding: false); // clear all reported rude edits: diagnosticService.Reanalyze(_workspace, documentIds: rudeEdits.Select(d => d.DocumentId)); // report emit/apply diagnostics: diagnosticUpdateSource.ReportDiagnostics(_workspace, solution, diagnosticData, rudeEdits); return(moduleUpdates, diagnosticData, rudeEdits, syntaxError); }
/// <summary> /// Gets <see cref="DiagnosticData"/> objects for error list entries, filtered based on the given parameters. /// </summary> public async Task <ImmutableArray <DiagnosticData> > GetItemsAsync(bool selectedEntriesOnly, bool isAddSuppression, bool isSuppressionInSource, bool onlyCompilerDiagnostics, CancellationToken cancellationToken) { var builder = ImmutableArray.CreateBuilder <DiagnosticData>(); Dictionary <string, Project> projectNameToProjectMapOpt = null; Dictionary <Project, ImmutableDictionary <string, Document> > filePathToDocumentMapOpt = null; var entries = selectedEntriesOnly ? _tableControl.SelectedEntries : _tableControl.Entries; foreach (var entryHandle in entries) { cancellationToken.ThrowIfCancellationRequested(); DiagnosticData diagnosticData = null; int index; var roslynSnapshot = GetEntriesSnapshot(entryHandle, out index); if (roslynSnapshot != null) { diagnosticData = roslynSnapshot.GetItem(index)?.Primary; } else if (!isAddSuppression) { // For suppression removal, we also need to handle FxCop entries. bool isSuppressedEntry; if (!IsNonRoslynEntrySupportingSuppressionState(entryHandle, out isSuppressedEntry) || !isSuppressedEntry) { continue; } string errorCode = null, category = null, message = null, filePath = null, projectName = null; int line = -1; // FxCop only supports line, not column. var location = Location.None; if (entryHandle.TryGetValue(StandardTableColumnDefinitions.ErrorCode, out errorCode) && !string.IsNullOrEmpty(errorCode) && entryHandle.TryGetValue(StandardTableColumnDefinitions.ErrorCategory, out category) && !string.IsNullOrEmpty(category) && entryHandle.TryGetValue(StandardTableColumnDefinitions.Text, out message) && !string.IsNullOrEmpty(message) && entryHandle.TryGetValue(StandardTableColumnDefinitions.ProjectName, out projectName) && !string.IsNullOrEmpty(projectName)) { if (projectNameToProjectMapOpt == null) { projectNameToProjectMapOpt = new Dictionary <string, Project>(); foreach (var p in _workspace.CurrentSolution.Projects) { projectNameToProjectMapOpt[p.Name] = p; } } cancellationToken.ThrowIfCancellationRequested(); Project project; if (!projectNameToProjectMapOpt.TryGetValue(projectName, out project)) { // bail out continue; } Document document = null; var hasLocation = (entryHandle.TryGetValue(StandardTableColumnDefinitions.DocumentName, out filePath) && !string.IsNullOrEmpty(filePath)) && (entryHandle.TryGetValue(StandardTableColumnDefinitions.Line, out line) && line >= 0); if (hasLocation) { if (string.IsNullOrEmpty(filePath) || line < 0) { // bail out continue; } ImmutableDictionary <string, Document> filePathMap; filePathToDocumentMapOpt = filePathToDocumentMapOpt ?? new Dictionary <Project, ImmutableDictionary <string, Document> >(); if (!filePathToDocumentMapOpt.TryGetValue(project, out filePathMap)) { filePathMap = await GetFilePathToDocumentMapAsync(project, cancellationToken).ConfigureAwait(false); filePathToDocumentMapOpt[project] = filePathMap; } if (!filePathMap.TryGetValue(filePath, out document)) { // bail out continue; } var tree = await document.GetSyntaxTreeAsync(cancellationToken).ConfigureAwait(false); var linePosition = new LinePosition(line, 0); var linePositionSpan = new LinePositionSpan(start: linePosition, end: linePosition); var textSpan = (await tree.GetTextAsync(cancellationToken).ConfigureAwait(false)).Lines.GetTextSpan(linePositionSpan); location = tree.GetLocation(textSpan); } Contract.ThrowIfNull(project); Contract.ThrowIfFalse((document != null) == location.IsInSource); // Create a diagnostic with correct values for fields we care about: id, category, message, isSuppressed, location // and default values for the rest of the fields (not used by suppression fixer). var diagnostic = Diagnostic.Create( id: errorCode, category: category, message: message, severity: DiagnosticSeverity.Warning, defaultSeverity: DiagnosticSeverity.Warning, isEnabledByDefault: true, warningLevel: 1, isSuppressed: isSuppressedEntry, title: message, location: location, customTags: SuppressionHelpers.SynthesizedExternalSourceDiagnosticCustomTags); diagnosticData = document != null? DiagnosticData.Create(document, diagnostic) : DiagnosticData.Create(project, diagnostic); } } if (IsEntryWithConfigurableSuppressionState(diagnosticData)) { var isCompilerDiagnostic = SuppressionHelpers.IsCompilerDiagnostic(diagnosticData); if (onlyCompilerDiagnostics && !isCompilerDiagnostic) { continue; } if (isAddSuppression) { // Compiler diagnostics can only be suppressed in source. if (!diagnosticData.IsSuppressed && (isSuppressionInSource || !isCompilerDiagnostic)) { builder.Add(diagnosticData); } } else if (diagnosticData.IsSuppressed) { builder.Add(diagnosticData); } } } return(builder.ToImmutable()); }
private static IEnumerable <DiagnosticData> GetDiagnosticData(Project project, IEnumerable <Diagnostic> diagnostics) { return(diagnostics != null?diagnostics.Select(d => DiagnosticData.Create(project, d)) : null); }
public async Task Proxy(TestHost testHost) { var localComposition = EditorTestCompositions.EditorFeatures.WithTestHostParts(testHost); if (testHost == TestHost.InProcess) { localComposition = localComposition.AddParts(typeof(MockEncServiceFactory)); } using var localWorkspace = new TestWorkspace(composition: localComposition); MockEditAndContinueWorkspaceService mockEncService; var clientProvider = (InProcRemoteHostClientProvider?)localWorkspace.Services.GetService <IRemoteHostClientProvider>(); if (testHost == TestHost.InProcess) { Assert.Null(clientProvider); mockEncService = (MockEditAndContinueWorkspaceService)localWorkspace.Services.GetRequiredService <IEditAndContinueWorkspaceService>(); } else { Assert.NotNull(clientProvider); clientProvider !.AdditionalRemoteParts = new[] { typeof(MockEncServiceFactory) }; var client = await InProcRemoteHostClient.GetTestClientAsync(localWorkspace).ConfigureAwait(false); var remoteWorkspace = client.TestData.WorkspaceManager.GetWorkspace(); mockEncService = (MockEditAndContinueWorkspaceService)remoteWorkspace.Services.GetRequiredService <IEditAndContinueWorkspaceService>(); } localWorkspace.ChangeSolution(localWorkspace.CurrentSolution. AddProject("proj", "proj", LanguageNames.CSharp). AddMetadataReferences(TargetFrameworkUtil.GetReferences(TargetFramework.Mscorlib40)). AddDocument("test.cs", SourceText.From("class C { }", Encoding.UTF8), filePath: "test.cs").Project.Solution); var solution = localWorkspace.CurrentSolution; var project = solution.Projects.Single(); var document = project.Documents.Single(); var mockDiagnosticService = new Mock <IDiagnosticAnalyzerService>(MockBehavior.Strict); mockDiagnosticService.Setup(s => s.Reanalyze(It.IsAny <Workspace>(), It.IsAny <IEnumerable <ProjectId> >(), It.IsAny <IEnumerable <DocumentId> >(), It.IsAny <bool>())); void VerifyReanalyzeInvocation(ImmutableArray <DocumentId> documentIds) => mockDiagnosticService.Invocations.VerifyAndClear((nameof(IDiagnosticAnalyzerService.Reanalyze), new object?[] { localWorkspace, null, documentIds, false })); var diagnosticUpdateSource = new EditAndContinueDiagnosticUpdateSource(); var emitDiagnosticsUpdated = new List <DiagnosticsUpdatedArgs>(); var emitDiagnosticsClearedCount = 0; diagnosticUpdateSource.DiagnosticsUpdated += (object sender, DiagnosticsUpdatedArgs args) => emitDiagnosticsUpdated.Add(args); diagnosticUpdateSource.DiagnosticsCleared += (object sender, EventArgs args) => emitDiagnosticsClearedCount++; var span1 = new LinePositionSpan(new LinePosition(1, 2), new LinePosition(1, 5)); var moduleId1 = new Guid("{44444444-1111-1111-1111-111111111111}"); var methodId1 = new ManagedMethodId(moduleId1, token: 0x06000003, version: 2); var instructionId1 = new ManagedInstructionId(methodId1, ilOffset: 10); var as1 = new ManagedActiveStatementDebugInfo( instructionId1, documentName: "test.cs", span1.ToSourceSpan(), flags: ActiveStatementFlags.IsLeafFrame | ActiveStatementFlags.PartiallyExecuted); var methodId2 = new ManagedModuleMethodId(token: 0x06000002, version: 1); var exceptionRegionUpdate1 = new ManagedExceptionRegionUpdate( methodId2, delta: 1, newSpan: new SourceSpan(1, 2, 1, 5)); var document1 = localWorkspace.CurrentSolution.Projects.Single().Documents.Single(); var activeSpans1 = ImmutableArray.Create(TextSpan.FromBounds(1, 2), TextSpan.FromBounds(3, 4)); var solutionActiveStatementSpanProvider = new SolutionActiveStatementSpanProvider((documentId, cancellationToken) => { Assert.Equal(document1.Id, documentId); return(new(activeSpans1)); }); var documentActiveStatementSpanProvider = new DocumentActiveStatementSpanProvider(cancellationToken => new(activeSpans1)); var proxy = new RemoteEditAndContinueServiceProxy(localWorkspace); // StartDebuggingSession var called = false; mockEncService.StartDebuggingSessionImpl = solution => { Assert.Equal("proj", solution.Projects.Single().Name); called = true; }; await proxy.StartDebuggingSessionAsync(localWorkspace.CurrentSolution, CancellationToken.None).ConfigureAwait(false); Assert.True(called); // StartEditSession IManagedEditAndContinueDebuggerService?remoteDebuggeeModuleMetadataProvider = null; mockEncService.StartEditSessionImpl = (IManagedEditAndContinueDebuggerService debuggerService, out ImmutableArray <DocumentId> documentsToReanalyze) => { remoteDebuggeeModuleMetadataProvider = debuggerService; documentsToReanalyze = ImmutableArray <DocumentId> .Empty; }; await proxy.StartEditSessionAsync( mockDiagnosticService.Object, debuggerService : new MockManagedEditAndContinueDebuggerService() { IsEditAndContinueAvailable = _ => new ManagedEditAndContinueAvailability(ManagedEditAndContinueAvailabilityStatus.NotAllowedForModule, "can't do enc"), GetActiveStatementsImpl = () => ImmutableArray.Create(as1) }, CancellationToken.None).ConfigureAwait(false); VerifyReanalyzeInvocation(ImmutableArray <DocumentId> .Empty); var activeStatement = (await remoteDebuggeeModuleMetadataProvider !.GetActiveStatementsAsync(CancellationToken.None).ConfigureAwait(false)).Single(); Assert.Equal(as1.ActiveInstruction, activeStatement.ActiveInstruction); Assert.Equal(as1.SourceSpan, activeStatement.SourceSpan); Assert.Equal(as1.Flags, activeStatement.Flags); var availability = await remoteDebuggeeModuleMetadataProvider !.GetAvailabilityAsync(moduleId1, CancellationToken.None).ConfigureAwait(false); Assert.Equal(new ManagedEditAndContinueAvailability(ManagedEditAndContinueAvailabilityStatus.NotAllowedForModule, "can't do enc"), availability); // EndEditSession mockEncService.EndEditSessionImpl = (out ImmutableArray <DocumentId> documentsToReanalyze) => { documentsToReanalyze = ImmutableArray.Create(document.Id); }; await proxy.EndEditSessionAsync(mockDiagnosticService.Object, CancellationToken.None).ConfigureAwait(false); VerifyReanalyzeInvocation(ImmutableArray.Create(document.Id)); // EndDebuggingSession mockEncService.EndDebuggingSessionImpl = (out ImmutableArray <DocumentId> documentsToReanalyze) => { documentsToReanalyze = ImmutableArray.Create(document.Id); }; await proxy.EndDebuggingSessionAsync(diagnosticUpdateSource, mockDiagnosticService.Object, CancellationToken.None).ConfigureAwait(false); VerifyReanalyzeInvocation(ImmutableArray.Create(document.Id)); Assert.Equal(1, emitDiagnosticsClearedCount); emitDiagnosticsClearedCount = 0; Assert.Empty(emitDiagnosticsUpdated); // HasChanges mockEncService.HasChangesImpl = (solution, activeStatementSpanProvider, sourceFilePath) => { Assert.Equal("proj", solution.Projects.Single().Name); Assert.Equal("test.cs", sourceFilePath); AssertEx.Equal(activeSpans1, activeStatementSpanProvider(document1.Id, CancellationToken.None).Result); return(true); }; Assert.True(await proxy.HasChangesAsync(localWorkspace.CurrentSolution, solutionActiveStatementSpanProvider, "test.cs", CancellationToken.None).ConfigureAwait(false)); // EmitSolutionUpdate var diagnosticDescriptor1 = EditAndContinueDiagnosticDescriptors.GetDescriptor(EditAndContinueErrorCode.ErrorReadingFile); mockEncService.EmitSolutionUpdateImpl = (solution, activeStatementSpanProvider) => { var project = solution.Projects.Single(); Assert.Equal("proj", project.Name); AssertEx.Equal(activeSpans1, activeStatementSpanProvider(document1.Id, CancellationToken.None).Result); var deltas = ImmutableArray.Create(new ManagedModuleUpdate( module: moduleId1, ilDelta: ImmutableArray.Create <byte>(1, 2), metadataDelta: ImmutableArray.Create <byte>(3, 4), pdbDelta: ImmutableArray.Create <byte>(5, 6), updatedMethods: ImmutableArray.Create(0x06000001), sequencePoints: ImmutableArray.Create(new SequencePointUpdates("file.cs", ImmutableArray.Create(new SourceLineUpdate(1, 2)))), activeStatements: ImmutableArray.Create(new ManagedActiveStatementUpdate(instructionId1.Method.Method, instructionId1.ILOffset, span1.ToSourceSpan())), exceptionRegions: ImmutableArray.Create(exceptionRegionUpdate1))); var syntaxTree = project.Documents.Single().GetSyntaxTreeSynchronously(CancellationToken.None) !; var documentDiagnostic = DiagnosticData.Create(Diagnostic.Create(diagnosticDescriptor1, Location.Create(syntaxTree, TextSpan.FromBounds(1, 2)), new[] { "doc", "some error" }), document); var projectDiagnostic = DiagnosticData.Create(Diagnostic.Create(diagnosticDescriptor1, Location.None, new[] { "proj", "some error" }), project); var solutionDiagnostic = DiagnosticData.Create(Diagnostic.Create(diagnosticDescriptor1, Location.None, new[] { "sol", "some error" }), solution.Options); return(new(ManagedModuleUpdateStatus.Ready, deltas), ImmutableArray.Create(documentDiagnostic, projectDiagnostic, solutionDiagnostic)); }; var updates = await proxy.EmitSolutionUpdateAsync(localWorkspace.CurrentSolution, solutionActiveStatementSpanProvider, diagnosticUpdateSource, CancellationToken.None).ConfigureAwait(false); Assert.Equal(ManagedModuleUpdateStatus.Ready, updates.Status); Assert.Equal(1, emitDiagnosticsClearedCount); emitDiagnosticsClearedCount = 0; AssertEx.Equal(new[] { $"[{project.Id}] Error ENC1001: test.cs(0, 1, 0, 2): {string.Format(FeaturesResources.ErrorReadingFile, "doc", "some error")}", $"[{project.Id}] Error ENC1001: {string.Format(FeaturesResources.ErrorReadingFile, "proj", "some error")}", $"[] Error ENC1001: {string.Format(FeaturesResources.ErrorReadingFile, "sol", "some error")}", }, emitDiagnosticsUpdated.Select(update => { var d = update.GetPushDiagnostics(localWorkspace, InternalDiagnosticsOptions.NormalDiagnosticMode).Single(); return($"[{d.ProjectId}] {d.Severity} {d.Id}:" + (d.DataLocation != null ? $" {d.DataLocation.OriginalFilePath}({d.DataLocation.OriginalStartLine}, {d.DataLocation.OriginalStartColumn}, {d.DataLocation.OriginalEndLine}, {d.DataLocation.OriginalEndColumn}):" : "") + $" {d.Message}"); })); var delta = updates.Updates.Single(); Assert.Equal(moduleId1, delta.Module); AssertEx.Equal(new byte[] { 1, 2 }, delta.ILDelta); AssertEx.Equal(new byte[] { 3, 4 }, delta.MetadataDelta); AssertEx.Equal(new byte[] { 5, 6 }, delta.PdbDelta); AssertEx.Equal(new[] { 0x06000001 }, delta.UpdatedMethods); var lineEdit = delta.SequencePoints.Single(); Assert.Equal("file.cs", lineEdit.FileName); AssertEx.Equal(new[] { new SourceLineUpdate(1, 2) }, lineEdit.LineUpdates); Assert.Equal(exceptionRegionUpdate1, delta.ExceptionRegions.Single()); var activeStatements = delta.ActiveStatements.Single(); Assert.Equal(instructionId1.Method.Method, activeStatements.Method); Assert.Equal(instructionId1.ILOffset, activeStatements.ILOffset); Assert.Equal(span1, activeStatements.NewSpan.ToLinePositionSpan()); // CommitSolutionUpdate called = false; mockEncService.CommitSolutionUpdateImpl = () => called = true; await proxy.CommitSolutionUpdateAsync(CancellationToken.None).ConfigureAwait(false); Assert.True(called); // DiscardSolutionUpdate called = false; mockEncService.DiscardSolutionUpdateImpl = () => called = true; await proxy.DiscardSolutionUpdateAsync(CancellationToken.None).ConfigureAwait(false); Assert.True(called); // GetCurrentActiveStatementPosition mockEncService.GetCurrentActiveStatementPositionImpl = (solution, activeStatementSpanProvider, instructionId) => { Assert.Equal("proj", solution.Projects.Single().Name); Assert.Equal(instructionId1, instructionId); AssertEx.Equal(activeSpans1, activeStatementSpanProvider(document1.Id, CancellationToken.None).Result); return(new LinePositionSpan(new LinePosition(1, 2), new LinePosition(1, 5))); }; Assert.Equal(span1, await proxy.GetCurrentActiveStatementPositionAsync( localWorkspace.CurrentSolution, solutionActiveStatementSpanProvider, instructionId1, CancellationToken.None).ConfigureAwait(false)); // IsActiveStatementInExceptionRegion mockEncService.IsActiveStatementInExceptionRegionImpl = (solution, instructionId) => { Assert.Equal(instructionId1, instructionId); return(true); }; Assert.True(await proxy.IsActiveStatementInExceptionRegionAsync(localWorkspace.CurrentSolution, instructionId1, CancellationToken.None).ConfigureAwait(false)); // GetBaseActiveStatementSpans mockEncService.GetBaseActiveStatementSpansImpl = (solution, documentIds) => { AssertEx.Equal(new[] { document1.Id }, documentIds); return(ImmutableArray.Create(ImmutableArray.Create((span1, ActiveStatementFlags.IsNonLeafFrame | ActiveStatementFlags.PartiallyExecuted)))); }; var baseActiveSpans = await proxy.GetBaseActiveStatementSpansAsync(localWorkspace.CurrentSolution, ImmutableArray.Create(document1.Id), CancellationToken.None).ConfigureAwait(false); Assert.Equal((span1, ActiveStatementFlags.IsNonLeafFrame | ActiveStatementFlags.PartiallyExecuted), baseActiveSpans.Single().Single()); // GetDocumentActiveStatementSpans mockEncService.GetAdjustedActiveStatementSpansImpl = (document, activeStatementSpanProvider) => { Assert.Equal("test.cs", document.Name); AssertEx.Equal(activeSpans1, activeStatementSpanProvider(CancellationToken.None).Result); return(ImmutableArray.Create((span1, ActiveStatementFlags.IsNonLeafFrame | ActiveStatementFlags.PartiallyExecuted))); }; var documentActiveSpans = await proxy.GetAdjustedActiveStatementSpansAsync(document1, documentActiveStatementSpanProvider, CancellationToken.None).ConfigureAwait(false); Assert.Equal((span1, ActiveStatementFlags.IsNonLeafFrame | ActiveStatementFlags.PartiallyExecuted), documentActiveSpans.Single()); // GetDocumentActiveStatementSpans (default array) mockEncService.GetAdjustedActiveStatementSpansImpl = (document, _) => default; documentActiveSpans = await proxy.GetAdjustedActiveStatementSpansAsync(document1, documentActiveStatementSpanProvider, CancellationToken.None).ConfigureAwait(false); Assert.True(documentActiveSpans.IsDefault); // OnSourceFileUpdatedAsync called = false; mockEncService.OnSourceFileUpdatedImpl = updatedDocument => { Assert.Equal(document.Id, updatedDocument.Id); called = true; }; await proxy.OnSourceFileUpdatedAsync(document, CancellationToken.None).ConfigureAwait(false); Assert.True(called); }
public async ValueTask <ImmutableArray <Diagnostic> > GetDocumentDiagnosticsAsync(Document document, Document designTimeDocument, ActiveStatementSpanProvider activeStatementSpanProvider, CancellationToken cancellationToken) { var client = await RemoteHostClient.TryGetClientAsync(Workspace, cancellationToken).ConfigureAwait(false); if (client == null) { var diagnostics = await GetLocalService().GetDocumentDiagnosticsAsync(document, activeStatementSpanProvider, cancellationToken).ConfigureAwait(false); if (designTimeDocument != document) { diagnostics = diagnostics.SelectAsArray(diagnostic => RemapLocation(designTimeDocument, DiagnosticData.Create(diagnostic, document.Project))); } return(diagnostics); } var diagnosticData = await client.TryInvokeAsync <IRemoteEditAndContinueService, ImmutableArray <DiagnosticData> >( document.Project.Solution, (service, solutionInfo, callbackId, cancellationToken) => service.GetDocumentDiagnosticsAsync(solutionInfo, callbackId, document.Id, cancellationToken), callbackTarget : new ActiveStatementSpanProviderCallback(activeStatementSpanProvider), cancellationToken).ConfigureAwait(false); if (!diagnosticData.HasValue) { return(ImmutableArray <Diagnostic> .Empty); } var project = document.Project; using var _ = ArrayBuilder <Diagnostic> .GetInstance(out var result); foreach (var data in diagnosticData.Value) { Debug.Assert(data.DataLocation != null); Diagnostic diagnostic; // Workaround for solution crawler not supporting mapped locations to make Razor work. // We pretend the diagnostic is in the original document, but use the mapped line span. // Razor will ignore the column (which will be off because #line directives can't currently map columns) and only use the line number. if (designTimeDocument != document && data.DataLocation.IsMapped) { diagnostic = RemapLocation(designTimeDocument, data); } else { diagnostic = await data.ToDiagnosticAsync(document.Project, cancellationToken).ConfigureAwait(false); } result.Add(diagnostic); } return(result.ToImmutable()); }
public async Task DiagnosticData_ExternalAdditionalLocationIsPreserved() { using var workspace = new TestWorkspace( composition: EditorTestCompositions.EditorFeatures ); var additionalDocument = workspace.CurrentSolution .AddProject("TestProject", "TestProject", LanguageNames.CSharp) .AddDocument("test.cs", "") .Project.AddAdditionalDocument( "AdditionalDocument.txt", "First line in file", filePath: "AdditionalDocument.txt" ); var document = additionalDocument.Project.Documents.Single(); var externalAdditionalLocation = new DiagnosticDataLocation( additionalDocument.Id, sourceSpan: new TextSpan(0, 1), originalFilePath: additionalDocument.Name, originalStartLine: 0, originalStartColumn: 0, originalEndLine: 0, originalEndColumn: 1 ); var diagnosticData = new DiagnosticData( id: "test1", category: "Test", message: "test1 message", enuMessageForBingSearch: "test1 message format", severity: DiagnosticSeverity.Info, defaultSeverity: DiagnosticSeverity.Info, isEnabledByDefault: true, warningLevel: 1, projectId: document.Project.Id, customTags: ImmutableArray <string> .Empty, properties: ImmutableDictionary <string, string> .Empty, location: new DiagnosticDataLocation(document.Id), additionalLocations: ImmutableArray.Create(externalAdditionalLocation), language: document.Project.Language ); var diagnostic = await diagnosticData.ToDiagnosticAsync( document.Project, CancellationToken.None ); var roundTripDiagnosticData = DiagnosticData.Create(diagnostic, document); var roundTripAdditionalLocation = Assert.Single( roundTripDiagnosticData.AdditionalLocations ); Assert.Equal( externalAdditionalLocation.DocumentId, roundTripAdditionalLocation.DocumentId ); Assert.Equal( externalAdditionalLocation.SourceSpan, roundTripAdditionalLocation.SourceSpan ); Assert.Equal( externalAdditionalLocation.OriginalFilePath, roundTripAdditionalLocation.OriginalFilePath ); Assert.Equal( externalAdditionalLocation.OriginalStartLine, roundTripAdditionalLocation.OriginalStartLine ); Assert.Equal( externalAdditionalLocation.OriginalStartColumn, roundTripAdditionalLocation.OriginalStartColumn ); Assert.Equal( externalAdditionalLocation.OriginalEndLine, roundTripAdditionalLocation.OriginalEndLine ); Assert.Equal( externalAdditionalLocation.OriginalEndLine, roundTripAdditionalLocation.OriginalEndLine ); }
/// <summary> /// Reports diagnostics. /// </summary> /// <returns>Returns ids of documents that belong to <paramref name="projectId"/> and containing one or more diagnostics.</returns> public ImmutableArray <DocumentId> ReportDiagnostics(object errorId, Solution solution, ProjectId projectId, IEnumerable <Diagnostic> diagnostics) { Debug.Assert(errorId != null); Debug.Assert(solution != null); Debug.Assert(projectId != null); var updateEvent = DiagnosticsUpdated; var documentIds = PooledHashSet <DocumentId> .GetInstance(); var documentDiagnosticData = ArrayBuilder <DiagnosticData> .GetInstance(); var projectDiagnosticData = ArrayBuilder <DiagnosticData> .GetInstance(); var project = solution.GetProject(projectId); foreach (var diagnostic in diagnostics) { var documentOpt = solution.GetDocument(diagnostic.Location.SourceTree); if (documentOpt != null) { if (updateEvent != null) { documentDiagnosticData.Add(DiagnosticData.Create(documentOpt, diagnostic)); } // only add documents from the current project: if (documentOpt.Project.Id == projectId) { documentIds.Add(documentOpt.Id); } } else if (updateEvent != null) { projectDiagnosticData.Add(DiagnosticData.Create(project, diagnostic)); } } foreach (var documentDiagnostics in documentDiagnosticData.ToDictionary(data => data.DocumentId)) { updateEvent(this, DiagnosticsUpdatedArgs.DiagnosticsCreated( errorId, solution.Workspace, solution, projectId, documentId: documentDiagnostics.Key, diagnostics: documentDiagnostics.Value)); } if (projectDiagnosticData.Count > 0) { updateEvent(this, DiagnosticsUpdatedArgs.DiagnosticsCreated( errorId, solution.Workspace, solution, projectId, documentId: null, diagnostics: projectDiagnosticData.ToImmutable())); } var result = documentIds.AsImmutableOrEmpty(); documentDiagnosticData.Free(); projectDiagnosticData.Free(); documentIds.Free(); return(result); }
public void ReportDiagnostics(Solution solution, ProjectId?projectId, IEnumerable <Diagnostic> diagnostics) { RoslynDebug.Assert(solution != null); var updateEvent = DiagnosticsUpdated; if (updateEvent == null) { return; } var documentDiagnosticData = ArrayBuilder <DiagnosticData> .GetInstance(); var nonDocumentDiagnosticData = ArrayBuilder <DiagnosticData> .GetInstance(); var workspace = solution.Workspace; var options = solution.Options; var project = (projectId != null) ? solution.GetProject(projectId) : null; foreach (var diagnostic in diagnostics) { var document = solution.GetDocument(diagnostic.Location.SourceTree); if (document != null) { documentDiagnosticData.Add(DiagnosticData.Create(diagnostic, document)); } else if (project != null) { nonDocumentDiagnosticData.Add(DiagnosticData.Create(diagnostic, project)); } else { nonDocumentDiagnosticData.Add(DiagnosticData.Create(diagnostic, options)); } } if (documentDiagnosticData.Count > 0) { foreach (var(documentId, diagnosticData) in documentDiagnosticData.ToDictionary(data => data.DocumentId)) { var diagnosticGroupId = (this, documentId, projectId); updateEvent(this, DiagnosticsUpdatedArgs.DiagnosticsCreated( diagnosticGroupId, workspace, solution, projectId, documentId: documentId, diagnostics: diagnosticData)); } } if (nonDocumentDiagnosticData.Count > 0) { var diagnosticGroupId = (this, projectId); updateEvent(this, DiagnosticsUpdatedArgs.DiagnosticsCreated( diagnosticGroupId, workspace, solution, projectId, documentId: null, diagnostics: nonDocumentDiagnosticData.ToImmutable())); } documentDiagnosticData.Free(); nonDocumentDiagnosticData.Free(); }
public async Task AnalyzeSyntaxAsync(Document document, CancellationToken cancellationToken) { // right now, there is no way to observe diagnostics for closed file. if (!_workspace.IsDocumentOpen(document.Id) || !_workspace.Options.GetOption(InternalRuntimeDiagnosticOptions.Syntax)) { return; } var tree = await document.GetSyntaxTreeAsync(cancellationToken).ConfigureAwait(false); var diagnostics = tree.GetDiagnostics(cancellationToken); Contract.Requires(document.Project.Solution.Workspace == _workspace); var diagnosticData = diagnostics == null ? ImmutableArray <DiagnosticData> .Empty : diagnostics.Select(d => DiagnosticData.Create(document, d)).ToImmutableArrayOrEmpty(); _service.RaiseDiagnosticsUpdated( DiagnosticsUpdatedArgs.DiagnosticsCreated(new DefaultUpdateArgsId(_workspace.Kind, Syntax, document.Id), _workspace, document.Project.Solution, document.Project.Id, document.Id, diagnosticData)); }
public async Task AnalyzeDocumentAsync(Document document, SyntaxNode bodyOpt, CancellationToken cancellationToken) { // right now, there is no way to observe diagnostics for closed file. if (!_workspace.IsDocumentOpen(document.Id) || !_workspace.Options.GetOption(InternalRuntimeDiagnosticOptions.Semantic)) { return; } var model = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false); var diagnostics = model.GetMethodBodyDiagnostics(span: null, cancellationToken: cancellationToken).Concat( model.GetDeclarationDiagnostics(span: null, cancellationToken: cancellationToken)); Contract.Requires(document.Project.Solution.Workspace == _workspace); var diagnosticData = diagnostics == null ? ImmutableArray <DiagnosticData> .Empty : diagnostics.Select(d => DiagnosticData.Create(document, d)).ToImmutableArrayOrEmpty(); _service.RaiseDiagnosticsUpdated( DiagnosticsUpdatedArgs.DiagnosticsCreated(new DefaultUpdateArgsId(_workspace.Kind, Semantic, document.Id), _workspace, document.Project.Solution, document.Project.Id, document.Id, diagnosticData)); }