提交 19f62c46 编写于 作者: M Manish Vasani

Merge pull request #673 from mavasani/AnalyzerSpecificDiagnostics

This change addresses #259: below issues related to diagnostics generated for analyzer exceptions from third party analyzers.

1.Suppression of duplicate exception diagnostics: Current mechanism did the suppression in SuppressMessageState based on unique reported messages. This is obviously incorrect as an exception diagnostic will be reported non-suppressed and suppressed on subsequent queries to SuppressMessageState.IsDiagnosticSuppressed.


2.The IDE diagnostic service has multiple layers where document/project diagnostics are filtered and these analyzer exception diagnostics were getting dropped at various places.


So this change moves the exception diagnostics generation + reporting out of the regular analyzer diagnostic pipeline and in line with analyzer load failure diagnostics reporting in VS:

1.Add an event handler to AnalyzerDriverHelper to report analyzer exception diagnostics to interested clients.


2.Listen to these diagnostic events in IDE diagnostic service and wrap them with relevant workspace/project argument and generate updated events.


3.Add an AbstractHostDiagnosticUpdateSource in Features layer to listen and report analyzer exception diagnostic events from diagnostic service. Additionally, removal of an analyzer reference in workspace will clean up the diagnostics for the analyzers belonging to that analyzer reference.


4.Listen to exception diagnostic events in command line compiler and report as regular diagnostics.


Added typw AbstractHostDiagnosticUpdateSource can be extended in future to report other kind of host diagnostics which are not related to a project/document/analyzer.
......@@ -10,6 +10,7 @@
</PropertyGroup>
<ItemGroup>
<Compile Include="$(MSBuildThisFileDirectory)AnalyzerDriverHelper.cs" />
<Compile Include="$(MSBuildThisFileDirectory)AnalyzerExceptionDiagnosticArgs.cs" />
<Compile Include="$(MSBuildThisFileDirectory)AnalyzerManager.cs" />
<Compile Include="$(MSBuildThisFileDirectory)DeclarationComputer.cs" />
<Compile Include="$(MSBuildThisFileDirectory)DeclarationInfo.cs" />
......
......@@ -16,27 +16,27 @@ internal class AnalyzerDriverHelper
private const string DiagnosticId = "AD0001";
private const string DiagnosticCategory = "Compiler";
private static event EventHandler<AnalyzerExceptionDiagnosticArgs> AnalyzerExceptionDiagnostic;
/// <summary>
/// Executes the <see cref="DiagnosticAnalyzer.Initialize(AnalysisContext)"/> for the given analyzer.
/// </summary>
/// <param name="analyzer">Analyzer to get session wide analyzer actions.</param>
/// <param name="sessionScope">Session scope to store register session wide analyzer actions.</param>
/// <param name="addDiagnostic">Delegate to add diagnostics.</param>
/// <param name="continueOnAnalyzerException">Predicate to decide if exceptions from the action should be handled or not.</param>
/// <param name="cancellationToken">Cancellation token.</param>
/// <remarks>
/// Note that this API doesn't execute any <see cref="CompilationStartAnalyzerAction"/> registered by the Initialize invocation.
/// Use <see cref="ExecuteCompilationStartActions(ImmutableArray{CompilationStartAnalyzerAction}, HostCompilationStartAnalysisScope, Compilation, AnalyzerOptions, Action{Diagnostic}, Func{Exception, DiagnosticAnalyzer, bool}, CancellationToken)"/> API
/// Use <see cref="ExecuteCompilationStartActions(ImmutableArray{CompilationStartAnalyzerAction}, HostCompilationStartAnalysisScope, Compilation, AnalyzerOptions, Func{Exception, DiagnosticAnalyzer, bool}, CancellationToken)"/> API
/// to get execute these actions to get the per-compilation analyzer actions.
/// </remarks>
public static void ExecuteInitializeMethod(
DiagnosticAnalyzer analyzer,
HostSessionStartAnalysisScope sessionScope,
Action<Diagnostic> addDiagnostic,
Func<Exception, DiagnosticAnalyzer, bool> continueOnAnalyzerException,
CancellationToken cancellationToken)
{
ExecuteAndCatchIfThrows(analyzer, addDiagnostic, continueOnAnalyzerException, () =>
ExecuteAndCatchIfThrows(analyzer, continueOnAnalyzerException, () =>
{
// The Initialize method should be run asynchronously in case it is not well behaved, e.g. does not terminate.
analyzer.Initialize(new AnalyzerAnalysisContext(analyzer, sessionScope));
......@@ -50,7 +50,6 @@ internal class AnalyzerDriverHelper
/// <param name="compilationScope">Compilation scope to store the analyzer actions.</param>
/// <param name="compilation">Compilation to be used in the analysis.</param>
/// <param name="analyzerOptions">Analyzer options.</param>
/// <param name="addDiagnostic">Delegate to add diagnostics.</param>
/// <param name="continueOnAnalyzerException">Predicate to decide if exceptions from the action should be handled or not.</param>
/// <param name="cancellationToken">Cancellation token.</param>
public static void ExecuteCompilationStartActions(
......@@ -58,14 +57,13 @@ internal class AnalyzerDriverHelper
HostCompilationStartAnalysisScope compilationScope,
Compilation compilation,
AnalyzerOptions analyzerOptions,
Action<Diagnostic> addDiagnostic,
Func<Exception, DiagnosticAnalyzer, bool> continueOnAnalyzerException,
CancellationToken cancellationToken)
{
foreach (var startAction in actions)
{
cancellationToken.ThrowIfCancellationRequested();
ExecuteAndCatchIfThrows(startAction.Analyzer, addDiagnostic, continueOnAnalyzerException, () =>
ExecuteAndCatchIfThrows(startAction.Analyzer, continueOnAnalyzerException, () =>
{
startAction.Action(new AnalyzerCompilationStartAnalysisContext(startAction.Analyzer, compilationScope, compilation, analyzerOptions, cancellationToken));
}, cancellationToken);
......@@ -94,7 +92,7 @@ internal class AnalyzerDriverHelper
foreach (var endAction in actions.CompilationEndActions)
{
cancellationToken.ThrowIfCancellationRequested();
ExecuteAndCatchIfThrows(endAction.Analyzer, addDiagnostic, continueOnAnalyzerException, () =>
ExecuteAndCatchIfThrows(endAction.Analyzer, continueOnAnalyzerException, () =>
{
var context = new CompilationEndAnalysisContext(compilation, analyzerOptions, addDiagnostic, cancellationToken);
endAction.Action(context);
......@@ -133,7 +131,7 @@ internal class AnalyzerDriverHelper
var symbolContext = new SymbolAnalysisContext(symbol, compilation, analyzerOptions, addDiagnostic, cancellationToken);
// Catch Exception from action.
ExecuteAndCatchIfThrows(symbolAction.Analyzer, addDiagnostic, continueOnAnalyzerException, () => action(symbolContext), cancellationToken);
ExecuteAndCatchIfThrows(symbolAction.Analyzer, continueOnAnalyzerException, () => action(symbolContext), cancellationToken);
}
}
}
......@@ -161,7 +159,7 @@ internal class AnalyzerDriverHelper
cancellationToken.ThrowIfCancellationRequested();
// Catch Exception from action.
ExecuteAndCatchIfThrows(semanticModelAction.Analyzer, addDiagnostic, continueOnAnalyzerException, () =>
ExecuteAndCatchIfThrows(semanticModelAction.Analyzer, continueOnAnalyzerException, () =>
{
var context = new SemanticModelAnalysisContext(semanticModel, analyzerOptions, addDiagnostic, cancellationToken);
semanticModelAction.Action(context);
......@@ -191,7 +189,7 @@ internal class AnalyzerDriverHelper
cancellationToken.ThrowIfCancellationRequested();
// Catch Exception from action.
ExecuteAndCatchIfThrows(syntaxTreeAction.Analyzer, addDiagnostic, continueOnAnalyzerException, () =>
ExecuteAndCatchIfThrows(syntaxTreeAction.Analyzer, continueOnAnalyzerException, () =>
{
var context = new SyntaxTreeAnalysisContext(syntaxTree, analyzerOptions, addDiagnostic, cancellationToken);
syntaxTreeAction.Action(context);
......@@ -251,7 +249,7 @@ internal class AnalyzerDriverHelper
var syntaxNodeContext = new SyntaxNodeAnalysisContext(node, semanticModel, analyzerOptions, addDiagnostic, cancellationToken);
// Catch Exception from action.
ExecuteAndCatchIfThrows(analyzer, addDiagnostic, continueOnAnalyzerException, () => syntaxNodeAction(syntaxNodeContext), cancellationToken);
ExecuteAndCatchIfThrows(analyzer, continueOnAnalyzerException, () => syntaxNodeAction(syntaxNodeContext), cancellationToken);
}
/// <summary>
......@@ -329,7 +327,7 @@ internal class AnalyzerDriverHelper
foreach (var da in codeBlockStartActions)
{
// Catch Exception from the start action.
ExecuteAndCatchIfThrows(da.Analyzer, addDiagnostic, continueOnAnalyzerException, () =>
ExecuteAndCatchIfThrows(da.Analyzer, continueOnAnalyzerException, () =>
{
HostCodeBlockStartAnalysisScope<TLanguageKindEnum> codeBlockScope = new HostCodeBlockStartAnalysisScope<TLanguageKindEnum>();
CodeBlockStartAnalysisContext<TLanguageKindEnum> blockStartContext = new AnalyzerCodeBlockStartAnalysisContext<TLanguageKindEnum>(da.Analyzer, codeBlockScope, declaredNode, declaredSymbol, semanticModel, analyzerOptions, cancellationToken);
......@@ -353,7 +351,7 @@ internal class AnalyzerDriverHelper
foreach (var a in endedActions)
{
// Catch Exception from a.OnCodeBlockEnded
ExecuteAndCatchIfThrows(a.Analyzer, addDiagnostic, continueOnAnalyzerException, () => a.Action(new CodeBlockEndAnalysisContext(declaredNode, declaredSymbol, semanticModel, analyzerOptions, addDiagnostic, cancellationToken)), cancellationToken);
ExecuteAndCatchIfThrows(a.Analyzer, continueOnAnalyzerException, () => a.Action(new CodeBlockEndAnalysisContext(declaredNode, declaredSymbol, semanticModel, analyzerOptions, addDiagnostic, cancellationToken)), cancellationToken);
}
endedActions.Free();
......@@ -434,7 +432,7 @@ internal static bool CanHaveExecutableCodeBlock(ISymbol symbol)
}
}
internal static void ExecuteAndCatchIfThrows(DiagnosticAnalyzer analyzer, Action<Diagnostic> addDiagnostic, Func<Exception, DiagnosticAnalyzer, bool> continueOnAnalyzerException, Action analyze, CancellationToken cancellationToken)
internal static void ExecuteAndCatchIfThrows(DiagnosticAnalyzer analyzer, Func<Exception, DiagnosticAnalyzer, bool> continueOnAnalyzerException, Action analyze, CancellationToken cancellationToken)
{
try
{
......@@ -444,14 +442,18 @@ internal static void ExecuteAndCatchIfThrows(DiagnosticAnalyzer analyzer, Action
{
if (oce.CancellationToken != cancellationToken)
{
// Create a info diagnostic saying that the analyzer failed
addDiagnostic(GetAnalyzerDiagnostic(analyzer, oce));
// Raise an event with a diagnostic for analyzer exception
var diagnostic = GetAnalyzerDiagnostic(analyzer, oce);
var args = new AnalyzerExceptionDiagnosticArgs(analyzer, diagnostic);
AnalyzerExceptionDiagnostic?.Invoke(analyze, args);
}
}
catch (Exception e) when (continueOnAnalyzerException(e, analyzer))
{
// Create a info diagnostic saying that the analyzer failed
addDiagnostic(GetAnalyzerDiagnostic(analyzer, e));
// Raise an event with a diagnostic for analyzer exception
var diagnostic = GetAnalyzerDiagnostic(analyzer, e);
var args = new AnalyzerExceptionDiagnosticArgs(analyzer, diagnostic);
AnalyzerExceptionDiagnostic?.Invoke(analyze, args);
}
}
......@@ -471,11 +473,13 @@ internal static DiagnosticDescriptor GetDiagnosticDescriptor(string analyzerName
customTags: WellKnownDiagnosticTags.AnalyzerException);
}
internal static bool IsAnalyzerExceptionDiagnostic(string diagnosticId, IEnumerable<string> customTags)
internal static bool IsAnalyzerExceptionDiagnostic(Diagnostic diagnostic)
{
if (diagnosticId == DiagnosticId)
if (diagnostic.Id == DiagnosticId)
{
foreach (var tag in customTags)
#pragma warning disable RS0013 // Its ok to realize the Descriptor for analyzer exception diagnostics, which are descriptor based and also rare.
foreach (var tag in diagnostic.Descriptor.CustomTags)
#pragma warning restore RS0013
{
if (tag == WellKnownDiagnosticTags.AnalyzerException)
{
......@@ -484,7 +488,54 @@ internal static bool IsAnalyzerExceptionDiagnostic(string diagnosticId, IEnumera
}
}
return false;
return false;
}
internal static EventHandler<AnalyzerExceptionDiagnosticArgs> RegisterAnalyzerExceptionDiagnosticHandler(ImmutableArray<DiagnosticAnalyzer> analyzers, Func<Diagnostic, bool> addAnalyzerExceptionDiagnostic)
{
Action<object, AnalyzerExceptionDiagnosticArgs> onAnalyzerExceptionDiagnostic =
(sender, args) => addAnalyzerExceptionDiagnostic(args.Diagnostic);
return RegisterAnalyzerExceptionDiagnosticHandler(analyzers, onAnalyzerExceptionDiagnostic);
}
internal static EventHandler<AnalyzerExceptionDiagnosticArgs> RegisterAnalyzerExceptionDiagnosticHandler(ImmutableArray<DiagnosticAnalyzer> analyzers, Action<object, AnalyzerExceptionDiagnosticArgs> onAnayzerExceptionDiagnostic)
{
EventHandler<AnalyzerExceptionDiagnosticArgs> handler = (sender, args) =>
{
if (analyzers.Contains(args.FaultedAnalyzer))
{
onAnayzerExceptionDiagnostic(sender, args);
}
};
AnalyzerExceptionDiagnostic += handler;
return handler;
}
internal static EventHandler<AnalyzerExceptionDiagnosticArgs> RegisterAnalyzerExceptionDiagnosticHandler(DiagnosticAnalyzer analyzer, Func<Diagnostic, bool> addAnalyzerExceptionDiagnostic)
{
Action<object, AnalyzerExceptionDiagnosticArgs> onAnalyzerExceptionDiagnostic =
(sender, args) => addAnalyzerExceptionDiagnostic(args.Diagnostic);
return RegisterAnalyzerExceptionDiagnosticHandler(analyzer, onAnalyzerExceptionDiagnostic);
}
internal static EventHandler<AnalyzerExceptionDiagnosticArgs> RegisterAnalyzerExceptionDiagnosticHandler(DiagnosticAnalyzer analyzer, Action<object, AnalyzerExceptionDiagnosticArgs> onAnayzerExceptionDiagnostic)
{
EventHandler<AnalyzerExceptionDiagnosticArgs> handler = (sender, args) =>
{
if (analyzer == args.FaultedAnalyzer)
{
onAnayzerExceptionDiagnostic(sender, args);
}
};
AnalyzerExceptionDiagnostic += handler;
return handler;
}
internal static void UnregisterAnalyzerExceptionDiagnosticHandler(EventHandler<AnalyzerExceptionDiagnosticArgs> handler)
{
AnalyzerExceptionDiagnostic -= handler;
}
}
}
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
namespace Microsoft.CodeAnalysis.Diagnostics
{
internal class AnalyzerExceptionDiagnosticArgs : EventArgs
{
public readonly Diagnostic Diagnostic;
public readonly DiagnosticAnalyzer FaultedAnalyzer;
public AnalyzerExceptionDiagnosticArgs(DiagnosticAnalyzer analyzer, Diagnostic diagnostic)
{
this.FaultedAnalyzer = analyzer;
this.Diagnostic = diagnostic;
}
}
}
......@@ -45,7 +45,6 @@ internal class AnalyzerManager
DiagnosticAnalyzer analyzer,
HostSessionStartAnalysisScope sessionScope,
Compilation compilation,
Action<Diagnostic> addDiagnostic,
AnalyzerOptions analyzerOptions,
Func<Exception, DiagnosticAnalyzer, bool> continueOnAnalyzerException,
CancellationToken cancellationToken)
......@@ -56,7 +55,7 @@ internal class AnalyzerManager
{
var compilationAnalysisScope = new HostCompilationStartAnalysisScope(sessionScope);
AnalyzerDriverHelper.ExecuteCompilationStartActions(sessionScope.CompilationStartActions, compilationAnalysisScope, compilation,
analyzerOptions, addDiagnostic, continueOnAnalyzerException, cancellationToken);
analyzerOptions, continueOnAnalyzerException, cancellationToken);
return compilationAnalysisScope;
}, cancellationToken);
};
......@@ -69,7 +68,6 @@ internal class AnalyzerManager
DiagnosticAnalyzer analyzer,
HostSessionStartAnalysisScope sessionScope,
Compilation compilation,
Action<Diagnostic> addDiagnostic,
AnalyzerOptions analyzerOptions,
Func<Exception, DiagnosticAnalyzer, bool> continueOnAnalyzerException,
CancellationToken cancellationToken)
......@@ -77,7 +75,7 @@ internal class AnalyzerManager
try
{
return await GetCompilationAnalysisScopeCoreAsync(analyzer, sessionScope,
compilation, addDiagnostic, analyzerOptions, continueOnAnalyzerException, cancellationToken).ConfigureAwait(false);
compilation, analyzerOptions, continueOnAnalyzerException, cancellationToken).ConfigureAwait(false);
}
catch (OperationCanceledException)
{
......@@ -89,14 +87,13 @@ internal class AnalyzerManager
cancellationToken.ThrowIfCancellationRequested();
return await GetCompilationAnalysisScopeAsync(analyzer, sessionScope,
compilation, addDiagnostic, analyzerOptions, continueOnAnalyzerException, cancellationToken).ConfigureAwait(false);
compilation, analyzerOptions, continueOnAnalyzerException, cancellationToken).ConfigureAwait(false);
}
}
private Task<HostSessionStartAnalysisScope> GetSessionAnalysisScopeCoreAsync(
DiagnosticAnalyzer analyzer,
Action<Diagnostic> addDiagnostic,
Func<Exception, DiagnosticAnalyzer, bool> continueOnAnalyzerException,
CancellationToken cancellationToken)
{
......@@ -105,7 +102,7 @@ internal class AnalyzerManager
return Task.Run(() =>
{
var sessionScope = new HostSessionStartAnalysisScope();
AnalyzerDriverHelper.ExecuteInitializeMethod(a, sessionScope, addDiagnostic, continueOnAnalyzerException, cancellationToken);
AnalyzerDriverHelper.ExecuteInitializeMethod(a, sessionScope, continueOnAnalyzerException, cancellationToken);
return sessionScope;
}, cancellationToken);
};
......@@ -116,13 +113,12 @@ internal class AnalyzerManager
private async Task<HostSessionStartAnalysisScope> GetSessionAnalysisScopeAsync(
DiagnosticAnalyzer analyzer,
Action<Diagnostic> addDiagnostic,
Func<Exception, DiagnosticAnalyzer, bool> continueOnAnalyzerException,
CancellationToken cancellationToken)
{
try
{
return await GetSessionAnalysisScopeCoreAsync(analyzer, addDiagnostic, continueOnAnalyzerException, cancellationToken).ConfigureAwait(false);
return await GetSessionAnalysisScopeCoreAsync(analyzer, continueOnAnalyzerException, cancellationToken).ConfigureAwait(false);
}
catch (OperationCanceledException)
{
......@@ -131,7 +127,7 @@ internal class AnalyzerManager
_sessionScopeMap.Remove(analyzer);
cancellationToken.ThrowIfCancellationRequested();
return await GetSessionAnalysisScopeAsync(analyzer, addDiagnostic, continueOnAnalyzerException, cancellationToken).ConfigureAwait(false);
return await GetSessionAnalysisScopeAsync(analyzer, continueOnAnalyzerException, cancellationToken).ConfigureAwait(false);
}
}
......@@ -143,16 +139,15 @@ internal class AnalyzerManager
public async Task<AnalyzerActions> GetAnalyzerActionsAsync(
DiagnosticAnalyzer analyzer,
Compilation compilation,
Action<Diagnostic> addDiagnostic,
AnalyzerOptions analyzerOptions,
Func<Exception, DiagnosticAnalyzer, bool> continueOnAnalyzerException,
CancellationToken cancellationToken)
{
var sessionScope = await GetSessionAnalysisScopeAsync(analyzer, addDiagnostic, continueOnAnalyzerException, cancellationToken).ConfigureAwait(false);
var sessionScope = await GetSessionAnalysisScopeAsync(analyzer, continueOnAnalyzerException, cancellationToken).ConfigureAwait(false);
if (sessionScope.CompilationStartActions.Length > 0 && compilation != null)
{
var compilationScope = await GetCompilationAnalysisScopeAsync(analyzer, sessionScope,
compilation, addDiagnostic, analyzerOptions, continueOnAnalyzerException, cancellationToken).ConfigureAwait(false);
compilation, analyzerOptions, continueOnAnalyzerException, cancellationToken).ConfigureAwait(false);
return compilationScope.GetAnalyzerActions(analyzer);
}
......@@ -164,7 +159,6 @@ internal class AnalyzerManager
/// </summary>
public ImmutableArray<DiagnosticDescriptor> GetSupportedDiagnosticDescriptors(
DiagnosticAnalyzer analyzer,
Action<Diagnostic> addDiagnostic,
Func<Exception, DiagnosticAnalyzer, bool> continueOnAnalyzerException,
CancellationToken cancellationToken)
{
......@@ -173,7 +167,7 @@ internal class AnalyzerManager
var supportedDiagnostics = ImmutableArray<DiagnosticDescriptor>.Empty;
// Catch Exception from analyzer.SupportedDiagnostics
AnalyzerDriverHelper.ExecuteAndCatchIfThrows(analyzer, addDiagnostic, continueOnAnalyzerException, () => { supportedDiagnostics = analyzer.SupportedDiagnostics; }, cancellationToken);
AnalyzerDriverHelper.ExecuteAndCatchIfThrows(analyzer, continueOnAnalyzerException, () => { supportedDiagnostics = analyzer.SupportedDiagnostics; }, cancellationToken);
return supportedDiagnostics;
});
......
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics;
......@@ -332,8 +333,13 @@ private int RunCore(TextWriter consoleOutput, CancellationToken cancellationToke
var analyzerOptions = new AnalyzerOptions(ImmutableArray.Create<AdditionalText, AdditionalTextFile>(additionalTextFiles));
AnalyzerDriver analyzerDriver = null;
ConcurrentSet<Diagnostic> analyzerExceptionDiagnostics = null;
EventHandler<AnalyzerExceptionDiagnosticArgs> analyzerExceptionDiagnosticsHandler = null;
if (!analyzers.IsDefaultOrEmpty)
{
analyzerExceptionDiagnostics = new ConcurrentSet<Diagnostic>();
analyzerExceptionDiagnosticsHandler = AnalyzerDriverHelper.RegisterAnalyzerExceptionDiagnosticHandler(analyzers, analyzerExceptionDiagnostics.Add);
var analyzerManager = new AnalyzerManager();
analyzerDriver = AnalyzerDriver.Create(compilation, analyzers, analyzerOptions, analyzerManager, out compilation, cancellationToken);
}
......@@ -443,7 +449,10 @@ private int RunCore(TextWriter consoleOutput, CancellationToken cancellationToke
if (analyzerDriver != null)
{
var analyzerDiagnostics = analyzerDriver.GetDiagnosticsAsync().Result;
if (PrintErrors(analyzerDiagnostics, consoleOutput))
var allAnalyzerDiagnostics = analyzerDiagnostics.AddRange(analyzerExceptionDiagnostics);
AnalyzerDriverHelper.UnregisterAnalyzerExceptionDiagnosticHandler(analyzerExceptionDiagnosticsHandler);
if (PrintErrors(allAnalyzerDiagnostics, consoleOutput))
{
return Failed;
}
......
......@@ -173,12 +173,12 @@ public bool Equals(DiagnosticDescriptor other)
other != null &&
this.Category == other.Category &&
this.DefaultSeverity == other.DefaultSeverity &&
this.Description == other.Description &&
this.Description.Equals(other.Description) &&
this.HelpLinkUri == other.HelpLinkUri &&
this.Id == other.Id &&
this.IsEnabledByDefault == other.IsEnabledByDefault &&
this.MessageFormat == other.MessageFormat &&
this.Title == other.Title;
this.MessageFormat.Equals(other.MessageFormat) &&
this.Title.Equals(other.Title);
}
public override bool Equals(object obj)
......
......@@ -131,7 +131,7 @@ public override bool Equals(Diagnostic obj)
{
var other = obj as SimpleDiagnostic;
return other != null
&& _descriptor == other._descriptor
&& _descriptor.Equals(other._descriptor)
&& _messageArgs.SequenceEqual(other._messageArgs, (a, b) => a == b)
&& _location == other._location
&& _severity == other._severity
......
......@@ -113,5 +113,23 @@ public override string ToString(IFormatProvider formatProvider)
(_formatArguments.Length > 0 ? string.Format(resourceString, _formatArguments) : resourceString) :
string.Empty;
}
public override bool Equals(LocalizableString other)
{
var otherResourceString = other as LocalizableResourceString;
return other != null &&
_nameOfLocalizableResource == otherResourceString._nameOfLocalizableResource &&
_resourceManager == otherResourceString._resourceManager &&
_resourceSource == otherResourceString._resourceSource &&
_formatArguments.SequenceEqual(otherResourceString._formatArguments, (a, b) => a == b);
}
public override int GetHashCode()
{
return Hash.Combine(_nameOfLocalizableResource.GetHashCode(),
Hash.Combine(_resourceManager.GetHashCode(),
Hash.Combine(_resourceSource.GetHashCode(),
Hash.CombineValues(_formatArguments))));
}
}
}
......@@ -2,6 +2,7 @@
using System;
using System.Globalization;
using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis
{
......@@ -9,7 +10,7 @@ namespace Microsoft.CodeAnalysis
/// A string that may possibly be formatted differently depending on culture.
/// NOTE: Types implementing <see cref="LocalizableString"/> must be serializable.
/// </summary>
public abstract class LocalizableString : IFormattable
public abstract class LocalizableString : IFormattable, IEquatable<LocalizableString>
{
/// <summary>
/// Formats the value of the current instance using the optionally specified format.
......@@ -36,6 +37,14 @@ string IFormattable.ToString(string ignored, IFormatProvider formatProvider)
return ToString(formatProvider);
}
public abstract override int GetHashCode();
public abstract bool Equals(LocalizableString other);
public override bool Equals(object other)
{
return Equals(other as LocalizableString);
}
private sealed class FixedLocalizableString : LocalizableString
{
private readonly string _fixedString;
......@@ -49,6 +58,17 @@ public override string ToString(IFormatProvider formatProvider)
{
return _fixedString;
}
public override bool Equals(LocalizableString other)
{
var fixedStr = other as FixedLocalizableString;
return fixedStr != null && string.Equals(_fixedString, fixedStr.ToString());
}
public override int GetHashCode()
{
return _fixedString == null ? 0 : _fixedString.GetHashCode();
}
}
}
}
......@@ -126,7 +126,7 @@ private Task ExecuteSyntaxTreeActions(CancellationToken cancellationToken)
foreach (var syntaxTreeAction in analyzerAndActions)
{
// Catch Exception from executing the action
AnalyzerDriverHelper.ExecuteAndCatchIfThrows(syntaxTreeAction.Analyzer, _addDiagnostic, continueOnAnalyzerException, () =>
AnalyzerDriverHelper.ExecuteAndCatchIfThrows(syntaxTreeAction.Analyzer, continueOnAnalyzerException, () =>
{
var context = new SyntaxTreeAnalysisContext(tree, analyzerOptions, _addDiagnostic, cancellationToken);
syntaxTreeAction.Action(context);
......@@ -436,7 +436,7 @@ private void AddTasksForExecutingSymbolActions(SymbolDeclaredCompilationEvent sy
Debug.Assert(da.Analyzer == analyzer);
// Catch Exception from analyzing the symbol
AnalyzerDriverHelper.ExecuteAndCatchIfThrows(da.Analyzer, _addDiagnostic, continueOnAnalyzerException, () =>
AnalyzerDriverHelper.ExecuteAndCatchIfThrows(da.Analyzer, continueOnAnalyzerException, () =>
{
cancellationToken.ThrowIfCancellationRequested();
var symbolContext = new SymbolAnalysisContext(symbol, _compilation, this.analyzerOptions, addDiagnosticForSymbol, cancellationToken);
......@@ -483,7 +483,7 @@ private Task ProcessCompilationUnitCompleted(CompilationUnitCompletedEvent compl
Debug.Assert(semanticModelAction.Analyzer == analyzerAndActions.Key);
// Catch Exception from semanticModelAction
AnalyzerDriverHelper.ExecuteAndCatchIfThrows(semanticModelAction.Analyzer, _addDiagnostic, continueOnAnalyzerException, () =>
AnalyzerDriverHelper.ExecuteAndCatchIfThrows(semanticModelAction.Analyzer, continueOnAnalyzerException, () =>
{
cancellationToken.ThrowIfCancellationRequested();
var semanticModelContext = new SemanticModelAnalysisContext(semanticModel, this.analyzerOptions, _addDiagnostic, cancellationToken);
......@@ -519,7 +519,7 @@ private async Task ProcessCompilationCompletedAsync(CompilationCompletedEvent en
Debug.Assert(endAction.Analyzer == analyzerAndActions.Key);
// Catch Exception from endAction
AnalyzerDriverHelper.ExecuteAndCatchIfThrows(endAction.Analyzer, _addDiagnostic, continueOnAnalyzerException, () =>
AnalyzerDriverHelper.ExecuteAndCatchIfThrows(endAction.Analyzer, continueOnAnalyzerException, () =>
{
cancellationToken.ThrowIfCancellationRequested();
var compilationContext = new CompilationEndAnalysisContext(_compilation, this.analyzerOptions, _addDiagnostic, cancellationToken);
......@@ -715,7 +715,7 @@ internal protected Action<Diagnostic> GetDiagnosticSinkWithSuppression(ISymbol s
if (!IsDiagnosticAnalyzerSuppressed(analyzer, analyzerManager, compilation.Options, addDiagnostic, continueOnAnalyzerException, cancellationToken))
{
var analyzerActions = await analyzerManager.GetAnalyzerActionsAsync(analyzer,
compilation, addDiagnostic, analyzerOptions, continueOnAnalyzerException, cancellationToken).ConfigureAwait(false);
compilation, analyzerOptions, continueOnAnalyzerException, cancellationToken).ConfigureAwait(false);
allAnalyzerActions = allAnalyzerActions.Append(analyzerActions);
}
}
......@@ -741,7 +741,7 @@ internal protected Action<Diagnostic> GetDiagnosticSinkWithSuppression(ISymbol s
return false;
}
var supportedDiagnostics = analyzerManager.GetSupportedDiagnosticDescriptors(analyzer, addDiagnostic, continueOnAnalyzerException, cancellationToken);
var supportedDiagnostics = analyzerManager.GetSupportedDiagnosticDescriptors(analyzer, continueOnAnalyzerException, cancellationToken);
var diagnosticOptions = options.SpecificDiagnosticOptions;
foreach (var diag in supportedDiagnostics)
......
......@@ -27,8 +27,7 @@ internal partial class SuppressMessageAttributeState
private GlobalSuppressions _lazyGlobalSuppressions;
private ConcurrentDictionary<ISymbol, ImmutableArray<string>> _localSuppressionsBySymbol = new ConcurrentDictionary<ISymbol, ImmutableArray<string>>();
private ISymbol _lazySuppressMessageAttribute;
private ConcurrentSet<string> _faultedAnalyzerMessages;
private class GlobalSuppressions
{
private readonly HashSet<string> _compilationWideSuppressions = new HashSet<string>();
......@@ -75,21 +74,6 @@ public SuppressMessageAttributeState(Compilation compilation)
public bool IsDiagnosticSuppressed(Diagnostic diagnostic, ISymbol symbolOpt = null)
{
// Suppress duplicate analyzer exception diagnostics from the analyzer driver.
if (diagnostic.CustomTags.Contains(WellKnownDiagnosticTags.AnalyzerException))
{
if (_faultedAnalyzerMessages == null)
{
Interlocked.CompareExchange(ref _faultedAnalyzerMessages, new ConcurrentSet<string>(), null);
}
var message = diagnostic.GetMessage();
if (!_faultedAnalyzerMessages.Add(message))
{
return true;
}
}
if (symbolOpt != null && IsDiagnosticSuppressed(diagnostic.Id, symbolOpt))
{
return true;
......
......@@ -1542,6 +1542,7 @@ abstract Microsoft.CodeAnalysis.Diagnostics.CompilationStartAnalysisContext.Regi
abstract Microsoft.CodeAnalysis.Diagnostics.DiagnosticAnalyzer.Initialize(Microsoft.CodeAnalysis.Diagnostics.AnalysisContext context)
abstract Microsoft.CodeAnalysis.Diagnostics.DiagnosticAnalyzer.SupportedDiagnostics.get
abstract Microsoft.CodeAnalysis.DocumentationProvider.GetDocumentationForSymbol(string documentationMemberID, System.Globalization.CultureInfo preferredCulture, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken))
abstract Microsoft.CodeAnalysis.LocalizableString.Equals(Microsoft.CodeAnalysis.LocalizableString other)
abstract Microsoft.CodeAnalysis.LocalizableString.ToString(System.IFormatProvider formatProvider)
abstract Microsoft.CodeAnalysis.Location.Kind.get
abstract Microsoft.CodeAnalysis.Metadata.CommonCopy()
......@@ -1747,7 +1748,10 @@ override Microsoft.CodeAnalysis.Emit.SemanticEdit.GetHashCode()
override Microsoft.CodeAnalysis.FileLinePositionSpan.Equals(object other)
override Microsoft.CodeAnalysis.FileLinePositionSpan.GetHashCode()
override Microsoft.CodeAnalysis.FileLinePositionSpan.ToString()
override Microsoft.CodeAnalysis.LocalizableResourceString.Equals(Microsoft.CodeAnalysis.LocalizableString other)
override Microsoft.CodeAnalysis.LocalizableResourceString.GetHashCode()
override Microsoft.CodeAnalysis.LocalizableResourceString.ToString(System.IFormatProvider formatProvider)
override Microsoft.CodeAnalysis.LocalizableString.Equals(object other)
override Microsoft.CodeAnalysis.Location.ToString()
override Microsoft.CodeAnalysis.MetadataReferenceProperties.Equals(object obj)
override Microsoft.CodeAnalysis.MetadataReferenceProperties.GetHashCode()
......@@ -1834,6 +1838,7 @@ override abstract Microsoft.CodeAnalysis.Diagnostic.Equals(object obj)
override abstract Microsoft.CodeAnalysis.Diagnostic.GetHashCode()
override abstract Microsoft.CodeAnalysis.DocumentationProvider.Equals(object obj)
override abstract Microsoft.CodeAnalysis.DocumentationProvider.GetHashCode()
override abstract Microsoft.CodeAnalysis.LocalizableString.GetHashCode()
override abstract Microsoft.CodeAnalysis.Location.Equals(object obj)
override abstract Microsoft.CodeAnalysis.Location.GetHashCode()
override abstract Microsoft.CodeAnalysis.MetadataReferenceResolver.Equals(object other)
......
......@@ -56,11 +56,11 @@ public static void VerifyAnalyzerEngineIsSafeAgainstExceptions(Func<DiagnosticAn
handled[i] = analyzer.Thrown ? true : (bool?)null;
if (analyzer.Thrown)
{
Assert.True(diagnostics.Any(d => AnalyzerDriverHelper.IsAnalyzerExceptionDiagnostic(d.Id, d.Descriptor.CustomTags)));
Assert.True(diagnostics.Any(AnalyzerDriverHelper.IsAnalyzerExceptionDiagnostic));
}
else
{
Assert.False(diagnostics.Any(d => AnalyzerDriverHelper.IsAnalyzerExceptionDiagnostic(d.Id, d.Descriptor.CustomTags)));
Assert.False(diagnostics.Any(AnalyzerDriverHelper.IsAnalyzerExceptionDiagnostic));
}
}
catch (DeliberateException)
......
......@@ -85,7 +85,7 @@ class C
}
}
[Fact(Skip = "Failing test due to AnalyzerManager checkin")]
[Fact]
public void DiagnosticAnalyzerDriverIsSafeAgainstAnalyzerExceptions()
{
var source = TestResource.AllInOneCSharpCode;
......
......@@ -2,11 +2,13 @@
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using System.Threading;
using Microsoft.CodeAnalysis.CSharp.Diagnostics;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.Diagnostics.EngineV1;
using Microsoft.CodeAnalysis.Editor.UnitTests.Diagnostics;
using Microsoft.CodeAnalysis.LanguageServices;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Microsoft.CodeAnalysis.Text;
......@@ -25,6 +27,8 @@ private static IEnumerable<Diagnostic> GetDiagnostics(DiagnosticAnalyzer analyze
// If no user diagnostic analyzer, then test compiler diagnostics.
var analyzer = analyzerOpt ?? DiagnosticExtensions.GetCompilerDiagnosticAnalyzer(project.Language);
var exceptionDiagnosticsSource = new TestHostDiagnosticUpdateSource(project.Solution.Workspace);
if (getDocumentDiagnostics)
{
var tree = document.GetSyntaxTreeAsync().Result;
......@@ -40,8 +44,13 @@ private static IEnumerable<Diagnostic> GetDiagnostics(DiagnosticAnalyzer analyze
// (a) If the span is contained within a method level member and analyzer supports semantic in span: analyze in member span.
// (b) Otherwise, analyze entire syntax tree span.
var spanToTest = root.FullSpan;
Action<DiagnosticAnalyzer, ImmutableArray<Diagnostic>, Project> reportExceptionDiagnostics =
(a, diagnostics, p) => builder.AddRange(diagnostics);
var driver = new DiagnosticAnalyzerDriver(document, spanToTest, root, syntaxNodeAnalyzerService: nodeInBodyAnalyzerService, cancellationToken: CancellationToken.None, testOnly_DonotCatchAnalyzerExceptions: donotCatchAnalyzerExceptions);
var driver = new DiagnosticAnalyzerDriver(document, spanToTest, root,
syntaxNodeAnalyzerService: nodeInBodyAnalyzerService,
cancellationToken: CancellationToken.None,
testOnly_DonotCatchAnalyzerExceptions: donotCatchAnalyzerExceptions);
var diagnosticAnalyzerCategory = analyzer.GetDiagnosticAnalyzerCategory(driver);
bool supportsSemanticInSpan = (diagnosticAnalyzerCategory & DiagnosticAnalyzerCategory.SemanticSpanAnalysis) != 0;
if (supportsSemanticInSpan)
......@@ -59,12 +68,12 @@ private static IEnumerable<Diagnostic> GetDiagnostics(DiagnosticAnalyzer analyze
if ((diagnosticAnalyzerCategory & DiagnosticAnalyzerCategory.SyntaxAnalysis) != 0)
{
builder.AddRange(driver.GetSyntaxDiagnosticsAsync(analyzer).Result ?? SpecializedCollections.EmptyEnumerable<Diagnostic>());
builder.AddRange(driver.GetSyntaxDiagnosticsAsync(analyzer).Result);
}
if (supportsSemanticInSpan || (diagnosticAnalyzerCategory & DiagnosticAnalyzerCategory.SemanticDocumentAnalysis) != 0)
{
builder.AddRange(driver.GetSemanticDiagnosticsAsync(analyzer).Result ?? SpecializedCollections.EmptyEnumerable<Diagnostic>());
builder.AddRange(driver.GetSemanticDiagnosticsAsync(analyzer).Result);
}
documentDiagnostics = builder.Where(d => d.Location == Location.None ||
......@@ -80,11 +89,13 @@ private static IEnumerable<Diagnostic> GetDiagnostics(DiagnosticAnalyzer analyze
if (analyzer.SupportsProjectDiagnosticAnalysis(driver))
{
projectDiagnostics = driver.GetProjectDiagnosticsAsync(analyzer, null).Result ?? SpecializedCollections.EmptyEnumerable<Diagnostic>();
projectDiagnostics = driver.GetProjectDiagnosticsAsync(analyzer, null).Result;
}
}
return documentDiagnostics.Concat(projectDiagnostics);
var exceptionDiagnostics = exceptionDiagnosticsSource.TestOnly_GetReportedDiagnostics(analyzer).Select(d => d.ToDiagnostic(tree: null));
return documentDiagnostics.Concat(projectDiagnostics).Concat(exceptionDiagnostics);
}
public static IEnumerable<Diagnostic> GetAllDiagnostics(DiagnosticAnalyzer providerOpt, Document document, TextSpan span, bool donotCatchAnalyzerExceptions = true)
......
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using Microsoft.CodeAnalysis.Diagnostics;
namespace Microsoft.CodeAnalysis.Editor.UnitTests.Diagnostics
{
internal class TestHostDiagnosticUpdateSource : AbstractHostDiagnosticUpdateSource
{
private readonly Workspace _workspace;
public TestHostDiagnosticUpdateSource(Workspace workspace)
{
_workspace = workspace;
}
protected override Workspace Workspace
{
get
{
return _workspace;
}
}
}
}
......@@ -243,6 +243,7 @@
<Compile Include="Diagnostics\GenerateType\TestProjectManagementService.cs" />
<Compile Include="Diagnostics\SuppressMessageAttributeTests.cs" />
<Compile Include="CodeFixes\ExtensionOrderingTests.cs" />
<Compile Include="Diagnostics\TestHostDiagnosticUpdateSource.cs" />
<Compile Include="Extensions\SourceTextContainerExtensionsTests.cs" />
<Compile Include="Preview\TestOnly_CompilerDiagnosticAnalyzerProviderService.cs" />
<Compile Include="DocCommentFormatting\DocCommentFormattingTests.cs" />
......@@ -364,4 +365,4 @@
<Import Project="..\..\..\build\Roslyn.Toolsets.Xunit.targets" />
<Import Project="$(SolutionDir)\.nuget\NuGet.targets" Condition="Exists('$(SolutionDir)\.nuget\NuGet.targets')" />
</ImportGroup>
</Project>
</Project>
\ No newline at end of file
......@@ -6,6 +6,7 @@ Imports System.Threading
Imports Microsoft.CodeAnalysis
Imports Microsoft.CodeAnalysis.Diagnostics
Imports Microsoft.CodeAnalysis.Diagnostics.EngineV1
Imports Microsoft.CodeAnalysis.Editor.UnitTests.Diagnostics
Imports Microsoft.CodeAnalysis.Editor.UnitTests.Workspaces
Imports Microsoft.CodeAnalysis.Text
Imports Roslyn.Utilities
......@@ -413,6 +414,7 @@ Namespace Microsoft.CodeAnalysis.Editor.Implementation.Diagnostics.UnitTests
Dim analyzerReference = New AnalyzerImageReference(ImmutableArray.Create(Of DiagnosticAnalyzer)(analyzer))
project = project.AddAnalyzerReference(analyzerReference)
Dim exceptionDiagnosticsSource = New TestHostDiagnosticUpdateSource(workspace)
Dim diagnosticService = New DiagnosticAnalyzerService()
Dim descriptorsMap = diagnosticService.GetDiagnosticDescriptors(project)
......@@ -424,9 +426,12 @@ Namespace Microsoft.CodeAnalysis.Editor.Implementation.Diagnostics.UnitTests
Dim diagnostics = diagnosticService.GetDiagnosticsForSpanAsync(document,
document.GetSyntaxRootAsync().WaitAndGetResult(CancellationToken.None).FullSpan,
CancellationToken.None).WaitAndGetResult(CancellationToken.None)
Assert.Equal(0, diagnostics.Count())
diagnostics = exceptionDiagnosticsSource.TestOnly_GetReportedDiagnostics(analyzer)
Assert.Equal(1, diagnostics.Count())
Dim diagnostic = diagnostics.First()
Assert.True(AnalyzerDriverHelper.IsAnalyzerExceptionDiagnostic(diagnostic.Id, diagnostic.CustomTags))
Assert.True(AnalyzerDriverHelper.IsAnalyzerExceptionDiagnostic(diagnostic.ToDiagnostic(document.GetSyntaxTreeAsync().Result)))
Assert.Contains("CodeBlockStartedAnalyzer", diagnostic.Message)
End Using
End Sub
......
......@@ -58,7 +58,7 @@ End Class
End Using
End Sub
<Fact(Skip:="Failing test due to AnalyzerManager checkin")>
<Fact>
Public Sub DiagnosticAnalyzerDriverIsSafeAgainstAnalyzerExceptions()
Dim source = TestResource.AllInOneVisualBasicCode
Using Workspace = VisualBasicWorkspaceFactory.CreateWorkspaceFromFile(source)
......
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections.Immutable;
using System.Linq;
using System.Threading;
using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis.Diagnostics
{
/// <summary>
/// Diagnostic update source for reporting workspace host specific diagnostics,
/// which may not be related to any given project/document in the solution.
/// For example, these include diagnostics generated for exceptions from third party analyzers.
/// </summary>
internal abstract partial class AbstractHostDiagnosticUpdateSource : IDiagnosticUpdateSource
{
private static ImmutableDictionary<DiagnosticAnalyzer, ImmutableHashSet<DiagnosticData>> _analyzerHostDiagnosticsMap =
ImmutableDictionary<DiagnosticAnalyzer, ImmutableHashSet<DiagnosticData>>.Empty;
protected abstract Workspace Workspace { get; }
public bool SupportGetDiagnostics
{
get
{
return false;
}
}
public ImmutableArray<DiagnosticData> GetDiagnostics(Workspace workspace, ProjectId projectId, DocumentId documentId, object id, CancellationToken cancellationToken)
{
return ImmutableArray<DiagnosticData>.Empty;
}
public event EventHandler<DiagnosticsUpdatedArgs> DiagnosticsUpdated;
protected void RaiseDiagnosticsUpdated(DiagnosticsUpdatedArgs args)
{
var updated = this.DiagnosticsUpdated;
if (updated != null)
{
updated(this, args);
}
}
private void OnAnalyzerExceptionDiagnostic(object sender, WorkspaceAnalyzerExceptionDiagnosticArgs args)
{
if (this.Workspace != args.Workspace)
{
return;
}
Contract.ThrowIfFalse(AnalyzerDriverHelper.IsAnalyzerExceptionDiagnostic(args.Diagnostic));
bool raiseDiagnosticsUpdated = true;
var diagnosticData = args.ProjectOpt != null ?
DiagnosticData.Create(args.ProjectOpt, args.Diagnostic) :
DiagnosticData.Create(args.Workspace, args.Diagnostic);
var dxs = ImmutableInterlocked.AddOrUpdate(ref _analyzerHostDiagnosticsMap,
args.FaultedAnalyzer,
ImmutableHashSet.Create(diagnosticData),
(a, existing) =>
{
var newDiags = existing.Add(diagnosticData);
raiseDiagnosticsUpdated = newDiags.Count > existing.Count;
return newDiags;
});
if (raiseDiagnosticsUpdated)
{
RaiseDiagnosticsUpdated(MakeArgs(args.FaultedAnalyzer, dxs, args.ProjectOpt));
}
}
public void ClearAnalyzerReferenceDiagnostics(AnalyzerFileReference analyzerReference, string language, ProjectId projectId)
{
foreach (var analyzer in analyzerReference.GetAnalyzers(language))
{
ClearAnalyzerDiagnostics(analyzer, projectId);
}
}
private void ClearAnalyzerDiagnostics(DiagnosticAnalyzer analyzer, ProjectId projectId)
{
ImmutableHashSet<DiagnosticData> existing;
if (!_analyzerHostDiagnosticsMap.TryGetValue(analyzer, out existing))
{
return;
}
// Check if analyzer is shared by analyzer references from different projects.
var sharedAnalyzer = existing.Contains(d => d.ProjectId != null && d.ProjectId != projectId);
if (sharedAnalyzer)
{
var newDiags = existing.Where(d => d.ProjectId != projectId).ToImmutableHashSet();
if (newDiags.Count < existing.Count &&
ImmutableInterlocked.TryUpdate(ref _analyzerHostDiagnosticsMap, analyzer, newDiags, existing))
{
var project = this.Workspace.CurrentSolution.GetProject(projectId);
RaiseDiagnosticsUpdated(MakeArgs(analyzer, ImmutableHashSet<DiagnosticData>.Empty, project));
}
}
else if (ImmutableInterlocked.TryRemove(ref _analyzerHostDiagnosticsMap, analyzer, out existing))
{
var project = this.Workspace.CurrentSolution.GetProject(projectId);
RaiseDiagnosticsUpdated(MakeArgs(analyzer, ImmutableHashSet<DiagnosticData>.Empty, project));
if (existing.Any(d => d.ProjectId == null))
{
RaiseDiagnosticsUpdated(MakeArgs(analyzer, ImmutableHashSet<DiagnosticData>.Empty, project: null));
}
}
}
private DiagnosticsUpdatedArgs MakeArgs(DiagnosticAnalyzer analyzer, ImmutableHashSet<DiagnosticData> items, Project project)
{
var id = WorkspaceAnalyzerManager.GetUniqueIdForAnalyzer(analyzer);
return new DiagnosticsUpdatedArgs(
id: Tuple.Create(this, id, project?.Id),
workspace: this.Workspace,
solution: project?.Solution,
projectId: project?.Id,
documentId: null,
diagnostics: items.ToImmutableArray());
}
internal ImmutableHashSet<DiagnosticData> TestOnly_GetReportedDiagnostics(DiagnosticAnalyzer analyzer)
{
ImmutableHashSet<DiagnosticData> diagnostics;
if (!_analyzerHostDiagnosticsMap.TryGetValue(analyzer, out diagnostics))
{
diagnostics = ImmutableHashSet<DiagnosticData>.Empty;
}
return diagnostics;
}
}
}
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections.Immutable;
namespace Microsoft.CodeAnalysis.Diagnostics
{
internal abstract partial class AbstractHostDiagnosticUpdateSource
{
internal static event EventHandler<WorkspaceAnalyzerExceptionDiagnosticArgs> AnalyzerExceptionDiagnostic;
protected AbstractHostDiagnosticUpdateSource()
{
// Register for exception diagnostics from workspace's analyzer manager.
AnalyzerExceptionDiagnostic += OnAnalyzerExceptionDiagnostic;
}
~AbstractHostDiagnosticUpdateSource()
{
// Unregister for exception diagnostics from workspace's analyzer manager.
AnalyzerExceptionDiagnostic -= OnAnalyzerExceptionDiagnostic;
}
internal static EventHandler<AnalyzerExceptionDiagnosticArgs> RegisterAnalyzerExceptionDiagnosticHandler(ImmutableArray<DiagnosticAnalyzer> analyzers, Workspace workspace)
{
return RegisterAnalyzerExceptionDiagnosticHandler(analyzers, workspace, project: null);
}
internal static EventHandler<AnalyzerExceptionDiagnosticArgs> RegisterAnalyzerExceptionDiagnosticHandler(ImmutableArray<DiagnosticAnalyzer> analyzers, Project project)
{
return RegisterAnalyzerExceptionDiagnosticHandler(analyzers, project.Solution.Workspace, project);
}
private static EventHandler<AnalyzerExceptionDiagnosticArgs> RegisterAnalyzerExceptionDiagnosticHandler(ImmutableArray<DiagnosticAnalyzer> analyzers, Workspace workspace, Project project)
{
Action<object, AnalyzerExceptionDiagnosticArgs> onAnalyzerExceptionDiagnostic = (sender, args) =>
ReportAnalyzerExceptionDiagnostic(sender, args, workspace, project);
return AnalyzerDriverHelper.RegisterAnalyzerExceptionDiagnosticHandler(analyzers, onAnalyzerExceptionDiagnostic);
}
internal static EventHandler<AnalyzerExceptionDiagnosticArgs> RegisterAnalyzerExceptionDiagnosticHandler(DiagnosticAnalyzer analyzer, Workspace workspace)
{
return RegisterAnalyzerExceptionDiagnosticHandler(analyzer, workspace, project: null);
}
internal static EventHandler<AnalyzerExceptionDiagnosticArgs> RegisterAnalyzerExceptionDiagnosticHandler(DiagnosticAnalyzer analyzer, Project project)
{
return RegisterAnalyzerExceptionDiagnosticHandler(analyzer, project.Solution.Workspace, project);
}
private static EventHandler<AnalyzerExceptionDiagnosticArgs> RegisterAnalyzerExceptionDiagnosticHandler(DiagnosticAnalyzer analyzer, Workspace workspace, Project project)
{
Action<object, AnalyzerExceptionDiagnosticArgs> onAnalyzerExceptionDiagnostic = (sender, args) =>
ReportAnalyzerExceptionDiagnostic(sender, args, workspace, project);
return AnalyzerDriverHelper.RegisterAnalyzerExceptionDiagnosticHandler(analyzer, onAnalyzerExceptionDiagnostic);
}
internal static void UnregisterAnalyzerExceptionDiagnosticHandler(EventHandler<AnalyzerExceptionDiagnosticArgs> handler)
{
AnalyzerDriverHelper.UnregisterAnalyzerExceptionDiagnosticHandler(handler);
}
internal static void ReportAnalyzerExceptionDiagnostic(object sender, DiagnosticAnalyzer analyzer, Diagnostic diagnostic, Workspace workspace)
{
ReportAnalyzerExceptionDiagnostic(sender, analyzer, diagnostic, workspace, project: null);
}
internal static void ReportAnalyzerExceptionDiagnostic(object sender, DiagnosticAnalyzer analyzer, Diagnostic diagnostic, Project project)
{
ReportAnalyzerExceptionDiagnostic(sender, analyzer, diagnostic, project.Solution.Workspace, project);
}
private static void ReportAnalyzerExceptionDiagnostic(object sender, DiagnosticAnalyzer analyzer, Diagnostic diagnostic, Workspace workspace, Project project)
{
var args = new WorkspaceAnalyzerExceptionDiagnosticArgs(analyzer, diagnostic, workspace, project);
AnalyzerExceptionDiagnostic?.Invoke(sender, args);
}
private static void ReportAnalyzerExceptionDiagnostic(object sender, AnalyzerExceptionDiagnosticArgs args, Workspace workspace, Project project)
{
var workspaceArgs = new WorkspaceAnalyzerExceptionDiagnosticArgs(args.FaultedAnalyzer, args.Diagnostic, workspace, project);
AnalyzerExceptionDiagnostic?.Invoke(sender, workspaceArgs);
}
}
}
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Threading;
......@@ -12,6 +13,11 @@ namespace Microsoft.CodeAnalysis.Diagnostics
{
internal abstract class BaseDiagnosticIncrementalAnalyzer : IIncrementalAnalyzer
{
protected BaseDiagnosticIncrementalAnalyzer(Workspace workspace)
{
this.Workspace = workspace;
}
#region IIncrementalAnalyzer
public abstract Task AnalyzeDocumentAsync(Document document, SyntaxNode bodyOpt, CancellationToken cancellationToken);
public abstract Task AnalyzeProjectAsync(Project project, bool semanticsChanged, CancellationToken cancellationToken);
......@@ -34,6 +40,8 @@ internal abstract class BaseDiagnosticIncrementalAnalyzer : IIncrementalAnalyzer
public abstract Task<IEnumerable<DiagnosticData>> GetDiagnosticsForSpanAsync(Document document, TextSpan range, CancellationToken cancellationToken);
#endregion
public Workspace Workspace { get; private set; }
public virtual bool NeedsReanalysisOnOptionChanged(object sender, OptionChangedEventArgs e)
{
return false;
......
......@@ -20,8 +20,8 @@ internal partial class DiagnosticAnalyzerService : IDiagnosticAnalyzerService
[ImportingConstructor]
public DiagnosticAnalyzerService([Import(AllowDefault = true)]IWorkspaceDiagnosticAnalyzerProviderService diagnosticAnalyzerProviderService = null)
: this(workspaceAnalyzerAssemblies: diagnosticAnalyzerProviderService != null ?
diagnosticAnalyzerProviderService.GetWorkspaceAnalyzerAssemblies() :
SpecializedCollections.EmptyEnumerable<string>())
diagnosticAnalyzerProviderService.GetWorkspaceAnalyzerAssemblies() :
SpecializedCollections.EmptyEnumerable<string>())
{
}
......
......@@ -60,7 +60,6 @@ private void OnDocumentActiveContextChanged(object sender, DocumentEventArgs e)
// internal for testing
internal class IncrementalAnalyzerDelegatee : BaseDiagnosticIncrementalAnalyzer
{
private readonly Workspace _workspace;
private readonly WorkspaceAnalyzerManager _workspaceAnalyzerManager;
private readonly DiagnosticAnalyzerService _owner;
......@@ -71,16 +70,16 @@ internal class IncrementalAnalyzerDelegatee : BaseDiagnosticIncrementalAnalyzer
private readonly EngineV2.DiagnosticIncrementalAnalyzer _engineV2;
public IncrementalAnalyzerDelegatee(DiagnosticAnalyzerService owner, Workspace workspace, WorkspaceAnalyzerManager workspaceAnalyzerManager)
: base(workspace)
{
_workspace = workspace;
_workspaceAnalyzerManager = workspaceAnalyzerManager;
_owner = owner;
var v1CorrelationId = LogAggregator.GetNextId();
_engineV1 = new EngineV1.DiagnosticIncrementalAnalyzer(_owner, v1CorrelationId, _workspace, _workspaceAnalyzerManager);
_engineV1 = new EngineV1.DiagnosticIncrementalAnalyzer(_owner, v1CorrelationId, workspace, _workspaceAnalyzerManager);
var v2CorrelationId = LogAggregator.GetNextId();
_engineV2 = new EngineV2.DiagnosticIncrementalAnalyzer(_owner, v2CorrelationId, _workspace, _workspaceAnalyzerManager);
_engineV2 = new EngineV2.DiagnosticIncrementalAnalyzer(_owner, v2CorrelationId, workspace, _workspaceAnalyzerManager);
}
#region IIncrementalAnalyzer
......@@ -180,7 +179,7 @@ public void TurnOff(bool useV2)
{
var turnedOffAnalyzer = GetAnalyzer(!useV2);
foreach (var project in _workspace.CurrentSolution.Projects)
foreach (var project in Workspace.CurrentSolution.Projects)
{
foreach (var document in project.Documents)
{
......@@ -196,7 +195,7 @@ internal BaseDiagnosticIncrementalAnalyzer Analyzer
{
get
{
var option = _workspace.Options.GetOption(InternalDiagnosticsOptions.UseDiagnosticEngineV2);
var option = Workspace.Options.GetOption(InternalDiagnosticsOptions.UseDiagnosticEngineV2);
return GetAnalyzer(option);
}
}
......
......@@ -282,6 +282,26 @@ private static void SwapIfNeeded(ref LinePosition startLinePosition, ref LinePos
}
}
public static DiagnosticData Create(Workspace workspace, Diagnostic diagnostic)
{
return new DiagnosticData(
diagnostic.Id,
diagnostic.Descriptor.Category,
diagnostic.GetMessage(CultureInfo.CurrentUICulture),
diagnostic.Descriptor.MessageFormat.ToString(USCultureInfo),
diagnostic.Severity,
diagnostic.DefaultSeverity,
diagnostic.Descriptor.IsEnabledByDefault,
diagnostic.WarningLevel,
diagnostic.Descriptor.CustomTags.AsImmutableOrEmpty(),
diagnostic.Properties,
workspace,
projectId: null,
title: diagnostic.Descriptor.Title.ToString(CultureInfo.CurrentUICulture),
description: diagnostic.Descriptor.Description.ToString(CultureInfo.CurrentUICulture),
helpLink: diagnostic.Descriptor.HelpLinkUri);
}
public static DiagnosticData Create(Project project, Diagnostic diagnostic)
{
if (diagnostic.Location.IsInSource)
......
......@@ -11,7 +11,6 @@
using Microsoft.CodeAnalysis.GeneratedCodeRecognition;
using Microsoft.CodeAnalysis.Internal.Log;
using Microsoft.CodeAnalysis.LanguageServices;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Microsoft.CodeAnalysis.Text;
using Roslyn.Utilities;
......@@ -80,10 +79,7 @@ public DiagnosticAnalyzerDriver(Project project, LogAggregator logAggregator, Ca
}
// internal for testing purposes
internal DiagnosticAnalyzerDriver(
Project project,
ISyntaxNodeAnalyzerService syntaxNodeAnalyzerService,
CancellationToken cancellationToken)
internal DiagnosticAnalyzerDriver(Project project, ISyntaxNodeAnalyzerService syntaxNodeAnalyzerService, CancellationToken cancellationToken)
{
_project = project;
_cancellationToken = cancellationToken;
......@@ -237,12 +233,11 @@ private ImmutableArray<SyntaxNode> GetSyntaxNodesToAnalyze()
return _lazyAllSyntaxNodesToAnalyze;
}
public async Task<IEnumerable<Diagnostic>> GetSyntaxDiagnosticsAsync(DiagnosticAnalyzer analyzer)
public async Task<ImmutableArray<Diagnostic>> GetSyntaxDiagnosticsAsync(DiagnosticAnalyzer analyzer)
{
var compilation = _document.Project.SupportsCompilation ? await _document.Project.GetCompilationAsync(_cancellationToken).ConfigureAwait(false) : null;
Contract.ThrowIfNull(_document);
Contract.ThrowIfFalse(analyzer.SupportsSyntaxDiagnosticAnalysis(this));
using (var pooledObject = SharedPools.Default<List<Diagnostic>>().GetPooledObject())
{
......@@ -259,12 +254,19 @@ public async Task<IEnumerable<Diagnostic>> GetSyntaxDiagnosticsAsync(DiagnosticA
}
catch (Exception e) when (CatchAnalyzerException(e, analyzer))
{
var exceptionDiagnostics = AnalyzerExceptionToDiagnostics(analyzer, e, _cancellationToken);
return GetFilteredDocumentDiagnostics(exceptionDiagnostics, compilation).ToImmutableArray();
var exceptionDiagnostic = AnalyzerExceptionToDiagnostic(analyzer, e, _cancellationToken);
if (exceptionDiagnostic != null)
{
ReportAnalyzerExceptionDiagnostic(analyzer, exceptionDiagnostic, compilation);
}
return ImmutableArray<Diagnostic>.Empty;
}
}
var analyzerActions = await this.GetAnalyzerActionsAsync(analyzer, diagnostics.Add).ConfigureAwait(false);
var handler = AbstractHostDiagnosticUpdateSource.RegisterAnalyzerExceptionDiagnosticHandler(analyzer, this.Project);
var analyzerActions = await this.GetAnalyzerActionsCoreAsync(analyzer).ConfigureAwait(false);
DiagnosticAnalyzerLogger.UpdateAnalyzerTypeCount(analyzer, analyzerActions, (DiagnosticLogAggregator)_logAggregator);
......@@ -279,9 +281,10 @@ public async Task<IEnumerable<Diagnostic>> GetSyntaxDiagnosticsAsync(DiagnosticA
if (diagnostics.Count == 0)
{
return SpecializedCollections.EmptyEnumerable<Diagnostic>();
return ImmutableArray<Diagnostic>.Empty;
}
AbstractHostDiagnosticUpdateSource.UnregisterAnalyzerExceptionDiagnosticHandler(handler);
return GetFilteredDocumentDiagnostics(diagnostics, compilation).ToImmutableArray();
}
}
......@@ -307,21 +310,50 @@ private IEnumerable<Diagnostic> GetFilteredDocumentDiagnosticsCore(IEnumerable<D
: CompilationWithAnalyzers.GetEffectiveDiagnostics(diagsFilteredByLocation, compilation);
}
public async Task<AnalyzerActions> GetAnalyzerActionsAsync(DiagnosticAnalyzer analyzer, Action<Diagnostic> addDiagnostic)
internal void ReportAnalyzerExceptionDiagnostic(DiagnosticAnalyzer analyzer, Diagnostic exceptionDiagnostic, Compilation compilation)
{
Contract.ThrowIfFalse(_project.SupportsCompilation);
Contract.ThrowIfFalse(AnalyzerDriverHelper.IsAnalyzerExceptionDiagnostic(exceptionDiagnostic));
if (compilation != null)
{
var effectiveDiagnostic = CompilationWithAnalyzers.GetEffectiveDiagnostics(ImmutableArray.Create(exceptionDiagnostic), compilation).SingleOrDefault();
if (effectiveDiagnostic == null)
{
return;
}
else
{
exceptionDiagnostic = effectiveDiagnostic;
}
}
AbstractHostDiagnosticUpdateSource.ReportAnalyzerExceptionDiagnostic(this, analyzer, exceptionDiagnostic, this.Project);
}
var compilation = await _project.GetCompilationAsync(_cancellationToken).ConfigureAwait(false);
var analyzerActions = await AnalyzerManager.Default.GetAnalyzerActionsAsync(analyzer, compilation, addDiagnostic, _analyzerOptions, CatchAnalyzerException, _cancellationToken).ConfigureAwait(false);
public async Task<AnalyzerActions> GetAnalyzerActionsAsync(DiagnosticAnalyzer analyzer)
{
var handler = AbstractHostDiagnosticUpdateSource.RegisterAnalyzerExceptionDiagnosticHandler(analyzer, this.Project);
var actions = await GetAnalyzerActionsCoreAsync(analyzer).ConfigureAwait(false);
AbstractHostDiagnosticUpdateSource.UnregisterAnalyzerExceptionDiagnosticHandler(handler);
return actions;
}
private async Task<AnalyzerActions> GetAnalyzerActionsCoreAsync(DiagnosticAnalyzer analyzer)
{
var compilation = _project.SupportsCompilation ?
await _project.GetCompilationAsync(_cancellationToken).ConfigureAwait(false) :
null;
var analyzerActions = await AnalyzerManager.Default.GetAnalyzerActionsAsync(analyzer, compilation, _analyzerOptions, CatchAnalyzerException, _cancellationToken).ConfigureAwait(false);
DiagnosticAnalyzerLogger.UpdateAnalyzerTypeCount(analyzer, analyzerActions, (DiagnosticLogAggregator)_logAggregator);
return analyzerActions;
}
public async Task<IEnumerable<Diagnostic>> GetSemanticDiagnosticsAsync(DiagnosticAnalyzer analyzer)
public async Task<ImmutableArray<Diagnostic>> GetSemanticDiagnosticsAsync(DiagnosticAnalyzer analyzer)
{
var model = await _document.GetSemanticModelAsync(_cancellationToken).ConfigureAwait(false);
var compilation = model?.Compilation;
Contract.ThrowIfNull(_document);
Contract.ThrowIfFalse(analyzer.SupportsSemanticDiagnosticAnalysis(this));
using (var pooledObject = SharedPools.Default<List<Diagnostic>>().GetPooledObject())
{
......@@ -343,14 +375,20 @@ public async Task<IEnumerable<Diagnostic>> GetSemanticDiagnosticsAsync(Diagnosti
}
catch (Exception e) when (CatchAnalyzerException(e, analyzer))
{
var exceptionDiagnostics = AnalyzerExceptionToDiagnostics(analyzer, e, _cancellationToken);
return model == null ? exceptionDiagnostics : GetFilteredDocumentDiagnostics(exceptionDiagnostics, model.Compilation);
var exceptionDiagnostic = AnalyzerExceptionToDiagnostic(analyzer, e, _cancellationToken);
if (exceptionDiagnostic != null)
{
ReportAnalyzerExceptionDiagnostic(analyzer, exceptionDiagnostic, compilation);
}
return ImmutableArray<Diagnostic>.Empty;
}
}
else
{
var analyzerActions = await GetAnalyzerActionsAsync(analyzer, diagnostics.Add).ConfigureAwait(false);
var handler = AbstractHostDiagnosticUpdateSource.RegisterAnalyzerExceptionDiagnosticHandler(analyzer, this.Project);
var analyzerActions = await GetAnalyzerActionsCoreAsync(analyzer).ConfigureAwait(false);
if (analyzerActions != null)
{
// SemanticModel actions.
......@@ -360,8 +398,6 @@ public async Task<IEnumerable<Diagnostic>> GetSemanticDiagnosticsAsync(Diagnosti
diagnostics.Add, CatchAnalyzerException, _cancellationToken);
}
var compilation = model.Compilation;
// Symbol actions.
if (analyzerActions.SymbolActionsCount > 0)
{
......@@ -387,22 +423,19 @@ public async Task<IEnumerable<Diagnostic>> GetSemanticDiagnosticsAsync(Diagnosti
}
}
}
}
var result = model == null
? diagnostics
: GetFilteredDocumentDiagnostics(diagnostics, model.Compilation);
AbstractHostDiagnosticUpdateSource.UnregisterAnalyzerExceptionDiagnosticHandler(handler);
}
return result.ToImmutableArray();
return GetFilteredDocumentDiagnostics(diagnostics, compilation).ToImmutableArray();
}
}
public async Task<IEnumerable<Diagnostic>> GetProjectDiagnosticsAsync(DiagnosticAnalyzer analyzer, Action<Project, DiagnosticAnalyzer, CancellationToken> forceAnalyzeAllDocuments)
public async Task<ImmutableArray<Diagnostic>> GetProjectDiagnosticsAsync(DiagnosticAnalyzer analyzer, Action<Project, DiagnosticAnalyzer, CancellationToken> forceAnalyzeAllDocuments)
{
Contract.ThrowIfNull(_project);
Contract.ThrowIfFalse(_document == null);
Contract.ThrowIfFalse(analyzer.SupportsProjectDiagnosticAnalysis(this));
using (var diagnostics = SharedPools.Default<List<Diagnostic>>().GetPooledObject())
{
if (_project.SupportsCompilation)
......@@ -430,14 +463,12 @@ private async Task GetProjectDiagnosticsWorkerAsync(DiagnosticAnalyzer analyzer,
}
catch (Exception e) when (CatchAnalyzerException(e, analyzer))
{
var exceptionDiagnostics = AnalyzerExceptionToDiagnostics(analyzer, e, _cancellationToken);
if (_project.SupportsCompilation)
var exceptionDiagnostic = AnalyzerExceptionToDiagnostic(analyzer, e, _cancellationToken);
if (exceptionDiagnostic != null)
{
var compilation = await _project.GetCompilationAsync(_cancellationToken).ConfigureAwait(false);
exceptionDiagnostics = CompilationWithAnalyzers.GetEffectiveDiagnostics(exceptionDiagnostics, compilation).ToImmutableArray();
ReportAnalyzerExceptionDiagnostic(analyzer, exceptionDiagnostic, compilation);
}
diagnostics.AddRange(exceptionDiagnostics);
}
}
......@@ -449,8 +480,10 @@ private async Task GetCompilationDiagnosticsAsync(DiagnosticAnalyzer analyzer, L
{
var localDiagnostics = pooledObject.Object;
var handler = AbstractHostDiagnosticUpdateSource.RegisterAnalyzerExceptionDiagnosticHandler(analyzer, this.Project);
// Get all the analyzer actions, including the per-compilation actions.
var analyzerActions = await GetAnalyzerActionsAsync(analyzer, localDiagnostics.Add).ConfigureAwait(false);
var analyzerActions = await GetAnalyzerActionsCoreAsync(analyzer).ConfigureAwait(false);
if (analyzerActions.CompilationEndActionsCount > 0 && analyzerActions.CompilationStartActionsCount > 0 && forceAnalyzeAllDocuments != null)
{
......@@ -470,19 +503,22 @@ private async Task GetCompilationDiagnosticsAsync(DiagnosticAnalyzer analyzer, L
// CompilationEnd actions.
var compilation = await _project.GetCompilationAsync(_cancellationToken).ConfigureAwait(false);
AnalyzerDriverHelper.ExecuteCompilationEndActions(analyzerActions, compilation, _analyzerOptions, localDiagnostics.Add, CatchAnalyzerException, _cancellationToken);
diagnostics.AddRange(CompilationWithAnalyzers.GetEffectiveDiagnostics(localDiagnostics, compilation));
AbstractHostDiagnosticUpdateSource.UnregisterAnalyzerExceptionDiagnosticHandler(handler);
var filteredDiagnostics = CompilationWithAnalyzers.GetEffectiveDiagnostics(localDiagnostics, compilation);
diagnostics.AddRange(filteredDiagnostics);
}
}
private static ImmutableArray<Diagnostic> AnalyzerExceptionToDiagnostics(DiagnosticAnalyzer analyzer, Exception e, CancellationToken cancellationToken)
private static Diagnostic AnalyzerExceptionToDiagnostic(DiagnosticAnalyzer analyzer, Exception e, CancellationToken cancellationToken)
{
if (!IsCanceled(e, cancellationToken))
{
// Create a info diagnostic saying that the analyzer failed
return ImmutableArray.Create(AnalyzerDriverHelper.GetAnalyzerDiagnostic(analyzer, e));
return AnalyzerDriverHelper.GetAnalyzerDiagnostic(analyzer, e);
}
return ImmutableArray<Diagnostic>.Empty;
return null;
}
private static bool IsCanceled(Exception e, CancellationToken cancellationToken)
......
......@@ -171,21 +171,9 @@ internal static DiagnosticState GetOrCreateDiagnosticState(DiagnosticState[,] di
Contract.ThrowIfNull(provider);
// Get the unique ID for given diagnostic analyzer.
// note that we also put version stamp so that we can detect changed provider
var providerType = provider.GetType();
var location = providerType.Assembly.Location;
return ValueTuple.Create(UserDiagnosticsPrefixTableName + "_" + type.ToString() + "_" + providerType.AssemblyQualifiedName, GetProviderVersion(location));
}
private static VersionStamp GetProviderVersion(string path)
{
if (path == null || !File.Exists(path))
{
return VersionStamp.Default;
}
return VersionStamp.Create(File.GetLastWriteTimeUtc(path));
// note that we also put version stamp so that we can detect changed analyzer.
var tuple = WorkspaceAnalyzerManager.GetUniqueIdForAnalyzer(provider);
return ValueTuple.Create(UserDiagnosticsPrefixTableName + "_" + type.ToString() + "_" + tuple.Item1, tuple.Item2);
}
public DiagnosticState GetDiagnosticState(StateType stateType, ProviderId providerId, ProjectId projectId, string language)
......
......@@ -36,6 +36,7 @@ internal partial class DiagnosticIncrementalAnalyzer : BaseDiagnosticIncremental
private DiagnosticLogAggregator _diagnosticLogAggregator;
public DiagnosticIncrementalAnalyzer(DiagnosticAnalyzerService owner, int correlationId, Workspace workspace, WorkspaceAnalyzerManager workspaceAnalyzerManager)
: base(workspace)
{
_owner = owner;
_correlationId = correlationId;
......
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
......@@ -14,14 +15,13 @@ internal class DiagnosticIncrementalAnalyzer : BaseDiagnosticIncrementalAnalyzer
{
private readonly int _correlationId;
private readonly DiagnosticAnalyzerService _owner;
private readonly Workspace _workspace;
private readonly WorkspaceAnalyzerManager _workspaceAnalyzerManager;
public DiagnosticIncrementalAnalyzer(DiagnosticAnalyzerService owner, int correlationId, Workspace workspace, WorkspaceAnalyzerManager workspaceAnalyzerManager)
: base(workspace)
{
_correlationId = correlationId;
_owner = owner;
_workspace = workspace;
_workspaceAnalyzerManager = workspaceAnalyzerManager;
}
......@@ -61,13 +61,13 @@ public override Task NewSolutionSnapshotAsync(Solution solution, CancellationTok
public override void RemoveDocument(DocumentId documentId)
{
_owner.RaiseDiagnosticsUpdated(
this, new DiagnosticsUpdatedArgs(ValueTuple.Create(this, documentId), _workspace, null, null, null, ImmutableArray<DiagnosticData>.Empty));
this, new DiagnosticsUpdatedArgs(ValueTuple.Create(this, documentId), Workspace, null, null, null, ImmutableArray<DiagnosticData>.Empty));
}
public override void RemoveProject(ProjectId projectId)
{
_owner.RaiseDiagnosticsUpdated(
this, new DiagnosticsUpdatedArgs(ValueTuple.Create(this, projectId), _workspace, null, null, null, ImmutableArray<DiagnosticData>.Empty));
this, new DiagnosticsUpdatedArgs(ValueTuple.Create(this, projectId), Workspace, null, null, null, ImmutableArray<DiagnosticData>.Empty));
}
#endregion
......@@ -156,11 +156,17 @@ private async Task<ImmutableArray<DiagnosticData>> GetProjectDiagnosticsAsync(Pr
var analyzers = _workspaceAnalyzerManager.CreateDiagnosticAnalyzers(project);
var handler = AbstractHostDiagnosticUpdateSource.RegisterAnalyzerExceptionDiagnosticHandler(analyzers, project);
var compilationWithAnalyzer = compilation.WithAnalyzers(analyzers, project.AnalyzerOptions, cancellationToken);
// REVIEW: this API is a bit strange.
// if getting diagnostic is cancelled, it has to create new compilation and do everything from scretch again?
return GetDiagnosticData(project, await compilationWithAnalyzer.GetAnalyzerDiagnosticsAsync().ConfigureAwait(false)).ToImmutableArrayOrEmpty();
var dxs = GetDiagnosticData(project, await compilationWithAnalyzer.GetAnalyzerDiagnosticsAsync().ConfigureAwait(false)).ToImmutableArrayOrEmpty();
AbstractHostDiagnosticUpdateSource.UnregisterAnalyzerExceptionDiagnosticHandler(handler);
return dxs;
}
private IEnumerable<DiagnosticData> GetDiagnosticData(Project project, ImmutableArray<Diagnostic> diagnostics)
......
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
namespace Microsoft.CodeAnalysis.Diagnostics
{
internal class WorkspaceAnalyzerExceptionDiagnosticArgs : EventArgs
{
public readonly Diagnostic Diagnostic;
public readonly DiagnosticAnalyzer FaultedAnalyzer;
public readonly Workspace Workspace;
public readonly Project ProjectOpt;
public WorkspaceAnalyzerExceptionDiagnosticArgs(AnalyzerExceptionDiagnosticArgs args, Workspace workspace, Project projectOpt = null)
: this(args.FaultedAnalyzer, args.Diagnostic, workspace, projectOpt)
{
}
public WorkspaceAnalyzerExceptionDiagnosticArgs(DiagnosticAnalyzer analyzer, Diagnostic diagnostic, Workspace workspace, Project projectOpt = null)
{
this.FaultedAnalyzer = analyzer;
this.Diagnostic = diagnostic;
this.Workspace = workspace;
this.ProjectOpt = projectOpt;
}
}
}
......@@ -4,9 +4,9 @@
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Threading;
using Microsoft.CodeAnalysis.Diagnostics.Log;
using Roslyn.Utilities;
......@@ -22,7 +22,7 @@ namespace Microsoft.CodeAnalysis.Diagnostics
///
/// this should be alway thread-safe.
/// </summary>
internal sealed class WorkspaceAnalyzerManager
internal sealed partial class WorkspaceAnalyzerManager
{
/// <summary>
/// Key is analyzer reference identity <see cref="GetAnalyzerReferenceIdentity(AnalyzerReference)"/>.
......@@ -76,10 +76,9 @@ public string GetAnalyzerReferenceIdentity(AnalyzerReference reference)
public ImmutableArray<DiagnosticDescriptor> GetDiagnosticDescriptors(DiagnosticAnalyzer analyzer)
{
// TODO: report diagnostics from exceptions thrown in DiagnosticAnalyzer.SupportedDiagnostics
Action<Diagnostic> dummyAddDiagnostic = _ => { };
Func<Exception, DiagnosticAnalyzer, bool> continueOnAnalyzerException = (ex, a) => !AnalyzerHelper.IsBuiltInAnalyzer(analyzer);
return AnalyzerManager.Default.GetSupportedDiagnosticDescriptors(analyzer, dummyAddDiagnostic, continueOnAnalyzerException, CancellationToken.None);
return AnalyzerManager.Default.GetSupportedDiagnosticDescriptors(analyzer, continueOnAnalyzerException, CancellationToken.None);
}
/// <summary>
......@@ -172,6 +171,24 @@ public ImmutableArray<DiagnosticAnalyzer> CreateDiagnosticAnalyzers(Project proj
return builder.ToImmutable();
}
internal static ValueTuple<string, VersionStamp> GetUniqueIdForAnalyzer(DiagnosticAnalyzer analyzer)
{
// Get the unique ID for given diagnostic analyzer.
// note that we also put version stamp so that we can detect changed analyzer.
var type = analyzer.GetType();
return ValueTuple.Create(type.AssemblyQualifiedName, GetProviderVersion(type.Assembly.Location));
}
private static VersionStamp GetProviderVersion(string path)
{
if (path == null || !File.Exists(path))
{
return VersionStamp.Default;
}
return VersionStamp.Create(File.GetLastWriteTimeUtc(path));
}
private static string GetAnalyzerReferenceId(AnalyzerReference reference)
{
return reference.Display ?? FeaturesResources.Unknown;
......
......@@ -177,7 +177,10 @@
<Compile Include="Completion\Providers\KeywordCompletionItem.cs" />
<Compile Include="Completion\Providers\RecommendedKeyword.cs" />
<Compile Include="Diagnostics\AnalyzerDriverResources.cs" />
<Compile Include="Diagnostics\WorkspaceAnalyzerExceptionDiagnosticArgs.cs" />
<Compile Include="Diagnostics\AnalyzerHelper.cs" />
<Compile Include="Diagnostics\AbstractHostDiagnosticUpdateSource.cs" />
<Compile Include="Diagnostics\AbstractHostDiagnosticUpdateSource_Registration.cs" />
<Compile Include="Diagnostics\WorkspaceAnalyzerManager.cs" />
<Compile Include="Diagnostics\Analyzers\IDEDiagnosticIds.cs" />
<Compile Include="Diagnostics\BaseDiagnosticIncrementalAnalyzer.cs" />
......@@ -537,7 +540,9 @@
<Compile Include="CodeFixes\Suppression\SuppressionCodeAction.cs" />
<None Include="packages.config" />
</ItemGroup>
<ItemGroup />
<ItemGroup>
<PublicAPI Include="PublicAPI.txt" />
</ItemGroup>
<Import Project="..\..\Compilers\Core\AnalyzerDriver\AnalyzerDriver.projitems" Label="Shared" />
<ImportGroup Label="Targets">
<Import Project="..\..\Tools\Microsoft.CodeAnalysis.Toolset.Open\Targets\VSL.Imports.targets" />
......
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Collections.Immutable;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.Diagnostics.EngineV1;
using Roslyn.Utilities;
......@@ -26,12 +27,7 @@ public static DiagnosticAnalyzerCategory GetDiagnosticAnalyzerCategory(this Diag
// to be able to operate on a limited span of the document. In practical terms, no analyzer
// can have both SemanticDocumentAnalysis and SemanticSpanAnalysis as categories.
bool cantSupportSemanticSpanAnalysis = false;
var analyzerActions = AnalyzerManager.Default.GetAnalyzerActionsAsync(analyzer,
null,
_ => { },
null,
driver.CatchAnalyzerExceptionHandler,
driver.CancellationToken).WaitAndGetResult(driver.CancellationToken);
var analyzerActions = driver.GetAnalyzerActionsAsync(analyzer).WaitAndGetResult(driver.CancellationToken);
if (analyzerActions != null)
{
if (analyzerActions.SyntaxTreeActionsCount > 0)
......
......@@ -328,8 +328,8 @@ Global
Compilers\Core\AnalyzerDriver\AnalyzerDriver.projitems*{d0bc9be7-24f6-40ca-8dc6-fcb93bd44b34}*SharedItemsImports = 13
Compilers\VisualBasic\BasicAnalyzerDriver\BasicAnalyzerDriver.projitems*{a1bcd0ce-6c2f-4f8c-9a48-d9d93928e26d}*SharedItemsImports = 4
Compilers\Core\SharedCollections\SharedCollections.projitems*{afde6bea-5038-4a4a-a88e-dbd2e4088eed}*SharedItemsImports = 4
Compilers\Core\AnalyzerDriver\AnalyzerDriver.projitems*{1ee8cad3-55f9-4d91-96b2-084641da9a6c}*SharedItemsImports = 4
Compilers\Core\SharedCollections\SharedCollections.projitems*{1ee8cad3-55f9-4d91-96b2-084641da9a6c}*SharedItemsImports = 4
Compilers\Core\AnalyzerDriver\AnalyzerDriver.projitems*{1ee8cad3-55f9-4d91-96b2-084641da9a6c}*SharedItemsImports = 4
Compilers\CSharp\CSharpAnalyzerDriver\CSharpAnalyzerDriver.projitems*{3973b09a-4fbf-44a5-8359-3d22ceb71f71}*SharedItemsImports = 4
Compilers\CSharp\CSharpAnalyzerDriver\CSharpAnalyzerDriver.projitems*{b501a547-c911-4a05-ac6e-274a50dff30e}*SharedItemsImports = 4
Compilers\VisualBasic\BasicAnalyzerDriver\BasicAnalyzerDriver.projitems*{2523d0e6-df32-4a3e-8ae0-a19bffae2ef6}*SharedItemsImports = 4
......
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.IO;
......@@ -11,6 +12,7 @@
using Microsoft.CodeAnalysis.Emit;
using Microsoft.CodeAnalysis.Test.Utilities;
using Roslyn.Test.Utilities;
using Roslyn.Utilities;
using Xunit;
namespace Microsoft.CodeAnalysis
......@@ -132,11 +134,18 @@ public static ImmutableArray<Diagnostic> GetAnalyzerDiagnostics<TCompilation>(th
{
// We want unit tests to throw if any analyzer OR the driver throws, unless the test explicitly provides a delegate.
continueOnAnalyzerException = continueOnAnalyzerException ?? DonotCatchAnalyzerExceptions;
var analyzersArray = analyzers.ToImmutableArray();
var exceptionDiagnostics = new ConcurrentSet<Diagnostic>();
var handler = AnalyzerDriverHelper.RegisterAnalyzerExceptionDiagnosticHandler(analyzersArray, exceptionDiagnostics.Add);
Compilation newCompilation;
var driver = AnalyzerDriver.Create(c, analyzers.ToImmutableArray(), options, AnalyzerManager.Default, out newCompilation, continueOnAnalyzerException, CancellationToken.None);
var driver = AnalyzerDriver.Create(c, analyzersArray, options, AnalyzerManager.Default, out newCompilation, continueOnAnalyzerException, CancellationToken.None);
var discarded = newCompilation.GetDiagnostics();
diagnostics = driver.GetDiagnosticsAsync().Result;
diagnostics = driver.GetDiagnosticsAsync().Result.AddRange(exceptionDiagnostics);
AnalyzerDriverHelper.UnregisterAnalyzerExceptionDiagnosticHandler(handler);
return (TCompilation)newCompilation; // note this is a new compilation
}
......
......@@ -117,14 +117,17 @@ private void OnAnalyzerLoadError(object sender, AnalyzerLoadFailureEventArgs e)
public void Dispose()
{
if (_analyzerReference is AnalyzerFileReference)
var analyzerFileReference = _analyzerReference as AnalyzerFileReference;
if (analyzerFileReference != null)
{
((AnalyzerFileReference)_analyzerReference).AnalyzerLoadFailed -= OnAnalyzerLoadError;
analyzerFileReference.AnalyzerLoadFailed -= OnAnalyzerLoadError;
if (_analyzerLoadErrors != null && _analyzerLoadErrors.Count > 0)
{
_hostDiagnosticUpdateSource.ClearDiagnosticsForProject(_projectId, this);
}
_hostDiagnosticUpdateSource.ClearAnalyzerReferenceDiagnostics(analyzerFileReference, _language, _projectId);
}
_analyzerLoadErrors = null;
......
......@@ -236,7 +236,7 @@ protected override ImmutableArray<ITrackingPoint> GetTrackingPoints(ImmutableArr
private int GetProjectRank(ProjectId projectId)
{
var rank = 0;
if (_source._projectRanks.TryGetValue(projectId, out rank))
if (projectId != null &&_source._projectRanks.TryGetValue(projectId, out rank))
{
return rank;
}
......
......@@ -395,7 +395,8 @@ public void AddNewErrors(DocumentId documentId, DiagnosticData diagnostic)
}
private void GetLiveProjectAndDocumentErrors(
out IDictionary<ProjectId, IList<DiagnosticData>> projectErrors, out IDictionary<DocumentId, IList<DiagnosticData>> documentErrors)
out IDictionary<ProjectId, IList<DiagnosticData>> projectErrors,
out IDictionary<DocumentId, IList<DiagnosticData>> documentErrors)
{
projectErrors = null;
documentErrors = null;
......
......@@ -14,41 +14,36 @@ namespace Microsoft.VisualStudio.LanguageServices.Implementation.TaskList
{
[Export(typeof(IDiagnosticUpdateSource))]
[Export(typeof(HostDiagnosticUpdateSource))]
internal sealed class HostDiagnosticUpdateSource : IDiagnosticUpdateSource
internal sealed class HostDiagnosticUpdateSource : AbstractHostDiagnosticUpdateSource
{
private readonly VisualStudioWorkspaceImpl _workspace;
private readonly Dictionary<ProjectId, HashSet<object>> _diagnosticMap = new Dictionary<ProjectId, HashSet<object>>();
[ImportingConstructor]
public HostDiagnosticUpdateSource(
VisualStudioWorkspaceImpl workspace)
public HostDiagnosticUpdateSource(VisualStudioWorkspaceImpl workspace)
{
_workspace = workspace;
}
public event EventHandler<DiagnosticsUpdatedArgs> DiagnosticsUpdated;
public bool SupportGetDiagnostics { get { return false; } }
public ImmutableArray<DiagnosticData> GetDiagnostics(Workspace workspace, ProjectId projectId, DocumentId documentId, object id, CancellationToken cancellationToken)
protected override Workspace Workspace
{
return ImmutableArray<DiagnosticData>.Empty;
get
{
return _workspace;
}
}
private void RaiseDiagnosticsUpdatedForProject(ProjectId projectId, object key, IEnumerable<DiagnosticData> items)
{
var diagnosticsUpdated = DiagnosticsUpdated;
if (diagnosticsUpdated != null)
{
diagnosticsUpdated(this, new DiagnosticsUpdatedArgs(
id: Tuple.Create(this, projectId, key),
workspace: _workspace,
solution: null,
projectId: projectId,
documentId: null,
diagnostics: items.AsImmutableOrEmpty()));
}
var args = new DiagnosticsUpdatedArgs(
id: Tuple.Create(this, projectId, key),
workspace: _workspace,
solution: null,
projectId: projectId,
documentId: null,
diagnostics: items.AsImmutableOrEmpty());
RaiseDiagnosticsUpdated(args);
}
public void UpdateDiagnosticsForProject(ProjectId projectId, object key, IEnumerable<DiagnosticData> items)
......@@ -77,7 +72,7 @@ public void ClearAllDiagnosticsForProject(ProjectId projectId)
{
RaiseDiagnosticsUpdatedForProject(projectId, key, SpecializedCollections.EmptyEnumerable<DiagnosticData>());
}
}
}
}
public void ClearDiagnosticsForProject(ProjectId projectId, object key)
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册