diff --git a/src/Compilers/CSharp/Portable/CSharpResources.Designer.cs b/src/Compilers/CSharp/Portable/CSharpResources.Designer.cs index be6f596a5e50fab662ac45b8bf0006630d3514f6..34a990f0305c02cb3fbfd3da14cfbad9fab747c4 100644 --- a/src/Compilers/CSharp/Portable/CSharpResources.Designer.cs +++ b/src/Compilers/CSharp/Portable/CSharpResources.Designer.cs @@ -3634,6 +3634,15 @@ internal class CSharpResources { } } + /// + /// Looks up a localized string similar to Cannot update '{0}'; attribute '{1}' is missing.. + /// + internal static string ERR_EncUpdateFailedMissingAttribute { + get { + return ResourceManager.GetString("ERR_EncUpdateFailedMissingAttribute", resourceCulture); + } + } + /// /// Looks up a localized string similar to #endif directive expected. /// diff --git a/src/Compilers/CSharp/Portable/CSharpResources.resx b/src/Compilers/CSharp/Portable/CSharpResources.resx index b79a6b5f7320cbce4ce1356120e2f08f68b4d429..52a20b1e7345c6b1a69f0b720f9dfa7ac05b7a8c 100644 --- a/src/Compilers/CSharp/Portable/CSharpResources.resx +++ b/src/Compilers/CSharp/Portable/CSharpResources.resx @@ -3839,6 +3839,9 @@ You should consider suppressing the warning only if you're sure that you don't w Cannot use fixed local '{0}' inside an anonymous method, lambda expression, or query expression + + Cannot update '{0}'; attribute '{1}' is missing. + An expression tree may not contain a named argument specification diff --git a/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs b/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs index 150fa469968017113119ba9dc147d112ae8e7097..0b688ed36fe79fd4a0bb146d12f29ba12d8fface 100644 --- a/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs +++ b/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs @@ -1174,6 +1174,7 @@ internal enum ErrorCode // ERR_NameIllegallyOverrides3 = 7040, // Not used anymore due to 'Single Meaning' relaxation changes ERR_ResourceFileNameNotUnique = 7041, ERR_DllImportOnGenericMethod = 7042, + ERR_EncUpdateFailedMissingAttribute = 7043, ERR_ParameterNotValidForType = 7045, ERR_AttributeParameterRequired1 = 7046, diff --git a/src/Compilers/CSharp/Portable/Errors/MessageProvider.cs b/src/Compilers/CSharp/Portable/Errors/MessageProvider.cs index 68f9753fea4295dc49f73562057dfb2358efe697..bfd94287cce2f342c28a559bc37fcf1de93a4c17 100644 --- a/src/Compilers/CSharp/Portable/Errors/MessageProvider.cs +++ b/src/Compilers/CSharp/Portable/Errors/MessageProvider.cs @@ -95,7 +95,7 @@ public override Diagnostic CreateDiagnostic(int code, Location location, params 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. if (symbol.Kind == SymbolKind.Assembly || symbol.Kind == SymbolKind.Namespace) @@ -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_PeWritingFailure { get { return (int)ErrorCode.ERR_PeWritingFailure; } } 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) { diff --git a/src/Compilers/CSharp/Test/Emit/Emit/EditAndContinue/EditAndContinueStateMachineTests.cs b/src/Compilers/CSharp/Test/Emit/Emit/EditAndContinue/EditAndContinueStateMachineTests.cs index 206c4255744ff574fed0a01ff36f10e7e8cf96ed..4ae07c8d49447e1bd6316afc6115d1f7d5e46ea1 100644 --- a/src/Compilers/CSharp/Test/Emit/Emit/EditAndContinue/EditAndContinueStateMachineTests.cs +++ b/src/Compilers/CSharp/Test/Emit/Emit/EditAndContinue/EditAndContinueStateMachineTests.cs @@ -4756,8 +4756,8 @@ public IEnumerable F() new SemanticEdit(SemanticEditKind.Update, f0, f1, GetSyntaxMapFromMarkers(source0, source1), preserveLocalVariables: true))); diff1.EmitResult.Diagnostics.Verify( - // error CS7038: Failed to emit module '{0}'. - Diagnostic(ErrorCode.ERR_ModuleEmitFailure).WithArguments(compilation0.SourceModule.Name)); + // error CS7043: Cannot update 'C.F()'; attribute 'System.Runtime.CompilerServices.IteratorStateMachineAttribute' is missing. + Diagnostic(ErrorCode.ERR_EncUpdateFailedMissingAttribute).WithArguments("C.F()", "System.Runtime.CompilerServices.IteratorStateMachineAttribute")); } [Fact, WorkItem(9119, "https://github.com/dotnet/roslyn/issues/9119")] @@ -4811,10 +4811,10 @@ public async Task F() new SemanticEdit(SemanticEditKind.Update, f0, f1, GetSyntaxMapFromMarkers(source0, source1), preserveLocalVariables: true))); diff1.EmitResult.Diagnostics.Verify( - // error CS7038: Failed to emit module '{0}'. - Diagnostic(ErrorCode.ERR_ModuleEmitFailure).WithArguments(compilation0.SourceModule.Name), - // error CS7038: Failed to emit module '{0}'. - Diagnostic(ErrorCode.ERR_ModuleEmitFailure).WithArguments(compilation0.SourceModule.Name)); + // error CS7043: Cannot update 'C.F()'; attribute 'System.Runtime.CompilerServices.AsyncStateMachineAttribute' is missing. + Diagnostic(ErrorCode.ERR_EncUpdateFailedMissingAttribute).WithArguments("C.F()", "System.Runtime.CompilerServices.AsyncStateMachineAttribute"), + // error CS7043: Cannot update 'C.F()'; attribute 'System.Runtime.CompilerServices.AsyncStateMachineAttribute' is missing. + Diagnostic(ErrorCode.ERR_EncUpdateFailedMissingAttribute).WithArguments("C.F()", "System.Runtime.CompilerServices.AsyncStateMachineAttribute")); } } } diff --git a/src/Compilers/CSharp/Test/Syntax/Diagnostics/DiagnosticTest.cs b/src/Compilers/CSharp/Test/Syntax/Diagnostics/DiagnosticTest.cs index 57b84c16767e36c74ef8282a50373373c3b4b109..60f4130d5f800df097664361bd736706bc7e6919 100644 --- a/src/Compilers/CSharp/Test/Syntax/Diagnostics/DiagnosticTest.cs +++ b/src/Compilers/CSharp/Test/Syntax/Diagnostics/DiagnosticTest.cs @@ -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); } } diff --git a/src/Compilers/Core/CodeAnalysisTest/PEWriter/BlobTests.cs b/src/Compilers/Core/CodeAnalysisTest/PEWriter/BlobTests.cs index d3938cbc6bf839bae0670fcc4ef3fa12f5bc6edd..71e8fe4f0bbe7d439d5039547459c2e57739ae7f 100644 --- a/src/Compilers/Core/CodeAnalysisTest/PEWriter/BlobTests.cs +++ b/src/Compilers/Core/CodeAnalysisTest/PEWriter/BlobTests.cs @@ -187,8 +187,10 @@ public void GetChunks_DestructingEnum() } } + + [Fact] - public void ToArray() + public void ToArray1() { var builder = new BlobBuilder(16); @@ -226,6 +228,43 @@ public void ToArray() 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] public void ToArray_Errors() { diff --git a/src/Compilers/Core/Portable/CodeAnalysisResources.Designer.cs b/src/Compilers/Core/Portable/CodeAnalysisResources.Designer.cs index 34a0d23c7995cc7dd95d9a57399a93153c4d954a..8857c7680fa224c6ea525a86a589c9ae97c124bf 100644 --- a/src/Compilers/Core/Portable/CodeAnalysisResources.Designer.cs +++ b/src/Compilers/Core/Portable/CodeAnalysisResources.Designer.cs @@ -314,6 +314,15 @@ internal class CodeAnalysisResources { } } + /// + /// Looks up a localized string similar to The compilation references multiple assemblies whose versions only differ in auto-generated build and/or revision numbers.. + /// + internal static string CompilationReferencesAssembliesWithDifferentAutoGeneratedVersion { + get { + return ResourceManager.GetString("CompilationReferencesAssembliesWithDifferentAutoGeneratedVersion", resourceCulture); + } + } + /// /// Looks up a localized string similar to Analyzer Failure. /// diff --git a/src/Compilers/Core/Portable/CodeAnalysisResources.resx b/src/Compilers/Core/Portable/CodeAnalysisResources.resx index d6dd1fbcef73ea43703e5d2b72d9db7d763f57ad..217be60dfc3a6ba4ad0d18367fbc6776ac8ca54c 100644 --- a/src/Compilers/Core/Portable/CodeAnalysisResources.resx +++ b/src/Compilers/Core/Portable/CodeAnalysisResources.resx @@ -519,4 +519,7 @@ Tuples must have at least two elements. + + The compilation references multiple assemblies whose versions only differ in auto-generated build and/or revision numbers. + \ No newline at end of file diff --git a/src/Compilers/Core/Portable/Diagnostic/CommonMessageProvider.cs b/src/Compilers/Core/Portable/Diagnostic/CommonMessageProvider.cs index 3754056736e2cb0750f6a926ed87fedbde6eaa41..1db72196394a8b9834e003ff37c917195008295f 100644 --- a/src/Compilers/Core/Portable/Diagnostic/CommonMessageProvider.cs +++ b/src/Compilers/Core/Portable/Diagnostic/CommonMessageProvider.cs @@ -86,9 +86,9 @@ public Diagnostic CreateDiagnostic(int code, Location location) public abstract string GetMessagePrefix(string id, DiagnosticSeverity severity, bool isWarningAsError, CultureInfo culture); /// - /// convert given symbol to string representation based on given error code + /// Convert given symbol to string representation. /// - public abstract string ConvertSymbolToString(int errorCode, ISymbol symbol); + public abstract string GetErrorDisplayString(ISymbol symbol); /// /// Given an error code (like 1234) return the identifier (CS1234 or BC1234). @@ -206,6 +206,7 @@ public DiagnosticInfo FilterDiagnosticInfo(DiagnosticInfo diagnosticInfo, Compil public abstract int ERR_TooManyUserStrings { get; } public abstract int ERR_PeWritingFailure { 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 ReportInvalidNamedArgument(DiagnosticBag diagnostics, SyntaxNode attributeSyntax, int namedArgumentIndex, ITypeSymbol attributeClass, string parameterName); diff --git a/src/Compilers/Core/Portable/Diagnostic/DiagnosticInfo.cs b/src/Compilers/Core/Portable/Diagnostic/DiagnosticInfo.cs index e8092585d762da24c6b658325dc550331ee24ef5..035a9e55de583c00b9e54faa5f42555666e47feb 100644 --- a/src/Compilers/Core/Portable/Diagnostic/DiagnosticInfo.cs +++ b/src/Compilers/Core/Portable/Diagnostic/DiagnosticInfo.cs @@ -356,7 +356,7 @@ protected object[] GetArgumentsToUse(IFormatProvider formatProvider) if (symbol != null) { argumentsToUse = InitializeArgumentListIfNeeded(argumentsToUse); - argumentsToUse[i] = _messageProvider.ConvertSymbolToString(_errorCode, symbol); + argumentsToUse[i] = _messageProvider.GetErrorDisplayString(symbol); } } diff --git a/src/Compilers/Core/Portable/Emit/EditAndContinue/EncVariableSlotAllocator.cs b/src/Compilers/Core/Portable/Emit/EditAndContinue/EncVariableSlotAllocator.cs index b1c87c8feacec8e1c19700fa7054d39e906b7c7b..89aa9e89e079ca84d39ce0e3dd90d0b5e23a0e45 100644 --- a/src/Compilers/Core/Portable/Emit/EditAndContinue/EncVariableSlotAllocator.cs +++ b/src/Compilers/Core/Portable/Emit/EditAndContinue/EncVariableSlotAllocator.cs @@ -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. if (_hoistedLocalSlotsOpt == null) { - // TODO: better error message https://github.com/dotnet/roslyn/issues/9196 - diagnostics.Add(_messageProvider.CreateDiagnostic(_messageProvider.ERR_ModuleEmitFailure, NoLocation.Singleton, _previousTopLevelMethod.ContainingModule.Name)); + ReportMissingStateMachineAttribute(diagnostics); slotIndex = -1; return false; } @@ -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. if (_awaiterMapOpt == null) { - // TODO: better error message https://github.com/dotnet/roslyn/issues/9196 - diagnostics.Add(_messageProvider.CreateDiagnostic(_messageProvider.ERR_ModuleEmitFailure, NoLocation.Singleton, _previousTopLevelMethod.ContainingModule.Name)); + ReportMissingStateMachineAttribute(diagnostics); slotIndex = -1; return false; } @@ -240,6 +238,16 @@ public override bool TryGetPreviousAwaiterSlotIndex(Cci.ITypeReference currentTy 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) { // no syntax map diff --git a/src/Compilers/Core/Portable/FileSystem/FileUtilities.cs b/src/Compilers/Core/Portable/FileSystem/FileUtilities.cs index 8e2502adf5288317f1eba8bd5a3db7bff24bdc1f..7f8f962fa7eea5385a4ba39c252e5ccbea1c4f1a 100644 --- a/src/Compilers/Core/Portable/FileSystem/FileUtilities.cs +++ b/src/Compilers/Core/Portable/FileSystem/FileUtilities.cs @@ -177,9 +177,7 @@ private static string ResolveRelativePath(PathKind kind, string path, string bas return path; default: - // EDMAURER this is not using ExceptionUtilities.UnexpectedValue() because this file - // is shared via linking with other code that doesn't have the ExceptionUtilities. - throw new InvalidOperationException(string.Format("Unexpected PathKind {0}.", kind)); + throw ExceptionUtilities.UnexpectedValue(kind); } } diff --git a/src/Compilers/Core/Portable/MetadataReference/PortableExecutableReference.cs b/src/Compilers/Core/Portable/MetadataReference/PortableExecutableReference.cs index b5e1c8f704ecd3f1f66e7a16228b7d49362617c1..195e2066b764bfe2f9b19b57ef1393bb4938e851 100644 --- a/src/Compilers/Core/Portable/MetadataReference/PortableExecutableReference.cs +++ b/src/Compilers/Core/Portable/MetadataReference/PortableExecutableReference.cs @@ -162,6 +162,9 @@ internal Metadata GetMetadataNoCopy() /// Returns a copy of the object this /// contains. This copy does not need to be d. /// + /// If the PE image format is invalid. + /// The metadata image content can't be read. + /// The metadata image is stored in a file that can't be found. public Metadata GetMetadata() { return GetMetadataNoCopy().Copy(); diff --git a/src/Compilers/Core/Portable/ReferenceManager/CommonReferenceManager.State.cs b/src/Compilers/Core/Portable/ReferenceManager/CommonReferenceManager.State.cs index 37a4c052a7ce753018a6407b9968cf23533f637a..acc32315c5896fd9d92f2591ed351d6cf87301d1 100644 --- a/src/Compilers/Core/Portable/ReferenceManager/CommonReferenceManager.State.cs +++ b/src/Compilers/Core/Portable/ReferenceManager/CommonReferenceManager.State.cs @@ -514,8 +514,8 @@ internal bool IsBound if (lazyBuilder.ContainsKey(sourceIdentity)) { - // TODO: localize message, report as diagnostic (https://github.com/dotnet/roslyn/issues/8910) - throw new NotSupportedException("The compilation references multiple assemblies whose versions only differ in auto-generated build and/or revision numbers."); + // 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); diff --git a/src/Compilers/Core/Portable/System/Reflection/BlobBuilder.cs b/src/Compilers/Core/Portable/System/Reflection/BlobBuilder.cs index 775e6427c8a927788525729594972d833105efd6..97101d29bd99fb0bae4b167857e733fa8a626431 100644 --- a/src/Compilers/Core/Portable/System/Reflection/BlobBuilder.cs +++ b/src/Compilers/Core/Portable/System/Reflection/BlobBuilder.cs @@ -289,29 +289,33 @@ public byte[] ToArray(int start, int byteCount) var result = new byte[byteCount]; - int chunkStartPosition = 0; - int resultOffset = 0; + int chunkStart = 0; + int bufferStart = start; + int bufferEnd = start + byteCount; 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); - if (bytesToCopy == 0) + int bytesToCopy = Math.Min(bufferEnd, chunkEnd) - bufferStart; + Debug.Assert(bytesToCopy >= 0); + + Array.Copy(chunk._buffer, bufferStart - chunkStart, result, bufferStart - start, bytesToCopy); + bufferStart += bytesToCopy; + + if (bufferStart == bufferEnd) { 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; } diff --git a/src/Compilers/VisualBasic/Portable/Errors/Errors.vb b/src/Compilers/VisualBasic/Portable/Errors/Errors.vb index e54f8f369bd694c2eea8b78b067d83b6b5196f38..155ce7e89246360488e2baa57f8ac9fb17b7a63f 100644 --- a/src/Compilers/VisualBasic/Portable/Errors/Errors.vb +++ b/src/Compilers/VisualBasic/Portable/Errors/Errors.vb @@ -1605,6 +1605,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic ERR_PublicKeyContainerFailure = 36981 ERR_InvalidAssemblyCulture = 36982 + ERR_EncUpdateFailedMissingAttribute = 36983 ERR_CantAwaitAsyncSub1 = 37001 ERR_ResumableLambdaInExpressionTree = 37050 diff --git a/src/Compilers/VisualBasic/Portable/Errors/MessageProvider.vb b/src/Compilers/VisualBasic/Portable/Errors/MessageProvider.vb index 89a95f260994c45dd2671d78745912c868d496b7..4367c4ffbec8e0835554b28665986c79b763e566 100644 --- a/src/Compilers/VisualBasic/Portable/Errors/MessageProvider.vb +++ b/src/Compilers/VisualBasic/Portable/Errors/MessageProvider.vb @@ -95,7 +95,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic Return New VBDiagnostic(ErrorFactory.ErrorInfo(CType(code, ERRID), args), location) 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. If symbol.Kind = SymbolKind.Assembly OrElse symbol.Kind = SymbolKind.Namespace Then Return symbol.ToString() @@ -498,6 +498,12 @@ Namespace Microsoft.CodeAnalysis.VisualBasic Return ERRID.ERR_ModuleEmitFailure End Get End Property + + Public Overrides ReadOnly Property ERR_EncUpdateFailedMissingAttribute As Integer + Get + Return ERRID.ERR_EncUpdateFailedMissingAttribute + End Get + End Property End Class End Namespace diff --git a/src/Compilers/VisualBasic/Portable/VBResources.Designer.vb b/src/Compilers/VisualBasic/Portable/VBResources.Designer.vb index 5709cb39c935728bf218fd5e499ccd5101332f22..1c9f2293646998ffc014d4396367c414e28803b4 100644 --- a/src/Compilers/VisualBasic/Portable/VBResources.Designer.vb +++ b/src/Compilers/VisualBasic/Portable/VBResources.Designer.vb @@ -3296,6 +3296,15 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Get End Property + ''' + ''' Looks up a localized string similar to Cannot update '{0}'; attribute '{1}' is missing.. + ''' + Friend ReadOnly Property ERR_EncUpdateFailedMissingAttribute() As String + Get + Return ResourceManager.GetString("ERR_EncUpdateFailedMissingAttribute", resourceCulture) + End Get + End Property + ''' ''' Looks up a localized string similar to 'End Class' must be preceded by a matching 'Class'.. ''' diff --git a/src/Compilers/VisualBasic/Portable/VBResources.resx b/src/Compilers/VisualBasic/Portable/VBResources.resx index ba42112d5ed0e8d4a75909680f37e84707818805..8e4fad42679eb3845bd9d79b3b4c3c2a995af42a 100644 --- a/src/Compilers/VisualBasic/Portable/VBResources.resx +++ b/src/Compilers/VisualBasic/Portable/VBResources.resx @@ -818,6 +818,9 @@ Failed to emit module '{0}'. + + Cannot update '{0}'; attribute '{1}' is missing. + {0} '{1}' cannot be declared 'Overrides' because it does not override a {0} in a base class. diff --git a/src/Compilers/VisualBasic/Test/Emit/Emit/EditAndContinue/EditAndContinueStateMachineTests.vb b/src/Compilers/VisualBasic/Test/Emit/Emit/EditAndContinue/EditAndContinueStateMachineTests.vb index acb7dbd0d76cd25baf55a071bff76eaa466c8b53..786280bd43478b3fdec605ee68486b48c3c61a34 100644 --- a/src/Compilers/VisualBasic/Test/Emit/Emit/EditAndContinue/EditAndContinueStateMachineTests.vb +++ b/src/Compilers/VisualBasic/Test/Emit/Emit/EditAndContinue/EditAndContinueStateMachineTests.vb @@ -4447,7 +4447,7 @@ End Class ImmutableArray.Create(New SemanticEdit(SemanticEditKind.Update, f0, f1, GetSyntaxMapFromMarkers(source0, source1), preserveLocalVariables:=True))) 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 @@ -4501,8 +4501,8 @@ End Class ImmutableArray.Create(New SemanticEdit(SemanticEditKind.Update, f0, f1, GetSyntaxMapFromMarkers(source0, source1), preserveLocalVariables:=True))) diff1.EmitResult.Diagnostics.Verify( - Diagnostic(ERRID.ERR_ModuleEmitFailure).WithArguments(compilation0.SourceModule.Name), - 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_EncUpdateFailedMissingAttribute).WithArguments("Public Function F() As Task(Of Integer)", "System.Runtime.CompilerServices.AsyncStateMachineAttribute")) End Sub End Class End Namespace diff --git a/src/Compilers/VisualBasic/Test/Syntax/Syntax/GeneratedTests.vb b/src/Compilers/VisualBasic/Test/Syntax/Syntax/GeneratedTests.vb index 14bf73ddd419e5ecd13ded5250661563f62f3933..5f36fccee10183c3f9934bfdbe9808c65fe80478 100644 --- a/src/Compilers/VisualBasic/Test/Syntax/Syntax/GeneratedTests.vb +++ b/src/Compilers/VisualBasic/Test/Syntax/Syntax/GeneratedTests.vb @@ -81,8 +81,8 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.UnitTests Return String.Empty End Function - Public Overrides Function ConvertSymbolToString(errorCode As Integer, symbol As ISymbol) As String - Return MessageProvider.Instance.ConvertSymbolToString(errorCode, symbol) + Public Overrides Function GetErrorDisplayString(symbol As ISymbol) As String + Return MessageProvider.Instance.GetErrorDisplayString(symbol) End Function End Class diff --git a/src/Compilers/VisualBasic/Test/Syntax/TestSyntaxNodes.vb b/src/Compilers/VisualBasic/Test/Syntax/TestSyntaxNodes.vb index 4b02fecaf9871fb011a2fe003fb1027a01f574ca..9d08ec45f46a62b6016a8853d900a163402519e1 100644 --- a/src/Compilers/VisualBasic/Test/Syntax/TestSyntaxNodes.vb +++ b/src/Compilers/VisualBasic/Test/Syntax/TestSyntaxNodes.vb @@ -1030,8 +1030,8 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.UnitTests Return 0 End Function - Public Overrides Function ConvertSymbolToString(errorCode As Integer, symbol As ISymbol) As String - Return MessageProvider.Instance.ConvertSymbolToString(errorCode, symbol) + Public Overrides Function GetErrorDisplayString(symbol As ISymbol) As String + Return MessageProvider.Instance.GetErrorDisplayString(symbol) End Function End Class diff --git a/src/Features/Core/Portable/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.ProjectState.cs b/src/Features/Core/Portable/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.ProjectState.cs index d16165ff6ec01f5eac292c3512ab0e3a51f55be0..cc7c5fa4c994f88951b64e890f5afe276bab3988 100644 --- a/src/Features/Core/Portable/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.ProjectState.cs +++ b/src/Features/Core/Portable/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.ProjectState.cs @@ -99,7 +99,8 @@ public async Task GetAnalysisDataAsync(Project project, bool avo 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(); @@ -178,7 +179,8 @@ public async Task GetProjectAnalysisDataAsync(Project project, b 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(); diff --git a/src/Features/Core/Portable/EditAndContinue/RudeEditDiagnosticDescriptors.cs b/src/Features/Core/Portable/EditAndContinue/RudeEditDiagnosticDescriptors.cs index 636a5d8d44e7e46490d77e4a04e5b0bf9a2f3f50..d25904d9c2b75b5f4d0620fad0af3aa479970d0d 100644 --- a/src/Features/Core/Portable/EditAndContinue/RudeEditDiagnosticDescriptors.cs +++ b/src/Features/Core/Portable/EditAndContinue/RudeEditDiagnosticDescriptors.cs @@ -80,9 +80,7 @@ internal static class RudeEditDiagnosticDescriptors { GetDescriptorPair(RudeEditKind.PartiallyExecutedActiveStatementDelete, FeaturesResources.AnActiveStatementHasBeenRemoved) }, { GetDescriptorPair(RudeEditKind.InsertFile, FeaturesResources.AddingANewFile) }, { GetDescriptorPair(RudeEditKind.UpdatingStateMachineMethodAroundActiveStatement, FeaturesResources.UpdatingStateMachineMethodAroundActive) }, - // TODO: move message to resources https://github.com/dotnet/roslyn/issues/9196 - { GetDescriptorPair(RudeEditKind.UpdatingStateMachineMethodMissingAttribute, "Attribute '{0}' is missing. Updating an async method or an iterator will prevent the debug session from continuing.") }, - + { GetDescriptorPair(RudeEditKind.UpdatingStateMachineMethodMissingAttribute, FeaturesResources.UpdatingStateMachineMethodMissingAttribute) }, { GetDescriptorPair(RudeEditKind.RUDE_EDIT_COMPLEX_QUERY_EXPRESSION, FeaturesResources.ModifyingAWhichContainsComplexQuery) }, // VB specific, diff --git a/src/Features/Core/Portable/FeaturesResources.Designer.cs b/src/Features/Core/Portable/FeaturesResources.Designer.cs index 4f7580c97822bbbe335797ef8a05a6db397dfa8c..8727f58739d52e8a06c3cc1a3de005ff7e6d124c 100644 --- a/src/Features/Core/Portable/FeaturesResources.Designer.cs +++ b/src/Features/Core/Portable/FeaturesResources.Designer.cs @@ -2475,6 +2475,15 @@ internal class FeaturesResources { } } + /// + /// Looks up a localized string similar to Attribute '{0}' is missing. Updating an async method or an iterator will prevent the debug session from continuing.. + /// + internal static string UpdatingStateMachineMethodMissingAttribute { + get { + return ResourceManager.GetString("UpdatingStateMachineMethodMissingAttribute", resourceCulture); + } + } + /// /// Looks up a localized string similar to Updating the alias of Declare Statement will prevent the debug session from continuing.. /// diff --git a/src/Features/Core/Portable/FeaturesResources.resx b/src/Features/Core/Portable/FeaturesResources.resx index 5c9e26a6f11b5faedad8ee6e0eb40beb1c9b16ec..078c769e58cd1d38d331b154488dc1444c6c9379 100644 --- a/src/Features/Core/Portable/FeaturesResources.resx +++ b/src/Features/Core/Portable/FeaturesResources.resx @@ -574,6 +574,9 @@ Adding a new file will prevent the debug session from continuing. + + Attribute '{0}' is missing. Updating an async method or an iterator will prevent the debug session from continuing. + Unexpected interface member kind: {0} diff --git a/src/Test/Utilities/Shared/Mocks/TestMessageProvider.cs b/src/Test/Utilities/Shared/Mocks/TestMessageProvider.cs index 3c7c9da4234702e9a946a9d740e34177a3fdbea9..548241f5514e798fe820a5e608d5db8b16091186 100644 --- a/src/Test/Utilities/Shared/Mocks/TestMessageProvider.cs +++ b/src/Test/Utilities/Shared/Mocks/TestMessageProvider.cs @@ -386,5 +386,13 @@ public override int ERR_ModuleEmitFailure throw new NotImplementedException(); } } + + public override int ERR_EncUpdateFailedMissingAttribute + { + get + { + throw new NotImplementedException(); + } + } } } diff --git a/src/VisualStudio/Core/Def/Implementation/AnalyzerDependency/AnalyzerDependencyCheckingService.cs b/src/VisualStudio/Core/Def/Implementation/AnalyzerDependency/AnalyzerDependencyCheckingService.cs index 984bc5de393ba1297ecd276c4ebe5f07a5cc8e3c..7588a8eb050a526e661539f07de995f5ed629875 100644 --- a/src/VisualStudio/Core/Def/Implementation/AnalyzerDependency/AnalyzerDependencyCheckingService.cs +++ b/src/VisualStudio/Core/Def/Implementation/AnalyzerDependency/AnalyzerDependencyCheckingService.cs @@ -33,6 +33,22 @@ internal sealed class AnalyzerDependencyCheckingService private Task _task = Task.FromResult(AnalyzerDependencyResults.Empty); private ImmutableHashSet _analyzerPaths = ImmutableHashSet.Create(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] public AnalyzerDependencyCheckingService( VisualStudioWorkspaceImpl workspace, @@ -74,7 +90,12 @@ public async void CheckForConflictsAsync() if (project.CurrentProjectAnalyzersContains(conflict.AnalyzerFilePath1) || 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() { 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) })); } - 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.Empty, - properties: ImmutableDictionary.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.Empty, - properties: ImmutableDictionary.Empty, - workspace: _workspace, - projectId: projectId, - title: ServicesVSResources.WRN_MissingAnalyzerReferenceTitle); - - return data; - } - private Task GetConflictsAsync() { ImmutableHashSet currentAnalyzerPaths = _workspace.CurrentSolution diff --git a/src/VisualStudio/Core/Def/Implementation/AnalyzerDependency/AnalyzerFileWatcherService.cs b/src/VisualStudio/Core/Def/Implementation/AnalyzerDependency/AnalyzerFileWatcherService.cs index b28cd8fb3c51391f474632a44104022ea057e66b..98f059e5a641506f12505dead88010019aa84721 100644 --- a/src/VisualStudio/Core/Def/Implementation/AnalyzerDependency/AnalyzerFileWatcherService.cs +++ b/src/VisualStudio/Core/Def/Implementation/AnalyzerDependency/AnalyzerFileWatcherService.cs @@ -29,6 +29,14 @@ internal sealed class AnalyzerFileWatcherService 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] public AnalyzerFileWatcherService( VisualStudioWorkspaceImpl workspace, @@ -67,21 +75,12 @@ internal void RemoveAnalyzerAlreadyLoadedDiagnostics(ProjectId projectId, string private void RaiseAnalyzerChangedWarning(ProjectId projectId, string analyzerPath) { - string message = string.Format(ServicesVSResources.WRN_AnalyzerChangedMessage, analyzerPath); - - DiagnosticData data = new DiagnosticData( - IDEDiagnosticIds.AnalyzerChangedId, - FeaturesResources.ErrorCategory, - 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)); + var messageArguments = new string[] { analyzerPath }; + DiagnosticData diagnostic; + if (DiagnosticData.TryCreate(_analyzerChangedRule, messageArguments, projectId, _workspace, out diagnostic)) + { + _updateSource.UpdateDiagnosticsForProject(projectId, Tuple.Create(s_analyzerChangedErrorId, analyzerPath), SpecializedCollections.SingletonEnumerable(diagnostic)); + } } private DateTime? GetLastUpdateTimeUtc(string fullPath) diff --git a/src/VisualStudio/Core/Def/Implementation/ProjectSystem/AbstractProject.cs b/src/VisualStudio/Core/Def/Implementation/ProjectSystem/AbstractProject.cs index f6590821c748d8a0a36e1e259228d412c560ec1a..78ffb0ee69b35dbc5f41484dcb2394c657993dfe 100644 --- a/src/VisualStudio/Core/Def/Implementation/ProjectSystem/AbstractProject.cs +++ b/src/VisualStudio/Core/Def/Implementation/ProjectSystem/AbstractProject.cs @@ -99,6 +99,14 @@ internal abstract partial class AbstractProject : IVisualStudioHostProject private static readonly EventHandler s_additionalDocumentClosingEventHandler = OnAdditionalDocumentClosing; 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( VisualStudioProjectTracker projectTracker, Func reportExternalErrorCreatorOpt, @@ -1257,20 +1265,12 @@ protected void UpdateRuleSetError(IRuleSetFile ruleSetFile) } else { - string message = string.Format(ServicesVSResources.ERR_CantReadRulesetFileMessage, ruleSetFile.FilePath, ruleSetFile.GetException().Message); - var data = new DiagnosticData( - id: IDEDiagnosticIds.ErrorReadingRulesetId, - category: FeaturesResources.ErrorCategory, - message: message, - 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)); + var messageArguments = new string[] { ruleSetFile.FilePath, ruleSetFile.GetException().Message }; + DiagnosticData diagnostic; + if (DiagnosticData.TryCreate(_errorReadingRulesetRule, messageArguments, this.Id, this.Workspace, out diagnostic)) + { + this.HostDiagnosticUpdateSource.UpdateDiagnosticsForProject(this.Id, RuleSetErrorId, SpecializedCollections.SingletonEnumerable(diagnostic)); + } } } diff --git a/src/VisualStudio/Core/Def/Implementation/TaskList/ExternalErrorDiagnosticUpdateSource.cs b/src/VisualStudio/Core/Def/Implementation/TaskList/ExternalErrorDiagnosticUpdateSource.cs index c588b6ac4b1df7dfe20f747a27119b63df43f3ac..ad753f44031fb7a0dd4c4c3bb2093c442f7b72a6 100644 --- a/src/VisualStudio/Core/Def/Implementation/TaskList/ExternalErrorDiagnosticUpdateSource.cs +++ b/src/VisualStudio/Core/Def/Implementation/TaskList/ExternalErrorDiagnosticUpdateSource.cs @@ -76,6 +76,11 @@ public ImmutableArray GetBuildErrors() return _lastBuiltResult; } + public bool SupportedDiagnosticId(ProjectId projectId, string id) + { + return _state?.SupportedDiagnosticId(projectId, id) ?? false; + } + public void ClearErrors(ProjectId projectId) { // capture state if it exists @@ -88,7 +93,7 @@ public void ClearErrors(ProjectId projectId) // otherwise (such as closing solution or removing project), no need to record it state?.Built(projectId); - ClearProjectErrors(projectId); + ClearProjectErrors(state?.Solution ?? _workspace.CurrentSolution, projectId); }).CompletesAsyncOperation(asyncToken); } @@ -102,7 +107,7 @@ private void OnWorkspaceChanged(object sender, WorkspaceChangeEventArgs e) case WorkspaceChangeKind.SolutionReloaded: { 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; } @@ -110,7 +115,7 @@ private void OnWorkspaceChanged(object sender, WorkspaceChangeEventArgs e) case WorkspaceChangeKind.ProjectReloaded: { var asyncToken = _listener.BeginAsyncOperation("OnProjectChanged"); - _taskQueue.ScheduleTask(() => ClearProjectErrors(e.ProjectId, e.OldSolution)).CompletesAsyncOperation(asyncToken); + _taskQueue.ScheduleTask(() => ClearProjectErrors(e.OldSolution, e.ProjectId)).CompletesAsyncOperation(asyncToken); break; } @@ -118,7 +123,7 @@ private void OnWorkspaceChanged(object sender, WorkspaceChangeEventArgs e) case WorkspaceChangeKind.DocumentReloaded: { 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; } @@ -169,16 +174,11 @@ internal void OnSolutionBuild(object sender, UIContextChangedEventArgs e) // pause live analyzer 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 liveDiagnosticChecker = d => { // 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. - var project = solution.GetProject(d.ProjectId); + var project = inprogressState.Solution.GetProject(d.ProjectId); if (project == null) { // project doesn't exist @@ -194,8 +194,7 @@ internal void OnSolutionBuild(object sender, UIContextChangedEventArgs e) return false; } - HashSet set; - if (supportedIdMap.TryGetValue(d.ProjectId, out set) && set.Contains(d.Id)) + if (inprogressState.SupportedDiagnosticId(d.ProjectId, d.Id)) { return true; } @@ -206,8 +205,8 @@ internal void OnSolutionBuild(object sender, UIContextChangedEventArgs e) var diagnosticService = _diagnosticService as DiagnosticAnalyzerService; if (diagnosticService != null) { - await CleanupAllLiveErrorsIfNeededAsync(diagnosticService, solution, inprogressState).ConfigureAwait(false); - await SyncBuildErrorsAndReportAsync(diagnosticService, inprogressState.GetLiveDiagnosticsPerProject(liveDiagnosticChecker)).ConfigureAwait(false); + await CleanupAllLiveErrorsIfNeededAsync(diagnosticService, inprogressState.Solution, inprogressState).ConfigureAwait(false); + await SyncBuildErrorsAndReportAsync(diagnosticService, inprogressState.Solution, inprogressState.GetLiveDiagnosticsPerProject(liveDiagnosticChecker)).ConfigureAwait(false); } inprogressState.Done(); @@ -239,7 +238,8 @@ private System.Threading.Tasks.Task CleanupAllLiveErrors(DiagnosticAnalyzerServi return diagnosticService.SynchronizeWithBuildAsync(_workspace, map); } - private async System.Threading.Tasks.Task SyncBuildErrorsAndReportAsync(DiagnosticAnalyzerService diagnosticService, ImmutableDictionary> map) + private async System.Threading.Tasks.Task SyncBuildErrorsAndReportAsync( + DiagnosticAnalyzerService diagnosticService, Solution solution, ImmutableDictionary> map) { // make those errors live errors await diagnosticService.SynchronizeWithBuildAsync(_workspace, map).ConfigureAwait(false); @@ -253,56 +253,36 @@ private async System.Threading.Tasks.Task SyncBuildErrorsAndReportAsync(Diagnost foreach (var projectGroup in group.GroupBy(g => g.ProjectId)) { Contract.ThrowIfNull(projectGroup.Key); - ReportBuildErrors(projectGroup.Key, projectGroup.ToImmutableArray()); + ReportBuildErrors(projectGroup.Key, solution, projectGroup.ToImmutableArray()); } continue; } - ReportBuildErrors(group.Key, group.ToImmutableArray()); + ReportBuildErrors(group.Key, solution, group.ToImmutableArray()); } } - private void ReportBuildErrors(T item, ImmutableArray buildErrors) + private void ReportBuildErrors(T item, Solution solution, ImmutableArray buildErrors) { var projectId = item as ProjectId; if (projectId != null) { - RaiseDiagnosticsCreated(projectId, projectId, null, buildErrors); + RaiseDiagnosticsCreated(projectId, solution, projectId, null, buildErrors); return; } // must be not null var documentId = item as DocumentId; - RaiseDiagnosticsCreated(documentId, documentId.ProjectId, documentId, buildErrors); - } - - private Dictionary> GetSupportedLiveDiagnosticId(Solution solution, InprogressState state) - { - var map = new Dictionary>(); - - // 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(descriptorMap.Values.SelectMany(v => v.Select(d => d.Id)))); - } - - return map; + RaiseDiagnosticsCreated(documentId, solution, documentId.ProjectId, documentId, buildErrors); } - private void ClearProjectErrors(ProjectId projectId, Solution solution = null) + private void ClearProjectErrors(Solution solution, ProjectId projectId) { // 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) { return; @@ -311,13 +291,25 @@ private void ClearProjectErrors(ProjectId projectId, Solution solution = null) // remove all document errors 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) @@ -354,22 +346,25 @@ private InprogressState GetOrCreateInprogressState() { 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; } - private void RaiseDiagnosticsCreated(object id, ProjectId projectId, DocumentId documentId, ImmutableArray items) + private void RaiseDiagnosticsCreated(object id, Solution solution, ProjectId projectId, DocumentId documentId, ImmutableArray items) { 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( - CreateArgumentKey(id), _workspace, _workspace.CurrentSolution, projectId, documentId)); + CreateArgumentKey(id), _workspace, solution, projectId, documentId)); } private static ArgumentKey CreateArgumentKey(object id) => new ArgumentKey(id); @@ -392,14 +387,17 @@ private void RaiseBuildStarted(bool started) private class InprogressState { private readonly ExternalErrorDiagnosticUpdateSource _owner; + private readonly Solution _solution; private readonly HashSet _builtProjects = new HashSet(); private readonly Dictionary> _projectMap = new Dictionary>(); private readonly Dictionary> _documentMap = new Dictionary>(); + private readonly Dictionary> _diagnosticIdMap = new Dictionary>(); - public InprogressState(ExternalErrorDiagnosticUpdateSource owner) + public InprogressState(ExternalErrorDiagnosticUpdateSource owner, Solution solution) { _owner = owner; + _solution = solution; // let people know build has started // TODO: to be more accurate, it probably needs to be counted. but for now, @@ -407,11 +405,38 @@ public InprogressState(ExternalErrorDiagnosticUpdateSource owner) _owner.RaiseBuildStarted(started: true); } + public Solution Solution => _solution; + public void Done() { _owner.RaiseBuildStarted(started: false); } + public bool SupportedDiagnosticId(ProjectId projectId, string id) + { + HashSet ids; + if (_diagnosticIdMap.TryGetValue(projectId, out ids)) + { + return ids.Contains(id); + } + + // set ids set + var map = new HashSet(); + _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 GetBuildDiagnostics() { 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) AddError(_documentMap, key, diagnostic); } + public void AddError(ProjectId key, DiagnosticData diagnostic) + { + AddError(_projectMap, key, diagnostic); + } + private void AddErrors(Dictionary> map, T key, HashSet diagnostics) { var errors = GetErrorSet(map, key); diff --git a/src/VisualStudio/Core/Def/Implementation/TaskList/ProjectExternalErrorReporter.cs b/src/VisualStudio/Core/Def/Implementation/TaskList/ProjectExternalErrorReporter.cs index 01e4d6816d3095ab76963c49ceb9a01075b2ffe4..1efc6ff67b8049b8ade59e8e370f839793336708 100644 --- a/src/VisualStudio/Core/Def/Implementation/TaskList/ProjectExternalErrorReporter.cs +++ b/src/VisualStudio/Core/Def/Implementation/TaskList/ProjectExternalErrorReporter.cs @@ -24,6 +24,7 @@ internal class ProjectExternalErrorReporter : IVsReportExternalErrors, IVsLangua private readonly ProjectId _projectId; private readonly string _errorCodePrefix; + private readonly VisualStudioWorkspaceImpl _workspace; private readonly ExternalErrorDiagnosticUpdateSource _diagnosticProvider; @@ -31,11 +32,23 @@ public ProjectExternalErrorReporter(ProjectId projectId, string errorCodePrefix, { _projectId = projectId; _errorCodePrefix = errorCodePrefix; - _diagnosticProvider = serviceProvider.GetMefService(); + _workspace = serviceProvider.GetMefService(); + _diagnosticProvider = serviceProvider.GetMefService(); - Debug.Assert(_diagnosticProvider != 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) @@ -134,28 +147,20 @@ public int ReportError(string bstrErrorMessage, string bstrErrorId, [ComAliasNam // 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) { - if ((iEndLine >= 0 && iEndColumn >= 0) && - ((iEndLine < iStartLine) || - (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) + // first we check whether given error is something we can take care. + if (!CanHandle(bstrErrorId)) { + // it is not, let project system takes care. throw new NotImplementedException(); } - var hostProject = _workspace.GetHostProject(_projectId); - if (!hostProject.ContainsFile(bstrFileName)) + if ((iEndLine >= 0 && iEndColumn >= 0) && + ((iEndLine < iStartLine) || + (iEndLine == iStartLine && iEndColumn < iStartColumn))) { - throw new NotImplementedException(); + throw new ArgumentException(ServicesVSResources.EndPositionMustBeGreaterThanStart); } - var hostDocument = hostProject.GetCurrentDocumentFromPath(bstrFileName); - var priority = (VSTASKPRIORITY)nPriority; DiagnosticSeverity severity; switch (priority) @@ -173,6 +178,32 @@ public void ReportError2(string bstrErrorMessage, string bstrErrorId, [ComAliasN 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( hostDocument.Id, bstrErrorId, bstrErrorMessage, severity, null, iStartLine, iStartColumn, iEndLine, iEndColumn, diff --git a/src/VisualStudio/Core/Impl/Options/AbstractOptionPageControl.cs b/src/VisualStudio/Core/Impl/Options/AbstractOptionPageControl.cs index 600267b6cb7312e337e3a58cb12725210f6e3037..ad16d2c02fdb9496b112c1e0e97b6ebee1633636 100644 --- a/src/VisualStudio/Core/Impl/Options/AbstractOptionPageControl.cs +++ b/src/VisualStudio/Core/Impl/Options/AbstractOptionPageControl.cs @@ -32,8 +32,18 @@ public AbstractOptionPageControl(IServiceProvider serviceProvider) var checkBoxStyle = new System.Windows.Style(typeof(CheckBox)); 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); + + 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 optionKey) diff --git a/src/VisualStudio/Core/Test/Diagnostics/ExternalDiagnosticUpdateSourceTests.vb b/src/VisualStudio/Core/Test/Diagnostics/ExternalDiagnosticUpdateSourceTests.vb index 0772d8052bc1eedd8133a424fb0970e43f86510f..4b8c4f1f8442925348c92648981da9abe6f0ea85 100644 --- a/src/VisualStudio/Core/Test/Diagnostics/ExternalDiagnosticUpdateSourceTests.vb +++ b/src/VisualStudio/Core/Test/Diagnostics/ExternalDiagnosticUpdateSourceTests.vb @@ -53,6 +53,21 @@ Namespace Microsoft.VisualStudio.LanguageServices.UnitTests.Diagnostics End Using End Function + + 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 + Public Async Function TestExternalDiagnostics_DuplicatedError() As Task Using workspace = Await TestWorkspace.CreateCSharpAsync(String.Empty) @@ -169,7 +184,7 @@ Namespace Microsoft.VisualStudio.LanguageServices.UnitTests.Diagnostics End Sub 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 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 diff --git a/src/VisualStudio/VisualStudioDiagnosticsToolWindow/OptionPages/ForceLowMemoryMode.cs b/src/VisualStudio/VisualStudioDiagnosticsToolWindow/OptionPages/ForceLowMemoryMode.cs new file mode 100644 index 0000000000000000000000000000000000000000..d0b604e51d6c599b808947ca95d6c9bc31b776b6 --- /dev/null +++ b/src/VisualStudio/VisualStudioDiagnosticsToolWindow/OptionPages/ForceLowMemoryMode.cs @@ -0,0 +1,133 @@ +// 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 _blocks = new List(); + 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(); + } + } + } + } +} diff --git a/src/VisualStudio/VisualStudioDiagnosticsToolWindow/OptionPages/InternalFeaturesOnOffPage.cs b/src/VisualStudio/VisualStudioDiagnosticsToolWindow/OptionPages/InternalFeaturesOnOffPage.cs index 4e44c7651c80bbf8cbaf5e89f1ecc4ebcb9454a8..5ed986e9cd107edfd53253d99ab48d4071550070 100644 --- a/src/VisualStudio/VisualStudioDiagnosticsToolWindow/OptionPages/InternalFeaturesOnOffPage.cs +++ b/src/VisualStudio/VisualStudioDiagnosticsToolWindow/OptionPages/InternalFeaturesOnOffPage.cs @@ -1,10 +1,20 @@ // 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.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.VisualStudio.LanguageServices; using Microsoft.VisualStudio.LanguageServices.Implementation.Options; +using Roslyn.Utilities; namespace Roslyn.VisualStudio.DiagnosticsWindow.OptionsPages { @@ -13,7 +23,61 @@ internal class InternalFeaturesOnOffPage : AbstractOptionPage { 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; + } } } } diff --git a/src/VisualStudio/VisualStudioDiagnosticsToolWindow/OptionPages/InternalOptionsControl.cs b/src/VisualStudio/VisualStudioDiagnosticsToolWindow/OptionPages/InternalOptionsControl.cs index 702c19691ee5f934c933e159c4e463417e0b952e..66e04b3ef9aaceaf6637311bef3259c924d0f176 100644 --- a/src/VisualStudio/VisualStudioDiagnosticsToolWindow/OptionPages/InternalOptionsControl.cs +++ b/src/VisualStudio/VisualStudioDiagnosticsToolWindow/OptionPages/InternalOptionsControl.cs @@ -12,22 +12,15 @@ namespace Microsoft.VisualStudio.LanguageServices.Implementation.Options { internal partial class InternalOptionsControl : AbstractOptionPageControl { + private readonly string _featureOptionName; + public InternalOptionsControl(string featureOptionName, IServiceProvider serviceProvider) : base(serviceProvider) { + _featureOptionName = featureOptionName; + var panel = new StackPanel(); - 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); - } - } + this.AddOptions(panel); var viewer = new ScrollViewer(); viewer.VerticalScrollBarVisibility = ScrollBarVisibility.Auto; @@ -50,7 +43,23 @@ public InternalOptionsControl(string featureOptionName, IServiceProvider service 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); if (uiElement != null) @@ -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); if (uiElement != null) diff --git a/src/VisualStudio/VisualStudioDiagnosticsToolWindow/VisualStudioDiagnosticsWindow.csproj b/src/VisualStudio/VisualStudioDiagnosticsToolWindow/VisualStudioDiagnosticsWindow.csproj index d94ddf82ff3d15ced5fcdb05301de60c4ad9dbd8..146fc58e352a72a7699642bf13b6c7727b150d24 100644 --- a/src/VisualStudio/VisualStudioDiagnosticsToolWindow/VisualStudioDiagnosticsWindow.csproj +++ b/src/VisualStudio/VisualStudioDiagnosticsToolWindow/VisualStudioDiagnosticsWindow.csproj @@ -90,7 +90,9 @@ - false + + false + false @@ -113,6 +115,7 @@ + diff --git a/src/Workspaces/Core/Portable/Diagnostics/DiagnosticData.cs b/src/Workspaces/Core/Portable/Diagnostics/DiagnosticData.cs index b658451ee2bdbf43317e8012acd269fe831ced08..746061315f242b86442e46d5fcca6a9521445e5f 100644 --- a/src/Workspaces/Core/Portable/Diagnostics/DiagnosticData.cs +++ b/src/Workspaces/Core/Portable/Diagnostics/DiagnosticData.cs @@ -431,6 +431,35 @@ public static DiagnosticData Create(Document document, Diagnostic diagnostic) 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) { var diagnosticSpanMappingService = document.Project.Solution.Workspace.Services.GetService(); diff --git a/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Metadata.cs b/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Metadata.cs index 4f182b14d43f465ba6cd0ea0cb5c14643a9d20f1..529e465dce777a503dfd83ae8c68ac4a7ed5098a 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Metadata.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Metadata.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.IO; using System.Linq; using System.Reflection; using System.Reflection.Metadata; @@ -13,6 +14,18 @@ namespace Microsoft.CodeAnalysis.FindSymbols { 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; + } + } + /// /// this gives you SymbolTreeInfo for a metadata /// @@ -22,7 +35,7 @@ internal partial class SymbolTreeInfo bool loadOnly, CancellationToken cancellationToken) { - var metadata = reference.GetMetadata(); + var metadata = GetMetadataNoThrow(reference); if (metadata == null) { return null; @@ -76,7 +89,7 @@ internal partial class SymbolTreeInfo { var unsortedNodes = new List { new Node("", Node.RootNodeParentIndex) }; - foreach (var moduleMetadata in GetModuleMetadata(reference.GetMetadata())) + foreach (var moduleMetadata in GetModuleMetadata(GetMetadataNoThrow(reference))) { MetadataReader reader; try