提交 a2f2e628 编写于 作者: P Paul Vick

Merge remote-tracking branch 'upstream/master' into merge-master-into-future20160518-150017

...@@ -3634,6 +3634,15 @@ internal class CSharpResources { ...@@ -3634,6 +3634,15 @@ internal class CSharpResources {
} }
} }
/// <summary>
/// Looks up a localized string similar to Cannot update &apos;{0}&apos;; attribute &apos;{1}&apos; is missing..
/// </summary>
internal static string ERR_EncUpdateFailedMissingAttribute {
get {
return ResourceManager.GetString("ERR_EncUpdateFailedMissingAttribute", resourceCulture);
}
}
/// <summary> /// <summary>
/// Looks up a localized string similar to #endif directive expected. /// Looks up a localized string similar to #endif directive expected.
/// </summary> /// </summary>
......
...@@ -3839,6 +3839,9 @@ You should consider suppressing the warning only if you're sure that you don't w ...@@ -3839,6 +3839,9 @@ You should consider suppressing the warning only if you're sure that you don't w
<data name="ERR_FixedLocalInLambda" xml:space="preserve"> <data name="ERR_FixedLocalInLambda" xml:space="preserve">
<value>Cannot use fixed local '{0}' inside an anonymous method, lambda expression, or query expression</value> <value>Cannot use fixed local '{0}' inside an anonymous method, lambda expression, or query expression</value>
</data> </data>
<data name="ERR_EncUpdateFailedMissingAttribute" xml:space="preserve">
<value>Cannot update '{0}'; attribute '{1}' is missing.</value>
</data>
<data name="ERR_ExpressionTreeContainsNamedArgument" xml:space="preserve"> <data name="ERR_ExpressionTreeContainsNamedArgument" xml:space="preserve">
<value>An expression tree may not contain a named argument specification</value> <value>An expression tree may not contain a named argument specification</value>
</data> </data>
......
...@@ -1174,6 +1174,7 @@ internal enum ErrorCode ...@@ -1174,6 +1174,7 @@ internal enum ErrorCode
// ERR_NameIllegallyOverrides3 = 7040, // Not used anymore due to 'Single Meaning' relaxation changes // ERR_NameIllegallyOverrides3 = 7040, // Not used anymore due to 'Single Meaning' relaxation changes
ERR_ResourceFileNameNotUnique = 7041, ERR_ResourceFileNameNotUnique = 7041,
ERR_DllImportOnGenericMethod = 7042, ERR_DllImportOnGenericMethod = 7042,
ERR_EncUpdateFailedMissingAttribute = 7043,
ERR_ParameterNotValidForType = 7045, ERR_ParameterNotValidForType = 7045,
ERR_AttributeParameterRequired1 = 7046, ERR_AttributeParameterRequired1 = 7046,
......
...@@ -95,7 +95,7 @@ public override Diagnostic CreateDiagnostic(int code, Location location, params ...@@ -95,7 +95,7 @@ public override Diagnostic CreateDiagnostic(int code, Location location, params
return new CSDiagnostic(info, location); return new CSDiagnostic(info, location);
} }
public override string ConvertSymbolToString(int errorCode, ISymbol symbol) public override string GetErrorDisplayString(ISymbol symbol)
{ {
// show extra info for assembly if possible such as version, public key token etc. // show extra info for assembly if possible such as version, public key token etc.
if (symbol.Kind == SymbolKind.Assembly || symbol.Kind == SymbolKind.Namespace) if (symbol.Kind == SymbolKind.Assembly || symbol.Kind == SymbolKind.Namespace)
...@@ -205,6 +205,7 @@ public override void ReportDuplicateMetadataReferenceWeak(DiagnosticBag diagnost ...@@ -205,6 +205,7 @@ public override void ReportDuplicateMetadataReferenceWeak(DiagnosticBag diagnost
public override int ERR_TooManyUserStrings { get { return (int)ErrorCode.ERR_TooManyUserStrings; } } public override int ERR_TooManyUserStrings { get { return (int)ErrorCode.ERR_TooManyUserStrings; } }
public override int ERR_PeWritingFailure { get { return (int)ErrorCode.ERR_PeWritingFailure; } } public override int ERR_PeWritingFailure { get { return (int)ErrorCode.ERR_PeWritingFailure; } }
public override int ERR_ModuleEmitFailure { get { return (int)ErrorCode.ERR_ModuleEmitFailure; } } public override int ERR_ModuleEmitFailure { get { return (int)ErrorCode.ERR_ModuleEmitFailure; } }
public override int ERR_EncUpdateFailedMissingAttribute { get { return (int)ErrorCode.ERR_EncUpdateFailedMissingAttribute; } }
public override void ReportInvalidAttributeArgument(DiagnosticBag diagnostics, SyntaxNode attributeSyntax, int parameterIndex, AttributeData attribute) public override void ReportInvalidAttributeArgument(DiagnosticBag diagnostics, SyntaxNode attributeSyntax, int parameterIndex, AttributeData attribute)
{ {
......
...@@ -4756,8 +4756,8 @@ public IEnumerable<int> F() ...@@ -4756,8 +4756,8 @@ public IEnumerable<int> F()
new SemanticEdit(SemanticEditKind.Update, f0, f1, GetSyntaxMapFromMarkers(source0, source1), preserveLocalVariables: true))); new SemanticEdit(SemanticEditKind.Update, f0, f1, GetSyntaxMapFromMarkers(source0, source1), preserveLocalVariables: true)));
diff1.EmitResult.Diagnostics.Verify( diff1.EmitResult.Diagnostics.Verify(
// error CS7038: Failed to emit module '{0}'. // error CS7043: Cannot update 'C.F()'; attribute 'System.Runtime.CompilerServices.IteratorStateMachineAttribute' is missing.
Diagnostic(ErrorCode.ERR_ModuleEmitFailure).WithArguments(compilation0.SourceModule.Name)); Diagnostic(ErrorCode.ERR_EncUpdateFailedMissingAttribute).WithArguments("C.F()", "System.Runtime.CompilerServices.IteratorStateMachineAttribute"));
} }
[Fact, WorkItem(9119, "https://github.com/dotnet/roslyn/issues/9119")] [Fact, WorkItem(9119, "https://github.com/dotnet/roslyn/issues/9119")]
...@@ -4811,10 +4811,10 @@ public async Task<int> F() ...@@ -4811,10 +4811,10 @@ public async Task<int> F()
new SemanticEdit(SemanticEditKind.Update, f0, f1, GetSyntaxMapFromMarkers(source0, source1), preserveLocalVariables: true))); new SemanticEdit(SemanticEditKind.Update, f0, f1, GetSyntaxMapFromMarkers(source0, source1), preserveLocalVariables: true)));
diff1.EmitResult.Diagnostics.Verify( diff1.EmitResult.Diagnostics.Verify(
// error CS7038: Failed to emit module '{0}'. // error CS7043: Cannot update 'C.F()'; attribute 'System.Runtime.CompilerServices.AsyncStateMachineAttribute' is missing.
Diagnostic(ErrorCode.ERR_ModuleEmitFailure).WithArguments(compilation0.SourceModule.Name), Diagnostic(ErrorCode.ERR_EncUpdateFailedMissingAttribute).WithArguments("C.F()", "System.Runtime.CompilerServices.AsyncStateMachineAttribute"),
// error CS7038: Failed to emit module '{0}'. // error CS7043: Cannot update 'C.F()'; attribute 'System.Runtime.CompilerServices.AsyncStateMachineAttribute' is missing.
Diagnostic(ErrorCode.ERR_ModuleEmitFailure).WithArguments(compilation0.SourceModule.Name)); Diagnostic(ErrorCode.ERR_EncUpdateFailedMissingAttribute).WithArguments("C.F()", "System.Runtime.CompilerServices.AsyncStateMachineAttribute"));
} }
} }
} }
...@@ -2226,9 +2226,9 @@ public override int GetWarningLevel(int code) ...@@ -2226,9 +2226,9 @@ public override int GetWarningLevel(int code)
} }
} }
public override string ConvertSymbolToString(int errorCode, ISymbol symbol) public override string GetErrorDisplayString(ISymbol symbol)
{ {
return MessageProvider.Instance.ConvertSymbolToString(errorCode, symbol); return MessageProvider.Instance.GetErrorDisplayString(symbol);
} }
} }
......
...@@ -187,8 +187,10 @@ public void GetChunks_DestructingEnum() ...@@ -187,8 +187,10 @@ public void GetChunks_DestructingEnum()
} }
} }
[Fact] [Fact]
public void ToArray() public void ToArray1()
{ {
var builder = new BlobBuilder(16); var builder = new BlobBuilder(16);
...@@ -226,6 +228,43 @@ public void ToArray() ...@@ -226,6 +228,43 @@ public void ToArray()
AssertEx.Equal(new byte[] { 0xdd, 0xcc, 0xbb, 0xaa }, builder.ToArray(13, 4)); AssertEx.Equal(new byte[] { 0xdd, 0xcc, 0xbb, 0xaa }, builder.ToArray(13, 4));
} }
[Fact]
public void ToArray2()
{
var builder = new BlobBuilder(16);
AssertEx.Equal(new byte[] { }, builder.ToArray(0, 0));
for (int i = 0; i < 34; i++)
{
builder.WriteByte((byte)i);
}
AssertEx.Equal(new byte[]
{
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F,
0x20, 0x21
}, builder.ToArray());
AssertEx.Equal(new byte[]
{
0x0E, 0x0F,
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F,
0x20, 0x21
}, builder.ToArray(0x0e, 20));
AssertEx.Equal(new byte[] { 0x0E }, builder.ToArray(0x0e, 1));
AssertEx.Equal(new byte[] { 0x0E, 0x0F }, builder.ToArray(0x0e, 2));
AssertEx.Equal(new byte[] { 0x0E, 0x0F, 0x10 }, builder.ToArray(0x0e, 3));
AssertEx.Equal(new byte[] { 0x0E, 0x0F, 0x10, 0x11 }, builder.ToArray(0x0e, 4));
AssertEx.Equal(new byte[] { 0x1E }, builder.ToArray(0x1e, 1));
AssertEx.Equal(new byte[] { 0x1E, 0x1F }, builder.ToArray(0x1e, 2));
AssertEx.Equal(new byte[] { 0x1E, 0x1F, 0x20 }, builder.ToArray(0x1e, 3));
AssertEx.Equal(new byte[] { 0x1E, 0x1F, 0x20, 0x21 }, builder.ToArray(0x1e, 4));
}
[Fact] [Fact]
public void ToArray_Errors() public void ToArray_Errors()
{ {
......
...@@ -314,6 +314,15 @@ internal class CodeAnalysisResources { ...@@ -314,6 +314,15 @@ internal class CodeAnalysisResources {
} }
} }
/// <summary>
/// Looks up a localized string similar to The compilation references multiple assemblies whose versions only differ in auto-generated build and/or revision numbers..
/// </summary>
internal static string CompilationReferencesAssembliesWithDifferentAutoGeneratedVersion {
get {
return ResourceManager.GetString("CompilationReferencesAssembliesWithDifferentAutoGeneratedVersion", resourceCulture);
}
}
/// <summary> /// <summary>
/// Looks up a localized string similar to Analyzer Failure. /// Looks up a localized string similar to Analyzer Failure.
/// </summary> /// </summary>
......
...@@ -519,4 +519,7 @@ ...@@ -519,4 +519,7 @@
<data name="TuplesNeedAtLeastTwoElements" xml:space="preserve"> <data name="TuplesNeedAtLeastTwoElements" xml:space="preserve">
<value>Tuples must have at least two elements.</value> <value>Tuples must have at least two elements.</value>
</data> </data>
<data name="CompilationReferencesAssembliesWithDifferentAutoGeneratedVersion" xml:space="preserve">
<value>The compilation references multiple assemblies whose versions only differ in auto-generated build and/or revision numbers.</value>
</data>
</root> </root>
\ No newline at end of file
...@@ -86,9 +86,9 @@ public Diagnostic CreateDiagnostic(int code, Location location) ...@@ -86,9 +86,9 @@ public Diagnostic CreateDiagnostic(int code, Location location)
public abstract string GetMessagePrefix(string id, DiagnosticSeverity severity, bool isWarningAsError, CultureInfo culture); public abstract string GetMessagePrefix(string id, DiagnosticSeverity severity, bool isWarningAsError, CultureInfo culture);
/// <summary> /// <summary>
/// convert given symbol to string representation based on given error code /// Convert given symbol to string representation.
/// </summary> /// </summary>
public abstract string ConvertSymbolToString(int errorCode, ISymbol symbol); public abstract string GetErrorDisplayString(ISymbol symbol);
/// <summary> /// <summary>
/// Given an error code (like 1234) return the identifier (CS1234 or BC1234). /// Given an error code (like 1234) return the identifier (CS1234 or BC1234).
...@@ -206,6 +206,7 @@ public DiagnosticInfo FilterDiagnosticInfo(DiagnosticInfo diagnosticInfo, Compil ...@@ -206,6 +206,7 @@ public DiagnosticInfo FilterDiagnosticInfo(DiagnosticInfo diagnosticInfo, Compil
public abstract int ERR_TooManyUserStrings { get; } public abstract int ERR_TooManyUserStrings { get; }
public abstract int ERR_PeWritingFailure { get; } public abstract int ERR_PeWritingFailure { get; }
public abstract int ERR_ModuleEmitFailure { get; } public abstract int ERR_ModuleEmitFailure { get; }
public abstract int ERR_EncUpdateFailedMissingAttribute { get; }
public abstract void ReportInvalidAttributeArgument(DiagnosticBag diagnostics, SyntaxNode attributeSyntax, int parameterIndex, AttributeData attribute); public abstract void ReportInvalidAttributeArgument(DiagnosticBag diagnostics, SyntaxNode attributeSyntax, int parameterIndex, AttributeData attribute);
public abstract void ReportInvalidNamedArgument(DiagnosticBag diagnostics, SyntaxNode attributeSyntax, int namedArgumentIndex, ITypeSymbol attributeClass, string parameterName); public abstract void ReportInvalidNamedArgument(DiagnosticBag diagnostics, SyntaxNode attributeSyntax, int namedArgumentIndex, ITypeSymbol attributeClass, string parameterName);
......
...@@ -356,7 +356,7 @@ protected object[] GetArgumentsToUse(IFormatProvider formatProvider) ...@@ -356,7 +356,7 @@ protected object[] GetArgumentsToUse(IFormatProvider formatProvider)
if (symbol != null) if (symbol != null)
{ {
argumentsToUse = InitializeArgumentListIfNeeded(argumentsToUse); argumentsToUse = InitializeArgumentListIfNeeded(argumentsToUse);
argumentsToUse[i] = _messageProvider.ConvertSymbolToString(_errorCode, symbol); argumentsToUse[i] = _messageProvider.GetErrorDisplayString(symbol);
} }
} }
......
...@@ -195,8 +195,7 @@ private bool TryGetPreviousLocalId(SyntaxNode currentDeclarator, LocalDebugId cu ...@@ -195,8 +195,7 @@ private bool TryGetPreviousLocalId(SyntaxNode currentDeclarator, LocalDebugId cu
// Should rarely happen since the IDE reports a rude edit if the attribute type doesn't exist. // Should rarely happen since the IDE reports a rude edit if the attribute type doesn't exist.
if (_hoistedLocalSlotsOpt == null) if (_hoistedLocalSlotsOpt == null)
{ {
// TODO: better error message https://github.com/dotnet/roslyn/issues/9196 ReportMissingStateMachineAttribute(diagnostics);
diagnostics.Add(_messageProvider.CreateDiagnostic(_messageProvider.ERR_ModuleEmitFailure, NoLocation.Singleton, _previousTopLevelMethod.ContainingModule.Name));
slotIndex = -1; slotIndex = -1;
return false; return false;
} }
...@@ -231,8 +230,7 @@ public override bool TryGetPreviousAwaiterSlotIndex(Cci.ITypeReference currentTy ...@@ -231,8 +230,7 @@ public override bool TryGetPreviousAwaiterSlotIndex(Cci.ITypeReference currentTy
// Should rarely happen since the IDE reports a rude edit if the attribute type doesn't exist. // Should rarely happen since the IDE reports a rude edit if the attribute type doesn't exist.
if (_awaiterMapOpt == null) if (_awaiterMapOpt == null)
{ {
// TODO: better error message https://github.com/dotnet/roslyn/issues/9196 ReportMissingStateMachineAttribute(diagnostics);
diagnostics.Add(_messageProvider.CreateDiagnostic(_messageProvider.ERR_ModuleEmitFailure, NoLocation.Singleton, _previousTopLevelMethod.ContainingModule.Name));
slotIndex = -1; slotIndex = -1;
return false; return false;
} }
...@@ -240,6 +238,16 @@ public override bool TryGetPreviousAwaiterSlotIndex(Cci.ITypeReference currentTy ...@@ -240,6 +238,16 @@ public override bool TryGetPreviousAwaiterSlotIndex(Cci.ITypeReference currentTy
return _awaiterMapOpt.TryGetValue(_symbolMap.MapReference(currentType), out slotIndex); return _awaiterMapOpt.TryGetValue(_symbolMap.MapReference(currentType), out slotIndex);
} }
private void ReportMissingStateMachineAttribute(DiagnosticBag diagnostics)
{
diagnostics.Add(_messageProvider.CreateDiagnostic(
_messageProvider.ERR_EncUpdateFailedMissingAttribute,
NoLocation.Singleton,
_messageProvider.GetErrorDisplayString(_previousTopLevelMethod),
(_previousTopLevelMethod.IsAsync ? AttributeDescription.AsyncStateMachineAttribute : AttributeDescription.IteratorStateMachineAttribute).FullName));
}
private bool TryGetPreviousSyntaxOffset(SyntaxNode currentSyntax, out int previousSyntaxOffset) private bool TryGetPreviousSyntaxOffset(SyntaxNode currentSyntax, out int previousSyntaxOffset)
{ {
// no syntax map // no syntax map
......
...@@ -177,9 +177,7 @@ private static string ResolveRelativePath(PathKind kind, string path, string bas ...@@ -177,9 +177,7 @@ private static string ResolveRelativePath(PathKind kind, string path, string bas
return path; return path;
default: default:
// EDMAURER this is not using ExceptionUtilities.UnexpectedValue() because this file throw ExceptionUtilities.UnexpectedValue(kind);
// is shared via linking with other code that doesn't have the ExceptionUtilities.
throw new InvalidOperationException(string.Format("Unexpected PathKind {0}.", kind));
} }
} }
......
...@@ -162,6 +162,9 @@ internal Metadata GetMetadataNoCopy() ...@@ -162,6 +162,9 @@ internal Metadata GetMetadataNoCopy()
/// Returns a copy of the <see cref="Metadata"/> object this <see cref="PortableExecutableReference"/> /// Returns a copy of the <see cref="Metadata"/> object this <see cref="PortableExecutableReference"/>
/// contains. This copy does not need to be <see cref="IDisposable.Dispose"/>d. /// contains. This copy does not need to be <see cref="IDisposable.Dispose"/>d.
/// </summary> /// </summary>
/// <exception cref="BadImageFormatException">If the PE image format is invalid.</exception>
/// <exception cref="IOException">The metadata image content can't be read.</exception>
/// <exception cref="FileNotFoundException">The metadata image is stored in a file that can't be found.</exception>
public Metadata GetMetadata() public Metadata GetMetadata()
{ {
return GetMetadataNoCopy().Copy(); return GetMetadataNoCopy().Copy();
......
...@@ -514,8 +514,8 @@ internal bool IsBound ...@@ -514,8 +514,8 @@ internal bool IsBound
if (lazyBuilder.ContainsKey(sourceIdentity)) if (lazyBuilder.ContainsKey(sourceIdentity))
{ {
// TODO: localize message, report as diagnostic (https://github.com/dotnet/roslyn/issues/8910) // The compilation references multiple assemblies whose versions only differ in auto-generated build and/or revision numbers.
throw new NotSupportedException("The compilation references multiple assemblies whose versions only differ in auto-generated build and/or revision numbers."); throw new NotSupportedException(CodeAnalysisResources.CompilationReferencesAssembliesWithDifferentAutoGeneratedVersion);
} }
lazyBuilder.Add(sourceIdentity, originalIdentity); lazyBuilder.Add(sourceIdentity, originalIdentity);
......
...@@ -289,29 +289,33 @@ public byte[] ToArray(int start, int byteCount) ...@@ -289,29 +289,33 @@ public byte[] ToArray(int start, int byteCount)
var result = new byte[byteCount]; var result = new byte[byteCount];
int chunkStartPosition = 0; int chunkStart = 0;
int resultOffset = 0; int bufferStart = start;
int bufferEnd = start + byteCount;
foreach (var chunk in GetChunks()) foreach (var chunk in GetChunks())
{ {
int chunkEndPosition = chunkStartPosition + chunk.Length; int chunkEnd = chunkStart + chunk.Length;
Debug.Assert(bufferStart >= chunkStart);
if (chunkEndPosition > start) if (chunkEnd > bufferStart)
{ {
int bytesToCopy = Math.Min(chunk.Length, result.Length - resultOffset); int bytesToCopy = Math.Min(bufferEnd, chunkEnd) - bufferStart;
if (bytesToCopy == 0) Debug.Assert(bytesToCopy >= 0);
Array.Copy(chunk._buffer, bufferStart - chunkStart, result, bufferStart - start, bytesToCopy);
bufferStart += bytesToCopy;
if (bufferStart == bufferEnd)
{ {
break; break;
} }
Array.Copy(chunk._buffer, Math.Max(start - chunkStartPosition, 0), result, resultOffset, bytesToCopy);
resultOffset += bytesToCopy;
} }
chunkStartPosition = chunkEndPosition; chunkStart = chunkEnd;
} }
Debug.Assert(resultOffset == result.Length); Debug.Assert(bufferStart == bufferEnd);
return result; return result;
} }
......
...@@ -1605,6 +1605,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic ...@@ -1605,6 +1605,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic
ERR_PublicKeyContainerFailure = 36981 ERR_PublicKeyContainerFailure = 36981
ERR_InvalidAssemblyCulture = 36982 ERR_InvalidAssemblyCulture = 36982
ERR_EncUpdateFailedMissingAttribute = 36983
ERR_CantAwaitAsyncSub1 = 37001 ERR_CantAwaitAsyncSub1 = 37001
ERR_ResumableLambdaInExpressionTree = 37050 ERR_ResumableLambdaInExpressionTree = 37050
......
...@@ -95,7 +95,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic ...@@ -95,7 +95,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic
Return New VBDiagnostic(ErrorFactory.ErrorInfo(CType(code, ERRID), args), location) Return New VBDiagnostic(ErrorFactory.ErrorInfo(CType(code, ERRID), args), location)
End Function End Function
Public Overrides Function ConvertSymbolToString(errorCode As Integer, symbol As ISymbol) As String Public Overrides Function GetErrorDisplayString(symbol As ISymbol) As String
' show extra info for assembly if possible such as version, public key token etc. ' show extra info for assembly if possible such as version, public key token etc.
If symbol.Kind = SymbolKind.Assembly OrElse symbol.Kind = SymbolKind.Namespace Then If symbol.Kind = SymbolKind.Assembly OrElse symbol.Kind = SymbolKind.Namespace Then
Return symbol.ToString() Return symbol.ToString()
...@@ -498,6 +498,12 @@ Namespace Microsoft.CodeAnalysis.VisualBasic ...@@ -498,6 +498,12 @@ Namespace Microsoft.CodeAnalysis.VisualBasic
Return ERRID.ERR_ModuleEmitFailure Return ERRID.ERR_ModuleEmitFailure
End Get End Get
End Property End Property
Public Overrides ReadOnly Property ERR_EncUpdateFailedMissingAttribute As Integer
Get
Return ERRID.ERR_EncUpdateFailedMissingAttribute
End Get
End Property
End Class End Class
End Namespace End Namespace
...@@ -3296,6 +3296,15 @@ Namespace Microsoft.CodeAnalysis.VisualBasic ...@@ -3296,6 +3296,15 @@ Namespace Microsoft.CodeAnalysis.VisualBasic
End Get End Get
End Property End Property
'''<summary>
''' Looks up a localized string similar to Cannot update &apos;{0}&apos;; attribute &apos;{1}&apos; is missing..
'''</summary>
Friend ReadOnly Property ERR_EncUpdateFailedMissingAttribute() As String
Get
Return ResourceManager.GetString("ERR_EncUpdateFailedMissingAttribute", resourceCulture)
End Get
End Property
'''<summary> '''<summary>
''' Looks up a localized string similar to &apos;End Class&apos; must be preceded by a matching &apos;Class&apos;.. ''' Looks up a localized string similar to &apos;End Class&apos; must be preceded by a matching &apos;Class&apos;..
'''</summary> '''</summary>
......
...@@ -818,6 +818,9 @@ ...@@ -818,6 +818,9 @@
<data name="ERR_ModuleEmitFailure" xml:space="preserve"> <data name="ERR_ModuleEmitFailure" xml:space="preserve">
<value>Failed to emit module '{0}'.</value> <value>Failed to emit module '{0}'.</value>
</data> </data>
<data name="ERR_EncUpdateFailedMissingAttribute" xml:space="preserve">
<value>Cannot update '{0}'; attribute '{1}' is missing.</value>
</data>
<data name="ERR_OverrideNotNeeded3" xml:space="preserve"> <data name="ERR_OverrideNotNeeded3" xml:space="preserve">
<value>{0} '{1}' cannot be declared 'Overrides' because it does not override a {0} in a base class.</value> <value>{0} '{1}' cannot be declared 'Overrides' because it does not override a {0} in a base class.</value>
</data> </data>
......
...@@ -4447,7 +4447,7 @@ End Class ...@@ -4447,7 +4447,7 @@ End Class
ImmutableArray.Create(New SemanticEdit(SemanticEditKind.Update, f0, f1, GetSyntaxMapFromMarkers(source0, source1), preserveLocalVariables:=True))) ImmutableArray.Create(New SemanticEdit(SemanticEditKind.Update, f0, f1, GetSyntaxMapFromMarkers(source0, source1), preserveLocalVariables:=True)))
diff1.EmitResult.Diagnostics.Verify( diff1.EmitResult.Diagnostics.Verify(
Diagnostic(ERRID.ERR_ModuleEmitFailure).WithArguments(compilation0.SourceModule.Name)) Diagnostic(ERRID.ERR_EncUpdateFailedMissingAttribute).WithArguments("Public Function F() As IEnumerable(Of Integer)", "System.Runtime.CompilerServices.IteratorStateMachineAttribute"))
End Sub End Sub
<Fact, WorkItem(9119, "https://github.com/dotnet/roslyn/issues/9119")> <Fact, WorkItem(9119, "https://github.com/dotnet/roslyn/issues/9119")>
...@@ -4501,8 +4501,8 @@ End Class ...@@ -4501,8 +4501,8 @@ End Class
ImmutableArray.Create(New SemanticEdit(SemanticEditKind.Update, f0, f1, GetSyntaxMapFromMarkers(source0, source1), preserveLocalVariables:=True))) ImmutableArray.Create(New SemanticEdit(SemanticEditKind.Update, f0, f1, GetSyntaxMapFromMarkers(source0, source1), preserveLocalVariables:=True)))
diff1.EmitResult.Diagnostics.Verify( diff1.EmitResult.Diagnostics.Verify(
Diagnostic(ERRID.ERR_ModuleEmitFailure).WithArguments(compilation0.SourceModule.Name), Diagnostic(ERRID.ERR_EncUpdateFailedMissingAttribute).WithArguments("Public Function F() As Task(Of Integer)", "System.Runtime.CompilerServices.AsyncStateMachineAttribute"),
Diagnostic(ERRID.ERR_ModuleEmitFailure).WithArguments(compilation0.SourceModule.Name)) Diagnostic(ERRID.ERR_EncUpdateFailedMissingAttribute).WithArguments("Public Function F() As Task(Of Integer)", "System.Runtime.CompilerServices.AsyncStateMachineAttribute"))
End Sub End Sub
End Class End Class
End Namespace End Namespace
...@@ -81,8 +81,8 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.UnitTests ...@@ -81,8 +81,8 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.UnitTests
Return String.Empty Return String.Empty
End Function End Function
Public Overrides Function ConvertSymbolToString(errorCode As Integer, symbol As ISymbol) As String Public Overrides Function GetErrorDisplayString(symbol As ISymbol) As String
Return MessageProvider.Instance.ConvertSymbolToString(errorCode, symbol) Return MessageProvider.Instance.GetErrorDisplayString(symbol)
End Function End Function
End Class End Class
......
...@@ -1030,8 +1030,8 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.UnitTests ...@@ -1030,8 +1030,8 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.UnitTests
Return 0 Return 0
End Function End Function
Public Overrides Function ConvertSymbolToString(errorCode As Integer, symbol As ISymbol) As String Public Overrides Function GetErrorDisplayString(symbol As ISymbol) As String
Return MessageProvider.Instance.ConvertSymbolToString(errorCode, symbol) Return MessageProvider.Instance.GetErrorDisplayString(symbol)
End Function End Function
End Class End Class
......
...@@ -99,7 +99,8 @@ public async Task<AnalysisResult> GetAnalysisDataAsync(Project project, bool avo ...@@ -99,7 +99,8 @@ public async Task<AnalysisResult> GetAnalysisDataAsync(Project project, bool avo
if (!await TryDeserializeAsync(serializer, project, project.Id, _owner.NonLocalStateName, builder.AddOthers, cancellationToken).ConfigureAwait(false)) if (!await TryDeserializeAsync(serializer, project, project.Id, _owner.NonLocalStateName, builder.AddOthers, cancellationToken).ConfigureAwait(false))
{ {
Contract.Requires(false, "How this can happen?"); // this can happen if SaveAsync is not yet called but active file merge happened. one of case is if user did build before the very first
// analysis happened.
} }
return builder.ToResult(); return builder.ToResult();
...@@ -178,7 +179,8 @@ public async Task<AnalysisResult> GetProjectAnalysisDataAsync(Project project, b ...@@ -178,7 +179,8 @@ public async Task<AnalysisResult> GetProjectAnalysisDataAsync(Project project, b
if (!await TryDeserializeAsync(serializer, project, project.Id, _owner.NonLocalStateName, builder.AddOthers, cancellationToken).ConfigureAwait(false)) if (!await TryDeserializeAsync(serializer, project, project.Id, _owner.NonLocalStateName, builder.AddOthers, cancellationToken).ConfigureAwait(false))
{ {
Contract.Requires(false, "How this can happen?"); // this can happen if SaveAsync is not yet called but active file merge happened. one of case is if user did build before the very first
// analysis happened.
} }
return builder.ToResult(); return builder.ToResult();
......
...@@ -80,9 +80,7 @@ internal static class RudeEditDiagnosticDescriptors ...@@ -80,9 +80,7 @@ internal static class RudeEditDiagnosticDescriptors
{ GetDescriptorPair(RudeEditKind.PartiallyExecutedActiveStatementDelete, FeaturesResources.AnActiveStatementHasBeenRemoved) }, { GetDescriptorPair(RudeEditKind.PartiallyExecutedActiveStatementDelete, FeaturesResources.AnActiveStatementHasBeenRemoved) },
{ GetDescriptorPair(RudeEditKind.InsertFile, FeaturesResources.AddingANewFile) }, { GetDescriptorPair(RudeEditKind.InsertFile, FeaturesResources.AddingANewFile) },
{ GetDescriptorPair(RudeEditKind.UpdatingStateMachineMethodAroundActiveStatement, FeaturesResources.UpdatingStateMachineMethodAroundActive) }, { GetDescriptorPair(RudeEditKind.UpdatingStateMachineMethodAroundActiveStatement, FeaturesResources.UpdatingStateMachineMethodAroundActive) },
// TODO: move message to resources https://github.com/dotnet/roslyn/issues/9196 { GetDescriptorPair(RudeEditKind.UpdatingStateMachineMethodMissingAttribute, FeaturesResources.UpdatingStateMachineMethodMissingAttribute) },
{ GetDescriptorPair(RudeEditKind.UpdatingStateMachineMethodMissingAttribute, "Attribute '{0}' is missing. Updating an async method or an iterator will prevent the debug session from continuing.") },
{ GetDescriptorPair(RudeEditKind.RUDE_EDIT_COMPLEX_QUERY_EXPRESSION, FeaturesResources.ModifyingAWhichContainsComplexQuery) }, { GetDescriptorPair(RudeEditKind.RUDE_EDIT_COMPLEX_QUERY_EXPRESSION, FeaturesResources.ModifyingAWhichContainsComplexQuery) },
// VB specific, // VB specific,
......
...@@ -2475,6 +2475,15 @@ internal class FeaturesResources { ...@@ -2475,6 +2475,15 @@ internal class FeaturesResources {
} }
} }
/// <summary>
/// Looks up a localized string similar to Attribute &apos;{0}&apos; is missing. Updating an async method or an iterator will prevent the debug session from continuing..
/// </summary>
internal static string UpdatingStateMachineMethodMissingAttribute {
get {
return ResourceManager.GetString("UpdatingStateMachineMethodMissingAttribute", resourceCulture);
}
}
/// <summary> /// <summary>
/// Looks up a localized string similar to Updating the alias of Declare Statement will prevent the debug session from continuing.. /// Looks up a localized string similar to Updating the alias of Declare Statement will prevent the debug session from continuing..
/// </summary> /// </summary>
......
...@@ -574,6 +574,9 @@ ...@@ -574,6 +574,9 @@
<data name="AddingANewFile" xml:space="preserve"> <data name="AddingANewFile" xml:space="preserve">
<value>Adding a new file will prevent the debug session from continuing.</value> <value>Adding a new file will prevent the debug session from continuing.</value>
</data> </data>
<data name="UpdatingStateMachineMethodMissingAttribute" xml:space="preserve">
<value>Attribute '{0}' is missing. Updating an async method or an iterator will prevent the debug session from continuing.</value>
</data>
<data name="UnexpectedInterfaceMemberKind" xml:space="preserve"> <data name="UnexpectedInterfaceMemberKind" xml:space="preserve">
<value>Unexpected interface member kind: {0}</value> <value>Unexpected interface member kind: {0}</value>
</data> </data>
......
...@@ -386,5 +386,13 @@ public override int ERR_ModuleEmitFailure ...@@ -386,5 +386,13 @@ public override int ERR_ModuleEmitFailure
throw new NotImplementedException(); throw new NotImplementedException();
} }
} }
public override int ERR_EncUpdateFailedMissingAttribute
{
get
{
throw new NotImplementedException();
}
}
} }
} }
...@@ -33,6 +33,22 @@ internal sealed class AnalyzerDependencyCheckingService ...@@ -33,6 +33,22 @@ internal sealed class AnalyzerDependencyCheckingService
private Task<AnalyzerDependencyResults> _task = Task.FromResult(AnalyzerDependencyResults.Empty); private Task<AnalyzerDependencyResults> _task = Task.FromResult(AnalyzerDependencyResults.Empty);
private ImmutableHashSet<string> _analyzerPaths = ImmutableHashSet.Create<string>(StringComparer.OrdinalIgnoreCase); private ImmutableHashSet<string> _analyzerPaths = ImmutableHashSet.Create<string>(StringComparer.OrdinalIgnoreCase);
private readonly DiagnosticDescriptor _missingAnalyzerReferenceRule = new DiagnosticDescriptor(
id: IDEDiagnosticIds.MissingAnalyzerReferenceId,
title: ServicesVSResources.WRN_MissingAnalyzerReferenceTitle,
messageFormat: ServicesVSResources.WRN_MissingAnalyzerReferenceMessage,
category: FeaturesResources.ErrorCategory,
defaultSeverity: DiagnosticSeverity.Warning,
isEnabledByDefault: true);
private readonly DiagnosticDescriptor _analyzerDependencyConflictRule = new DiagnosticDescriptor(
id: IDEDiagnosticIds.AnalyzerDependencyConflictId,
title: ServicesVSResources.WRN_AnalyzerDependencyConflictTitle,
messageFormat: ServicesVSResources.WRN_AnalyzerDependencyConflictMessage,
category: FeaturesResources.ErrorCategory,
defaultSeverity: DiagnosticSeverity.Warning,
isEnabledByDefault: true);
[ImportingConstructor] [ImportingConstructor]
public AnalyzerDependencyCheckingService( public AnalyzerDependencyCheckingService(
VisualStudioWorkspaceImpl workspace, VisualStudioWorkspaceImpl workspace,
...@@ -74,7 +90,12 @@ public async void CheckForConflictsAsync() ...@@ -74,7 +90,12 @@ public async void CheckForConflictsAsync()
if (project.CurrentProjectAnalyzersContains(conflict.AnalyzerFilePath1) || if (project.CurrentProjectAnalyzersContains(conflict.AnalyzerFilePath1) ||
project.CurrentProjectAnalyzersContains(conflict.AnalyzerFilePath2)) project.CurrentProjectAnalyzersContains(conflict.AnalyzerFilePath2))
{ {
builder.Add(CreateDiagnostic(project.Id, conflict)); var messageArguments = new string[] { conflict.AnalyzerFilePath1, conflict.AnalyzerFilePath2, conflict.Identity.ToString() };
DiagnosticData diagnostic;
if (DiagnosticData.TryCreate(_analyzerDependencyConflictRule, messageArguments, project.Id, _workspace, out diagnostic))
{
builder.Add(diagnostic);
}
} }
} }
...@@ -82,7 +103,12 @@ public async void CheckForConflictsAsync() ...@@ -82,7 +103,12 @@ public async void CheckForConflictsAsync()
{ {
if (project.CurrentProjectAnalyzersContains(missingDependency.AnalyzerPath)) if (project.CurrentProjectAnalyzersContains(missingDependency.AnalyzerPath))
{ {
builder.Add(CreateDiagnostic(project.Id, missingDependency)); var messageArguments = new string[] { missingDependency.AnalyzerPath, missingDependency.DependencyIdentity.ToString() };
DiagnosticData diagnostic;
if (DiagnosticData.TryCreate(_missingAnalyzerReferenceRule, messageArguments, project.Id, _workspace, out diagnostic))
{
builder.Add(diagnostic);
}
} }
} }
...@@ -123,57 +149,6 @@ private void LogMissingDependency(MissingAnalyzerDependency missingDependency) ...@@ -123,57 +149,6 @@ private void LogMissingDependency(MissingAnalyzerDependency missingDependency)
})); }));
} }
private DiagnosticData CreateDiagnostic(ProjectId projectId, AnalyzerDependencyConflict conflict)
{
string message = string.Format(
ServicesVSResources.WRN_AnalyzerDependencyConflictMessage,
conflict.AnalyzerFilePath1,
conflict.AnalyzerFilePath2,
conflict.Identity.ToString());
DiagnosticData data = new DiagnosticData(
IDEDiagnosticIds.AnalyzerDependencyConflictId,
FeaturesResources.ErrorCategory,
message,
ServicesVSResources.WRN_AnalyzerDependencyConflictMessage,
severity: DiagnosticSeverity.Warning,
defaultSeverity: DiagnosticSeverity.Warning,
isEnabledByDefault: true,
warningLevel: 0,
customTags: ImmutableArray<string>.Empty,
properties: ImmutableDictionary<string, string>.Empty,
workspace: _workspace,
projectId: projectId,
title: ServicesVSResources.WRN_AnalyzerDependencyConflictTitle);
return data;
}
private DiagnosticData CreateDiagnostic(ProjectId projectId, MissingAnalyzerDependency missingDependency)
{
string message = string.Format(
ServicesVSResources.WRN_MissingAnalyzerReferenceMessage,
missingDependency.AnalyzerPath,
missingDependency.DependencyIdentity.ToString());
DiagnosticData data = new DiagnosticData(
IDEDiagnosticIds.MissingAnalyzerReferenceId,
FeaturesResources.ErrorCategory,
message,
ServicesVSResources.WRN_MissingAnalyzerReferenceMessage,
severity: DiagnosticSeverity.Warning,
defaultSeverity: DiagnosticSeverity.Warning,
isEnabledByDefault: true,
warningLevel: 0,
customTags: ImmutableArray<string>.Empty,
properties: ImmutableDictionary<string, string>.Empty,
workspace: _workspace,
projectId: projectId,
title: ServicesVSResources.WRN_MissingAnalyzerReferenceTitle);
return data;
}
private Task<AnalyzerDependencyResults> GetConflictsAsync() private Task<AnalyzerDependencyResults> GetConflictsAsync()
{ {
ImmutableHashSet<string> currentAnalyzerPaths = _workspace.CurrentSolution ImmutableHashSet<string> currentAnalyzerPaths = _workspace.CurrentSolution
......
...@@ -29,6 +29,14 @@ internal sealed class AnalyzerFileWatcherService ...@@ -29,6 +29,14 @@ internal sealed class AnalyzerFileWatcherService
private readonly object _guard = new object(); private readonly object _guard = new object();
private readonly DiagnosticDescriptor _analyzerChangedRule = new DiagnosticDescriptor(
id: IDEDiagnosticIds.AnalyzerChangedId,
title: ServicesVSResources.WRN_AnalyzerChangedTitle,
messageFormat: ServicesVSResources.WRN_AnalyzerChangedMessage,
category: FeaturesResources.ErrorCategory,
defaultSeverity: DiagnosticSeverity.Warning,
isEnabledByDefault: true);
[ImportingConstructor] [ImportingConstructor]
public AnalyzerFileWatcherService( public AnalyzerFileWatcherService(
VisualStudioWorkspaceImpl workspace, VisualStudioWorkspaceImpl workspace,
...@@ -67,21 +75,12 @@ internal void RemoveAnalyzerAlreadyLoadedDiagnostics(ProjectId projectId, string ...@@ -67,21 +75,12 @@ internal void RemoveAnalyzerAlreadyLoadedDiagnostics(ProjectId projectId, string
private void RaiseAnalyzerChangedWarning(ProjectId projectId, string analyzerPath) private void RaiseAnalyzerChangedWarning(ProjectId projectId, string analyzerPath)
{ {
string message = string.Format(ServicesVSResources.WRN_AnalyzerChangedMessage, analyzerPath); var messageArguments = new string[] { analyzerPath };
DiagnosticData diagnostic;
DiagnosticData data = new DiagnosticData( if (DiagnosticData.TryCreate(_analyzerChangedRule, messageArguments, projectId, _workspace, out diagnostic))
IDEDiagnosticIds.AnalyzerChangedId, {
FeaturesResources.ErrorCategory, _updateSource.UpdateDiagnosticsForProject(projectId, Tuple.Create(s_analyzerChangedErrorId, analyzerPath), SpecializedCollections.SingletonEnumerable(diagnostic));
message, }
ServicesVSResources.WRN_AnalyzerChangedMessage,
severity: DiagnosticSeverity.Warning,
isEnabledByDefault: true,
warningLevel: 0,
workspace: _workspace,
projectId: projectId,
title: ServicesVSResources.WRN_AnalyzerChangedTitle);
_updateSource.UpdateDiagnosticsForProject(projectId, Tuple.Create(s_analyzerChangedErrorId, analyzerPath), SpecializedCollections.SingletonEnumerable(data));
} }
private DateTime? GetLastUpdateTimeUtc(string fullPath) private DateTime? GetLastUpdateTimeUtc(string fullPath)
......
...@@ -99,6 +99,14 @@ internal abstract partial class AbstractProject : IVisualStudioHostProject ...@@ -99,6 +99,14 @@ internal abstract partial class AbstractProject : IVisualStudioHostProject
private static readonly EventHandler<bool> s_additionalDocumentClosingEventHandler = OnAdditionalDocumentClosing; private static readonly EventHandler<bool> s_additionalDocumentClosingEventHandler = OnAdditionalDocumentClosing;
private static readonly EventHandler s_additionalDocumentUpdatedOnDiskEventHandler = OnAdditionalDocumentUpdatedOnDisk; private static readonly EventHandler s_additionalDocumentUpdatedOnDiskEventHandler = OnAdditionalDocumentUpdatedOnDisk;
private readonly DiagnosticDescriptor _errorReadingRulesetRule = new DiagnosticDescriptor(
id: IDEDiagnosticIds.ErrorReadingRulesetId,
title: ServicesVSResources.ERR_CantReadRulesetFileTitle,
messageFormat: ServicesVSResources.ERR_CantReadRulesetFileMessage,
category: FeaturesResources.ErrorCategory,
defaultSeverity: DiagnosticSeverity.Error,
isEnabledByDefault: true);
public AbstractProject( public AbstractProject(
VisualStudioProjectTracker projectTracker, VisualStudioProjectTracker projectTracker,
Func<ProjectId, IVsReportExternalErrors> reportExternalErrorCreatorOpt, Func<ProjectId, IVsReportExternalErrors> reportExternalErrorCreatorOpt,
...@@ -1257,20 +1265,12 @@ protected void UpdateRuleSetError(IRuleSetFile ruleSetFile) ...@@ -1257,20 +1265,12 @@ protected void UpdateRuleSetError(IRuleSetFile ruleSetFile)
} }
else else
{ {
string message = string.Format(ServicesVSResources.ERR_CantReadRulesetFileMessage, ruleSetFile.FilePath, ruleSetFile.GetException().Message); var messageArguments = new string[] { ruleSetFile.FilePath, ruleSetFile.GetException().Message };
var data = new DiagnosticData( DiagnosticData diagnostic;
id: IDEDiagnosticIds.ErrorReadingRulesetId, if (DiagnosticData.TryCreate(_errorReadingRulesetRule, messageArguments, this.Id, this.Workspace, out diagnostic))
category: FeaturesResources.ErrorCategory, {
message: message, this.HostDiagnosticUpdateSource.UpdateDiagnosticsForProject(this.Id, RuleSetErrorId, SpecializedCollections.SingletonEnumerable(diagnostic));
enuMessageForBingSearch: ServicesVSResources.ERR_CantReadRulesetFileMessage, }
severity: DiagnosticSeverity.Error,
isEnabledByDefault: true,
warningLevel: 0,
workspace: this.Workspace,
projectId: this.Id,
title: ServicesVSResources.ERR_CantReadRulesetFileTitle);
this.HostDiagnosticUpdateSource.UpdateDiagnosticsForProject(this.Id, RuleSetErrorId, SpecializedCollections.SingletonEnumerable(data));
} }
} }
......
...@@ -76,6 +76,11 @@ public ImmutableArray<DiagnosticData> GetBuildErrors() ...@@ -76,6 +76,11 @@ public ImmutableArray<DiagnosticData> GetBuildErrors()
return _lastBuiltResult; return _lastBuiltResult;
} }
public bool SupportedDiagnosticId(ProjectId projectId, string id)
{
return _state?.SupportedDiagnosticId(projectId, id) ?? false;
}
public void ClearErrors(ProjectId projectId) public void ClearErrors(ProjectId projectId)
{ {
// capture state if it exists // capture state if it exists
...@@ -88,7 +93,7 @@ public void ClearErrors(ProjectId projectId) ...@@ -88,7 +93,7 @@ public void ClearErrors(ProjectId projectId)
// otherwise (such as closing solution or removing project), no need to record it // otherwise (such as closing solution or removing project), no need to record it
state?.Built(projectId); state?.Built(projectId);
ClearProjectErrors(projectId); ClearProjectErrors(state?.Solution ?? _workspace.CurrentSolution, projectId);
}).CompletesAsyncOperation(asyncToken); }).CompletesAsyncOperation(asyncToken);
} }
...@@ -102,7 +107,7 @@ private void OnWorkspaceChanged(object sender, WorkspaceChangeEventArgs e) ...@@ -102,7 +107,7 @@ private void OnWorkspaceChanged(object sender, WorkspaceChangeEventArgs e)
case WorkspaceChangeKind.SolutionReloaded: case WorkspaceChangeKind.SolutionReloaded:
{ {
var asyncToken = _listener.BeginAsyncOperation("OnSolutionChanged"); var asyncToken = _listener.BeginAsyncOperation("OnSolutionChanged");
_taskQueue.ScheduleTask(() => e.OldSolution.ProjectIds.Do(p => ClearProjectErrors(p, e.OldSolution))).CompletesAsyncOperation(asyncToken); _taskQueue.ScheduleTask(() => e.OldSolution.ProjectIds.Do(p => ClearProjectErrors(e.OldSolution, p))).CompletesAsyncOperation(asyncToken);
break; break;
} }
...@@ -110,7 +115,7 @@ private void OnWorkspaceChanged(object sender, WorkspaceChangeEventArgs e) ...@@ -110,7 +115,7 @@ private void OnWorkspaceChanged(object sender, WorkspaceChangeEventArgs e)
case WorkspaceChangeKind.ProjectReloaded: case WorkspaceChangeKind.ProjectReloaded:
{ {
var asyncToken = _listener.BeginAsyncOperation("OnProjectChanged"); var asyncToken = _listener.BeginAsyncOperation("OnProjectChanged");
_taskQueue.ScheduleTask(() => ClearProjectErrors(e.ProjectId, e.OldSolution)).CompletesAsyncOperation(asyncToken); _taskQueue.ScheduleTask(() => ClearProjectErrors(e.OldSolution, e.ProjectId)).CompletesAsyncOperation(asyncToken);
break; break;
} }
...@@ -118,7 +123,7 @@ private void OnWorkspaceChanged(object sender, WorkspaceChangeEventArgs e) ...@@ -118,7 +123,7 @@ private void OnWorkspaceChanged(object sender, WorkspaceChangeEventArgs e)
case WorkspaceChangeKind.DocumentReloaded: case WorkspaceChangeKind.DocumentReloaded:
{ {
var asyncToken = _listener.BeginAsyncOperation("OnDocumentRemoved"); var asyncToken = _listener.BeginAsyncOperation("OnDocumentRemoved");
_taskQueue.ScheduleTask(() => ClearDocumentErrors(e.ProjectId, e.DocumentId)).CompletesAsyncOperation(asyncToken); _taskQueue.ScheduleTask(() => ClearDocumentErrors(e.OldSolution, e.ProjectId, e.DocumentId)).CompletesAsyncOperation(asyncToken);
break; break;
} }
...@@ -169,16 +174,11 @@ internal void OnSolutionBuild(object sender, UIContextChangedEventArgs e) ...@@ -169,16 +174,11 @@ internal void OnSolutionBuild(object sender, UIContextChangedEventArgs e)
// pause live analyzer // pause live analyzer
using (var operation = _notificationService.Start("BuildDone")) using (var operation = _notificationService.Start("BuildDone"))
{ {
// we will have a race here since we can't track version of solution the out of proc build actually used.
// result of the race will be us dropping some diagnostics from the build to the floor.
var solution = _workspace.CurrentSolution;
var supportedIdMap = GetSupportedLiveDiagnosticId(solution, inprogressState);
Func<DiagnosticData, bool> liveDiagnosticChecker = d => Func<DiagnosticData, bool> liveDiagnosticChecker = d =>
{ {
// REVIEW: we probably need a better design on de-duplicating live and build errors. or don't de-dup at all. // REVIEW: we probably need a better design on de-duplicating live and build errors. or don't de-dup at all.
// for now, we are special casing compiler error case. // for now, we are special casing compiler error case.
var project = solution.GetProject(d.ProjectId); var project = inprogressState.Solution.GetProject(d.ProjectId);
if (project == null) if (project == null)
{ {
// project doesn't exist // project doesn't exist
...@@ -194,8 +194,7 @@ internal void OnSolutionBuild(object sender, UIContextChangedEventArgs e) ...@@ -194,8 +194,7 @@ internal void OnSolutionBuild(object sender, UIContextChangedEventArgs e)
return false; return false;
} }
HashSet<string> set; if (inprogressState.SupportedDiagnosticId(d.ProjectId, d.Id))
if (supportedIdMap.TryGetValue(d.ProjectId, out set) && set.Contains(d.Id))
{ {
return true; return true;
} }
...@@ -206,8 +205,8 @@ internal void OnSolutionBuild(object sender, UIContextChangedEventArgs e) ...@@ -206,8 +205,8 @@ internal void OnSolutionBuild(object sender, UIContextChangedEventArgs e)
var diagnosticService = _diagnosticService as DiagnosticAnalyzerService; var diagnosticService = _diagnosticService as DiagnosticAnalyzerService;
if (diagnosticService != null) if (diagnosticService != null)
{ {
await CleanupAllLiveErrorsIfNeededAsync(diagnosticService, solution, inprogressState).ConfigureAwait(false); await CleanupAllLiveErrorsIfNeededAsync(diagnosticService, inprogressState.Solution, inprogressState).ConfigureAwait(false);
await SyncBuildErrorsAndReportAsync(diagnosticService, inprogressState.GetLiveDiagnosticsPerProject(liveDiagnosticChecker)).ConfigureAwait(false); await SyncBuildErrorsAndReportAsync(diagnosticService, inprogressState.Solution, inprogressState.GetLiveDiagnosticsPerProject(liveDiagnosticChecker)).ConfigureAwait(false);
} }
inprogressState.Done(); inprogressState.Done();
...@@ -239,7 +238,8 @@ private System.Threading.Tasks.Task CleanupAllLiveErrors(DiagnosticAnalyzerServi ...@@ -239,7 +238,8 @@ private System.Threading.Tasks.Task CleanupAllLiveErrors(DiagnosticAnalyzerServi
return diagnosticService.SynchronizeWithBuildAsync(_workspace, map); return diagnosticService.SynchronizeWithBuildAsync(_workspace, map);
} }
private async System.Threading.Tasks.Task SyncBuildErrorsAndReportAsync(DiagnosticAnalyzerService diagnosticService, ImmutableDictionary<ProjectId, ImmutableArray<DiagnosticData>> map) private async System.Threading.Tasks.Task SyncBuildErrorsAndReportAsync(
DiagnosticAnalyzerService diagnosticService, Solution solution, ImmutableDictionary<ProjectId, ImmutableArray<DiagnosticData>> map)
{ {
// make those errors live errors // make those errors live errors
await diagnosticService.SynchronizeWithBuildAsync(_workspace, map).ConfigureAwait(false); await diagnosticService.SynchronizeWithBuildAsync(_workspace, map).ConfigureAwait(false);
...@@ -253,56 +253,36 @@ private async System.Threading.Tasks.Task SyncBuildErrorsAndReportAsync(Diagnost ...@@ -253,56 +253,36 @@ private async System.Threading.Tasks.Task SyncBuildErrorsAndReportAsync(Diagnost
foreach (var projectGroup in group.GroupBy(g => g.ProjectId)) foreach (var projectGroup in group.GroupBy(g => g.ProjectId))
{ {
Contract.ThrowIfNull(projectGroup.Key); Contract.ThrowIfNull(projectGroup.Key);
ReportBuildErrors(projectGroup.Key, projectGroup.ToImmutableArray()); ReportBuildErrors(projectGroup.Key, solution, projectGroup.ToImmutableArray());
} }
continue; continue;
} }
ReportBuildErrors(group.Key, group.ToImmutableArray()); ReportBuildErrors(group.Key, solution, group.ToImmutableArray());
} }
} }
private void ReportBuildErrors<T>(T item, ImmutableArray<DiagnosticData> buildErrors) private void ReportBuildErrors<T>(T item, Solution solution, ImmutableArray<DiagnosticData> buildErrors)
{ {
var projectId = item as ProjectId; var projectId = item as ProjectId;
if (projectId != null) if (projectId != null)
{ {
RaiseDiagnosticsCreated(projectId, projectId, null, buildErrors); RaiseDiagnosticsCreated(projectId, solution, projectId, null, buildErrors);
return; return;
} }
// must be not null // must be not null
var documentId = item as DocumentId; var documentId = item as DocumentId;
RaiseDiagnosticsCreated(documentId, documentId.ProjectId, documentId, buildErrors); RaiseDiagnosticsCreated(documentId, solution, documentId.ProjectId, documentId, buildErrors);
}
private Dictionary<ProjectId, HashSet<string>> GetSupportedLiveDiagnosticId(Solution solution, InprogressState state)
{
var map = new Dictionary<ProjectId, HashSet<string>>();
// here, we don't care about perf that much since build is already expensive work
foreach (var projectId in state.GetProjectsWithErrors(solution))
{
var project = solution.GetProject(projectId);
if (project == null)
{
continue;
}
var descriptorMap = _diagnosticService.GetDiagnosticDescriptors(project);
map.Add(project.Id, new HashSet<string>(descriptorMap.Values.SelectMany(v => v.Select(d => d.Id))));
}
return map;
} }
private void ClearProjectErrors(ProjectId projectId, Solution solution = null) private void ClearProjectErrors(Solution solution, ProjectId projectId)
{ {
// remove all project errors // remove all project errors
RaiseDiagnosticsRemoved(projectId, projectId, documentId: null); RaiseDiagnosticsRemoved(projectId, solution, projectId, documentId: null);
var project = (solution ?? _workspace.CurrentSolution).GetProject(projectId); var project = solution.GetProject(projectId);
if (project == null) if (project == null)
{ {
return; return;
...@@ -311,13 +291,25 @@ private void ClearProjectErrors(ProjectId projectId, Solution solution = null) ...@@ -311,13 +291,25 @@ private void ClearProjectErrors(ProjectId projectId, Solution solution = null)
// remove all document errors // remove all document errors
foreach (var documentId in project.DocumentIds) foreach (var documentId in project.DocumentIds)
{ {
ClearDocumentErrors(projectId, documentId); ClearDocumentErrors(solution, projectId, documentId);
} }
} }
private void ClearDocumentErrors(ProjectId projectId, DocumentId documentId) private void ClearDocumentErrors(Solution solution, ProjectId projectId, DocumentId documentId)
{ {
RaiseDiagnosticsRemoved(documentId, projectId, documentId); RaiseDiagnosticsRemoved(documentId, solution, projectId, documentId);
}
public void AddNewErrors(ProjectId projectId, DiagnosticData diagnostic)
{
// capture state that will be processed in background thread.
var state = GetOrCreateInprogressState();
var asyncToken = _listener.BeginAsyncOperation("Project New Errors");
_taskQueue.ScheduleTask(() =>
{
state.AddError(projectId, diagnostic);
}).CompletesAsyncOperation(asyncToken);
} }
public void AddNewErrors(DocumentId documentId, DiagnosticData diagnostic) public void AddNewErrors(DocumentId documentId, DiagnosticData diagnostic)
...@@ -354,22 +346,25 @@ private InprogressState GetOrCreateInprogressState() ...@@ -354,22 +346,25 @@ private InprogressState GetOrCreateInprogressState()
{ {
if (_state == null) if (_state == null)
{ {
Interlocked.CompareExchange(ref _state, new InprogressState(this), null); // here, we take current snapshot of solution when the state is first created. and through out this code, we use this snapshot.
// since we have no idea what actual snapshot of solution the out of proc build has picked up, it doesn't remove the race we can have
// between build and diagnostic service, but this at least make us to consistent inside of our code.
Interlocked.CompareExchange(ref _state, new InprogressState(this, _workspace.CurrentSolution), null);
} }
return _state; return _state;
} }
private void RaiseDiagnosticsCreated(object id, ProjectId projectId, DocumentId documentId, ImmutableArray<DiagnosticData> items) private void RaiseDiagnosticsCreated(object id, Solution solution, ProjectId projectId, DocumentId documentId, ImmutableArray<DiagnosticData> items)
{ {
DiagnosticsUpdated?.Invoke(this, DiagnosticsUpdatedArgs.DiagnosticsCreated( DiagnosticsUpdated?.Invoke(this, DiagnosticsUpdatedArgs.DiagnosticsCreated(
CreateArgumentKey(id), _workspace, _workspace.CurrentSolution, projectId, documentId, items)); CreateArgumentKey(id), _workspace, solution, projectId, documentId, items));
} }
private void RaiseDiagnosticsRemoved(object id, ProjectId projectId, DocumentId documentId) private void RaiseDiagnosticsRemoved(object id, Solution solution, ProjectId projectId, DocumentId documentId)
{ {
DiagnosticsUpdated?.Invoke(this, DiagnosticsUpdatedArgs.DiagnosticsRemoved( DiagnosticsUpdated?.Invoke(this, DiagnosticsUpdatedArgs.DiagnosticsRemoved(
CreateArgumentKey(id), _workspace, _workspace.CurrentSolution, projectId, documentId)); CreateArgumentKey(id), _workspace, solution, projectId, documentId));
} }
private static ArgumentKey CreateArgumentKey(object id) => new ArgumentKey(id); private static ArgumentKey CreateArgumentKey(object id) => new ArgumentKey(id);
...@@ -392,14 +387,17 @@ private void RaiseBuildStarted(bool started) ...@@ -392,14 +387,17 @@ private void RaiseBuildStarted(bool started)
private class InprogressState private class InprogressState
{ {
private readonly ExternalErrorDiagnosticUpdateSource _owner; private readonly ExternalErrorDiagnosticUpdateSource _owner;
private readonly Solution _solution;
private readonly HashSet<ProjectId> _builtProjects = new HashSet<ProjectId>(); private readonly HashSet<ProjectId> _builtProjects = new HashSet<ProjectId>();
private readonly Dictionary<ProjectId, HashSet<DiagnosticData>> _projectMap = new Dictionary<ProjectId, HashSet<DiagnosticData>>(); private readonly Dictionary<ProjectId, HashSet<DiagnosticData>> _projectMap = new Dictionary<ProjectId, HashSet<DiagnosticData>>();
private readonly Dictionary<DocumentId, HashSet<DiagnosticData>> _documentMap = new Dictionary<DocumentId, HashSet<DiagnosticData>>(); private readonly Dictionary<DocumentId, HashSet<DiagnosticData>> _documentMap = new Dictionary<DocumentId, HashSet<DiagnosticData>>();
private readonly Dictionary<ProjectId, HashSet<string>> _diagnosticIdMap = new Dictionary<ProjectId, HashSet<string>>();
public InprogressState(ExternalErrorDiagnosticUpdateSource owner) public InprogressState(ExternalErrorDiagnosticUpdateSource owner, Solution solution)
{ {
_owner = owner; _owner = owner;
_solution = solution;
// let people know build has started // let people know build has started
// TODO: to be more accurate, it probably needs to be counted. but for now, // TODO: to be more accurate, it probably needs to be counted. but for now,
...@@ -407,11 +405,38 @@ public InprogressState(ExternalErrorDiagnosticUpdateSource owner) ...@@ -407,11 +405,38 @@ public InprogressState(ExternalErrorDiagnosticUpdateSource owner)
_owner.RaiseBuildStarted(started: true); _owner.RaiseBuildStarted(started: true);
} }
public Solution Solution => _solution;
public void Done() public void Done()
{ {
_owner.RaiseBuildStarted(started: false); _owner.RaiseBuildStarted(started: false);
} }
public bool SupportedDiagnosticId(ProjectId projectId, string id)
{
HashSet<string> ids;
if (_diagnosticIdMap.TryGetValue(projectId, out ids))
{
return ids.Contains(id);
}
// set ids set
var map = new HashSet<string>();
_diagnosticIdMap.Add(projectId, map);
var project = _solution.GetProject(projectId);
if (project == null)
{
// projectId no longer exist, return false;
return false;
}
var descriptorMap = _owner._diagnosticService.GetDiagnosticDescriptors(project);
map.UnionWith(descriptorMap.Values.SelectMany(v => v.Select(d => d.Id)));
return map.Contains(id);
}
public ImmutableArray<DiagnosticData> GetBuildDiagnostics() public ImmutableArray<DiagnosticData> GetBuildDiagnostics()
{ {
return ImmutableArray.CreateRange(_projectMap.Values.SelectMany(d => d).Concat(_documentMap.Values.SelectMany(d => d))); return ImmutableArray.CreateRange(_projectMap.Values.SelectMany(d => d).Concat(_documentMap.Values.SelectMany(d => d)));
...@@ -467,6 +492,11 @@ public void AddError(DocumentId key, DiagnosticData diagnostic) ...@@ -467,6 +492,11 @@ public void AddError(DocumentId key, DiagnosticData diagnostic)
AddError(_documentMap, key, diagnostic); AddError(_documentMap, key, diagnostic);
} }
public void AddError(ProjectId key, DiagnosticData diagnostic)
{
AddError(_projectMap, key, diagnostic);
}
private void AddErrors<T>(Dictionary<T, HashSet<DiagnosticData>> map, T key, HashSet<DiagnosticData> diagnostics) private void AddErrors<T>(Dictionary<T, HashSet<DiagnosticData>> map, T key, HashSet<DiagnosticData> diagnostics)
{ {
var errors = GetErrorSet(map, key); var errors = GetErrorSet(map, key);
......
...@@ -24,6 +24,7 @@ internal class ProjectExternalErrorReporter : IVsReportExternalErrors, IVsLangua ...@@ -24,6 +24,7 @@ internal class ProjectExternalErrorReporter : IVsReportExternalErrors, IVsLangua
private readonly ProjectId _projectId; private readonly ProjectId _projectId;
private readonly string _errorCodePrefix; private readonly string _errorCodePrefix;
private readonly VisualStudioWorkspaceImpl _workspace; private readonly VisualStudioWorkspaceImpl _workspace;
private readonly ExternalErrorDiagnosticUpdateSource _diagnosticProvider; private readonly ExternalErrorDiagnosticUpdateSource _diagnosticProvider;
...@@ -31,11 +32,23 @@ public ProjectExternalErrorReporter(ProjectId projectId, string errorCodePrefix, ...@@ -31,11 +32,23 @@ public ProjectExternalErrorReporter(ProjectId projectId, string errorCodePrefix,
{ {
_projectId = projectId; _projectId = projectId;
_errorCodePrefix = errorCodePrefix; _errorCodePrefix = errorCodePrefix;
_diagnosticProvider = serviceProvider.GetMefService<ExternalErrorDiagnosticUpdateSource>();
_workspace = serviceProvider.GetMefService<VisualStudioWorkspaceImpl>(); _workspace = serviceProvider.GetMefService<VisualStudioWorkspaceImpl>();
_diagnosticProvider = serviceProvider.GetMefService<ExternalErrorDiagnosticUpdateSource>();
Debug.Assert(_diagnosticProvider != null);
Debug.Assert(_workspace != null); Debug.Assert(_workspace != null);
Debug.Assert(_diagnosticProvider != null);
}
private bool CanHandle(string errorId)
{
// we accept all compiler diagnostics
if (errorId.StartsWith(_errorCodePrefix))
{
return true;
}
return _diagnosticProvider.SupportedDiagnosticId(_projectId, errorId);
} }
public int AddNewErrors(IVsEnumExternalErrors pErrors) public int AddNewErrors(IVsEnumExternalErrors pErrors)
...@@ -134,28 +147,20 @@ public int ReportError(string bstrErrorMessage, string bstrErrorId, [ComAliasNam ...@@ -134,28 +147,20 @@ public int ReportError(string bstrErrorMessage, string bstrErrorId, [ComAliasNam
// TODO: Use PreserveSig instead of throwing these exceptions for common cases. // TODO: Use PreserveSig instead of throwing these exceptions for common cases.
public void ReportError2(string bstrErrorMessage, string bstrErrorId, [ComAliasName("VsShell.VSTASKPRIORITY")]VSTASKPRIORITY nPriority, int iStartLine, int iStartColumn, int iEndLine, int iEndColumn, string bstrFileName) public void ReportError2(string bstrErrorMessage, string bstrErrorId, [ComAliasName("VsShell.VSTASKPRIORITY")]VSTASKPRIORITY nPriority, int iStartLine, int iStartColumn, int iEndLine, int iEndColumn, string bstrFileName)
{ {
if ((iEndLine >= 0 && iEndColumn >= 0) && // first we check whether given error is something we can take care.
((iEndLine < iStartLine) || if (!CanHandle(bstrErrorId))
(iEndLine == iStartLine && iEndColumn < iStartColumn)))
{
throw new ArgumentException(ServicesVSResources.EndPositionMustBeGreaterThanStart);
}
// We only handle errors that have positions. For the rest, we punt back to the
// project system.
if (iStartLine < 0 || iStartColumn < 0)
{ {
// it is not, let project system takes care.
throw new NotImplementedException(); throw new NotImplementedException();
} }
var hostProject = _workspace.GetHostProject(_projectId); if ((iEndLine >= 0 && iEndColumn >= 0) &&
if (!hostProject.ContainsFile(bstrFileName)) ((iEndLine < iStartLine) ||
(iEndLine == iStartLine && iEndColumn < iStartColumn)))
{ {
throw new NotImplementedException(); throw new ArgumentException(ServicesVSResources.EndPositionMustBeGreaterThanStart);
} }
var hostDocument = hostProject.GetCurrentDocumentFromPath(bstrFileName);
var priority = (VSTASKPRIORITY)nPriority; var priority = (VSTASKPRIORITY)nPriority;
DiagnosticSeverity severity; DiagnosticSeverity severity;
switch (priority) switch (priority)
...@@ -173,6 +178,32 @@ public void ReportError2(string bstrErrorMessage, string bstrErrorId, [ComAliasN ...@@ -173,6 +178,32 @@ public void ReportError2(string bstrErrorMessage, string bstrErrorId, [ComAliasN
throw new ArgumentException(ServicesVSResources.NotAValidValue, nameof(nPriority)); throw new ArgumentException(ServicesVSResources.NotAValidValue, nameof(nPriority));
} }
if (iStartLine < 0 || iStartColumn < 0)
{
// we now takes care of errors that is not belong to file as well.
var projectDiagnostic = GetDiagnosticData(
null, bstrErrorId, bstrErrorMessage, severity,
null, 0, 0, 0, 0,
bstrFileName, 0, 0, 0, 0);
_diagnosticProvider.AddNewErrors(_projectId, projectDiagnostic);
return;
}
var hostProject = _workspace.GetHostProject(_projectId);
if (!hostProject.ContainsFile(bstrFileName))
{
var projectDiagnostic = GetDiagnosticData(
null, bstrErrorId, bstrErrorMessage, severity,
null, iStartLine, iStartColumn, iEndLine, iEndColumn,
bstrFileName, iStartLine, iStartColumn, iEndLine, iEndColumn);
_diagnosticProvider.AddNewErrors(_projectId, projectDiagnostic);
return;
}
var hostDocument = hostProject.GetCurrentDocumentFromPath(bstrFileName);
var diagnostic = GetDiagnosticData( var diagnostic = GetDiagnosticData(
hostDocument.Id, bstrErrorId, bstrErrorMessage, severity, hostDocument.Id, bstrErrorId, bstrErrorMessage, severity,
null, iStartLine, iStartColumn, iEndLine, iEndColumn, null, iStartLine, iStartColumn, iEndLine, iEndColumn,
......
...@@ -32,8 +32,18 @@ public AbstractOptionPageControl(IServiceProvider serviceProvider) ...@@ -32,8 +32,18 @@ public AbstractOptionPageControl(IServiceProvider serviceProvider)
var checkBoxStyle = new System.Windows.Style(typeof(CheckBox)); var checkBoxStyle = new System.Windows.Style(typeof(CheckBox));
checkBoxStyle.Setters.Add(new Setter(CheckBox.MarginProperty, new Thickness() { Bottom = 7 })); checkBoxStyle.Setters.Add(new Setter(CheckBox.MarginProperty, new Thickness() { Bottom = 7 }));
groupBoxStyle.Setters.Add(new Setter(GroupBox.ForegroundProperty, new DynamicResourceExtension(SystemColors.WindowTextBrushKey))); checkBoxStyle.Setters.Add(new Setter(CheckBox.ForegroundProperty, new DynamicResourceExtension(SystemColors.WindowTextBrushKey)));
Resources.Add(typeof(CheckBox), checkBoxStyle); Resources.Add(typeof(CheckBox), checkBoxStyle);
var textBoxStyle = new Style(typeof(TextBox));
textBoxStyle.Setters.Add(new Setter(TextBox.MarginProperty, new Thickness() { Left = 7, Right = 7 }));
textBoxStyle.Setters.Add(new Setter(TextBox.ForegroundProperty, new DynamicResourceExtension(SystemColors.WindowTextBrushKey)));
Resources.Add(typeof(TextBox), textBoxStyle);
}
protected void AddBinding(BindingExpressionBase bindingExpression)
{
_bindingExpressions.Add(bindingExpression);
} }
protected void BindToOption(CheckBox checkbox, Option<bool> optionKey) protected void BindToOption(CheckBox checkbox, Option<bool> optionKey)
......
...@@ -53,6 +53,21 @@ Namespace Microsoft.VisualStudio.LanguageServices.UnitTests.Diagnostics ...@@ -53,6 +53,21 @@ Namespace Microsoft.VisualStudio.LanguageServices.UnitTests.Diagnostics
End Using End Using
End Function End Function
<Fact>
Public Async Function TestExternalDiagnostics_SupportedId() As Task
Using workspace = Await TestWorkspace.CreateCSharpAsync(String.Empty)
Dim waiter = New Waiter()
Dim service = New TestDiagnosticAnalyzerService()
Dim source = New ExternalErrorDiagnosticUpdateSource(workspace, service, New MockDiagnosticUpdateSourceRegistrationService(), waiter)
Dim project = workspace.CurrentSolution.Projects.First()
source.OnSolutionBuild(Me, Shell.UIContextChangedEventArgs.From(True))
Assert.True(source.SupportedDiagnosticId(project.Id, "CS1002"))
Assert.False(source.SupportedDiagnosticId(project.Id, "CA1002"))
End Using
End Function
<Fact> <Fact>
Public Async Function TestExternalDiagnostics_DuplicatedError() As Task Public Async Function TestExternalDiagnostics_DuplicatedError() As Task
Using workspace = Await TestWorkspace.CreateCSharpAsync(String.Empty) Using workspace = Await TestWorkspace.CreateCSharpAsync(String.Empty)
...@@ -169,7 +184,7 @@ Namespace Microsoft.VisualStudio.LanguageServices.UnitTests.Diagnostics ...@@ -169,7 +184,7 @@ Namespace Microsoft.VisualStudio.LanguageServices.UnitTests.Diagnostics
End Sub End Sub
Public Function GetDiagnosticDescriptors(projectOpt As Project) As ImmutableDictionary(Of String, ImmutableArray(Of DiagnosticDescriptor)) Implements IDiagnosticAnalyzerService.GetDiagnosticDescriptors Public Function GetDiagnosticDescriptors(projectOpt As Project) As ImmutableDictionary(Of String, ImmutableArray(Of DiagnosticDescriptor)) Implements IDiagnosticAnalyzerService.GetDiagnosticDescriptors
Return ImmutableDictionary(Of String, ImmutableArray(Of DiagnosticDescriptor)).Empty Return ImmutableDictionary(Of String, ImmutableArray(Of DiagnosticDescriptor)).Empty.Add("reference", ImmutableArray.Create(Of DiagnosticDescriptor)(New DiagnosticDescriptor("CS1002", "test", "test", "test", DiagnosticSeverity.Warning, True)))
End Function End Function
Public Function GetDiagnosticsForSpanAsync(document As Document, range As TextSpan, Optional includeSuppressedDiagnostics As Boolean = False, Optional cancellationToken As CancellationToken = Nothing) As Task(Of IEnumerable(Of DiagnosticData)) Implements IDiagnosticAnalyzerService.GetDiagnosticsForSpanAsync Public Function GetDiagnosticsForSpanAsync(document As Document, range As TextSpan, Optional includeSuppressedDiagnostics As Boolean = False, Optional cancellationToken As CancellationToken = Nothing) As Task(Of IEnumerable(Of DiagnosticData)) Implements IDiagnosticAnalyzerService.GetDiagnosticsForSpanAsync
......
// 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.Threading.Tasks;
namespace Roslyn.VisualStudio.DiagnosticsWindow.OptionsPages
{
internal class ForceLowMemoryMode
{
private MemoryHogger _hogger;
public static readonly ForceLowMemoryMode Instance = new ForceLowMemoryMode();
private ForceLowMemoryMode()
{
}
public int Size { get; set; } = 500; // default to 500 MB
public bool Enabled
{
get
{
return _hogger != null;
}
set
{
if (value && _hogger == null)
{
_hogger = new MemoryHogger();
var ignore = _hogger.PopulateAndMonitorAsync(this.Size);
}
else if (!value && _hogger != null)
{
_hogger.Cancel();
_hogger = null;
}
}
}
private class MemoryHogger
{
private const int BlockSize = 1024 * 1024; // megabyte blocks
private const int MonitorDelay = 10000; // 10 seconds
private readonly List<byte[]> _blocks = new List<byte[]>();
private bool _cancelled;
public MemoryHogger()
{
}
public int Count
{
get { return _blocks.Count; }
}
public void Cancel()
{
_cancelled = true;
}
public Task PopulateAndMonitorAsync(int size)
{
// run on background thread
return Task.Run(() => this.PopulateAndMonitorWorkerAsync(size));
}
private async Task PopulateAndMonitorWorkerAsync(int size)
{
try
{
for (int n = 0; n < size && !_cancelled; n++)
{
var block = new byte[BlockSize];
// initialize block bits (so the memory actually gets allocated.. silly runtime!)
for (int i = 0; i < BlockSize; i++)
{
block[i] = 0xFF;
}
_blocks.Add(block);
// don't hog the thread
await Task.Yield();
}
}
catch (OutOfMemoryException)
{
}
// monitor memory to keep it paged in
while (!_cancelled)
{
try
{
// access all block bytes
for (var b = 0; b < _blocks.Count && !_cancelled; b++)
{
var block = _blocks[b];
byte tmp;
for (int i = 0; i < block.Length; i++)
{
tmp = block[i];
}
// don't hog the thread
await Task.Yield();
}
}
catch (OutOfMemoryException)
{
}
await Task.Delay(MonitorDelay);
}
_blocks.Clear();
// force garbage collection
for (int i = 0; i < 5; i++)
{
GC.Collect(GC.MaxGeneration);
GC.WaitForPendingFinalizers();
}
}
}
}
}
// 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;
using System.Collections.Generic;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Options;
using Microsoft.CodeAnalysis.Editor.Shared.Options; using Microsoft.CodeAnalysis.Editor.Shared.Options;
using Microsoft.VisualStudio.LanguageServices; using Microsoft.VisualStudio.LanguageServices;
using Microsoft.VisualStudio.LanguageServices.Implementation.Options; using Microsoft.VisualStudio.LanguageServices.Implementation.Options;
using Roslyn.Utilities;
namespace Roslyn.VisualStudio.DiagnosticsWindow.OptionsPages namespace Roslyn.VisualStudio.DiagnosticsWindow.OptionsPages
{ {
...@@ -13,7 +23,61 @@ internal class InternalFeaturesOnOffPage : AbstractOptionPage ...@@ -13,7 +23,61 @@ internal class InternalFeaturesOnOffPage : AbstractOptionPage
{ {
protected override AbstractOptionPageControl CreateOptionPage(IServiceProvider serviceProvider) protected override AbstractOptionPageControl CreateOptionPage(IServiceProvider serviceProvider)
{ {
return new InternalOptionsControl(InternalFeatureOnOffOptions.OptionName, serviceProvider); return new InternalFeaturesOptionsControl(InternalFeatureOnOffOptions.OptionName, serviceProvider);
}
internal class InternalFeaturesOptionsControl : InternalOptionsControl
{
public InternalFeaturesOptionsControl(string featureOptionName, IServiceProvider serviceProvider)
: base(featureOptionName, serviceProvider)
{
}
protected override void AddOptions(Panel panel)
{
base.AddOptions(panel);
// add force low memory mode option
var group = new WrapPanel();
var lowMemoryMode = ForceLowMemoryMode.Instance;
var cb = CreateBoundCheckBox("Forced Low Memory Mode", lowMemoryMode, "Enabled");
group.Children.Add(cb);
var tb = CreateBoundTextBox("", lowMemoryMode, "Size");
group.Children.Add(tb);
var text = new TextBlock() { Text = "MB" };
group.Children.Add(text);
panel.Children.Add(group);
}
private CheckBox CreateBoundCheckBox(string content, object source, string sourcePropertyName)
{
var cb = new CheckBox { Content = content };
var binding = new Binding()
{
Source = source,
Path = new PropertyPath(sourcePropertyName)
};
base.AddBinding(cb.SetBinding(CheckBox.IsCheckedProperty, binding));
return cb;
}
private TextBox CreateBoundTextBox(string content, object source, string sourcePropertyName)
{
var tb = new TextBox { Text = content };
var binding = new Binding()
{
Source = source,
Path = new PropertyPath(sourcePropertyName)
};
base.AddBinding(tb.SetBinding(TextBox.TextProperty, binding));
return tb;
}
} }
} }
} }
...@@ -12,22 +12,15 @@ namespace Microsoft.VisualStudio.LanguageServices.Implementation.Options ...@@ -12,22 +12,15 @@ namespace Microsoft.VisualStudio.LanguageServices.Implementation.Options
{ {
internal partial class InternalOptionsControl : AbstractOptionPageControl internal partial class InternalOptionsControl : AbstractOptionPageControl
{ {
private readonly string _featureOptionName;
public InternalOptionsControl(string featureOptionName, IServiceProvider serviceProvider) public InternalOptionsControl(string featureOptionName, IServiceProvider serviceProvider)
: base(serviceProvider) : base(serviceProvider)
{ {
_featureOptionName = featureOptionName;
var panel = new StackPanel(); var panel = new StackPanel();
foreach (var option in OptionService.GetRegisteredOptions().Where(o => o.Feature == featureOptionName).OrderBy(o => o.Name)) this.AddOptions(panel);
{
if (!option.IsPerLanguage)
{
AddOption(panel, option);
}
else
{
AddPerLanguageOption(panel, option, LanguageNames.CSharp);
AddPerLanguageOption(panel, option, LanguageNames.VisualBasic);
}
}
var viewer = new ScrollViewer(); var viewer = new ScrollViewer();
viewer.VerticalScrollBarVisibility = ScrollBarVisibility.Auto; viewer.VerticalScrollBarVisibility = ScrollBarVisibility.Auto;
...@@ -50,7 +43,23 @@ public InternalOptionsControl(string featureOptionName, IServiceProvider service ...@@ -50,7 +43,23 @@ public InternalOptionsControl(string featureOptionName, IServiceProvider service
this.Content = viewer; this.Content = viewer;
} }
private void AddOption(StackPanel panel, IOption option) protected virtual void AddOptions(Panel panel)
{
foreach (var option in OptionService.GetRegisteredOptions().Where(o => o.Feature == _featureOptionName).OrderBy(o => o.Name))
{
if (!option.IsPerLanguage)
{
AddOption(panel, option);
}
else
{
AddPerLanguageOption(panel, option, LanguageNames.CSharp);
AddPerLanguageOption(panel, option, LanguageNames.VisualBasic);
}
}
}
private void AddOption(Panel panel, IOption option)
{ {
var uiElement = CreateControl(option); var uiElement = CreateControl(option);
if (uiElement != null) if (uiElement != null)
...@@ -59,7 +68,7 @@ private void AddOption(StackPanel panel, IOption option) ...@@ -59,7 +68,7 @@ private void AddOption(StackPanel panel, IOption option)
} }
} }
private void AddPerLanguageOption(StackPanel panel, IOption option, string languageName) private void AddPerLanguageOption(Panel panel, IOption option, string languageName)
{ {
var uiElement = CreateControl(option, languageName); var uiElement = CreateControl(option, languageName);
if (uiElement != null) if (uiElement != null)
......
...@@ -90,7 +90,9 @@ ...@@ -90,7 +90,9 @@
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "> <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<Reference Include="Microsoft.VisualStudio.ComponentModelHost, Version=$(VisualStudioReferenceAssemblyVersion), Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"><Private>false</Private></Reference> <Reference Include="Microsoft.VisualStudio.ComponentModelHost, Version=$(VisualStudioReferenceAssemblyVersion), Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<Private>false</Private>
</Reference>
<Reference Include="envdte, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"> <Reference Include="envdte, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<Private>false</Private> <Private>false</Private>
</Reference> </Reference>
...@@ -113,6 +115,7 @@ ...@@ -113,6 +115,7 @@
<ItemGroup> <ItemGroup>
<Compile Include="DiagnosticsWindow.cs" /> <Compile Include="DiagnosticsWindow.cs" />
<Compile Include="Guids.cs" /> <Compile Include="Guids.cs" />
<Compile Include="OptionPages\ForceLowMemoryMode.cs" />
<Compile Include="OptionPages\InternalSolutionCrawlerPage.cs" /> <Compile Include="OptionPages\InternalSolutionCrawlerPage.cs" />
<Compile Include="OptionPages\InternalComponentsOnOffPage.cs" /> <Compile Include="OptionPages\InternalComponentsOnOffPage.cs" />
<Compile Include="OptionPages\InternalDiagnosticsPage.cs" /> <Compile Include="OptionPages\InternalDiagnosticsPage.cs" />
......
...@@ -431,6 +431,35 @@ public static DiagnosticData Create(Document document, Diagnostic diagnostic) ...@@ -431,6 +431,35 @@ public static DiagnosticData Create(Document document, Diagnostic diagnostic)
isSuppressed: diagnostic.IsSuppressed); isSuppressed: diagnostic.IsSuppressed);
} }
public static bool TryCreate(DiagnosticDescriptor descriptor, string[] messageArguments, ProjectId projectId, Workspace workspace, out DiagnosticData diagnosticData, CancellationToken cancellationToken = default(CancellationToken))
{
diagnosticData = null;
var project = workspace.CurrentSolution.GetProject(projectId);
if (project == null)
{
return false;
}
var diagnostic = Diagnostic.Create(descriptor, Location.None, messageArguments);
if (project.SupportsCompilation)
{
// Get diagnostic with effective severity.
// Additionally, if the diagnostic was suppressed by a source suppression, effectiveDiagnostics will have a diagnostic with IsSuppressed = true.
var compilation = project.GetCompilationAsync(cancellationToken).WaitAndGetResult(cancellationToken);
var effectiveDiagnostics = CompilationWithAnalyzers.GetEffectiveDiagnostics(SpecializedCollections.SingletonEnumerable(diagnostic), compilation);
if (effectiveDiagnostics == null || effectiveDiagnostics.IsEmpty())
{
// Rule is disabled by compilation options.
return false;
}
diagnostic = effectiveDiagnostics.Single();
}
diagnosticData = diagnostic.ToDiagnosticData(project);
return true;
}
private static void GetLocationInfo(Document document, Location location, out TextSpan sourceSpan, out FileLinePositionSpan originalLineInfo, out FileLinePositionSpan mappedLineInfo) private static void GetLocationInfo(Document document, Location location, out TextSpan sourceSpan, out FileLinePositionSpan originalLineInfo, out FileLinePositionSpan mappedLineInfo)
{ {
var diagnosticSpanMappingService = document.Project.Solution.Workspace.Services.GetService<IWorkspaceVenusSpanMappingService>(); var diagnosticSpanMappingService = document.Project.Solution.Workspace.Services.GetService<IWorkspaceVenusSpanMappingService>();
......
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO;
using System.Linq; using System.Linq;
using System.Reflection; using System.Reflection;
using System.Reflection.Metadata; using System.Reflection.Metadata;
...@@ -13,6 +14,18 @@ namespace Microsoft.CodeAnalysis.FindSymbols ...@@ -13,6 +14,18 @@ namespace Microsoft.CodeAnalysis.FindSymbols
{ {
internal partial class SymbolTreeInfo internal partial class SymbolTreeInfo
{ {
private static Metadata GetMetadataNoThrow(PortableExecutableReference reference)
{
try
{
return reference.GetMetadata();
}
catch (Exception e) when (e is BadImageFormatException || e is IOException)
{
return null;
}
}
/// <summary> /// <summary>
/// this gives you SymbolTreeInfo for a metadata /// this gives you SymbolTreeInfo for a metadata
/// </summary> /// </summary>
...@@ -22,7 +35,7 @@ internal partial class SymbolTreeInfo ...@@ -22,7 +35,7 @@ internal partial class SymbolTreeInfo
bool loadOnly, bool loadOnly,
CancellationToken cancellationToken) CancellationToken cancellationToken)
{ {
var metadata = reference.GetMetadata(); var metadata = GetMetadataNoThrow(reference);
if (metadata == null) if (metadata == null)
{ {
return null; return null;
...@@ -76,7 +89,7 @@ internal partial class SymbolTreeInfo ...@@ -76,7 +89,7 @@ internal partial class SymbolTreeInfo
{ {
var unsortedNodes = new List<Node> { new Node("", Node.RootNodeParentIndex) }; var unsortedNodes = new List<Node> { new Node("", Node.RootNodeParentIndex) };
foreach (var moduleMetadata in GetModuleMetadata(reference.GetMetadata())) foreach (var moduleMetadata in GetModuleMetadata(GetMetadataNoThrow(reference)))
{ {
MetadataReader reader; MetadataReader reader;
try try
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册