提交 96bbb86d 编写于 作者: M Manish Vasani

Merge pull request #3002 from mavasani/AnalyzerDriverNullCheck

Handle analyzers which have enabled rules, but no registered actions.

Customer scenario: Analyzer author writes a bolier plate analyzer that defines the rule that it intends to implement in future, but registers no analyzer actions for analysis. Adding such an analyzer to your analyzer assembly brings down the entire analysis, and no analyzers execute during command line build. The issue was reported for StyleCopAnalyzers package which has many such to-be-implemented analyzers.

Fixes #2980

Fix description: Add a null check around the place we compute set of all registered actions for analyzers.

Testing: Added regression tests + existing tests.
......@@ -1070,12 +1070,39 @@ void Method()
}
}";
var analyzers = new DiagnosticAnalyzer[] { new CSharpGenericNameAnalyzer() };
TestGenericNameCore(source, new CSharpGenericNameAnalyzer());
}
private void TestGenericNameCore(string source, params DiagnosticAnalyzer[] analyzers)
{
// Verify, no duplicate diagnostics on generic name.
CreateCompilationWithMscorlib45(source)
.VerifyAnalyzerDiagnostics(analyzers, null, null, logAnalyzerExceptionAsDiagnostics: false,
expected: Diagnostic(CSharpGenericNameAnalyzer.DiagnosticId, @"Nullable<int>").WithLocation(9, 17));
}
[Fact, WorkItem(2980, "https://github.com/dotnet/roslyn/issues/2980")]
public void TestAnalyzerWithNoActions()
{
var source = @"
using System;
using System.Text;
namespace ConsoleApplication1
{
class MyClass
{
private Nullable<int> myVar = 5;
void Method()
{
}
}
}";
// Ensure that adding a dummy analyzer with no actions doesn't bring down entire analysis.
// See https://github.com/dotnet/roslyn/issues/2980 for details.
TestGenericNameCore(source, new AnalyzerWithNoActions(), new CSharpGenericNameAnalyzer());
}
}
}
......@@ -92,11 +92,11 @@ private void Initialize(Compilation comp, AnalyzerExecutor analyzerExecutor, Can
// create the primary driver task.
cancellationToken.ThrowIfCancellationRequested();
_primaryTask = Task.Run(async () =>
{
await initializeTask.ConfigureAwait(false);
{
await initializeTask.ConfigureAwait(false);
await ProcessCompilationEventsAsync(cancellationToken).ConfigureAwait(false);
}, cancellationToken)
await ProcessCompilationEventsAsync(cancellationToken).ConfigureAwait(false);
}, cancellationToken)
.ContinueWith(c => DiagnosticQueue.TryComplete(), cancellationToken, TaskContinuationOptions.ExecuteSynchronously, TaskScheduler.Default);
}
finally
......@@ -335,13 +335,13 @@ private async Task ProcessCompilationEventsAsync(CancellationToken cancellationT
for (int i = 0; i < _workerCount; i++)
{
workerTasks[i] = Task.Run(async () =>
{
var result = await ProcessCompilationEventsCoreAsync(cancellationToken).ConfigureAwait(false);
if (result != null)
{
completedEvent = result;
}
}, cancellationToken);
var result = await ProcessCompilationEventsCoreAsync(cancellationToken).ConfigureAwait(false);
if (result != null)
{
completedEvent = result;
}
}, cancellationToken);
}
// Kick off tasks to execute syntax tree actions.
......@@ -634,7 +634,10 @@ private static Diagnostic GetFilteredDiagnostic(Diagnostic diagnostic, Compilati
if (!IsDiagnosticAnalyzerSuppressed(analyzer, analyzerExecutor.Compilation.Options, analyzerManager, analyzerExecutor))
{
var analyzerActions = await analyzerManager.GetAnalyzerActionsAsync(analyzer, analyzerExecutor).ConfigureAwait(false);
allAnalyzerActions = allAnalyzerActions.Append(analyzerActions);
if (analyzerActions != null)
{
allAnalyzerActions = allAnalyzerActions.Append(analyzerActions);
}
}
}
......
......@@ -1597,15 +1597,15 @@ namespace ConsoleApplication1
</Project>
</Workspace>
TestGenericNameCore(test, New CSharpGenericNameAnalyzer, "Message")
TestGenericNameCore(test, CSharpGenericNameAnalyzer.Message, CSharpGenericNameAnalyzer.DiagnosticId, New CSharpGenericNameAnalyzer)
End Sub
Private Sub TestGenericNameCore(test As XElement, analyzer As DiagnosticAnalyzer, expectedMessage As String)
Private Sub TestGenericNameCore(test As XElement, expectedMessage As String, expectedId As String, ParamArray analyzers As DiagnosticAnalyzer())
Using workspace = TestWorkspaceFactory.CreateWorkspace(test)
Dim project = workspace.CurrentSolution.Projects.Single()
' Add analyzer
Dim analyzerReference = New AnalyzerImageReference(ImmutableArray.Create(Of DiagnosticAnalyzer)(analyzer))
Dim analyzerReference = New AnalyzerImageReference(analyzers.ToImmutableArray())
project = project.AddAnalyzerReference(analyzerReference)
Dim diagnosticService = New TestDiagnosticAnalyzerService()
......@@ -1621,9 +1621,37 @@ namespace ConsoleApplication1
CancellationToken.None).WaitAndGetResult(CancellationToken.None)
Assert.Equal(1, diagnostics.Count())
Dim diagnostic = diagnostics.Single(Function(d) d.Id = analyzer.SupportedDiagnostics.Single().Id)
Dim diagnostic = diagnostics.Single(Function(d) d.Id = expectedId)
Assert.Equal(expectedMessage, diagnostic.Message)
End Using
End Sub
<Fact, WorkItem(2980, "https://github.com/dotnet/roslyn/issues/2980")>
Public Sub TestAnalyzerWithNoActions()
Dim test = <Workspace>
<Project Language="C#" CommonReferences="true">
<Document><![CDATA[
using System;
using System.Text;
namespace ConsoleApplication1
{
class MyClass
{
private Nullable<int> myVar = 5;
void Method()
{
}
}
}]]>
</Document>
</Project>
</Workspace>
' Ensure that adding a dummy analyzer with no actions doesn't bring down entire analysis.
' See https//github.com/dotnet/roslyn/issues/2980 for details.
TestGenericNameCore(test, CSharpGenericNameAnalyzer.Message, CSharpGenericNameAnalyzer.DiagnosticId, New AnalyzerWithNoActions, New CSharpGenericNameAnalyzer)
End Sub
End Class
End Namespace
......@@ -269,5 +269,20 @@ private void AnalyzeNode(SyntaxNodeAnalysisContext context)
context.ReportDiagnostic(Diagnostic.Create(Rule, context.Node.GetLocation()));
}
}
[DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)]
public sealed class AnalyzerWithNoActions : DiagnosticAnalyzer
{
public static readonly DiagnosticDescriptor DummyRule = new DiagnosticDescriptor(
"ID1",
"Title1",
"Message1",
"Category1",
defaultSeverity: DiagnosticSeverity.Warning,
isEnabledByDefault: true);
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => ImmutableArray.Create(DummyRule);
public override void Initialize(AnalysisContext context) { }
}
}
}
\ No newline at end of file
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册