提交 dc04d6b2 编写于 作者: S Shyam N 提交者: GitHub

Merge pull request #20419 from dotnet/merges/master-to-dev15.6-20170623-070014

Merge master to dev15.6
...@@ -12,28 +12,29 @@ Syntax Grammar ...@@ -12,28 +12,29 @@ Syntax Grammar
This grammar is represented as a diff from the current spec grammar. This grammar is represented as a diff from the current spec grammar.
```diff ```diff
declaration-statement declaration_statement
: local-variable-declaration ';' : local_variable_declaration ';'
| local-constant-declaration ';' | local_constant_declaration ';'
+ | local-function-declaration + | local_function_declaration
; ;
+local-function-declaration +local_function_declaration
+ : local-function-header local-function-body + : local_function_header local_function_body
+ ; + ;
+local-function-header +local_function_header
+ : local-function-modifiers? return-type identifier type-parameter-list? + : local_function_modifier* return_type identifier type_parameter_list?
+ ( formal-parameter-list? ) type-parameter-constraints-clauses + '(' formal_parameter_list? ')' type_parameter_constraints_clauses
+ ; + ;
+local-function-modifiers +local_function_modifier
+ : (async | unsafe) + : 'async'
+ | 'unsafe'
+ ; + ;
+local-function-body +local_function_body
+ : block + : block
+ | arrow-expression-body + | arrow_expression_body
+ ; + ;
``` ```
......
...@@ -916,6 +916,31 @@ public void TestReportingDiagnosticWithInvalidLocation() ...@@ -916,6 +916,31 @@ public void TestReportingDiagnosticWithInvalidLocation()
} }
} }
[Fact]
public void TestReportingDiagnosticWithInvalidSpan()
{
var source1 = @"class C1 { void M() { int i = 0; i++; } }";
var compilation = CreateCompilationWithMscorlib45(source1, parseOptions: TestOptions.RegularWithIOperationFeature);
var treeInAnotherCompilation = compilation.SyntaxTrees.Single();
var badSpan = new Text.TextSpan(100000, 10000);
string message = new ArgumentException(
string.Format(CodeAnalysisResources.InvalidDiagnosticSpanReported, AnalyzerWithInvalidDiagnosticSpan.Descriptor.Id, badSpan, treeInAnotherCompilation.FilePath), "diagnostic").Message;
compilation.VerifyDiagnostics();
var analyzer = new AnalyzerWithInvalidDiagnosticSpan(badSpan);
var analyzers = new DiagnosticAnalyzer[] { analyzer };
compilation
.VerifyAnalyzerDiagnostics(analyzers, null, null, logAnalyzerExceptionAsDiagnostics: true,
expected:
Diagnostic("AD0001")
.WithArguments("Microsoft.CodeAnalysis.CommonDiagnosticAnalyzers+AnalyzerWithInvalidDiagnosticSpan", "System.ArgumentException", message)
.WithLocation(1, 1)
);
}
[Fact, WorkItem(13120, "https://github.com/dotnet/roslyn/issues/13120")] [Fact, WorkItem(13120, "https://github.com/dotnet/roslyn/issues/13120")]
public void TestRegisteringAsyncAnalyzerMethod() public void TestRegisteringAsyncAnalyzerMethod()
{ {
...@@ -1903,7 +1928,7 @@ public class RegularClass ...@@ -1903,7 +1928,7 @@ public class RegularClass
var tree = CSharpSyntaxTree.ParseText(source, path: "Source.cs"); var tree = CSharpSyntaxTree.ParseText(source, path: "Source.cs");
var compilation = CreateCompilationWithMscorlib45(new[] { tree }); var compilation = CreateCompilationWithMscorlib45(new[] { tree });
compilation.VerifyDiagnostics(); compilation.VerifyDiagnostics();
var analyzers = new DiagnosticAnalyzer[] { new GeneratedCodeAnalyzer(GeneratedCodeAnalysisFlags.None) }; var analyzers = new DiagnosticAnalyzer[] { new GeneratedCodeAnalyzer(GeneratedCodeAnalysisFlags.None) };
compilation.VerifyAnalyzerDiagnostics(analyzers, null, null, true, compilation.VerifyAnalyzerDiagnostics(analyzers, null, null, true,
Diagnostic("GeneratedCodeAnalyzerWarning", "}").WithArguments("Source.cs").WithLocation(11, 1), Diagnostic("GeneratedCodeAnalyzerWarning", "}").WithArguments("Source.cs").WithLocation(11, 1),
......
...@@ -20,7 +20,7 @@ namespace Microsoft.CodeAnalysis { ...@@ -20,7 +20,7 @@ namespace Microsoft.CodeAnalysis {
// class via a tool like ResGen or Visual Studio. // class via a tool like ResGen or Visual Studio.
// To add or remove a member, edit your .ResX file then rerun ResGen // To add or remove a member, edit your .ResX file then rerun ResGen
// with the /str option, or rebuild your VS project. // with the /str option, or rebuild your VS project.
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "15.0.0.0")]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
internal class CodeAnalysisResources { internal class CodeAnalysisResources {
...@@ -748,6 +748,15 @@ internal class CodeAnalysisResources { ...@@ -748,6 +748,15 @@ internal class CodeAnalysisResources {
} }
} }
/// <summary>
/// Looks up a localized string similar to Reported diagnostic &apos;{0}&apos; has a source location &apos;{1}&apos; in file &apos;{2}&apos;, which is outside of the given file..
/// </summary>
internal static string InvalidDiagnosticSpanReported {
get {
return ResourceManager.GetString("InvalidDiagnosticSpanReported", resourceCulture);
}
}
/// <summary> /// <summary>
/// Looks up a localized string similar to Invalid hash.. /// Looks up a localized string similar to Invalid hash..
/// </summary> /// </summary>
...@@ -1334,7 +1343,7 @@ internal class CodeAnalysisResources { ...@@ -1334,7 +1343,7 @@ internal class CodeAnalysisResources {
} }
/// <summary> /// <summary>
/// Looks up a localized string similar to Windows PDB writer doesn&apos;t support deterministic compilation: &apos;{0}&apos;. /// Looks up a localized string similar to Windows PDB writer doesn&apos;t support SourceLink feature: &apos;{0}&apos;.
/// </summary> /// </summary>
internal static string SymWriterDoesNotSupportSourceLink { internal static string SymWriterDoesNotSupportSourceLink {
get { get {
...@@ -1343,7 +1352,7 @@ internal class CodeAnalysisResources { ...@@ -1343,7 +1352,7 @@ internal class CodeAnalysisResources {
} }
/// <summary> /// <summary>
/// Looks up a localized string similar to Windows PDB writer is not available -- could not find Microsoft.DiaSymReader.Native.{0}.dll. /// Looks up a localized string similar to Windows PDB writer doesn&apos;t support deterministic compilation: &apos;{0}&apos;.
/// </summary> /// </summary>
internal static string SymWriterNotDeterministic { internal static string SymWriterNotDeterministic {
get { get {
......
...@@ -612,4 +612,7 @@ ...@@ -612,4 +612,7 @@
<data name="Stream_contains_invalid_data" xml:space="preserve"> <data name="Stream_contains_invalid_data" xml:space="preserve">
<value>Stream contains invalid data</value> <value>Stream contains invalid data</value>
</data> </data>
<data name="InvalidDiagnosticSpanReported" xml:space="preserve">
<value>Reported diagnostic '{0}' has a source location '{1}' in file '{2}', which is outside of the given file.</value>
</data>
</root> </root>
\ No newline at end of file
...@@ -73,7 +73,7 @@ internal static void VerifyDiagnosticLocationsInCompilation(Diagnostic diagnosti ...@@ -73,7 +73,7 @@ internal static void VerifyDiagnosticLocationsInCompilation(Diagnostic diagnosti
} }
} }
} }
internal static void VerifyIOperationFeatureFlag(bool isIOperationFeatureEnabled) internal static void VerifyIOperationFeatureFlag(bool isIOperationFeatureEnabled)
{ {
if (!isIOperationFeatureEnabled) if (!isIOperationFeatureEnabled)
...@@ -84,11 +84,22 @@ internal static void VerifyIOperationFeatureFlag(bool isIOperationFeatureEnabled ...@@ -84,11 +84,22 @@ internal static void VerifyIOperationFeatureFlag(bool isIOperationFeatureEnabled
private static void VerifyDiagnosticLocationInCompilation(string id, Location location, Compilation compilation) private static void VerifyDiagnosticLocationInCompilation(string id, Location location, Compilation compilation)
{ {
if (location.IsInSource && !compilation.ContainsSyntaxTree(location.SourceTree)) if (!location.IsInSource)
{
return;
}
if (!compilation.ContainsSyntaxTree(location.SourceTree))
{ {
// Disallow diagnostics with source locations outside this compilation. // Disallow diagnostics with source locations outside this compilation.
throw new ArgumentException(string.Format(CodeAnalysisResources.InvalidDiagnosticLocationReported, id, location.SourceTree.FilePath), "diagnostic"); throw new ArgumentException(string.Format(CodeAnalysisResources.InvalidDiagnosticLocationReported, id, location.SourceTree.FilePath), "diagnostic");
} }
if (location.SourceSpan.End > location.SourceTree.Length)
{
// Disallow diagnostics with source locations outside this compilation.
throw new ArgumentException(string.Format(CodeAnalysisResources.InvalidDiagnosticSpanReported, id, location.SourceSpan, location.SourceTree.FilePath), "diagnostic");
}
} }
private static void VerifyAction<TContext>(Action<TContext> action) private static void VerifyAction<TContext>(Action<TContext> action)
......
...@@ -4,6 +4,7 @@ ...@@ -4,6 +4,7 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.Immutable; using System.Collections.Immutable;
using System.Linq; using System.Linq;
using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Test.Utilities; using Microsoft.CodeAnalysis.CSharp.Test.Utilities;
...@@ -312,5 +313,34 @@ public void AnalyzeNode(SyntaxNodeAnalysisContext context) ...@@ -312,5 +313,34 @@ public void AnalyzeNode(SyntaxNodeAnalysisContext context)
} }
} }
} }
[Fact]
public async Task TestDiagnosticSpan()
{
var source = @"// empty code";
var analyzer = new InvalidSpanAnalyzer();
using (var compilerEngineWorkspace = TestWorkspace.CreateCSharp(source))
{
var compilerEngineCompilation = (CSharpCompilation)(await compilerEngineWorkspace.CurrentSolution.Projects.Single().GetCompilationAsync());
var diagnostics = compilerEngineCompilation.GetAnalyzerDiagnostics(new[] { analyzer });
AssertEx.Any(diagnostics, d => d.Id == AnalyzerHelper.AnalyzerExceptionDiagnosticId);
}
}
private class InvalidSpanAnalyzer : DiagnosticAnalyzer
{
public static DiagnosticDescriptor Descriptor = DescriptorFactory.CreateSimpleDescriptor("DummyDiagnostic");
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics
=> ImmutableArray.Create(Descriptor);
public override void Initialize(AnalysisContext context)
=> context.RegisterSyntaxTreeAction(Analyze);
private void Analyze(SyntaxTreeAnalysisContext context)
=> context.ReportDiagnostic(Diagnostic.Create(Descriptor, Location.Create(context.Tree, TextSpan.FromBounds(1000, 2000))));
}
} }
} }
...@@ -8,6 +8,7 @@ ...@@ -8,6 +8,7 @@
using Microsoft.CodeAnalysis.Editor.Shared.Preview; using Microsoft.CodeAnalysis.Editor.Shared.Preview;
using Microsoft.CodeAnalysis.Editor.Shared.Tagging; using Microsoft.CodeAnalysis.Editor.Shared.Tagging;
using Microsoft.CodeAnalysis.Editor.Tagging; using Microsoft.CodeAnalysis.Editor.Tagging;
using Microsoft.CodeAnalysis.ErrorReporting;
using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.Options;
using Microsoft.CodeAnalysis.Text; using Microsoft.CodeAnalysis.Text;
using Microsoft.CodeAnalysis.Text.Shared.Extensions; using Microsoft.CodeAnalysis.Text.Shared.Extensions;
...@@ -63,7 +64,7 @@ protected override ITaggerEventSource CreateEventSource(ITextView textViewOpt, I ...@@ -63,7 +64,7 @@ protected override ITaggerEventSource CreateEventSource(ITextView textViewOpt, I
// We act as a source of events ourselves. When the diagnostics service tells // We act as a source of events ourselves. When the diagnostics service tells
// us about new diagnostics, we'll use that to kick of the asynchronous tagging // us about new diagnostics, we'll use that to kick of the asynchronous tagging
// work. // work.
var eventSource = TaggerEventSources.Compose( var eventSource = TaggerEventSources.Compose(
TaggerEventSources.OnWorkspaceRegistrationChanged(subjectBuffer, TaggerDelay.Medium), TaggerEventSources.OnWorkspaceRegistrationChanged(subjectBuffer, TaggerDelay.Medium),
this); this);
...@@ -83,69 +84,79 @@ protected override Task ProduceTagsAsync(TaggerContext<TTag> context, DocumentSn ...@@ -83,69 +84,79 @@ protected override Task ProduceTagsAsync(TaggerContext<TTag> context, DocumentSn
private void ProduceTags(TaggerContext<TTag> context, DocumentSnapshotSpan spanToTag) private void ProduceTags(TaggerContext<TTag> context, DocumentSnapshotSpan spanToTag)
{ {
if (!_owner.IsEnabled) try
{ {
return; if (!_owner.IsEnabled)
} {
return;
}
var document = spanToTag.Document; var document = spanToTag.Document;
if (document == null) if (document == null)
{ {
return; return;
} }
// See if we've marked any spans as those we want to suppress diagnostics for. // See if we've marked any spans as those we want to suppress diagnostics for.
// This can happen for buffers used in the preview workspace where some feature // This can happen for buffers used in the preview workspace where some feature
// is generating code that it doesn't want errors shown for. // is generating code that it doesn't want errors shown for.
var buffer = spanToTag.SnapshotSpan.Snapshot.TextBuffer; var buffer = spanToTag.SnapshotSpan.Snapshot.TextBuffer;
var suppressedDiagnosticsSpans = default(NormalizedSnapshotSpanCollection); var suppressedDiagnosticsSpans = default(NormalizedSnapshotSpanCollection);
buffer?.Properties.TryGetProperty(PredefinedPreviewTaggerKeys.SuppressDiagnosticsSpansKey, out suppressedDiagnosticsSpans); buffer?.Properties.TryGetProperty(PredefinedPreviewTaggerKeys.SuppressDiagnosticsSpansKey, out suppressedDiagnosticsSpans);
// Producing tags is simple. We just grab the diagnostics we were already told about, // Producing tags is simple. We just grab the diagnostics we were already told about,
// and we convert them to tag spans. // and we convert them to tag spans.
object id; object id;
ImmutableArray<DiagnosticData> diagnostics; ImmutableArray<DiagnosticData> diagnostics;
SourceText sourceText; SourceText sourceText;
ITextSnapshot editorSnapshot; ITextSnapshot editorSnapshot;
lock (_gate) lock (_gate)
{ {
id = _latestId; id = _latestId;
diagnostics = _latestDiagnostics; diagnostics = _latestDiagnostics;
sourceText = _latestSourceText; sourceText = _latestSourceText;
editorSnapshot = _latestEditorSnapshot; editorSnapshot = _latestEditorSnapshot;
} }
if (sourceText == null || editorSnapshot == null) if (sourceText == null || editorSnapshot == null)
{ {
return; return;
} }
var project = document.Project; var project = document.Project;
var requestedSnapshot = spanToTag.SnapshotSpan.Snapshot; var requestedSnapshot = spanToTag.SnapshotSpan.Snapshot;
var requestedSpan = spanToTag.SnapshotSpan; var requestedSpan = spanToTag.SnapshotSpan;
var isLiveUpdate = id is ISupportLiveUpdate; var isLiveUpdate = id is ISupportLiveUpdate;
foreach (var diagnosticData in diagnostics) foreach (var diagnosticData in diagnostics)
{
if (_owner.IncludeDiagnostic(diagnosticData))
{ {
var actualSpan = diagnosticData if (_owner.IncludeDiagnostic(diagnosticData))
.GetExistingOrCalculatedTextSpan(sourceText)
.ToSnapshotSpan(editorSnapshot)
.TranslateTo(requestedSnapshot, SpanTrackingMode.EdgeExclusive);
if (actualSpan.IntersectsWith(requestedSpan) &&
!IsSuppressed(suppressedDiagnosticsSpans, actualSpan))
{ {
var tagSpan = _owner.CreateTagSpan(isLiveUpdate, actualSpan, diagnosticData); var actualSpan = diagnosticData
if (tagSpan != null) .GetExistingOrCalculatedTextSpan(sourceText)
.ToSnapshotSpan(editorSnapshot)
.TranslateTo(requestedSnapshot, SpanTrackingMode.EdgeExclusive);
if (actualSpan.IntersectsWith(requestedSpan) &&
!IsSuppressed(suppressedDiagnosticsSpans, actualSpan))
{ {
context.AddTag(tagSpan); var tagSpan = _owner.CreateTagSpan(isLiveUpdate, actualSpan, diagnosticData);
if (tagSpan != null)
{
context.AddTag(tagSpan);
}
} }
} }
} }
} }
catch (ArgumentOutOfRangeException ex) when (FatalError.ReportWithoutCrash(ex))
{
// https://devdiv.visualstudio.com/DefaultCollection/DevDiv/_workitems?id=428328&_a=edit&triage=false
// explicitly report NFW to find out what is causing us for out of range.
// stop crashing on such occations
return;
}
} }
private bool IsSuppressed(NormalizedSnapshotSpanCollection suppressedSpans, SnapshotSpan span) private bool IsSuppressed(NormalizedSnapshotSpanCollection suppressedSpans, SnapshotSpan span)
......
...@@ -377,7 +377,7 @@ public IEnumerable<DiagnosticData> ConvertToLocalDiagnostics(Document targetDocu ...@@ -377,7 +377,7 @@ public IEnumerable<DiagnosticData> ConvertToLocalDiagnostics(Document targetDocu
try try
{ {
var diagnostics = await analyzer.AnalyzeProjectAsync(project, cancellationToken).ConfigureAwait(false); var diagnostics = (await analyzer.AnalyzeProjectAsync(project, cancellationToken).ConfigureAwait(false)).NullToEmpty();
// Apply filtering from compilation options (source suppressions, ruleset, etc.) // Apply filtering from compilation options (source suppressions, ruleset, etc.)
if (compilationOpt != null) if (compilationOpt != null)
...@@ -385,6 +385,13 @@ public IEnumerable<DiagnosticData> ConvertToLocalDiagnostics(Document targetDocu ...@@ -385,6 +385,13 @@ public IEnumerable<DiagnosticData> ConvertToLocalDiagnostics(Document targetDocu
diagnostics = CompilationWithAnalyzers.GetEffectiveDiagnostics(diagnostics, compilationOpt).ToImmutableArrayOrEmpty(); diagnostics = CompilationWithAnalyzers.GetEffectiveDiagnostics(diagnostics, compilationOpt).ToImmutableArrayOrEmpty();
} }
#if DEBUG
// since all ProjectDiagnosticAnalyzers are from internal users, we only do debug check. also this can be expensive at runtime
// since it requires await. if we find any offender through NFW, we should be able to fix those since all those should
// from intern teams.
await VerifyDiagnosticLocationsAsync(diagnostics, project, cancellationToken).ConfigureAwait(false);
#endif
return diagnostics; return diagnostics;
} }
catch (Exception e) when (!IsCanceled(e, cancellationToken)) catch (Exception e) when (!IsCanceled(e, cancellationToken))
...@@ -423,6 +430,13 @@ public IEnumerable<DiagnosticData> ConvertToLocalDiagnostics(Document targetDocu ...@@ -423,6 +430,13 @@ public IEnumerable<DiagnosticData> ConvertToLocalDiagnostics(Document targetDocu
return CompilationWithAnalyzers.GetEffectiveDiagnostics(diagnostics, compilationOpt); return CompilationWithAnalyzers.GetEffectiveDiagnostics(diagnostics, compilationOpt);
} }
#if DEBUG
// since all DocumentDiagnosticAnalyzers are from internal users, we only do debug check. also this can be expensive at runtime
// since it requires await. if we find any offender through NFW, we should be able to fix those since all those should
// from intern teams.
await VerifyDiagnosticLocationsAsync(diagnostics, document.Project, cancellationToken).ConfigureAwait(false);
#endif
return diagnostics; return diagnostics;
} }
catch (Exception e) when (!IsCanceled(e, cancellationToken)) catch (Exception e) when (!IsCanceled(e, cancellationToken))
...@@ -584,6 +598,86 @@ private IEnumerable<DiagnosticData> ConvertToLocalDiagnosticsWithCompilation(Doc ...@@ -584,6 +598,86 @@ private IEnumerable<DiagnosticData> ConvertToLocalDiagnosticsWithCompilation(Doc
} }
} }
private static async Task VerifyDiagnosticLocationsAsync(ImmutableArray<Diagnostic> diagnostics, Project project, CancellationToken cancellationToken)
{
foreach (var diagnostic in diagnostics)
{
await VerifyDiagnosticLocationAsync(diagnostic.Id, diagnostic.Location, project, cancellationToken).ConfigureAwait(false);
if (diagnostic.AdditionalLocations != null)
{
foreach (var location in diagnostic.AdditionalLocations)
{
await VerifyDiagnosticLocationAsync(diagnostic.Id, diagnostic.Location, project, cancellationToken).ConfigureAwait(false);
}
}
}
}
private static async Task VerifyDiagnosticLocationAsync(string id, Location location, Project project, CancellationToken cancellationToken)
{
switch (location.Kind)
{
case LocationKind.None:
case LocationKind.MetadataFile:
case LocationKind.XmlFile:
// ignore these kinds
break;
case LocationKind.SourceFile:
{
if (project.GetDocument(location.SourceTree) == null)
{
// Disallow diagnostics with source locations outside this project.
throw new ArgumentException(string.Format(FeaturesResources.Reported_diagnostic_0_has_a_source_location_in_file_1_which_is_not_part_of_the_compilation_being_analyzed, id, location.SourceTree.FilePath), "diagnostic");
}
if (location.SourceSpan.End > location.SourceTree.Length)
{
// Disallow diagnostics with source locations outside this project.
throw new ArgumentException(string.Format(FeaturesResources.Reported_diagnostic_0_has_a_source_location_1_in_file_2_which_is_outside_of_the_given_file, id, location.SourceSpan, location.SourceTree.FilePath), "diagnostic");
}
}
break;
case LocationKind.ExternalFile:
{
var filePath = location.GetLineSpan().Path;
var document = TryGetDocumentWithFilePath(project, filePath);
if (document == null)
{
// this is not a roslyn file. we don't care about this file.
return;
}
// this can be potentially expensive since it will load text if it is not already loaded.
// but, this text is most likely already loaded since producer of this diagnostic (Document/ProjectDiagnosticAnalyzers)
// should have loaded it to produce the diagnostic at the first place. once loaded, it should stay in memory until
// project cache goes away. when text is already there, await should return right away.
var text = await document.GetTextAsync(cancellationToken).ConfigureAwait(false);
if (location.SourceSpan.End > text.Length)
{
// Disallow diagnostics with locations outside this project.
throw new ArgumentException(string.Format(FeaturesResources.Reported_diagnostic_0_has_a_source_location_1_in_file_2_which_is_outside_of_the_given_file, id, location.SourceSpan, filePath), "diagnostic");
}
}
break;
default:
throw ExceptionUtilities.Unreachable;
}
}
private static Document TryGetDocumentWithFilePath(Project project, string path)
{
foreach (var documentId in project.Solution.GetDocumentIdsWithFilePath(path))
{
if (documentId.ProjectId == project.Id)
{
return project.GetDocument(documentId);
}
}
return null;
}
private static void GetLogFunctionIdAndTitle(AnalysisKind kind, out FunctionId functionId, out string title) private static void GetLogFunctionIdAndTitle(AnalysisKind kind, out FunctionId functionId, out string title)
{ {
switch (kind) switch (kind)
......
...@@ -225,7 +225,7 @@ public async Task SaveAsync(Project project, DiagnosticAnalysisResult result) ...@@ -225,7 +225,7 @@ public async Task SaveAsync(Project project, DiagnosticAnalysisResult result)
await SerializeAsync(serializer, project, result.ProjectId, _owner.NonLocalStateName, result.Others).ConfigureAwait(false); await SerializeAsync(serializer, project, result.ProjectId, _owner.NonLocalStateName, result.Others).ConfigureAwait(false);
AnalyzerABTestLogger.LogProjectDiagnostics(project, result); AnalyzerABTestLogger.LogProjectDiagnostics(project, _owner.StateName, result);
} }
public void ResetVersion() public void ResetVersion()
...@@ -244,7 +244,7 @@ public async Task MergeAsync(ActiveFileState state, Document document) ...@@ -244,7 +244,7 @@ public async Task MergeAsync(ActiveFileState state, Document document)
var syntax = state.GetAnalysisData(AnalysisKind.Syntax); var syntax = state.GetAnalysisData(AnalysisKind.Syntax);
var semantic = state.GetAnalysisData(AnalysisKind.Semantic); var semantic = state.GetAnalysisData(AnalysisKind.Semantic);
AnalyzerABTestLogger.LogDocumentDiagnostics(document, syntax.Items, semantic.Items); AnalyzerABTestLogger.LogDocumentDiagnostics(document, _owner.StateName, syntax.Items, semantic.Items);
var project = document.Project; var project = document.Project;
......
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. // 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.Generic;
using System.Collections.Immutable; using System.Collections.Immutable;
using System.Diagnostics;
using System.Globalization; using System.Globalization;
using System.Linq; using System.Linq;
using System.Threading; using System.Threading;
...@@ -15,6 +17,8 @@ internal partial class DiagnosticIncrementalAnalyzer ...@@ -15,6 +17,8 @@ internal partial class DiagnosticIncrementalAnalyzer
{ {
public async Task SynchronizeWithBuildAsync(Workspace workspace, ImmutableDictionary<ProjectId, ImmutableArray<DiagnosticData>> map) public async Task SynchronizeWithBuildAsync(Workspace workspace, ImmutableDictionary<ProjectId, ImmutableArray<DiagnosticData>> map)
{ {
DebugVerifyDiagnosticLocations(map);
if (!PreferBuildErrors(workspace)) if (!PreferBuildErrors(workspace))
{ {
// prefer live errors over build errors // prefer live errors over build errors
...@@ -51,6 +55,18 @@ public async Task SynchronizeWithBuildAsync(Workspace workspace, ImmutableDictio ...@@ -51,6 +55,18 @@ public async Task SynchronizeWithBuildAsync(Workspace workspace, ImmutableDictio
} }
} }
[Conditional("DEBUG")]
private void DebugVerifyDiagnosticLocations(ImmutableDictionary<ProjectId, ImmutableArray<DiagnosticData>> map)
{
foreach (var diagnostic in map.Values.SelectMany(v => v))
{
// errors from build shouldn't have any span set.
// this is debug check since it gets data from us only not from third party unlike one in compiler
// that checks span for third party reported diagnostics
Contract.Requires(!diagnostic.HasTextSpan);
}
}
private async Task<ProjectAnalysisData> CreateProjectAnalysisDataAsync(Project project, ImmutableArray<StateSet> stateSets, ImmutableArray<DiagnosticData> diagnostics) private async Task<ProjectAnalysisData> CreateProjectAnalysisDataAsync(Project project, ImmutableArray<StateSet> stateSets, ImmutableArray<DiagnosticData> diagnostics)
{ {
// we always load data sicne we don't know right version. // we always load data sicne we don't know right version.
......
...@@ -16,7 +16,7 @@ namespace Microsoft.CodeAnalysis.Experimentation ...@@ -16,7 +16,7 @@ namespace Microsoft.CodeAnalysis.Experimentation
internal static class AnalyzerABTestLogger internal static class AnalyzerABTestLogger
{ {
private static bool s_reportErrors = false; private static bool s_reportErrors = false;
private static readonly ConcurrentDictionary<object, object> s_reported = new ConcurrentDictionary<object, object>(concurrencyLevel: 2, capacity: 10); private static readonly ConcurrentDictionary<(object, string), object> s_reported = new ConcurrentDictionary<(object, string), object>(concurrencyLevel: 2, capacity: 10);
private const string Name = "LiveCodeAnalysisVsix"; private const string Name = "LiveCodeAnalysisVsix";
...@@ -59,9 +59,9 @@ public static void LogCandidacyRequirementsTracking(long lastTriggeredTimeBinary ...@@ -59,9 +59,9 @@ public static void LogCandidacyRequirementsTracking(long lastTriggeredTimeBinary
} }
} }
public static void LogProjectDiagnostics(Project project, DiagnosticAnalysisResult result) public static void LogProjectDiagnostics(Project project, string analyzerName, DiagnosticAnalysisResult result)
{ {
if (!s_reportErrors || !s_reported.TryAdd(project.Id, null)) if (!s_reportErrors || !s_reported.TryAdd((project.Id, analyzerName), null))
{ {
// doesn't meet the bar to report the issue. // doesn't meet the bar to report the issue.
return; return;
...@@ -82,9 +82,9 @@ public static void LogProjectDiagnostics(Project project, DiagnosticAnalysisResu ...@@ -82,9 +82,9 @@ public static void LogProjectDiagnostics(Project project, DiagnosticAnalysisResu
LogErrors(project, "ProjectDignostics", project.Id.Id, map); LogErrors(project, "ProjectDignostics", project.Id.Id, map);
} }
public static void LogDocumentDiagnostics(Document document, ImmutableArray<DiagnosticData> syntax, ImmutableArray<DiagnosticData> semantic) public static void LogDocumentDiagnostics(Document document, string analyzerName, ImmutableArray<DiagnosticData> syntax, ImmutableArray<DiagnosticData> semantic)
{ {
if (!s_reportErrors || !s_reported.TryAdd(document.Id, null)) if (!s_reportErrors || !s_reported.TryAdd((document.Id, analyzerName), null))
{ {
// doesn't meet the bar to report the issue. // doesn't meet the bar to report the issue.
return; return;
......
...@@ -2583,6 +2583,26 @@ internal class FeaturesResources { ...@@ -2583,6 +2583,26 @@ internal class FeaturesResources {
} }
} }
/// <summary>
/// Looks up a localized string similar to Reported diagnostic &apos;{0}&apos; has a source location &apos;{1}&apos; in file &apos;{2}&apos;, which is outside of the given file..
/// </summary>
internal static string Reported_diagnostic_0_has_a_source_location_1_in_file_2_which_is_outside_of_the_given_file {
get {
return ResourceManager.GetString("Reported_diagnostic_0_has_a_source_location_1_in_file_2_which_is_outside_of_the_g" +
"iven_file", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Reported diagnostic &apos;{0}&apos; has a source location in file &apos;{1}&apos;, which is not part of the compilation being analyzed..
/// </summary>
internal static string Reported_diagnostic_0_has_a_source_location_in_file_1_which_is_not_part_of_the_compilation_being_analyzed {
get {
return ResourceManager.GetString("Reported_diagnostic_0_has_a_source_location_in_file_1_which_is_not_part_of_the_co" +
"mpilation_being_analyzed", resourceCulture);
}
}
/// <summary> /// <summary>
/// Looks up a localized string similar to Reported diagnostic with ID &apos;{0}&apos; is not supported by the analyzer.. /// Looks up a localized string similar to Reported diagnostic with ID &apos;{0}&apos; is not supported by the analyzer..
/// </summary> /// </summary>
......
...@@ -1247,6 +1247,12 @@ This version used in: {2}</value> ...@@ -1247,6 +1247,12 @@ This version used in: {2}</value>
<data name="default_expression_can_be_simplified" xml:space="preserve"> <data name="default_expression_can_be_simplified" xml:space="preserve">
<value>'default' expression can be simplified</value> <value>'default' expression can be simplified</value>
</data> </data>
<data name="Reported_diagnostic_0_has_a_source_location_in_file_1_which_is_not_part_of_the_compilation_being_analyzed" xml:space="preserve">
<value>Reported diagnostic '{0}' has a source location in file '{1}', which is not part of the compilation being analyzed.</value>
</data>
<data name="Reported_diagnostic_0_has_a_source_location_1_in_file_2_which_is_outside_of_the_given_file" xml:space="preserve">
<value>Reported diagnostic '{0}' has a source location '{1}' in file '{2}', which is outside of the given file.</value>
</data>
<data name="Unreachable_code_detected" xml:space="preserve"> <data name="Unreachable_code_detected" xml:space="preserve">
<value>Unreachable code detected</value> <value>Unreachable code detected</value>
</data> </data>
......
...@@ -460,6 +460,25 @@ public override void Initialize(AnalysisContext context) ...@@ -460,6 +460,25 @@ public override void Initialize(AnalysisContext context)
} }
} }
[DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)]
public sealed class AnalyzerWithInvalidDiagnosticSpan : DiagnosticAnalyzer
{
private readonly TextSpan _badSpan;
public static readonly DiagnosticDescriptor Descriptor = new DiagnosticDescriptor(
"ID",
"Title1",
"Message",
"Category1",
defaultSeverity: DiagnosticSeverity.Warning,
isEnabledByDefault: true);
public AnalyzerWithInvalidDiagnosticSpan(TextSpan badSpan) => _badSpan = badSpan;
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => ImmutableArray.Create(Descriptor);
public override void Initialize(AnalysisContext context)
=> context.RegisterSyntaxTreeAction(c => c.ReportDiagnostic(Diagnostic.Create(Descriptor, SourceLocation.Create(c.Tree, _badSpan))));
}
[DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)] [DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)]
public sealed class AnalyzerWithInvalidDiagnosticLocation : DiagnosticAnalyzer public sealed class AnalyzerWithInvalidDiagnosticLocation : DiagnosticAnalyzer
{ {
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册