From d2df33fb5fd878147ad5775950a44575b73d9d25 Mon Sep 17 00:00:00 2001 From: James Ko Date: Thu, 26 May 2016 18:24:46 -0400 Subject: [PATCH 01/36] Ignore VS Code-generated dirs in gitignore --- .gitignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitignore b/.gitignore index d077e255cccbb..2997c11266581 100644 --- a/.gitignore +++ b/.gitignore @@ -172,3 +172,6 @@ $RECYCLE.BIN/ # Mac desktop service store files .DS_Store + +# Visual Studio Code +.vscode/ From e6c653ac66b2682700edb74b8beaacfe2f0e67a8 Mon Sep 17 00:00:00 2001 From: CyrusNajmabadi Date: Thu, 9 Jun 2016 17:48:43 -0700 Subject: [PATCH 02/36] Remove VB specialization. --- .../Completion/CompletionHelper.cs | 27 ++++++++++++++++++- .../Completion/VisualBasicCompletionHelper.vb | 12 --------- .../Completion/CommonCompletionService.cs | 2 +- .../CompletionServiceWithProviders.cs | 16 +++++------ .../EnumCompletionProvider.vb | 15 ++++++----- 5 files changed, 44 insertions(+), 28 deletions(-) diff --git a/src/EditorFeatures/Core/Extensibility/Completion/CompletionHelper.cs b/src/EditorFeatures/Core/Extensibility/Completion/CompletionHelper.cs index 0444f5c925607..0f5291e8d38d5 100644 --- a/src/EditorFeatures/Core/Extensibility/Completion/CompletionHelper.cs +++ b/src/EditorFeatures/Core/Extensibility/Completion/CompletionHelper.cs @@ -312,11 +312,36 @@ private static StringComparison GetComparision(bool isCaseSensitive) /// Returns true if the completion item should be "soft" selected, or false if it should be "hard" /// selected. /// - public virtual bool ShouldSoftSelectItem(CompletionItem item, string filterText, CompletionTrigger trigger) + public bool ShouldSoftSelectItem(CompletionItem item, string filterText, CompletionTrigger trigger) { + // If all that has been typed is puntuation, then don't hard select anything. + // It's possible the user is just typing language punctuation and selecting + // anything in the list will interfere. We only allow this if the filter text + // exactly matches something in the list already. + if (filterText.Length > 0 && IsAllPunctuation(filterText) && filterText != item.DisplayText) + { + return true; + } + + // If the user hasn't actually typed anything, then don't hard select any item. + // The only exception to this is if the completion provider has requested the + // item be preselected. return filterText.Length == 0 && !item.Rules.Preselect; } + private bool IsAllPunctuation(string filterText) + { + foreach (var ch in filterText) + { + if (!char.IsPunctuation(ch)) + { + return false; + } + } + + return true; + } + protected bool IsObjectCreationItem(CompletionItem item) { return item.Tags.Contains(CompletionTags.ObjectCreation); diff --git a/src/EditorFeatures/VisualBasic/Completion/VisualBasicCompletionHelper.vb b/src/EditorFeatures/VisualBasic/Completion/VisualBasicCompletionHelper.vb index 8321163f970aa..b90bf05840984 100644 --- a/src/EditorFeatures/VisualBasic/Completion/VisualBasicCompletionHelper.vb +++ b/src/EditorFeatures/VisualBasic/Completion/VisualBasicCompletionHelper.vb @@ -76,17 +76,5 @@ Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.Completion Return MyBase.IsBetterFilterMatch(item1, item2, filterText, trigger, filterReason, recentItems) End Function - - Public Overrides Function ShouldSoftSelectItem(item As CompletionItem, filterText As String, trigger As CompletionTrigger) As Boolean - - ' VB has additional specialized logic for soft selecting an item in completion when the only filter text Is "_" - If filterText.Length = 0 OrElse filterText = "_" Then - ' Object Creation hard selects even with no selected item - Return Not IsObjectCreationItem(item) - End If - - Return MyBase.ShouldSoftSelectItem(item, filterText, trigger) - End Function End Class - End Namespace \ No newline at end of file diff --git a/src/Features/Core/Portable/Completion/CommonCompletionService.cs b/src/Features/Core/Portable/Completion/CommonCompletionService.cs index ea20236f5f7b9..fb27191288306 100644 --- a/src/Features/Core/Portable/Completion/CommonCompletionService.cs +++ b/src/Features/Core/Portable/Completion/CommonCompletionService.cs @@ -42,7 +42,7 @@ protected override CompletionItem GetBetterItem(CompletionItem item, CompletionI return existingItem; } - return item; + return base.GetBetterItem(item, existingItem); } protected static bool IsKeywordItem(CompletionItem item) diff --git a/src/Features/Core/Portable/Completion/CompletionServiceWithProviders.cs b/src/Features/Core/Portable/Completion/CompletionServiceWithProviders.cs index 131818843e8fc..397de14de44fd 100644 --- a/src/Features/Core/Portable/Completion/CompletionServiceWithProviders.cs +++ b/src/Features/Core/Portable/Completion/CompletionServiceWithProviders.cs @@ -202,32 +202,32 @@ public override async Task GetCompletionsAsync( } // Now, ask all the triggered providers if they can provide a group. - var completionLists = new List(); + var completionContexts = new List(); foreach (var provider in triggeredProviders) { - var completionList = await GetContextAsync( + var completionContext = await GetContextAsync( provider, document, caretPosition, trigger, options, defaultItemSpan, cancellationToken).ConfigureAwait(false); - if (completionList != null) + if (completionContext != null) { - completionLists.Add(completionList); + completionContexts.Add(completionContext); } } // See if there was a group provided that was exclusive and had items in it. If so, then // that's all we'll return. - var firstExclusiveList = completionLists.FirstOrDefault(t => t.IsExclusive && t.Items.Any()); + var firstExclusiveContext = completionContexts.FirstOrDefault(t => t.IsExclusive && t.Items.Any()); - if (firstExclusiveList != null) + if (firstExclusiveContext != null) { return MergeAndPruneCompletionLists( - SpecializedCollections.SingletonEnumerable(firstExclusiveList), defaultItemSpan, + SpecializedCollections.SingletonEnumerable(firstExclusiveContext), defaultItemSpan, isExclusive: true); } // If no exclusive providers provided anything, then go through the remaining // triggered list and see if any provide items. - var nonExclusiveLists = completionLists.Where(t => !t.IsExclusive).ToList(); + var nonExclusiveLists = completionContexts.Where(t => !t.IsExclusive).ToList(); // If we still don't have any items, then we're definitely done. if (!nonExclusiveLists.Any(g => g.Items.Any())) diff --git a/src/Features/VisualBasic/Portable/Completion/CompletionProviders/EnumCompletionProvider.vb b/src/Features/VisualBasic/Portable/Completion/CompletionProviders/EnumCompletionProvider.vb index bcee6b14663cb..3406d615742bd 100644 --- a/src/Features/VisualBasic/Portable/Completion/CompletionProviders/EnumCompletionProvider.vb +++ b/src/Features/VisualBasic/Portable/Completion/CompletionProviders/EnumCompletionProvider.vb @@ -29,12 +29,15 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Completion.Providers Dim hideAdvancedMembers = options.GetOption(CodeAnalysis.Recommendations.RecommendationOptions.HideAdvancedMembers, context.SemanticModel.Language) ' We'll want to build a list of the actual enum members and all accessible instances of that enum, too - Return Task.FromResult(enumType.GetMembers().Where(Function(m As ISymbol) As Boolean - Return m.Kind = SymbolKind.Field AndAlso - DirectCast(m, IFieldSymbol).IsConst AndAlso - m.IsEditorBrowsable(hideAdvancedMembers, context.SemanticModel.Compilation) - End Function)) - + Dim result = enumType.GetMembers().Where( + Function(m As ISymbol) As Boolean + Return m.Kind = SymbolKind.Field AndAlso + DirectCast(m, IFieldSymbol).IsConst AndAlso + m.IsEditorBrowsable(hideAdvancedMembers, context.SemanticModel.Compilation) + End Function).ToList() + result.Add(enumType) + + Return Task.FromResult(Of IEnumerable(Of ISymbol))(result) End Function Protected Overrides Function GetSymbolsWorker(context As AbstractSyntaxContext, position As Integer, options As OptionSet, cancellationToken As CancellationToken) As Task(Of IEnumerable(Of ISymbol)) From cc44b005d0cf905fcb53afc11c830b04c6345c47 Mon Sep 17 00:00:00 2001 From: CyrusNajmabadi Date: Thu, 9 Jun 2016 18:26:37 -0700 Subject: [PATCH 03/36] Update tests. --- .../VisualBasicCompletionCommandHandlerTests.vb | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/EditorFeatures/Test2/IntelliSense/VisualBasicCompletionCommandHandlerTests.vb b/src/EditorFeatures/Test2/IntelliSense/VisualBasicCompletionCommandHandlerTests.vb index 57a0a22536944..0c3040b03baaa 100644 --- a/src/EditorFeatures/Test2/IntelliSense/VisualBasicCompletionCommandHandlerTests.vb +++ b/src/EditorFeatures/Test2/IntelliSense/VisualBasicCompletionCommandHandlerTests.vb @@ -985,7 +985,7 @@ class Foo state.SendTypeChars(", ") Await state.WaitForAsynchronousOperationsAsync() - Await state.AssertSelectedCompletionItem(displayText:="Numeros.Dos", isSoftSelected:=True) + Await state.AssertSelectedCompletionItem(displayText:="Numeros", isSoftSelected:=False) Assert.Equal(1, state.CurrentCompletionPresenterSession.PresentationItems.Where(Function(c) c.Item.DisplayText = "Numeros").Count()) End Using End Function @@ -1173,7 +1173,10 @@ End Module Imports System Module Program Sub Main() - Console.WriteLine$$ + Test$$ + End Sub + + Sub Test(i As Integer) End Sub End Module ) @@ -2032,7 +2035,7 @@ Class C End Class) state.SendTypeChars("(") Await state.AssertCompletionSession() - Assert.True(state.CurrentCompletionPresenterSession.IsSoftSelected) + Assert.False(state.CurrentCompletionPresenterSession.IsSoftSelected) End Using End Function From 5e72883d275bdbff27d13b72772803d8933bf411 Mon Sep 17 00:00:00 2001 From: CyrusNajmabadi Date: Thu, 9 Jun 2016 19:20:43 -0700 Subject: [PATCH 04/36] Provide a way for completion providers to specify if they want to be soft or hard selected. --- .../Completion/CompletionHelper.cs | 22 +++- ...isualBasicCompletionCommandHandlerTests.vb | 9 +- ...mAndCompletionListTagCompletionProvider.cs | 4 +- .../ObjectCreationCompletionProvider.cs | 10 +- .../Completion/CompletionItemRules.cs | 100 +++++++++++++++--- .../Core/Portable/PublicAPI.Unshipped.txt | 9 +- .../CompletionListTagCompletionProvider.vb | 3 +- .../EnumCompletionProvider.vb | 4 +- .../ObjectCreationCompletionProvider.vb | 5 +- 9 files changed, 135 insertions(+), 31 deletions(-) diff --git a/src/EditorFeatures/Core/Extensibility/Completion/CompletionHelper.cs b/src/EditorFeatures/Core/Extensibility/Completion/CompletionHelper.cs index 0f5291e8d38d5..589b98d5b3b31 100644 --- a/src/EditorFeatures/Core/Extensibility/Completion/CompletionHelper.cs +++ b/src/EditorFeatures/Core/Extensibility/Completion/CompletionHelper.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; using System.Collections.Immutable; +using System.Diagnostics; using System.Globalization; using System.Threading; using System.Threading.Tasks; @@ -326,7 +327,26 @@ public bool ShouldSoftSelectItem(CompletionItem item, string filterText, Complet // If the user hasn't actually typed anything, then don't hard select any item. // The only exception to this is if the completion provider has requested the // item be preselected. - return filterText.Length == 0 && !item.Rules.Preselect; + if (filterText.Length == 0) + { + // Item didn't want to be hard selected with no filter text. + // So definitely soft select it. + if (item.Rules.SelectionBehavior != CompletionItemSelectionBehavior.HardSelection) + { + return true; + } + + // Item did not ask to be preselected. So definitely soft select it. + if (!item.Rules.Preselect) + { + return true; + } + } + + // The user typed something, or the item asked to be preselected. In + // either case, don't soft select this. + Debug.Assert(filterText.Length > 0 || item.Rules.Preselect); + return false; } private bool IsAllPunctuation(string filterText) diff --git a/src/EditorFeatures/Test2/IntelliSense/VisualBasicCompletionCommandHandlerTests.vb b/src/EditorFeatures/Test2/IntelliSense/VisualBasicCompletionCommandHandlerTests.vb index 0c3040b03baaa..42407e27dfa4d 100644 --- a/src/EditorFeatures/Test2/IntelliSense/VisualBasicCompletionCommandHandlerTests.vb +++ b/src/EditorFeatures/Test2/IntelliSense/VisualBasicCompletionCommandHandlerTests.vb @@ -985,7 +985,7 @@ class Foo state.SendTypeChars(", ") Await state.WaitForAsynchronousOperationsAsync() - Await state.AssertSelectedCompletionItem(displayText:="Numeros", isSoftSelected:=False) + Await state.AssertSelectedCompletionItem(displayText:="Numeros", isSoftSelected:=True) Assert.Equal(1, state.CurrentCompletionPresenterSession.PresentationItems.Where(Function(c) c.Item.DisplayText = "Numeros").Count()) End Using End Function @@ -1173,10 +1173,7 @@ End Module Imports System Module Program Sub Main() - Test$$ - End Sub - - Sub Test(i As Integer) + Console.WriteLine$$ End Sub End Module ) @@ -2035,7 +2032,7 @@ Class C End Class) state.SendTypeChars("(") Await state.AssertCompletionSession() - Assert.False(state.CurrentCompletionPresenterSession.IsSoftSelected) + Assert.True(state.CurrentCompletionPresenterSession.IsSoftSelected) End Using End Function diff --git a/src/Features/CSharp/Portable/Completion/CompletionProviders/EnumAndCompletionListTagCompletionProvider.cs b/src/Features/CSharp/Portable/Completion/CompletionProviders/EnumAndCompletionListTagCompletionProvider.cs index 2d178e962d510..ccb596a1f8901 100644 --- a/src/Features/CSharp/Portable/Completion/CompletionProviders/EnumAndCompletionListTagCompletionProvider.cs +++ b/src/Features/CSharp/Portable/Completion/CompletionProviders/EnumAndCompletionListTagCompletionProvider.cs @@ -126,7 +126,9 @@ public override Task GetDescriptionAsync(Document documen } private static readonly CompletionItemRules s_rules = - CompletionItemRules.Default.WithCommitCharacterRules(ImmutableArray.Create(CharacterSetModificationRule.Create(CharacterSetModificationKind.Replace, '.'))); + CompletionItemRules.Default.WithCommitCharacterRules(ImmutableArray.Create(CharacterSetModificationRule.Create(CharacterSetModificationKind.Replace, '.'))) + .WithPreselect(true) + .WithSelectionBehavior(CompletionItemSelectionBehavior.HardSelection); private INamedTypeSymbol GetCompletionListType(ITypeSymbol type, INamedTypeSymbol within, Compilation compilation) { diff --git a/src/Features/CSharp/Portable/Completion/CompletionProviders/ObjectCreationCompletionProvider.cs b/src/Features/CSharp/Portable/Completion/CompletionProviders/ObjectCreationCompletionProvider.cs index 0844e235caa14..64e026155f63b 100644 --- a/src/Features/CSharp/Portable/Completion/CompletionProviders/ObjectCreationCompletionProvider.cs +++ b/src/Features/CSharp/Portable/Completion/CompletionProviders/ObjectCreationCompletionProvider.cs @@ -95,10 +95,16 @@ protected override ValueTuple GetDisplayAndInsertionText(ISymbol } private static readonly CompletionItemRules s_objectRules = - CompletionItemRules.Create(commitCharacterRules: ImmutableArray.Create(CharacterSetModificationRule.Create(CharacterSetModificationKind.Replace, ' ', '(', '['))); + CompletionItemRules.Create( + commitCharacterRules: ImmutableArray.Create(CharacterSetModificationRule.Create(CharacterSetModificationKind.Replace, ' ', '(', '[')), + preselect: true, + selectionBehavior: CompletionItemSelectionBehavior.HardSelection); private static readonly CompletionItemRules s_defaultRules = - CompletionItemRules.Create(commitCharacterRules: ImmutableArray.Create(CharacterSetModificationRule.Create(CharacterSetModificationKind.Replace, ' ', '(', '[', '{'))); + CompletionItemRules.Create( + commitCharacterRules: ImmutableArray.Create(CharacterSetModificationRule.Create(CharacterSetModificationKind.Replace, ' ', '(', '[', '{')), + preselect: true, + selectionBehavior: CompletionItemSelectionBehavior.HardSelection); protected override CompletionItemRules GetCompletionItemRules(IReadOnlyList symbols, AbstractSyntaxContext context) { diff --git a/src/Features/Core/Portable/Completion/CompletionItemRules.cs b/src/Features/Core/Portable/Completion/CompletionItemRules.cs index a3e52cb340dfc..4f737cf06d4c8 100644 --- a/src/Features/Core/Portable/Completion/CompletionItemRules.cs +++ b/src/Features/Core/Portable/Completion/CompletionItemRules.cs @@ -6,6 +6,28 @@ namespace Microsoft.CodeAnalysis.Completion { + public enum CompletionItemSelectionBehavior + { + Default, + + /// + /// If no text has been typed, the item should be soft selected. This is appropriate for + /// completion providers that want to provide suggestions that shouldn't interfere with + /// typing. For example a provider that comes up on space might offer items that are soft + /// selected so that an additional space (or other puntuation character) will not then + /// commit that item. + /// + SoftSelection, + + /// + /// If no text has been typed, the item should be hard selected. This is appropriate for + /// completion providers that are providing suggestions the user is nearly certain to + /// select. Because the item is hard selected, any commit characters typed after it will + /// cause it to be committed. + /// + HardSelection, + } + /// /// Rules for how the individual items are handled. /// @@ -20,7 +42,8 @@ public sealed class CompletionItemRules commitCharacterRules: default(ImmutableArray), enterKeyRule: EnterKeyRule.Default, formatOnCommit: false, - preselect: false); + preselect: false, + selectionBehavior: CompletionItemSelectionBehavior.Default); /// /// Rules that modify the set of characters that can be typed to filter the list of completion items. @@ -47,18 +70,48 @@ public sealed class CompletionItemRules /// public bool Preselect { get; } + /// + /// How this item should be selected when the completion list first appears and + /// before the user has typed any characters. + /// + public CompletionItemSelectionBehavior SelectionBehavior { get; } + private CompletionItemRules( + ImmutableArray filterCharacterRules, + ImmutableArray commitCharacterRules, + EnterKeyRule enterKeyRule, + bool formatOnCommit, + bool preselect, + CompletionItemSelectionBehavior selectionBehavior) + { + FilterCharacterRules = filterCharacterRules.IsDefault ? ImmutableArray.Empty : filterCharacterRules; + CommitCharacterRules = commitCharacterRules.IsDefault ? ImmutableArray.Empty : commitCharacterRules; + EnterKeyRule = enterKeyRule; + FormatOnCommit = formatOnCommit; + Preselect = preselect; + SelectionBehavior = selectionBehavior; + } + + /// + /// Creates a new instance. + /// + /// Rules about which keys typed are used to filter the list of completion items. + /// Rules about which keys typed caused the completion item to be committed. + /// Rule about whether the enter key is passed through to the editor after the selected item has been committed. + /// True if the modified text should be formatted automatically. + /// True if the related completion item should be initially selected. + /// + public static CompletionItemRules Create( ImmutableArray filterCharacterRules, ImmutableArray commitCharacterRules, EnterKeyRule enterKeyRule, bool formatOnCommit, bool preselect) { - this.FilterCharacterRules = filterCharacterRules.IsDefault ? ImmutableArray.Empty : filterCharacterRules; - this.CommitCharacterRules = commitCharacterRules.IsDefault ? ImmutableArray.Empty : commitCharacterRules; - this.EnterKeyRule = enterKeyRule; - this.FormatOnCommit = formatOnCommit; - this.Preselect = preselect; + return Create( + filterCharacterRules, commitCharacterRules, + enterKeyRule, formatOnCommit, preselect, + selectionBehavior: CompletionItemSelectionBehavior.Default); } /// @@ -69,25 +122,29 @@ private CompletionItemRules( /// Rule about whether the enter key is passed through to the editor after the selected item has been committed. /// True if the modified text should be formatted automatically. /// True if the related completion item should be initially selected. + /// How this item should be selected if no text has been typed after the completion list is brought up. /// public static CompletionItemRules Create( ImmutableArray filterCharacterRules = default(ImmutableArray), ImmutableArray commitCharacterRules = default(ImmutableArray), EnterKeyRule enterKeyRule = EnterKeyRule.Default, bool formatOnCommit = false, - bool preselect = false) + bool preselect = false, + CompletionItemSelectionBehavior selectionBehavior = CompletionItemSelectionBehavior.Default) { - if (filterCharacterRules.IsDefaultOrEmpty - && commitCharacterRules.IsDefaultOrEmpty - && enterKeyRule == Default.EnterKeyRule - && formatOnCommit == Default.FormatOnCommit - && preselect == Default.Preselect) + if (filterCharacterRules.IsDefaultOrEmpty && + commitCharacterRules.IsDefaultOrEmpty && + enterKeyRule == Default.EnterKeyRule && + formatOnCommit == Default.FormatOnCommit && + preselect == Default.Preselect && + selectionBehavior == Default.SelectionBehavior) { return Default; } else { - return new CompletionItemRules(filterCharacterRules, commitCharacterRules, enterKeyRule, formatOnCommit, preselect); + return new CompletionItemRules( + filterCharacterRules, commitCharacterRules, enterKeyRule, formatOnCommit, preselect, selectionBehavior); } } @@ -96,25 +153,28 @@ private CompletionItemRules With( Optional> commitRules = default(Optional>), Optional enterKeyRule = default(Optional), Optional formatOnCommit = default(Optional), - Optional preselect = default(Optional)) + Optional preselect = default(Optional), + Optional selectionBehavior = default(Optional)) { var newFilterRules = filterRules.HasValue ? filterRules.Value : this.FilterCharacterRules; var newCommitRules = commitRules.HasValue ? commitRules.Value : this.CommitCharacterRules; var newEnterKeyRule = enterKeyRule.HasValue ? enterKeyRule.Value : this.EnterKeyRule; var newFormatOnCommit = formatOnCommit.HasValue ? formatOnCommit.Value : this.FormatOnCommit; var newPreselect = preselect.HasValue ? preselect.Value : this.Preselect; + var newSelectionBehavior = selectionBehavior.HasValue ? selectionBehavior.Value : this.SelectionBehavior; if (newFilterRules == this.FilterCharacterRules && newCommitRules == this.CommitCharacterRules && newEnterKeyRule == this.EnterKeyRule && newFormatOnCommit == this.FormatOnCommit && - newPreselect == this.Preselect) + newPreselect == this.Preselect && + newSelectionBehavior == this.SelectionBehavior) { return this; } else { - return Create(newFilterRules, newCommitRules, newEnterKeyRule, newFormatOnCommit, newPreselect); + return Create(newFilterRules, newCommitRules, newEnterKeyRule, newFormatOnCommit, newPreselect, newSelectionBehavior); } } @@ -157,5 +217,13 @@ public CompletionItemRules WithPreselect(bool preselect) { return this.With(preselect: preselect); } + + /// + /// Creates a copy of this with the property changed. + /// + public CompletionItemRules WithSelectionBehavior(CompletionItemSelectionBehavior selectionBehavior) + { + return this.With(selectionBehavior: selectionBehavior); + } } } diff --git a/src/Features/Core/Portable/PublicAPI.Unshipped.txt b/src/Features/Core/Portable/PublicAPI.Unshipped.txt index abbefbb791eca..0ef47a21a85f3 100644 --- a/src/Features/Core/Portable/PublicAPI.Unshipped.txt +++ b/src/Features/Core/Portable/PublicAPI.Unshipped.txt @@ -53,11 +53,17 @@ Microsoft.CodeAnalysis.Completion.CompletionItemRules.EnterKeyRule.get -> Micros Microsoft.CodeAnalysis.Completion.CompletionItemRules.FilterCharacterRules.get -> System.Collections.Immutable.ImmutableArray Microsoft.CodeAnalysis.Completion.CompletionItemRules.FormatOnCommit.get -> bool Microsoft.CodeAnalysis.Completion.CompletionItemRules.Preselect.get -> bool +Microsoft.CodeAnalysis.Completion.CompletionItemRules.SelectionBehavior.get -> Microsoft.CodeAnalysis.Completion.CompletionItemSelectionBehavior Microsoft.CodeAnalysis.Completion.CompletionItemRules.WithCommitCharacterRules(System.Collections.Immutable.ImmutableArray commitCharacterRules) -> Microsoft.CodeAnalysis.Completion.CompletionItemRules Microsoft.CodeAnalysis.Completion.CompletionItemRules.WithEnterKeyRule(Microsoft.CodeAnalysis.Completion.EnterKeyRule enterKeyRule) -> Microsoft.CodeAnalysis.Completion.CompletionItemRules Microsoft.CodeAnalysis.Completion.CompletionItemRules.WithFilterCharacterRules(System.Collections.Immutable.ImmutableArray filterCharacterRules) -> Microsoft.CodeAnalysis.Completion.CompletionItemRules Microsoft.CodeAnalysis.Completion.CompletionItemRules.WithFormatOnCommit(bool formatOnCommit) -> Microsoft.CodeAnalysis.Completion.CompletionItemRules Microsoft.CodeAnalysis.Completion.CompletionItemRules.WithPreselect(bool preselect) -> Microsoft.CodeAnalysis.Completion.CompletionItemRules +Microsoft.CodeAnalysis.Completion.CompletionItemRules.WithSelectionBehavior(Microsoft.CodeAnalysis.Completion.CompletionItemSelectionBehavior selectionBehavior) -> Microsoft.CodeAnalysis.Completion.CompletionItemRules +Microsoft.CodeAnalysis.Completion.CompletionItemSelectionBehavior +Microsoft.CodeAnalysis.Completion.CompletionItemSelectionBehavior.Default = 0 -> Microsoft.CodeAnalysis.Completion.CompletionItemSelectionBehavior +Microsoft.CodeAnalysis.Completion.CompletionItemSelectionBehavior.HardSelection = 2 -> Microsoft.CodeAnalysis.Completion.CompletionItemSelectionBehavior +Microsoft.CodeAnalysis.Completion.CompletionItemSelectionBehavior.SoftSelection = 1 -> Microsoft.CodeAnalysis.Completion.CompletionItemSelectionBehavior Microsoft.CodeAnalysis.Completion.CompletionList Microsoft.CodeAnalysis.Completion.CompletionList.DefaultSpan.get -> Microsoft.CodeAnalysis.Text.TextSpan Microsoft.CodeAnalysis.Completion.CompletionList.Items.get -> System.Collections.Immutable.ImmutableArray @@ -189,7 +195,8 @@ static Microsoft.CodeAnalysis.Completion.CompletionChange.Create(System.Collecti static Microsoft.CodeAnalysis.Completion.CompletionDescription.Create(System.Collections.Immutable.ImmutableArray taggedParts) -> Microsoft.CodeAnalysis.Completion.CompletionDescription static Microsoft.CodeAnalysis.Completion.CompletionDescription.FromText(string text) -> Microsoft.CodeAnalysis.Completion.CompletionDescription static Microsoft.CodeAnalysis.Completion.CompletionItem.Create(string displayText, string filterText = null, string sortText = null, Microsoft.CodeAnalysis.Text.TextSpan span = default(Microsoft.CodeAnalysis.Text.TextSpan), System.Collections.Immutable.ImmutableDictionary properties = null, System.Collections.Immutable.ImmutableArray tags = default(System.Collections.Immutable.ImmutableArray), Microsoft.CodeAnalysis.Completion.CompletionItemRules rules = null) -> Microsoft.CodeAnalysis.Completion.CompletionItem -static Microsoft.CodeAnalysis.Completion.CompletionItemRules.Create(System.Collections.Immutable.ImmutableArray filterCharacterRules = default(System.Collections.Immutable.ImmutableArray), System.Collections.Immutable.ImmutableArray commitCharacterRules = default(System.Collections.Immutable.ImmutableArray), Microsoft.CodeAnalysis.Completion.EnterKeyRule enterKeyRule = Microsoft.CodeAnalysis.Completion.EnterKeyRule.Default, bool formatOnCommit = false, bool preselect = false) -> Microsoft.CodeAnalysis.Completion.CompletionItemRules +static Microsoft.CodeAnalysis.Completion.CompletionItemRules.Create(System.Collections.Immutable.ImmutableArray filterCharacterRules = default(System.Collections.Immutable.ImmutableArray), System.Collections.Immutable.ImmutableArray commitCharacterRules = default(System.Collections.Immutable.ImmutableArray), Microsoft.CodeAnalysis.Completion.EnterKeyRule enterKeyRule = Microsoft.CodeAnalysis.Completion.EnterKeyRule.Default, bool formatOnCommit = false, bool preselect = false, Microsoft.CodeAnalysis.Completion.CompletionItemSelectionBehavior selectionBehavior = Microsoft.CodeAnalysis.Completion.CompletionItemSelectionBehavior.Default) -> Microsoft.CodeAnalysis.Completion.CompletionItemRules +static Microsoft.CodeAnalysis.Completion.CompletionItemRules.Create(System.Collections.Immutable.ImmutableArray filterCharacterRules, System.Collections.Immutable.ImmutableArray commitCharacterRules, Microsoft.CodeAnalysis.Completion.EnterKeyRule enterKeyRule, bool formatOnCommit, bool preselect) -> Microsoft.CodeAnalysis.Completion.CompletionItemRules static Microsoft.CodeAnalysis.Completion.CompletionItemRules.Default -> Microsoft.CodeAnalysis.Completion.CompletionItemRules static Microsoft.CodeAnalysis.Completion.CompletionList.Create(Microsoft.CodeAnalysis.Text.TextSpan defaultSpan, System.Collections.Immutable.ImmutableArray items, Microsoft.CodeAnalysis.Completion.CompletionRules rules = null, Microsoft.CodeAnalysis.Completion.CompletionItem suggestionModeItem = null) -> Microsoft.CodeAnalysis.Completion.CompletionList static Microsoft.CodeAnalysis.Completion.CompletionRules.Create(bool dismissIfEmpty = false, bool dismissIfLastCharacterDeleted = false, System.Collections.Immutable.ImmutableArray defaultCommitCharacters = default(System.Collections.Immutable.ImmutableArray), Microsoft.CodeAnalysis.Completion.EnterKeyRule defaultEnterKeyRule = Microsoft.CodeAnalysis.Completion.EnterKeyRule.Default) -> Microsoft.CodeAnalysis.Completion.CompletionRules diff --git a/src/Features/VisualBasic/Portable/Completion/CompletionProviders/CompletionListTagCompletionProvider.vb b/src/Features/VisualBasic/Portable/Completion/CompletionProviders/CompletionListTagCompletionProvider.vb index 6dd8eccb10cfa..c876a8e99ae70 100644 --- a/src/Features/VisualBasic/Portable/Completion/CompletionProviders/CompletionListTagCompletionProvider.vb +++ b/src/Features/VisualBasic/Portable/Completion/CompletionProviders/CompletionListTagCompletionProvider.vb @@ -79,7 +79,6 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Completion.Providers glyph:=Glyph.EnumMember, preselect:=preselect, supportedPlatforms:=supportedPlatformData) - End Function End Class -End Namespace +End Namespace \ No newline at end of file diff --git a/src/Features/VisualBasic/Portable/Completion/CompletionProviders/EnumCompletionProvider.vb b/src/Features/VisualBasic/Portable/Completion/CompletionProviders/EnumCompletionProvider.vb index 3406d615742bd..3efb4293270f0 100644 --- a/src/Features/VisualBasic/Portable/Completion/CompletionProviders/EnumCompletionProvider.vb +++ b/src/Features/VisualBasic/Portable/Completion/CompletionProviders/EnumCompletionProvider.vb @@ -127,8 +127,10 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Completion.Providers rules:=GetCompletionItemRules(symbols, context)) End Function + Private Shared ReadOnly s_rules As CompletionItemRules = CompletionItemRules.Default.WithPreselect(True) + Protected Overrides Function GetCompletionItemRules(symbols As IReadOnlyList(Of ISymbol), context As AbstractSyntaxContext) As CompletionItemRules - Return CompletionItemRules.Default + Return s_rules End Function Public Overrides Function GetTextChangeAsync(document As Document, selectedItem As CompletionItem, ch As Char?, cancellationToken As CancellationToken) As Task(Of TextChange?) diff --git a/src/Features/VisualBasic/Portable/Completion/CompletionProviders/ObjectCreationCompletionProvider.vb b/src/Features/VisualBasic/Portable/Completion/CompletionProviders/ObjectCreationCompletionProvider.vb index d0ddb203f4efb..a40f1902decc6 100644 --- a/src/Features/VisualBasic/Portable/Completion/CompletionProviders/ObjectCreationCompletionProvider.vb +++ b/src/Features/VisualBasic/Portable/Completion/CompletionProviders/ObjectCreationCompletionProvider.vb @@ -47,7 +47,10 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Completion.Providers End Function Private Shared s_rules As CompletionItemRules = - CompletionItemRules.Create(commitCharacterRules:=ImmutableArray.Create(CharacterSetModificationRule.Create(CharacterSetModificationKind.Replace, " "c, "("c))) + CompletionItemRules.Create( + commitCharacterRules:=ImmutableArray.Create(CharacterSetModificationRule.Create(CharacterSetModificationKind.Replace, " "c, "("c)), + preselect:=True, + selectionBehavior:=CompletionItemSelectionBehavior.HardSelection) Protected Overrides Function GetCompletionItemRules(symbols As IReadOnlyList(Of ISymbol), context As AbstractSyntaxContext) As CompletionItemRules Return s_rules From 0a606a44481a4d647264ffec26a178490e6d04b4 Mon Sep 17 00:00:00 2001 From: CyrusNajmabadi Date: Thu, 9 Jun 2016 19:22:35 -0700 Subject: [PATCH 05/36] Move code out of completion helper. --- .../Completion/CompletionHelper.cs | 60 +------------------ .../Controller.Session_FilterModel.cs | 58 +++++++++++++++++- 2 files changed, 57 insertions(+), 61 deletions(-) diff --git a/src/EditorFeatures/Core/Extensibility/Completion/CompletionHelper.cs b/src/EditorFeatures/Core/Extensibility/Completion/CompletionHelper.cs index 589b98d5b3b31..97073e3d312f9 100644 --- a/src/EditorFeatures/Core/Extensibility/Completion/CompletionHelper.cs +++ b/src/EditorFeatures/Core/Extensibility/Completion/CompletionHelper.cs @@ -306,65 +306,7 @@ private static bool TextTypedSoFarMatchesItem(CompletionItem item, char ch, stri private static StringComparison GetComparision(bool isCaseSensitive) { - return isCaseSensitive? StringComparison.CurrentCulture: StringComparison.CurrentCultureIgnoreCase; - } - - /// - /// Returns true if the completion item should be "soft" selected, or false if it should be "hard" - /// selected. - /// - public bool ShouldSoftSelectItem(CompletionItem item, string filterText, CompletionTrigger trigger) - { - // If all that has been typed is puntuation, then don't hard select anything. - // It's possible the user is just typing language punctuation and selecting - // anything in the list will interfere. We only allow this if the filter text - // exactly matches something in the list already. - if (filterText.Length > 0 && IsAllPunctuation(filterText) && filterText != item.DisplayText) - { - return true; - } - - // If the user hasn't actually typed anything, then don't hard select any item. - // The only exception to this is if the completion provider has requested the - // item be preselected. - if (filterText.Length == 0) - { - // Item didn't want to be hard selected with no filter text. - // So definitely soft select it. - if (item.Rules.SelectionBehavior != CompletionItemSelectionBehavior.HardSelection) - { - return true; - } - - // Item did not ask to be preselected. So definitely soft select it. - if (!item.Rules.Preselect) - { - return true; - } - } - - // The user typed something, or the item asked to be preselected. In - // either case, don't soft select this. - Debug.Assert(filterText.Length > 0 || item.Rules.Preselect); - return false; - } - - private bool IsAllPunctuation(string filterText) - { - foreach (var ch in filterText) - { - if (!char.IsPunctuation(ch)) - { - return false; - } - } - - return true; - } - - protected bool IsObjectCreationItem(CompletionItem item) - { - return item.Tags.Contains(CompletionTags.ObjectCreation); + return isCaseSensitive ? StringComparison.CurrentCulture : StringComparison.CurrentCultureIgnoreCase; } } } \ No newline at end of file diff --git a/src/EditorFeatures/Core/Implementation/IntelliSense/Completion/Controller.Session_FilterModel.cs b/src/EditorFeatures/Core/Implementation/IntelliSense/Completion/Controller.Session_FilterModel.cs index 8d6ad949cddcb..bacab136dac93 100644 --- a/src/EditorFeatures/Core/Implementation/IntelliSense/Completion/Controller.Session_FilterModel.cs +++ b/src/EditorFeatures/Core/Implementation/IntelliSense/Completion/Controller.Session_FilterModel.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Collections.Immutable; +using System.Diagnostics; using System.Linq; using System.Threading; using Microsoft.CodeAnalysis.Completion; @@ -317,7 +318,7 @@ private bool IsHardSelection( var viewSpan = model.GetViewBufferSpan(bestFilterMatch.Item.Span); var fullFilterText = model.GetCurrentTextInSnapshot(viewSpan, textSnapshot, endPoint: null); - var shouldSoftSelect = completionRules.ShouldSoftSelectItem(bestFilterMatch.Item, fullFilterText, trigger); + var shouldSoftSelect = ShouldSoftSelectItem(bestFilterMatch.Item, fullFilterText, trigger); if (shouldSoftSelect) { return false; @@ -334,6 +335,59 @@ private bool IsHardSelection( // can hard select this. return true; } + + /// + /// Returns true if the completion item should be "soft" selected, or false if it should be "hard" + /// selected. + /// + private static bool ShouldSoftSelectItem(CompletionItem item, string filterText, CompletionTrigger trigger) + { + // If all that has been typed is puntuation, then don't hard select anything. + // It's possible the user is just typing language punctuation and selecting + // anything in the list will interfere. We only allow this if the filter text + // exactly matches something in the list already. + if (filterText.Length > 0 && IsAllPunctuation(filterText) && filterText != item.DisplayText) + { + return true; + } + + // If the user hasn't actually typed anything, then don't hard select any item. + // The only exception to this is if the completion provider has requested the + // item be preselected. + if (filterText.Length == 0) + { + // Item didn't want to be hard selected with no filter text. + // So definitely soft select it. + if (item.Rules.SelectionBehavior != CompletionItemSelectionBehavior.HardSelection) + { + return true; + } + + // Item did not ask to be preselected. So definitely soft select it. + if (!item.Rules.Preselect) + { + return true; + } + } + + // The user typed something, or the item asked to be preselected. In + // either case, don't soft select this. + Debug.Assert(filterText.Length > 0 || item.Rules.Preselect); + return false; + } + + private static bool IsAllPunctuation(string filterText) + { + foreach (var ch in filterText) + { + if (!char.IsPunctuation(ch)) + { + return false; + } + } + + return true; + } } } -} +} \ No newline at end of file From 69738084fd0e2806272c18e38f8b49e3816b9990 Mon Sep 17 00:00:00 2001 From: CyrusNajmabadi Date: Mon, 13 Jun 2016 15:14:35 -0700 Subject: [PATCH 06/36] Break out a base class for diagnostic taggers that want to show adornments. --- .../Portable/Diagnostic/DiagnosticSeverity.cs | 7 ++- .../Core/Portable/PublicAPI.Unshipped.txt | 1 + src/EditorFeatures/Core/EditorFeatures.csproj | 1 + ...tractDiagnosticsAdornmentTaggerProvider.cs | 61 +++++++++++++++++++ .../DiagnosticsSquiggleTaggerProvider.cs | 46 ++------------ 5 files changed, 75 insertions(+), 41 deletions(-) create mode 100644 src/EditorFeatures/Core/Implementation/Diagnostics/AbstractDiagnosticsAdornmentTaggerProvider.cs diff --git a/src/Compilers/Core/Portable/Diagnostic/DiagnosticSeverity.cs b/src/Compilers/Core/Portable/Diagnostic/DiagnosticSeverity.cs index b1f7813f5a55b..d98111fb9fc8c 100644 --- a/src/Compilers/Core/Portable/Diagnostic/DiagnosticSeverity.cs +++ b/src/Compilers/Core/Portable/Diagnostic/DiagnosticSeverity.cs @@ -27,7 +27,12 @@ public enum DiagnosticSeverity /// /// Something not allowed by the rules of the language or other authority. /// - Error = 3 + Error = 3, + + /// + /// Something that can be potentially improved. + /// + Suggestion = 4, } /// diff --git a/src/Compilers/Core/Portable/PublicAPI.Unshipped.txt b/src/Compilers/Core/Portable/PublicAPI.Unshipped.txt index da45a7f9029ad..3c0fa7fc6d940 100644 --- a/src/Compilers/Core/Portable/PublicAPI.Unshipped.txt +++ b/src/Compilers/Core/Portable/PublicAPI.Unshipped.txt @@ -1,5 +1,6 @@ Microsoft.CodeAnalysis.Compilation.CreateTupleTypeSymbol(Microsoft.CodeAnalysis.INamedTypeSymbol underlyingType, System.Collections.Immutable.ImmutableArray elementNames = default(System.Collections.Immutable.ImmutableArray)) -> Microsoft.CodeAnalysis.INamedTypeSymbol Microsoft.CodeAnalysis.Compilation.CreateTupleTypeSymbol(System.Collections.Immutable.ImmutableArray elementTypes, System.Collections.Immutable.ImmutableArray elementNames = default(System.Collections.Immutable.ImmutableArray)) -> Microsoft.CodeAnalysis.INamedTypeSymbol +Microsoft.CodeAnalysis.DiagnosticSeverity.Suggestion = 4 -> Microsoft.CodeAnalysis.DiagnosticSeverity Microsoft.CodeAnalysis.Diagnostics.AnalysisContext.RegisterOperationAction(System.Action action, params Microsoft.CodeAnalysis.OperationKind[] operationKinds) -> void Microsoft.CodeAnalysis.Diagnostics.AnalysisResult Microsoft.CodeAnalysis.Diagnostics.AnalysisResult.AnalyzerTelemetryInfo.get -> System.Collections.Immutable.ImmutableDictionary diff --git a/src/EditorFeatures/Core/EditorFeatures.csproj b/src/EditorFeatures/Core/EditorFeatures.csproj index 1d44595dac7d4..f4e1f7e16d199 100644 --- a/src/EditorFeatures/Core/EditorFeatures.csproj +++ b/src/EditorFeatures/Core/EditorFeatures.csproj @@ -264,6 +264,7 @@ + diff --git a/src/EditorFeatures/Core/Implementation/Diagnostics/AbstractDiagnosticsAdornmentTaggerProvider.cs b/src/EditorFeatures/Core/Implementation/Diagnostics/AbstractDiagnosticsAdornmentTaggerProvider.cs new file mode 100644 index 0000000000000..74e7cb1c9c884 --- /dev/null +++ b/src/EditorFeatures/Core/Implementation/Diagnostics/AbstractDiagnosticsAdornmentTaggerProvider.cs @@ -0,0 +1,61 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel.Composition; +using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.Shared.TestHooks; +using Microsoft.VisualStudio.Text; +using Microsoft.VisualStudio.Text.Tagging; + +namespace Microsoft.CodeAnalysis.Editor.Implementation.Diagnostics +{ + internal abstract class AbstractDiagnosticsAdornmentTaggerProvider : + AbstractDiagnosticsTaggerProvider + where TTag : ITag + { + public AbstractDiagnosticsAdornmentTaggerProvider( + IDiagnosticService diagnosticService, + IForegroundNotificationService notificationService, + [ImportMany] IEnumerable> listeners) + : base(diagnosticService, notificationService, new AggregateAsynchronousOperationListener(listeners, FeatureAttribute.ErrorSquiggles)) + { + } + + protected sealed internal override bool IsEnabled => true; + + protected sealed internal override ITagSpan CreateTagSpan( + bool isLiveUpdate, SnapshotSpan span, DiagnosticData data) + { + var errorTag = CreateTag(data); + if (errorTag == null) + { + return null; + } + + // Live update squiggles have to be at least 1 character long. + var minimumLength = isLiveUpdate ? 1 : 0; + var adjustedSpan = AdjustSnapshotSpan(span, minimumLength); + if (adjustedSpan.Length == 0) + { + return null; + } + + return new TagSpan(adjustedSpan, errorTag); + } + + private static SnapshotSpan AdjustSnapshotSpan(SnapshotSpan span, int minimumLength) + { + var snapshot = span.Snapshot; + + // new length + var length = Math.Max(span.Length, minimumLength); + + // make sure start + length is smaller than snapshot.Length and start is >= 0 + var start = Math.Max(0, Math.Min(span.Start, snapshot.Length - length)); + + // make sure length is smaller than snapshot.Length which can happen if start == 0 + return new SnapshotSpan(snapshot, start, Math.Min(start + length, snapshot.Length) - start); + } + + protected abstract TTag CreateTag(DiagnosticData diagnostic); + } +} \ No newline at end of file diff --git a/src/EditorFeatures/Core/Implementation/Diagnostics/DiagnosticsSquiggleTaggerProvider.cs b/src/EditorFeatures/Core/Implementation/Diagnostics/DiagnosticsSquiggleTaggerProvider.cs index 76baf6feee876..40c165a6c8dc1 100644 --- a/src/EditorFeatures/Core/Implementation/Diagnostics/DiagnosticsSquiggleTaggerProvider.cs +++ b/src/EditorFeatures/Core/Implementation/Diagnostics/DiagnosticsSquiggleTaggerProvider.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Generic; +using System.Collections.Immutable; using System.ComponentModel.Composition; using System.Linq; using Microsoft.CodeAnalysis.Diagnostics; @@ -10,7 +11,6 @@ using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.Shared.Options; using Microsoft.CodeAnalysis.Shared.TestHooks; -using Microsoft.VisualStudio.Text; using Microsoft.VisualStudio.Text.Adornments; using Microsoft.VisualStudio.Text.Tagging; using Microsoft.VisualStudio.Utilities; @@ -21,11 +21,12 @@ namespace Microsoft.CodeAnalysis.Editor.Implementation.Diagnostics [Export(typeof(ITaggerProvider))] [ContentType(ContentTypeNames.RoslynContentType)] [TagType(typeof(IErrorTag))] - internal partial class DiagnosticsSquiggleTaggerProvider : AbstractDiagnosticsTaggerProvider + internal partial class DiagnosticsSquiggleTaggerProvider : AbstractDiagnosticsAdornmentTaggerProvider { private readonly bool _blueSquiggleForBuildDiagnostic; - private static readonly IEnumerable> s_tagSourceOptions = new[] { EditorComponentOnOffOptions.Tagger, InternalFeatureOnOffOptions.Squiggles, ServiceComponentOnOffOptions.DiagnosticProvider }; + private static readonly IEnumerable> s_tagSourceOptions = + ImmutableArray.Create(EditorComponentOnOffOptions.Tagger, InternalFeatureOnOffOptions.Squiggles, ServiceComponentOnOffOptions.DiagnosticProvider); protected internal override IEnumerable> Options => s_tagSourceOptions; [ImportingConstructor] @@ -34,13 +35,11 @@ public DiagnosticsSquiggleTaggerProvider( IDiagnosticService diagnosticService, IForegroundNotificationService notificationService, [ImportMany] IEnumerable> listeners) - : base(diagnosticService, notificationService, new AggregateAsynchronousOperationListener(listeners, FeatureAttribute.ErrorSquiggles)) + : base(diagnosticService, notificationService, listeners) { _blueSquiggleForBuildDiagnostic = optionService.GetOption(InternalDiagnosticsOptions.BlueSquiggleForBuildDiagnostic); } - protected internal override bool IsEnabled => true; - protected internal override bool IncludeDiagnostic(DiagnosticData diagnostic) { var isUnnecessary = (diagnostic.Severity == DiagnosticSeverity.Hidden && diagnostic.CustomTags.Contains(WellKnownDiagnosticTags.Unnecessary)); @@ -50,40 +49,7 @@ protected internal override bool IncludeDiagnostic(DiagnosticData diagnostic) !string.IsNullOrWhiteSpace(diagnostic.Message); } - protected internal override ITagSpan CreateTagSpan(bool isLiveUpdate, SnapshotSpan span, DiagnosticData data) - { - var errorTag = CreateErrorTag(data); - if (errorTag == null) - { - return null; - } - - // Live update squiggles have to be at least 1 character long. - var minimumLength = isLiveUpdate ? 1 : 0; - var adjustedSpan = AdjustSnapshotSpan(span, minimumLength); - if (adjustedSpan.Length == 0) - { - return null; - } - - return new TagSpan(adjustedSpan, errorTag); - } - - private static SnapshotSpan AdjustSnapshotSpan(SnapshotSpan span, int minimumLength) - { - var snapshot = span.Snapshot; - - // new length - var length = Math.Max(span.Length, minimumLength); - - // make sure start + length is smaller than snapshot.Length and start is >= 0 - var start = Math.Max(0, Math.Min(span.Start, snapshot.Length - length)); - - // make sure length is smaller than snapshot.Length which can happen if start == 0 - return new SnapshotSpan(snapshot, start, Math.Min(start + length, snapshot.Length) - start); - } - - private IErrorTag CreateErrorTag(DiagnosticData diagnostic) + protected override IErrorTag CreateTag(DiagnosticData diagnostic) { Contract.Requires(!string.IsNullOrWhiteSpace(diagnostic.Message)); var errorType = GetErrorTypeFromDiagnostic(diagnostic); From 10e9e6be89f4eed92f9db18500b4c4a352f81f7d Mon Sep 17 00:00:00 2001 From: CyrusNajmabadi Date: Tue, 14 Jun 2016 11:47:23 -0700 Subject: [PATCH 07/36] Add support for the new 'DiagnosticSeverity.Suggestion' category --- .../Diagnostic/DiagnosticDescriptor.cs | 2 + .../Portable/Diagnostic/ReportDiagnostic.cs | 9 ++- .../Core/Portable/PublicAPI.Unshipped.txt | 1 + src/EditorFeatures/Core/EditorFeatures.csproj | 12 ++- .../AbstractAdornmentManagerProvider.cs | 51 ++++++++++++ .../AdornmentManager.cs | 2 +- .../GraphicsResult.cs | 16 +--- .../Implementation/Adornments/GraphicsTag.cs | 43 ++++++++++ ...tractDiagnosticsAdornmentTaggerProvider.cs | 2 +- ...DiagnosticsSuggestionLineTaggerProvider.cs | 64 +++++++++++++++ .../SuggestionLineAdornmentManagerProvider.cs | 44 +++++++++++ .../Diagnostics/SuggestionLineTag.cs | 79 +++++++++++++++++++ .../LineSeparators/GraphicsTag.cs | 19 ----- .../LineSeparatorAdornmentManagerProvider.cs | 37 +++------ .../LineSeparators/LineSeparatorTag.cs | 25 +----- ...oveUnnecessaryImportsDiagnosticAnalyzer.cs | 9 ++- .../Suppression/SuppressionHelpers.cs | 1 + ...nnecessaryImportsDiagnosticAnalyzerBase.cs | 27 ++++--- .../BaseDiagnosticIncrementalAnalyzer.cs | 2 + ...oveUnnecessaryImportsDiagnosticAnalyzer.vb | 6 +- ...DiagnosticListTable.LiveTableDataSource.cs | 12 ++- .../AbstractCodeStyleOptionViewModel.cs | 1 + .../NamingPreferences/EnforcementLevel.cs | 1 + .../NamingRuleDialogViewModel.cs | 1 + ...ngRuleTreeItemViewModel.TreeDisplayItem.cs | 1 + .../Portable/CodeStyle/CodeStyleOption.cs | 3 + .../Portable/CodeStyle/NotificationOption.cs | 1 + .../Core/Portable/PublicAPI.Unshipped.txt | 1 + 28 files changed, 368 insertions(+), 104 deletions(-) create mode 100644 src/EditorFeatures/Core/Implementation/Adornments/AbstractAdornmentManagerProvider.cs rename src/EditorFeatures/Core/Implementation/{LineSeparators => Adornments}/AdornmentManager.cs (99%) rename src/EditorFeatures/Core/Implementation/{LineSeparators => Adornments}/GraphicsResult.cs (65%) create mode 100644 src/EditorFeatures/Core/Implementation/Adornments/GraphicsTag.cs create mode 100644 src/EditorFeatures/Core/Implementation/Diagnostics/DiagnosticsSuggestionLineTaggerProvider.cs create mode 100644 src/EditorFeatures/Core/Implementation/Diagnostics/SuggestionLineAdornmentManagerProvider.cs create mode 100644 src/EditorFeatures/Core/Implementation/Diagnostics/SuggestionLineTag.cs delete mode 100644 src/EditorFeatures/Core/Implementation/LineSeparators/GraphicsTag.cs diff --git a/src/Compilers/Core/Portable/Diagnostic/DiagnosticDescriptor.cs b/src/Compilers/Core/Portable/Diagnostic/DiagnosticDescriptor.cs index bd6f6c12d2518..3efe87bc171fc 100644 --- a/src/Compilers/Core/Portable/Diagnostic/DiagnosticDescriptor.cs +++ b/src/Compilers/Core/Portable/Diagnostic/DiagnosticDescriptor.cs @@ -227,6 +227,8 @@ private static ReportDiagnostic MapSeverityToReport(DiagnosticSeverity severity) return ReportDiagnostic.Warn; case DiagnosticSeverity.Error: return ReportDiagnostic.Error; + case DiagnosticSeverity.Suggestion: + return ReportDiagnostic.Suggestion; default: throw ExceptionUtilities.Unreachable; } diff --git a/src/Compilers/Core/Portable/Diagnostic/ReportDiagnostic.cs b/src/Compilers/Core/Portable/Diagnostic/ReportDiagnostic.cs index c6b0b86fc5a35..655962d59042c 100644 --- a/src/Compilers/Core/Portable/Diagnostic/ReportDiagnostic.cs +++ b/src/Compilers/Core/Portable/Diagnostic/ReportDiagnostic.cs @@ -35,6 +35,11 @@ public enum ReportDiagnostic /// /// Suppress a diagnostic. /// - Suppress = 5 + Suppress = 5, + + /// + /// Report a diagnostic as a suggestion. + /// + Suggestion = 6, } -} +} \ No newline at end of file diff --git a/src/Compilers/Core/Portable/PublicAPI.Unshipped.txt b/src/Compilers/Core/Portable/PublicAPI.Unshipped.txt index 3c0fa7fc6d940..7de6d988fda0c 100644 --- a/src/Compilers/Core/Portable/PublicAPI.Unshipped.txt +++ b/src/Compilers/Core/Portable/PublicAPI.Unshipped.txt @@ -137,6 +137,7 @@ Microsoft.CodeAnalysis.OperationKind.WithStatement = 82 -> Microsoft.CodeAnalysi Microsoft.CodeAnalysis.OperationKind.YieldBreakStatement = 12 -> Microsoft.CodeAnalysis.OperationKind Microsoft.CodeAnalysis.OperationKind.YieldReturnStatement = 16 -> Microsoft.CodeAnalysis.OperationKind Microsoft.CodeAnalysis.PortableExecutableReference.GetMetadata() -> Microsoft.CodeAnalysis.Metadata +Microsoft.CodeAnalysis.ReportDiagnostic.Suggestion = 6 -> Microsoft.CodeAnalysis.ReportDiagnostic Microsoft.CodeAnalysis.SemanticModel.GetOperation(Microsoft.CodeAnalysis.SyntaxNode node, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> Microsoft.CodeAnalysis.IOperation Microsoft.CodeAnalysis.Semantics.ArgumentKind Microsoft.CodeAnalysis.Semantics.ArgumentKind.DefaultValue = 4 -> Microsoft.CodeAnalysis.Semantics.ArgumentKind diff --git a/src/EditorFeatures/Core/EditorFeatures.csproj b/src/EditorFeatures/Core/EditorFeatures.csproj index f4e1f7e16d199..9633acc52d83f 100644 --- a/src/EditorFeatures/Core/EditorFeatures.csproj +++ b/src/EditorFeatures/Core/EditorFeatures.csproj @@ -258,6 +258,7 @@ + @@ -265,6 +266,9 @@ + + + @@ -283,6 +287,7 @@ + @@ -536,10 +541,9 @@ - - - - + + + diff --git a/src/EditorFeatures/Core/Implementation/Adornments/AbstractAdornmentManagerProvider.cs b/src/EditorFeatures/Core/Implementation/Adornments/AbstractAdornmentManagerProvider.cs new file mode 100644 index 0000000000000..34865b81c89f6 --- /dev/null +++ b/src/EditorFeatures/Core/Implementation/Adornments/AbstractAdornmentManagerProvider.cs @@ -0,0 +1,51 @@ +// 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.ComponentModel.Composition; +using Microsoft.CodeAnalysis.Editor.Shared.Extensions; +using Microsoft.CodeAnalysis.Editor.Shared.Options; +using Microsoft.CodeAnalysis.Shared.TestHooks; +using Microsoft.VisualStudio.Text.Editor; +using Microsoft.VisualStudio.Text.Tagging; +using Microsoft.VisualStudio.Utilities; + +namespace Microsoft.CodeAnalysis.Editor.Implementation.Adornments +{ + internal abstract class AbstractAdornmentManagerProvider : + IWpfTextViewCreationListener + where TTag : GraphicsTag + { + private readonly IViewTagAggregatorFactoryService _tagAggregatorFactoryService; + private readonly IAsynchronousOperationListener _asyncListener; + + protected AbstractAdornmentManagerProvider( + IViewTagAggregatorFactoryService tagAggregatorFactoryService, + IEnumerable> asyncListeners) + { + _tagAggregatorFactoryService = tagAggregatorFactoryService; + _asyncListener = new AggregateAsynchronousOperationListener( + asyncListeners, + this.FeatureAttributeName); + } + + protected abstract string FeatureAttributeName { get; } + protected abstract string AdornmentLayerName { get; } + + public void TextViewCreated(IWpfTextView textView) + { + if (textView == null) + { + throw new ArgumentNullException(nameof(textView)); + } + + if (!textView.TextBuffer.GetOption(EditorComponentOnOffOptions.Adornment)) + { + return; + } + + // the manager keeps itself alive by listening to text view events. + AdornmentManager.Create(textView, _tagAggregatorFactoryService, _asyncListener, AdornmentLayerName); + } + } +} diff --git a/src/EditorFeatures/Core/Implementation/LineSeparators/AdornmentManager.cs b/src/EditorFeatures/Core/Implementation/Adornments/AdornmentManager.cs similarity index 99% rename from src/EditorFeatures/Core/Implementation/LineSeparators/AdornmentManager.cs rename to src/EditorFeatures/Core/Implementation/Adornments/AdornmentManager.cs index ffac806cbbd23..46f84eae7cd2d 100644 --- a/src/EditorFeatures/Core/Implementation/LineSeparators/AdornmentManager.cs +++ b/src/EditorFeatures/Core/Implementation/Adornments/AdornmentManager.cs @@ -11,7 +11,7 @@ using Microsoft.VisualStudio.Text.Tagging; using Roslyn.Utilities; -namespace Microsoft.CodeAnalysis.Editor.Implementation.LineSeparators +namespace Microsoft.CodeAnalysis.Editor.Implementation.Adornments { /// /// UI manager for graphic overlay tags. These tags will simply paint something related to the text. diff --git a/src/EditorFeatures/Core/Implementation/LineSeparators/GraphicsResult.cs b/src/EditorFeatures/Core/Implementation/Adornments/GraphicsResult.cs similarity index 65% rename from src/EditorFeatures/Core/Implementation/LineSeparators/GraphicsResult.cs rename to src/EditorFeatures/Core/Implementation/Adornments/GraphicsResult.cs index 28dfd4cf38246..8958262e9c75d 100644 --- a/src/EditorFeatures/Core/Implementation/LineSeparators/GraphicsResult.cs +++ b/src/EditorFeatures/Core/Implementation/Adornments/GraphicsResult.cs @@ -3,16 +3,16 @@ using System; using System.Windows; -namespace Microsoft.CodeAnalysis.Editor.Implementation.LineSeparators +namespace Microsoft.CodeAnalysis.Editor.Implementation.Adornments { internal class GraphicsResult : IDisposable { - private readonly UIElement _visualElement; + public UIElement VisualElement { get; } private Action _dispose; public GraphicsResult(UIElement visualElement, Action dispose) { - _visualElement = visualElement; + VisualElement = visualElement; _dispose = dispose; } @@ -25,13 +25,5 @@ public void Dispose() _dispose = null; } } - - public UIElement VisualElement - { - get - { - return _visualElement; - } - } } -} +} \ No newline at end of file diff --git a/src/EditorFeatures/Core/Implementation/Adornments/GraphicsTag.cs b/src/EditorFeatures/Core/Implementation/Adornments/GraphicsTag.cs new file mode 100644 index 0000000000000..0f30dfadc58af --- /dev/null +++ b/src/EditorFeatures/Core/Implementation/Adornments/GraphicsTag.cs @@ -0,0 +1,43 @@ +// 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.Windows.Media; +using Microsoft.VisualStudio.Text.Editor; +using Microsoft.VisualStudio.Text.Tagging; + +namespace Microsoft.CodeAnalysis.Editor.Implementation.Adornments +{ + /// + /// This needs to be public for testing the AdornmentManager + /// + internal abstract class GraphicsTag : ITag + { + protected static SolidColorBrush VerticalRuleBrush; + protected static Color VerticalRuleColor; + + protected virtual void Initialize(IWpfTextView view) + { + if (VerticalRuleBrush != null) + { + return; + } + + // TODO: Refresh this when the user changes fonts and colors + + // TODO: Get from resources + var lightGray = Color.FromRgb(0xE0, 0xE0, 0xE0); + + var outliningForegroundBrush = view.VisualElement.TryFindResource("outlining.verticalrule.foreground") as SolidColorBrush; + var darkGray = outliningForegroundBrush != null + ? outliningForegroundBrush.Color + : lightGray; + + VerticalRuleColor = darkGray; + VerticalRuleBrush = new SolidColorBrush(VerticalRuleColor); + } + + /// + /// This method allows corresponding adornment manager to ask for a graphical glyph. + /// + public abstract GraphicsResult GetGraphics(IWpfTextView view, Geometry bounds); + } +} \ No newline at end of file diff --git a/src/EditorFeatures/Core/Implementation/Diagnostics/AbstractDiagnosticsAdornmentTaggerProvider.cs b/src/EditorFeatures/Core/Implementation/Diagnostics/AbstractDiagnosticsAdornmentTaggerProvider.cs index 74e7cb1c9c884..6f8bdc006f6d2 100644 --- a/src/EditorFeatures/Core/Implementation/Diagnostics/AbstractDiagnosticsAdornmentTaggerProvider.cs +++ b/src/EditorFeatures/Core/Implementation/Diagnostics/AbstractDiagnosticsAdornmentTaggerProvider.cs @@ -42,7 +42,7 @@ protected sealed internal override ITagSpan CreateTagSpan( return new TagSpan(adjustedSpan, errorTag); } - private static SnapshotSpan AdjustSnapshotSpan(SnapshotSpan span, int minimumLength) + protected virtual SnapshotSpan AdjustSnapshotSpan(SnapshotSpan span, int minimumLength) { var snapshot = span.Snapshot; diff --git a/src/EditorFeatures/Core/Implementation/Diagnostics/DiagnosticsSuggestionLineTaggerProvider.cs b/src/EditorFeatures/Core/Implementation/Diagnostics/DiagnosticsSuggestionLineTaggerProvider.cs new file mode 100644 index 0000000000000..e7b3782970aed --- /dev/null +++ b/src/EditorFeatures/Core/Implementation/Diagnostics/DiagnosticsSuggestionLineTaggerProvider.cs @@ -0,0 +1,64 @@ +// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.ComponentModel.Composition; +using System.Linq; +using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.Editor.Implementation.EditAndContinue; +using Microsoft.CodeAnalysis.Editor.Shared.Options; +using Microsoft.CodeAnalysis.Options; +using Microsoft.CodeAnalysis.Shared.Options; +using Microsoft.CodeAnalysis.Shared.TestHooks; +using Microsoft.VisualStudio.Text; +using Microsoft.VisualStudio.Text.Adornments; +using Microsoft.VisualStudio.Text.Tagging; +using Microsoft.VisualStudio.Utilities; +using Roslyn.Utilities; + +namespace Microsoft.CodeAnalysis.Editor.Implementation.Diagnostics +{ + [Export(typeof(ITaggerProvider))] + [ContentType(ContentTypeNames.RoslynContentType)] + [TagType(typeof(SuggestionLineTag))] + internal partial class DiagnosticsSuggestionLineTaggerProvider : + AbstractDiagnosticsAdornmentTaggerProvider + { + private static readonly IEnumerable> s_tagSourceOptions = + ImmutableArray.Create(EditorComponentOnOffOptions.Tagger, InternalFeatureOnOffOptions.Squiggles, ServiceComponentOnOffOptions.DiagnosticProvider); + protected internal override IEnumerable> Options => s_tagSourceOptions; + + [ImportingConstructor] + public DiagnosticsSuggestionLineTaggerProvider( + IOptionService optionService, + IDiagnosticService diagnosticService, + IForegroundNotificationService notificationService, + [ImportMany] IEnumerable> listeners) + : base(diagnosticService, notificationService, listeners) + { + } + + protected internal override bool IncludeDiagnostic(DiagnosticData diagnostic) + { + return diagnostic.Severity == DiagnosticSeverity.Suggestion; + } + + protected override SuggestionLineTag CreateTag(DiagnosticData diagnostic) + { + return SuggestionLineTag.Instance; + } + + protected override SnapshotSpan AdjustSnapshotSpan(SnapshotSpan snapshotSpan, int minimumLength) + { + snapshotSpan = base.AdjustSnapshotSpan(snapshotSpan, minimumLength); + + // Cap a suggestion line length at two characters. + var span = snapshotSpan.Span; + snapshotSpan = new SnapshotSpan(snapshotSpan.Snapshot, + new Span(span.Start, Math.Min(span.Length, 2))); + + return snapshotSpan; + } + } +} \ No newline at end of file diff --git a/src/EditorFeatures/Core/Implementation/Diagnostics/SuggestionLineAdornmentManagerProvider.cs b/src/EditorFeatures/Core/Implementation/Diagnostics/SuggestionLineAdornmentManagerProvider.cs new file mode 100644 index 0000000000000..ea372a98996ac --- /dev/null +++ b/src/EditorFeatures/Core/Implementation/Diagnostics/SuggestionLineAdornmentManagerProvider.cs @@ -0,0 +1,44 @@ +// 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.ComponentModel.Composition; +using Microsoft.CodeAnalysis.Editor.Implementation.Adornments; +using Microsoft.CodeAnalysis.Shared.TestHooks; +using Microsoft.VisualStudio.Text.Editor; +using Microsoft.VisualStudio.Text.Tagging; +using Microsoft.VisualStudio.Utilities; + +namespace Microsoft.CodeAnalysis.Editor.Implementation.Diagnostics +{ + /// + /// This factory is called to create the view service that will manage line separators. + /// + [Export(typeof(IWpfTextViewCreationListener))] + [ContentType(ContentTypeNames.RoslynContentType)] + [TextViewRole(PredefinedTextViewRoles.Document)] + internal class SuggestionLineAdornmentManagerProvider : + AbstractAdornmentManagerProvider + { + private const string LayerName = "RoslynSuggestions"; + + [Export] + [Name(LayerName)] + [ContentType(ContentTypeNames.RoslynContentType)] + [Order(After = PredefinedAdornmentLayers.Selection, Before = PredefinedAdornmentLayers.Squiggle)] +#pragma warning disable 0169 + private readonly AdornmentLayerDefinition _lineSeparatorLayer; +#pragma warning restore 0169 + + [ImportingConstructor] + public SuggestionLineAdornmentManagerProvider( + IViewTagAggregatorFactoryService tagAggregatorFactoryService, + [ImportMany] IEnumerable> asyncListeners) + : base(tagAggregatorFactoryService, asyncListeners) + { + } + + protected override string FeatureAttributeName => FeatureAttribute.ErrorSquiggles; + protected override string AdornmentLayerName => LayerName; + } +} \ No newline at end of file diff --git a/src/EditorFeatures/Core/Implementation/Diagnostics/SuggestionLineTag.cs b/src/EditorFeatures/Core/Implementation/Diagnostics/SuggestionLineTag.cs new file mode 100644 index 0000000000000..de1f1af6a0f4e --- /dev/null +++ b/src/EditorFeatures/Core/Implementation/Diagnostics/SuggestionLineTag.cs @@ -0,0 +1,79 @@ +// 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.Windows; +using System.Windows.Controls; +using System.Windows.Media; +using System.Windows.Shapes; +using Microsoft.CodeAnalysis.Editor.Implementation.Adornments; +using Microsoft.CodeAnalysis.Editor.Implementation.LineSeparators; +using Microsoft.VisualStudio.Text.Editor; + +namespace Microsoft.CodeAnalysis.Editor.Implementation.Diagnostics +{ + /// + /// Tag that specifies line separator. + /// + internal class SuggestionLineTag : GraphicsTag + { + public static readonly SuggestionLineTag Instance = new SuggestionLineTag(); + + private static Pen s_pen; + + protected override void Initialize(IWpfTextView view) + { + base.Initialize(view); + + if (s_pen != null) + { + return; + } + + var color = Color.FromArgb(100, VerticalRuleColor.R, VerticalRuleColor.G, VerticalRuleColor.B); + s_pen = new Pen + { + Brush = new SolidColorBrush(color), + DashStyle = DashStyles.Dot, + Thickness = 2, + }; + } + + /// + /// Creates a very long line at the bottom of bounds. + /// + public override GraphicsResult GetGraphics(IWpfTextView view, Geometry bounds) + { + Initialize(view); + + var line = new Line + { + Width = bounds.Bounds.Width, + X2 = bounds.Bounds.BottomLeft.X, + Y1 = bounds.Bounds.BottomLeft.Y - 2, + X1 = bounds.Bounds.BottomRight.X, + Y2 = bounds.Bounds.BottomRight.Y - 2, + }; + RenderOptions.SetEdgeMode(line, EdgeMode.Aliased); + + ApplyPen(line, s_pen); + + return new GraphicsResult(line, null); + } + + public static void ApplyPen(Shape shape, Pen pen) + { + shape.Stroke = pen.Brush; + shape.StrokeThickness = pen.Thickness; + shape.StrokeDashCap = pen.DashCap; + if (pen.DashStyle != null) + { + shape.StrokeDashArray = pen.DashStyle.Dashes; + shape.StrokeDashOffset = pen.DashStyle.Offset; + } + shape.StrokeStartLineCap = pen.StartLineCap; + shape.StrokeEndLineCap = pen.EndLineCap; + shape.StrokeLineJoin = pen.LineJoin; + shape.StrokeMiterLimit = pen.MiterLimit; + } + } +} \ No newline at end of file diff --git a/src/EditorFeatures/Core/Implementation/LineSeparators/GraphicsTag.cs b/src/EditorFeatures/Core/Implementation/LineSeparators/GraphicsTag.cs deleted file mode 100644 index 4b254be054157..0000000000000 --- a/src/EditorFeatures/Core/Implementation/LineSeparators/GraphicsTag.cs +++ /dev/null @@ -1,19 +0,0 @@ -// 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.Windows.Media; -using Microsoft.VisualStudio.Text.Editor; -using Microsoft.VisualStudio.Text.Tagging; - -namespace Microsoft.CodeAnalysis.Editor.Implementation.LineSeparators -{ - /// - /// This needs to be public for testing the AdornmentManager - /// - internal abstract class GraphicsTag : ITag - { - /// - /// This method allows corresponding adornment manager to ask for a graphical glyph. - /// - public abstract GraphicsResult GetGraphics(IWpfTextView view, Geometry bounds); - } -} diff --git a/src/EditorFeatures/Core/Implementation/LineSeparators/LineSeparatorAdornmentManagerProvider.cs b/src/EditorFeatures/Core/Implementation/LineSeparators/LineSeparatorAdornmentManagerProvider.cs index 0be0c344cecdf..728e98aff123d 100644 --- a/src/EditorFeatures/Core/Implementation/LineSeparators/LineSeparatorAdornmentManagerProvider.cs +++ b/src/EditorFeatures/Core/Implementation/LineSeparators/LineSeparatorAdornmentManagerProvider.cs @@ -3,8 +3,7 @@ using System; using System.Collections.Generic; using System.ComponentModel.Composition; -using Microsoft.CodeAnalysis.Editor.Shared.Extensions; -using Microsoft.CodeAnalysis.Editor.Shared.Options; +using Microsoft.CodeAnalysis.Editor.Implementation.Adornments; using Microsoft.CodeAnalysis.Shared.TestHooks; using Microsoft.VisualStudio.Text.Editor; using Microsoft.VisualStudio.Text.Tagging; @@ -18,15 +17,13 @@ namespace Microsoft.CodeAnalysis.Editor.Implementation.LineSeparators [Export(typeof(IWpfTextViewCreationListener))] [ContentType(ContentTypeNames.RoslynContentType)] [TextViewRole(PredefinedTextViewRoles.Document)] - internal class LineSeparatorAdornmentManagerProvider : IWpfTextViewCreationListener + internal class LineSeparatorAdornmentManagerProvider : + AbstractAdornmentManagerProvider { - internal const string AdornmentLayerName = "RoslynLineSeparator"; - - private readonly IViewTagAggregatorFactoryService _tagAggregatorFactoryService; - private readonly IAsynchronousOperationListener _asyncListener; + private const string LayerName = "RoslynLineSeparator"; [Export] - [Name(AdornmentLayerName)] + [Name(LayerName)] [ContentType(ContentTypeNames.RoslynContentType)] [Order(After = PredefinedAdornmentLayers.Selection, Before = PredefinedAdornmentLayers.Squiggle)] #pragma warning disable 0169 @@ -37,27 +34,11 @@ internal class LineSeparatorAdornmentManagerProvider : IWpfTextViewCreationListe public LineSeparatorAdornmentManagerProvider( IViewTagAggregatorFactoryService tagAggregatorFactoryService, [ImportMany] IEnumerable> asyncListeners) + : base(tagAggregatorFactoryService, asyncListeners) { - _tagAggregatorFactoryService = tagAggregatorFactoryService; - _asyncListener = new AggregateAsynchronousOperationListener( - asyncListeners, - FeatureAttribute.LineSeparators); } - public void TextViewCreated(IWpfTextView textView) - { - if (textView == null) - { - throw new ArgumentNullException(nameof(textView)); - } - - if (!textView.TextBuffer.GetOption(EditorComponentOnOffOptions.Adornment)) - { - return; - } - - // the manager keeps itself alive by listening to text view events. - AdornmentManager.Create(textView, _tagAggregatorFactoryService, _asyncListener, AdornmentLayerName); - } + protected override string FeatureAttributeName => FeatureAttribute.LineSeparators; + protected override string AdornmentLayerName => LayerName; } -} +} \ No newline at end of file diff --git a/src/EditorFeatures/Core/Implementation/LineSeparators/LineSeparatorTag.cs b/src/EditorFeatures/Core/Implementation/LineSeparators/LineSeparatorTag.cs index ae832af91e57f..f60b309e17e28 100644 --- a/src/EditorFeatures/Core/Implementation/LineSeparators/LineSeparatorTag.cs +++ b/src/EditorFeatures/Core/Implementation/LineSeparators/LineSeparatorTag.cs @@ -4,6 +4,7 @@ using System.Windows; using System.Windows.Controls; using System.Windows.Media; +using Microsoft.CodeAnalysis.Editor.Implementation.Adornments; using Microsoft.VisualStudio.Text.Editor; namespace Microsoft.CodeAnalysis.Editor.Implementation.LineSeparators @@ -15,35 +16,15 @@ internal class LineSeparatorTag : GraphicsTag { public static readonly LineSeparatorTag Instance = new LineSeparatorTag(); - private static Brush s_brush; - - private void Initialize(IWpfTextView view) - { - // TODO: Refresh this when the user changes fonts and colors - - // TODO: Get from resources - var lightGray = Color.FromRgb(0xE0, 0xE0, 0xE0); - - var outliningForegroundBrush = view.VisualElement.TryFindResource("outlining.verticalrule.foreground") as SolidColorBrush; - var darkGray = outliningForegroundBrush != null - ? outliningForegroundBrush.Color - : lightGray; - - s_brush = new SolidColorBrush(darkGray); - } - /// /// Creates a very long line at the bottom of bounds. /// public override GraphicsResult GetGraphics(IWpfTextView view, Geometry bounds) { - if (s_brush == null) - { - Initialize(view); - } + Initialize(view); var border = new Border(); - border.BorderBrush = s_brush; + border.BorderBrush = VerticalRuleBrush; border.BorderThickness = new Thickness(0, 0, 0, bottom: 1); border.Height = 1; border.Width = view.ViewportWidth; diff --git a/src/Features/CSharp/Portable/Diagnostics/Analyzers/CSharpRemoveUnnecessaryImportsDiagnosticAnalyzer.cs b/src/Features/CSharp/Portable/Diagnostics/Analyzers/CSharpRemoveUnnecessaryImportsDiagnosticAnalyzer.cs index 66482c4ea6451..e367ae7fada5e 100644 --- a/src/Features/CSharp/Portable/Diagnostics/Analyzers/CSharpRemoveUnnecessaryImportsDiagnosticAnalyzer.cs +++ b/src/Features/CSharp/Portable/Diagnostics/Analyzers/CSharpRemoveUnnecessaryImportsDiagnosticAnalyzer.cs @@ -14,7 +14,8 @@ namespace Microsoft.CodeAnalysis.CSharp.Diagnostics.RemoveUnnecessaryImports { [DiagnosticAnalyzer(LanguageNames.CSharp)] - internal sealed class CSharpRemoveUnnecessaryImportsDiagnosticAnalyzer : RemoveUnnecessaryImportsDiagnosticAnalyzerBase + internal sealed class CSharpRemoveUnnecessaryImportsDiagnosticAnalyzer : + RemoveUnnecessaryImportsDiagnosticAnalyzerBase { private static readonly LocalizableString s_TitleAndMessageFormat = new LocalizableResourceString(nameof(CSharpFeaturesResources.RemoveUnnecessaryUsingsDiagnosticTitle), CSharpFeaturesResources.ResourceManager, typeof(CSharpFeaturesResources)); @@ -24,12 +25,14 @@ protected override LocalizableString GetTitleAndMessageFormatForClassificationId return s_TitleAndMessageFormat; } - protected override IEnumerable GetUnnecessaryImports(SemanticModel semanticModel, SyntaxNode root, CancellationToken cancellationToken = default(CancellationToken)) + protected override IEnumerable GetUnnecessaryImports( + SemanticModel semanticModel, SyntaxNode root, CancellationToken cancellationToken) { return CSharpRemoveUnnecessaryImportsService.GetUnnecessaryImports(semanticModel, root, cancellationToken); } - protected override IEnumerable GetFixableDiagnosticSpans(IEnumerable nodes, SyntaxTree tree, CancellationToken cancellationToken = default(CancellationToken)) + protected override IEnumerable GetFixableDiagnosticSpans( + IEnumerable nodes, SyntaxTree tree, CancellationToken cancellationToken) { Contract.ThrowIfFalse(nodes.Any()); diff --git a/src/Features/Core/Portable/CodeFixes/Suppression/SuppressionHelpers.cs b/src/Features/Core/Portable/CodeFixes/Suppression/SuppressionHelpers.cs index 51c3527a27fa7..430d7361ef26d 100644 --- a/src/Features/Core/Portable/CodeFixes/Suppression/SuppressionHelpers.cs +++ b/src/Features/Core/Portable/CodeFixes/Suppression/SuppressionHelpers.cs @@ -43,6 +43,7 @@ private static bool CanBeSuppressedOrUnsuppressed(Diagnostic diagnostic, bool ch case DiagnosticSeverity.Error: case DiagnosticSeverity.Warning: case DiagnosticSeverity.Info: + case DiagnosticSeverity.Suggestion: // We allow suppressions for all the remaining configurable, non-hidden diagnostics. // Note that compiler errors are not configurable by default, so only analyzer errors are suppressable. return true; diff --git a/src/Features/Core/Portable/Diagnostics/Analyzers/RemoveUnnecessaryImportsDiagnosticAnalyzerBase.cs b/src/Features/Core/Portable/Diagnostics/Analyzers/RemoveUnnecessaryImportsDiagnosticAnalyzerBase.cs index 43d5c2f8cca42..645a8281024d3 100644 --- a/src/Features/Core/Portable/Diagnostics/Analyzers/RemoveUnnecessaryImportsDiagnosticAnalyzerBase.cs +++ b/src/Features/Core/Portable/Diagnostics/Analyzers/RemoveUnnecessaryImportsDiagnosticAnalyzerBase.cs @@ -11,7 +11,8 @@ namespace Microsoft.CodeAnalysis.Diagnostics.RemoveUnnecessaryImports { - internal abstract class RemoveUnnecessaryImportsDiagnosticAnalyzerBase : DiagnosticAnalyzer, IBuiltInAnalyzer + internal abstract class RemoveUnnecessaryImportsDiagnosticAnalyzerBase : + DiagnosticAnalyzer, IBuiltInAnalyzer { // NOTE: This is a trigger diagnostic, which doesn't show up in the ruleset editor and hence doesn't need a conventional IDE Diagnostic ID string. internal const string DiagnosticFixableId = "RemoveUnnecessaryImportsFixable"; @@ -69,14 +70,17 @@ public override void Initialize(AnalysisContext context) private void AnalyzeSemanticModel(SemanticModelAnalysisContext context) { var tree = context.SemanticModel.SyntaxTree; + var cancellationToken = context.CancellationToken; + var root = tree.GetRoot(); - var unnecessaryImports = GetUnnecessaryImports(context.SemanticModel, root); + var unnecessaryImports = GetUnnecessaryImports(context.SemanticModel, root, cancellationToken); if (unnecessaryImports != null && unnecessaryImports.Any()) { Func getLastTokenFunc = GetLastTokenDelegateForContiguousSpans(); var contiguousSpans = unnecessaryImports.GetContiguousSpans(getLastTokenFunc); - var diagnostics = CreateClassificationDiagnostics(contiguousSpans, tree).Concat( - CreateFixableDiagnostics(unnecessaryImports, tree)); + var diagnostics = + CreateClassificationDiagnostics(contiguousSpans, tree, cancellationToken).Concat( + CreateFixableDiagnostics(unnecessaryImports, tree, cancellationToken)); foreach (var diagnostic in diagnostics) { @@ -85,14 +89,17 @@ private void AnalyzeSemanticModel(SemanticModelAnalysisContext context) } } - protected abstract IEnumerable GetUnnecessaryImports(SemanticModel semanticModel, SyntaxNode root, CancellationToken cancellationToken = default(CancellationToken)); + protected abstract IEnumerable GetUnnecessaryImports( + SemanticModel semanticModel, SyntaxNode root, CancellationToken cancellationToken); + protected virtual Func GetLastTokenDelegateForContiguousSpans() { return null; } // Create one diagnostic for each unnecessary span that will be classified as Unnecessary - private IEnumerable CreateClassificationDiagnostics(IEnumerable contiguousSpans, SyntaxTree tree, CancellationToken cancellationToken = default(CancellationToken)) + private IEnumerable CreateClassificationDiagnostics( + IEnumerable contiguousSpans, SyntaxTree tree, CancellationToken cancellationToken) { foreach (var span in contiguousSpans) { @@ -105,9 +112,11 @@ protected virtual Func GetLastTokenDelegateForContiguou } } - protected abstract IEnumerable GetFixableDiagnosticSpans(IEnumerable nodes, SyntaxTree tree, CancellationToken cancellationToken = default(CancellationToken)); + protected abstract IEnumerable GetFixableDiagnosticSpans( + IEnumerable nodes, SyntaxTree tree, CancellationToken cancellationToken); - private IEnumerable CreateFixableDiagnostics(IEnumerable nodes, SyntaxTree tree, CancellationToken cancellationToken = default(CancellationToken)) + private IEnumerable CreateFixableDiagnostics( + IEnumerable nodes, SyntaxTree tree, CancellationToken cancellationToken) { var spans = GetFixableDiagnosticSpans(nodes, tree, cancellationToken); @@ -122,4 +131,4 @@ public DiagnosticAnalyzerCategory GetAnalyzerCategory() return DiagnosticAnalyzerCategory.SemanticDocumentAnalysis; } } -} +} \ No newline at end of file diff --git a/src/Features/Core/Portable/Diagnostics/BaseDiagnosticIncrementalAnalyzer.cs b/src/Features/Core/Portable/Diagnostics/BaseDiagnosticIncrementalAnalyzer.cs index affd488b5e8c3..71bf490fbd441 100644 --- a/src/Features/Core/Portable/Diagnostics/BaseDiagnosticIncrementalAnalyzer.cs +++ b/src/Features/Core/Portable/Diagnostics/BaseDiagnosticIncrementalAnalyzer.cs @@ -251,6 +251,8 @@ protected static ReportDiagnostic MapSeverityToReport(DiagnosticSeverity severit return ReportDiagnostic.Warn; case DiagnosticSeverity.Error: return ReportDiagnostic.Error; + case DiagnosticSeverity.Suggestion: + return ReportDiagnostic.Suggestion; default: throw ExceptionUtilities.Unreachable; } diff --git a/src/Features/VisualBasic/Portable/Diagnostics/Analyzers/VisualBasicRemoveUnnecessaryImportsDiagnosticAnalyzer.vb b/src/Features/VisualBasic/Portable/Diagnostics/Analyzers/VisualBasicRemoveUnnecessaryImportsDiagnosticAnalyzer.vb index a9e6886a04e8c..9c2ed070d625b 100644 --- a/src/Features/VisualBasic/Portable/Diagnostics/Analyzers/VisualBasicRemoveUnnecessaryImportsDiagnosticAnalyzer.vb +++ b/src/Features/VisualBasic/Portable/Diagnostics/Analyzers/VisualBasicRemoveUnnecessaryImportsDiagnosticAnalyzer.vb @@ -18,11 +18,13 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Diagnostics.RemoveUnnecessaryImport Return s_TitleAndMessageFormat End Function - Protected Overrides Function GetUnnecessaryImports(semanticModel As SemanticModel, root As SyntaxNode, Optional cancellationToken As CancellationToken = Nothing) As IEnumerable(Of SyntaxNode) + Protected Overrides Function GetUnnecessaryImports( + semanticModel As SemanticModel, root As SyntaxNode, cancellationToken As CancellationToken) As IEnumerable(Of SyntaxNode) Return VisualBasicRemoveUnnecessaryImportsService.GetUnnecessaryImports(semanticModel, root, cancellationToken) End Function - Protected Overrides Function GetFixableDiagnosticSpans(nodes As IEnumerable(Of SyntaxNode), tree As SyntaxTree, Optional cancellationToken As CancellationToken = Nothing) As IEnumerable(Of TextSpan) + Protected Overrides Function GetFixableDiagnosticSpans( + nodes As IEnumerable(Of SyntaxNode), tree As SyntaxTree, cancellationToken As CancellationToken) As IEnumerable(Of TextSpan) ' Create one fixable diagnostic that contains the entire Imports list. Return SpecializedCollections.SingletonEnumerable(Of TextSpan)(tree.GetCompilationUnitRoot().Imports.GetContainedSpan()) End Function diff --git a/src/VisualStudio/Core/Def/Implementation/TableDataSource/VisualStudioBaseDiagnosticListTable.LiveTableDataSource.cs b/src/VisualStudio/Core/Def/Implementation/TableDataSource/VisualStudioBaseDiagnosticListTable.LiveTableDataSource.cs index 86fdfa2a51658..eea9a6876197f 100644 --- a/src/VisualStudio/Core/Def/Implementation/TableDataSource/VisualStudioBaseDiagnosticListTable.LiveTableDataSource.cs +++ b/src/VisualStudio/Core/Def/Implementation/TableDataSource/VisualStudioBaseDiagnosticListTable.LiveTableDataSource.cs @@ -179,7 +179,17 @@ private static bool ShouldInclude(DiagnosticData diagnostic) return false; } - return diagnostic?.Severity != DiagnosticSeverity.Hidden; + switch (diagnostic.Severity) + { + case DiagnosticSeverity.Info: + case DiagnosticSeverity.Warning: + case DiagnosticSeverity.Error: + return true; + case DiagnosticSeverity.Hidden: + case DiagnosticSeverity.Suggestion: + default: + return false; + } } private static IEnumerable> Order(IEnumerable> groupedItems) diff --git a/src/VisualStudio/Core/Impl/Options/AbstractCodeStyleOptionViewModel.cs b/src/VisualStudio/Core/Impl/Options/AbstractCodeStyleOptionViewModel.cs index b39519ceec168..59094c872ecca 100644 --- a/src/VisualStudio/Core/Impl/Options/AbstractCodeStyleOptionViewModel.cs +++ b/src/VisualStudio/Core/Impl/Options/AbstractCodeStyleOptionViewModel.cs @@ -73,6 +73,7 @@ private static List GetDefaultNotifications() { new NotificationOptionViewModel(NotificationOption.None, KnownMonikers.None), new NotificationOptionViewModel(NotificationOption.Info, KnownMonikers.StatusInformation), + new NotificationOptionViewModel(NotificationOption.Suggestion, KnownMonikers.None), new NotificationOptionViewModel(NotificationOption.Warning, KnownMonikers.StatusWarning), new NotificationOptionViewModel(NotificationOption.Error, KnownMonikers.StatusError) }; diff --git a/src/VisualStudio/Core/Impl/Options/Style/NamingPreferences/EnforcementLevel.cs b/src/VisualStudio/Core/Impl/Options/Style/NamingPreferences/EnforcementLevel.cs index 6cccc15db4c1a..89d0feb914d7a 100644 --- a/src/VisualStudio/Core/Impl/Options/Style/NamingPreferences/EnforcementLevel.cs +++ b/src/VisualStudio/Core/Impl/Options/Style/NamingPreferences/EnforcementLevel.cs @@ -16,6 +16,7 @@ public EnforcementLevel(DiagnosticSeverity severity) Value = severity; switch (severity) { + case DiagnosticSeverity.Suggestion: case DiagnosticSeverity.Hidden: Name = "None"; Moniker = KnownMonikers.None; diff --git a/src/VisualStudio/Core/Impl/Options/Style/NamingPreferences/NamingRuleDialogViewModel.cs b/src/VisualStudio/Core/Impl/Options/Style/NamingPreferences/NamingRuleDialogViewModel.cs index 5102d656afd6e..3702843059f0a 100644 --- a/src/VisualStudio/Core/Impl/Options/Style/NamingPreferences/NamingRuleDialogViewModel.cs +++ b/src/VisualStudio/Core/Impl/Options/Style/NamingPreferences/NamingRuleDialogViewModel.cs @@ -47,6 +47,7 @@ public NamingRuleDialogViewModel( { new EnforcementLevel(DiagnosticSeverity.Hidden), new EnforcementLevel(DiagnosticSeverity.Info), + new EnforcementLevel(DiagnosticSeverity.Suggestion), new EnforcementLevel(DiagnosticSeverity.Warning), new EnforcementLevel(DiagnosticSeverity.Error), }; diff --git a/src/VisualStudio/Core/Impl/Options/Style/NamingPreferences/NamingRuleTreeItemViewModel.TreeDisplayItem.cs b/src/VisualStudio/Core/Impl/Options/Style/NamingPreferences/NamingRuleTreeItemViewModel.TreeDisplayItem.cs index da967283a2268..80423e4760fd4 100644 --- a/src/VisualStudio/Core/Impl/Options/Style/NamingPreferences/NamingRuleTreeItemViewModel.TreeDisplayItem.cs +++ b/src/VisualStudio/Core/Impl/Options/Style/NamingPreferences/NamingRuleTreeItemViewModel.TreeDisplayItem.cs @@ -40,6 +40,7 @@ private ImageMoniker GetMoniker() switch (EnforcementLevel.Value) { + case CodeAnalysis.DiagnosticSeverity.Suggestion: case CodeAnalysis.DiagnosticSeverity.Hidden: return KnownMonikers.None; case CodeAnalysis.DiagnosticSeverity.Info: diff --git a/src/Workspaces/Core/Portable/CodeStyle/CodeStyleOption.cs b/src/Workspaces/Core/Portable/CodeStyle/CodeStyleOption.cs index 46c0d5284a5ef..84aa39ae3577a 100644 --- a/src/Workspaces/Core/Portable/CodeStyle/CodeStyleOption.cs +++ b/src/Workspaces/Core/Portable/CodeStyle/CodeStyleOption.cs @@ -56,6 +56,9 @@ public static CodeStyleOption FromXElement(XElement element) case DiagnosticSeverity.Info: notificationOption = NotificationOption.Info; break; + case DiagnosticSeverity.Suggestion: + notificationOption = NotificationOption.Suggestion; + break; case DiagnosticSeverity.Warning: notificationOption = NotificationOption.Warning; break; diff --git a/src/Workspaces/Core/Portable/CodeStyle/NotificationOption.cs b/src/Workspaces/Core/Portable/CodeStyle/NotificationOption.cs index f9113e45c8952..5159076e3f438 100644 --- a/src/Workspaces/Core/Portable/CodeStyle/NotificationOption.cs +++ b/src/Workspaces/Core/Portable/CodeStyle/NotificationOption.cs @@ -17,6 +17,7 @@ public class NotificationOption public static readonly NotificationOption None = new NotificationOption(nameof(None), DiagnosticSeverity.Hidden); public static readonly NotificationOption Info = new NotificationOption(nameof(Info), DiagnosticSeverity.Info); + public static readonly NotificationOption Suggestion = new NotificationOption(nameof(Suggestion), DiagnosticSeverity.Suggestion); public static readonly NotificationOption Warning = new NotificationOption(nameof(Warning), DiagnosticSeverity.Warning); public static readonly NotificationOption Error = new NotificationOption(nameof(Error), DiagnosticSeverity.Error); diff --git a/src/Workspaces/Core/Portable/PublicAPI.Unshipped.txt b/src/Workspaces/Core/Portable/PublicAPI.Unshipped.txt index cc8a346a476e5..3de210177382a 100644 --- a/src/Workspaces/Core/Portable/PublicAPI.Unshipped.txt +++ b/src/Workspaces/Core/Portable/PublicAPI.Unshipped.txt @@ -45,6 +45,7 @@ static readonly Microsoft.CodeAnalysis.CodeStyle.CodeStyleOptions.QualifyPropert static readonly Microsoft.CodeAnalysis.CodeStyle.NotificationOption.Error -> Microsoft.CodeAnalysis.CodeStyle.NotificationOption static readonly Microsoft.CodeAnalysis.CodeStyle.NotificationOption.Info -> Microsoft.CodeAnalysis.CodeStyle.NotificationOption static readonly Microsoft.CodeAnalysis.CodeStyle.NotificationOption.None -> Microsoft.CodeAnalysis.CodeStyle.NotificationOption +static readonly Microsoft.CodeAnalysis.CodeStyle.NotificationOption.Suggestion -> Microsoft.CodeAnalysis.CodeStyle.NotificationOption static readonly Microsoft.CodeAnalysis.CodeStyle.NotificationOption.Warning -> Microsoft.CodeAnalysis.CodeStyle.NotificationOption virtual Microsoft.CodeAnalysis.Editing.SyntaxGenerator.MemberAccessExpression(Microsoft.CodeAnalysis.SyntaxNode expression, Microsoft.CodeAnalysis.SyntaxNode memberName) -> Microsoft.CodeAnalysis.SyntaxNode virtual Microsoft.CodeAnalysis.Workspace.UpdateGeneratedDocuments(Microsoft.CodeAnalysis.ProjectId projectId, System.Collections.Immutable.ImmutableArray documentsRemoved, System.Collections.Immutable.ImmutableArray documentsAdded) -> void \ No newline at end of file From 9315a5a47260eada793d0f523305c2825a20c09d Mon Sep 17 00:00:00 2001 From: CyrusNajmabadi Date: Tue, 14 Jun 2016 11:57:01 -0700 Subject: [PATCH 08/36] Simplify code. --- .../Core/Implementation/Adornments/GraphicsTag.cs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/EditorFeatures/Core/Implementation/Adornments/GraphicsTag.cs b/src/EditorFeatures/Core/Implementation/Adornments/GraphicsTag.cs index 0f30dfadc58af..c12e80415c9e6 100644 --- a/src/EditorFeatures/Core/Implementation/Adornments/GraphicsTag.cs +++ b/src/EditorFeatures/Core/Implementation/Adornments/GraphicsTag.cs @@ -27,9 +27,7 @@ protected virtual void Initialize(IWpfTextView view) var lightGray = Color.FromRgb(0xE0, 0xE0, 0xE0); var outliningForegroundBrush = view.VisualElement.TryFindResource("outlining.verticalrule.foreground") as SolidColorBrush; - var darkGray = outliningForegroundBrush != null - ? outliningForegroundBrush.Color - : lightGray; + var darkGray = outliningForegroundBrush?.Color ?? lightGray; VerticalRuleColor = darkGray; VerticalRuleBrush = new SolidColorBrush(VerticalRuleColor); From 324cabeea52c9d70a368244121097fc34f543bd2 Mon Sep 17 00:00:00 2001 From: CyrusNajmabadi Date: Tue, 14 Jun 2016 11:58:40 -0700 Subject: [PATCH 09/36] Remove hardcoded values. --- .../Core/Implementation/Diagnostics/SuggestionLineTag.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/EditorFeatures/Core/Implementation/Diagnostics/SuggestionLineTag.cs b/src/EditorFeatures/Core/Implementation/Diagnostics/SuggestionLineTag.cs index de1f1af6a0f4e..add6fab91ddd3 100644 --- a/src/EditorFeatures/Core/Implementation/Diagnostics/SuggestionLineTag.cs +++ b/src/EditorFeatures/Core/Implementation/Diagnostics/SuggestionLineTag.cs @@ -49,9 +49,9 @@ public override GraphicsResult GetGraphics(IWpfTextView view, Geometry bounds) { Width = bounds.Bounds.Width, X2 = bounds.Bounds.BottomLeft.X, - Y1 = bounds.Bounds.BottomLeft.Y - 2, + Y1 = bounds.Bounds.BottomLeft.Y - s_pen.Thickness, X1 = bounds.Bounds.BottomRight.X, - Y2 = bounds.Bounds.BottomRight.Y - 2, + Y2 = bounds.Bounds.BottomRight.Y - s_pen.Thickness, }; RenderOptions.SetEdgeMode(line, EdgeMode.Aliased); From cc028e811ba4f45b8c40b4edd125acea7779bde3 Mon Sep 17 00:00:00 2001 From: CyrusNajmabadi Date: Tue, 14 Jun 2016 14:17:43 -0700 Subject: [PATCH 10/36] Fix rendering of suggestion line. --- ...DiagnosticsSuggestionLineTaggerProvider.cs | 4 ---- .../SuggestionLineAdornmentManagerProvider.cs | 3 ++- .../Diagnostics/SuggestionLineTag.cs | 23 +++++++++++-------- ...SimplifyTypeNamesDiagnosticAnalyzerBase.cs | 9 ++++++++ 4 files changed, 24 insertions(+), 15 deletions(-) diff --git a/src/EditorFeatures/Core/Implementation/Diagnostics/DiagnosticsSuggestionLineTaggerProvider.cs b/src/EditorFeatures/Core/Implementation/Diagnostics/DiagnosticsSuggestionLineTaggerProvider.cs index e7b3782970aed..5920e376a47f7 100644 --- a/src/EditorFeatures/Core/Implementation/Diagnostics/DiagnosticsSuggestionLineTaggerProvider.cs +++ b/src/EditorFeatures/Core/Implementation/Diagnostics/DiagnosticsSuggestionLineTaggerProvider.cs @@ -4,18 +4,14 @@ using System.Collections.Generic; using System.Collections.Immutable; using System.ComponentModel.Composition; -using System.Linq; using Microsoft.CodeAnalysis.Diagnostics; -using Microsoft.CodeAnalysis.Editor.Implementation.EditAndContinue; using Microsoft.CodeAnalysis.Editor.Shared.Options; using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.Shared.Options; using Microsoft.CodeAnalysis.Shared.TestHooks; using Microsoft.VisualStudio.Text; -using Microsoft.VisualStudio.Text.Adornments; using Microsoft.VisualStudio.Text.Tagging; using Microsoft.VisualStudio.Utilities; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Editor.Implementation.Diagnostics { diff --git a/src/EditorFeatures/Core/Implementation/Diagnostics/SuggestionLineAdornmentManagerProvider.cs b/src/EditorFeatures/Core/Implementation/Diagnostics/SuggestionLineAdornmentManagerProvider.cs index ea372a98996ac..f6c2ef14e87bf 100644 --- a/src/EditorFeatures/Core/Implementation/Diagnostics/SuggestionLineAdornmentManagerProvider.cs +++ b/src/EditorFeatures/Core/Implementation/Diagnostics/SuggestionLineAdornmentManagerProvider.cs @@ -25,7 +25,8 @@ internal class SuggestionLineAdornmentManagerProvider : [Export] [Name(LayerName)] [ContentType(ContentTypeNames.RoslynContentType)] - [Order(After = PredefinedAdornmentLayers.Selection, Before = PredefinedAdornmentLayers.Squiggle)] + [Order(After = PredefinedAdornmentLayers.Selection)] + [Order(After = PredefinedAdornmentLayers.Squiggle)] #pragma warning disable 0169 private readonly AdornmentLayerDefinition _lineSeparatorLayer; #pragma warning restore 0169 diff --git a/src/EditorFeatures/Core/Implementation/Diagnostics/SuggestionLineTag.cs b/src/EditorFeatures/Core/Implementation/Diagnostics/SuggestionLineTag.cs index add6fab91ddd3..80736f6e17738 100644 --- a/src/EditorFeatures/Core/Implementation/Diagnostics/SuggestionLineTag.cs +++ b/src/EditorFeatures/Core/Implementation/Diagnostics/SuggestionLineTag.cs @@ -1,12 +1,8 @@ // 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.Windows; -using System.Windows.Controls; using System.Windows.Media; using System.Windows.Shapes; using Microsoft.CodeAnalysis.Editor.Implementation.Adornments; -using Microsoft.CodeAnalysis.Editor.Implementation.LineSeparators; using Microsoft.VisualStudio.Text.Editor; namespace Microsoft.CodeAnalysis.Editor.Implementation.Diagnostics @@ -41,22 +37,29 @@ protected override void Initialize(IWpfTextView view) /// /// Creates a very long line at the bottom of bounds. /// - public override GraphicsResult GetGraphics(IWpfTextView view, Geometry bounds) + public override GraphicsResult GetGraphics(IWpfTextView view, Geometry geometry) { Initialize(view); + // We clip off a bit off the start of the line to prevent a half-square being + // drawn. + var clipRectangle = geometry.Bounds; + clipRectangle.Offset(1, 0); + var line = new Line { - Width = bounds.Bounds.Width, - X2 = bounds.Bounds.BottomLeft.X, - Y1 = bounds.Bounds.BottomLeft.Y - s_pen.Thickness, - X1 = bounds.Bounds.BottomRight.X, - Y2 = bounds.Bounds.BottomRight.Y - s_pen.Thickness, + X1 = geometry.Bounds.Left, + Y1 = geometry.Bounds.Bottom - s_pen.Thickness, + X2 = geometry.Bounds.Right, + Y2 = geometry.Bounds.Bottom - s_pen.Thickness, + Clip = new RectangleGeometry { Rect = clipRectangle } }; RenderOptions.SetEdgeMode(line, EdgeMode.Aliased); ApplyPen(line, s_pen); + // Shift the line over to offset the clipping we did. + line.RenderTransform = new TranslateTransform(-s_pen.Thickness, 0); return new GraphicsResult(line, null); } diff --git a/src/Features/Core/Portable/Diagnostics/Analyzers/SimplifyTypeNamesDiagnosticAnalyzerBase.cs b/src/Features/Core/Portable/Diagnostics/Analyzers/SimplifyTypeNamesDiagnosticAnalyzerBase.cs index 1b4193351f303..9eebce43bc677 100644 --- a/src/Features/Core/Portable/Diagnostics/Analyzers/SimplifyTypeNamesDiagnosticAnalyzerBase.cs +++ b/src/Features/Core/Portable/Diagnostics/Analyzers/SimplifyTypeNamesDiagnosticAnalyzerBase.cs @@ -48,6 +48,13 @@ internal abstract class SimplifyTypeNamesDiagnosticAnalyzerBase Date: Tue, 14 Jun 2016 17:24:41 -0700 Subject: [PATCH 11/36] Switch to round dots. Fixup tests. --- .../Core/Implementation/Diagnostics/SuggestionLineTag.cs | 1 + src/EditorFeatures/Test/LineSeparators/AdornmentManagerTests.cs | 1 + 2 files changed, 2 insertions(+) diff --git a/src/EditorFeatures/Core/Implementation/Diagnostics/SuggestionLineTag.cs b/src/EditorFeatures/Core/Implementation/Diagnostics/SuggestionLineTag.cs index 80736f6e17738..776ade1dce18a 100644 --- a/src/EditorFeatures/Core/Implementation/Diagnostics/SuggestionLineTag.cs +++ b/src/EditorFeatures/Core/Implementation/Diagnostics/SuggestionLineTag.cs @@ -30,6 +30,7 @@ protected override void Initialize(IWpfTextView view) { Brush = new SolidColorBrush(color), DashStyle = DashStyles.Dot, + DashCap = PenLineCap.Round, Thickness = 2, }; } diff --git a/src/EditorFeatures/Test/LineSeparators/AdornmentManagerTests.cs b/src/EditorFeatures/Test/LineSeparators/AdornmentManagerTests.cs index ed65f3927c744..da1820568b009 100644 --- a/src/EditorFeatures/Test/LineSeparators/AdornmentManagerTests.cs +++ b/src/EditorFeatures/Test/LineSeparators/AdornmentManagerTests.cs @@ -5,6 +5,7 @@ using System.Windows; using System.Windows.Media; using System.Windows.Threading; +using Microsoft.CodeAnalysis.Editor.Implementation.Adornments; using Microsoft.CodeAnalysis.Editor.Implementation.LineSeparators; using Microsoft.CodeAnalysis.Editor.UnitTests.Workspaces; using Microsoft.CodeAnalysis.Host; From 3fe2a5e0ffa21c4c6c3daf673c4e560cba01302e Mon Sep 17 00:00:00 2001 From: CyrusNajmabadi Date: Tue, 14 Jun 2016 17:28:47 -0700 Subject: [PATCH 12/36] use an elipsis moniker for suggestions. --- .../Core/Impl/Options/AbstractCodeStyleOptionViewModel.cs | 2 +- .../NamingRuleTreeItemViewModel.TreeDisplayItem.cs | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/VisualStudio/Core/Impl/Options/AbstractCodeStyleOptionViewModel.cs b/src/VisualStudio/Core/Impl/Options/AbstractCodeStyleOptionViewModel.cs index 59094c872ecca..19655b0c7e7e5 100644 --- a/src/VisualStudio/Core/Impl/Options/AbstractCodeStyleOptionViewModel.cs +++ b/src/VisualStudio/Core/Impl/Options/AbstractCodeStyleOptionViewModel.cs @@ -73,7 +73,7 @@ private static List GetDefaultNotifications() { new NotificationOptionViewModel(NotificationOption.None, KnownMonikers.None), new NotificationOptionViewModel(NotificationOption.Info, KnownMonikers.StatusInformation), - new NotificationOptionViewModel(NotificationOption.Suggestion, KnownMonikers.None), + new NotificationOptionViewModel(NotificationOption.Suggestion, KnownMonikers.Ellipsis), new NotificationOptionViewModel(NotificationOption.Warning, KnownMonikers.StatusWarning), new NotificationOptionViewModel(NotificationOption.Error, KnownMonikers.StatusError) }; diff --git a/src/VisualStudio/Core/Impl/Options/Style/NamingPreferences/NamingRuleTreeItemViewModel.TreeDisplayItem.cs b/src/VisualStudio/Core/Impl/Options/Style/NamingPreferences/NamingRuleTreeItemViewModel.TreeDisplayItem.cs index 80423e4760fd4..df109d439eb20 100644 --- a/src/VisualStudio/Core/Impl/Options/Style/NamingPreferences/NamingRuleTreeItemViewModel.TreeDisplayItem.cs +++ b/src/VisualStudio/Core/Impl/Options/Style/NamingPreferences/NamingRuleTreeItemViewModel.TreeDisplayItem.cs @@ -40,11 +40,12 @@ private ImageMoniker GetMoniker() switch (EnforcementLevel.Value) { - case CodeAnalysis.DiagnosticSeverity.Suggestion: case CodeAnalysis.DiagnosticSeverity.Hidden: return KnownMonikers.None; case CodeAnalysis.DiagnosticSeverity.Info: return KnownMonikers.StatusInformation; + case CodeAnalysis.DiagnosticSeverity.Suggestion: + return KnownMonikers.Ellipsis; case CodeAnalysis.DiagnosticSeverity.Warning: return KnownMonikers.StatusWarning; case CodeAnalysis.DiagnosticSeverity.Error: From 2df556e619e1fbd1f6a7f0b0e00908a1ca7a4437 Mon Sep 17 00:00:00 2001 From: CyrusNajmabadi Date: Tue, 14 Jun 2016 17:32:04 -0700 Subject: [PATCH 13/36] PR feedback. --- .../Core/Implementation/Adornments/GraphicsTag.cs | 4 ++-- .../Core/Implementation/Diagnostics/SuggestionLineTag.cs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/EditorFeatures/Core/Implementation/Adornments/GraphicsTag.cs b/src/EditorFeatures/Core/Implementation/Adornments/GraphicsTag.cs index c12e80415c9e6..8443b37773ad6 100644 --- a/src/EditorFeatures/Core/Implementation/Adornments/GraphicsTag.cs +++ b/src/EditorFeatures/Core/Implementation/Adornments/GraphicsTag.cs @@ -27,9 +27,9 @@ protected virtual void Initialize(IWpfTextView view) var lightGray = Color.FromRgb(0xE0, 0xE0, 0xE0); var outliningForegroundBrush = view.VisualElement.TryFindResource("outlining.verticalrule.foreground") as SolidColorBrush; - var darkGray = outliningForegroundBrush?.Color ?? lightGray; + var color = outliningForegroundBrush?.Color ?? lightGray; - VerticalRuleColor = darkGray; + VerticalRuleColor = color; VerticalRuleBrush = new SolidColorBrush(VerticalRuleColor); } diff --git a/src/EditorFeatures/Core/Implementation/Diagnostics/SuggestionLineTag.cs b/src/EditorFeatures/Core/Implementation/Diagnostics/SuggestionLineTag.cs index 776ade1dce18a..5434bb912aeaa 100644 --- a/src/EditorFeatures/Core/Implementation/Diagnostics/SuggestionLineTag.cs +++ b/src/EditorFeatures/Core/Implementation/Diagnostics/SuggestionLineTag.cs @@ -55,7 +55,7 @@ public override GraphicsResult GetGraphics(IWpfTextView view, Geometry geometry) Y2 = geometry.Bounds.Bottom - s_pen.Thickness, Clip = new RectangleGeometry { Rect = clipRectangle } }; - RenderOptions.SetEdgeMode(line, EdgeMode.Aliased); + // RenderOptions.SetEdgeMode(line, EdgeMode.Aliased); ApplyPen(line, s_pen); From 51fc6c9fa387f48ffec98f9e35c23ed073ea580b Mon Sep 17 00:00:00 2001 From: CyrusNajmabadi Date: Tue, 14 Jun 2016 17:34:54 -0700 Subject: [PATCH 14/36] PR feedback. --- .../Diagnostics/AbstractDiagnosticsAdornmentTaggerProvider.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/EditorFeatures/Core/Implementation/Diagnostics/AbstractDiagnosticsAdornmentTaggerProvider.cs b/src/EditorFeatures/Core/Implementation/Diagnostics/AbstractDiagnosticsAdornmentTaggerProvider.cs index 6f8bdc006f6d2..d1c4d13a7fc39 100644 --- a/src/EditorFeatures/Core/Implementation/Diagnostics/AbstractDiagnosticsAdornmentTaggerProvider.cs +++ b/src/EditorFeatures/Core/Implementation/Diagnostics/AbstractDiagnosticsAdornmentTaggerProvider.cs @@ -15,7 +15,7 @@ internal abstract class AbstractDiagnosticsAdornmentTaggerProvider : public AbstractDiagnosticsAdornmentTaggerProvider( IDiagnosticService diagnosticService, IForegroundNotificationService notificationService, - [ImportMany] IEnumerable> listeners) + IEnumerable> listeners) : base(diagnosticService, notificationService, new AggregateAsynchronousOperationListener(listeners, FeatureAttribute.ErrorSquiggles)) { } From 61d5d2cb489c919b34dce8f1a2e6b278e65395f7 Mon Sep 17 00:00:00 2001 From: CyrusNajmabadi Date: Tue, 14 Jun 2016 17:38:19 -0700 Subject: [PATCH 15/36] Rename tag. --- src/EditorFeatures/Core/EditorFeatures.csproj | 6 +++--- ...der.cs => DiagnosticsSuggestionTaggerProvider.cs} | 12 ++++++------ ...ider.cs => SuggestionAdornmentManagerProvider.cs} | 6 +++--- .../{SuggestionLineTag.cs => SuggestionTag.cs} | 4 ++-- 4 files changed, 14 insertions(+), 14 deletions(-) rename src/EditorFeatures/Core/Implementation/Diagnostics/{DiagnosticsSuggestionLineTaggerProvider.cs => DiagnosticsSuggestionTaggerProvider.cs} (85%) rename src/EditorFeatures/Core/Implementation/Diagnostics/{SuggestionLineAdornmentManagerProvider.cs => SuggestionAdornmentManagerProvider.cs} (90%) rename src/EditorFeatures/Core/Implementation/Diagnostics/{SuggestionLineTag.cs => SuggestionTag.cs} (95%) diff --git a/src/EditorFeatures/Core/EditorFeatures.csproj b/src/EditorFeatures/Core/EditorFeatures.csproj index 9633acc52d83f..603b939e866ae 100644 --- a/src/EditorFeatures/Core/EditorFeatures.csproj +++ b/src/EditorFeatures/Core/EditorFeatures.csproj @@ -266,9 +266,9 @@ - - - + + + diff --git a/src/EditorFeatures/Core/Implementation/Diagnostics/DiagnosticsSuggestionLineTaggerProvider.cs b/src/EditorFeatures/Core/Implementation/Diagnostics/DiagnosticsSuggestionTaggerProvider.cs similarity index 85% rename from src/EditorFeatures/Core/Implementation/Diagnostics/DiagnosticsSuggestionLineTaggerProvider.cs rename to src/EditorFeatures/Core/Implementation/Diagnostics/DiagnosticsSuggestionTaggerProvider.cs index 5920e376a47f7..b058691c52b8a 100644 --- a/src/EditorFeatures/Core/Implementation/Diagnostics/DiagnosticsSuggestionLineTaggerProvider.cs +++ b/src/EditorFeatures/Core/Implementation/Diagnostics/DiagnosticsSuggestionTaggerProvider.cs @@ -17,16 +17,16 @@ namespace Microsoft.CodeAnalysis.Editor.Implementation.Diagnostics { [Export(typeof(ITaggerProvider))] [ContentType(ContentTypeNames.RoslynContentType)] - [TagType(typeof(SuggestionLineTag))] - internal partial class DiagnosticsSuggestionLineTaggerProvider : - AbstractDiagnosticsAdornmentTaggerProvider + [TagType(typeof(SuggestionTag))] + internal partial class DiagnosticsSuggestionTaggerProvider : + AbstractDiagnosticsAdornmentTaggerProvider { private static readonly IEnumerable> s_tagSourceOptions = ImmutableArray.Create(EditorComponentOnOffOptions.Tagger, InternalFeatureOnOffOptions.Squiggles, ServiceComponentOnOffOptions.DiagnosticProvider); protected internal override IEnumerable> Options => s_tagSourceOptions; [ImportingConstructor] - public DiagnosticsSuggestionLineTaggerProvider( + public DiagnosticsSuggestionTaggerProvider( IOptionService optionService, IDiagnosticService diagnosticService, IForegroundNotificationService notificationService, @@ -40,9 +40,9 @@ protected internal override bool IncludeDiagnostic(DiagnosticData diagnostic) return diagnostic.Severity == DiagnosticSeverity.Suggestion; } - protected override SuggestionLineTag CreateTag(DiagnosticData diagnostic) + protected override SuggestionTag CreateTag(DiagnosticData diagnostic) { - return SuggestionLineTag.Instance; + return SuggestionTag.Instance; } protected override SnapshotSpan AdjustSnapshotSpan(SnapshotSpan snapshotSpan, int minimumLength) diff --git a/src/EditorFeatures/Core/Implementation/Diagnostics/SuggestionLineAdornmentManagerProvider.cs b/src/EditorFeatures/Core/Implementation/Diagnostics/SuggestionAdornmentManagerProvider.cs similarity index 90% rename from src/EditorFeatures/Core/Implementation/Diagnostics/SuggestionLineAdornmentManagerProvider.cs rename to src/EditorFeatures/Core/Implementation/Diagnostics/SuggestionAdornmentManagerProvider.cs index f6c2ef14e87bf..9441882b38517 100644 --- a/src/EditorFeatures/Core/Implementation/Diagnostics/SuggestionLineAdornmentManagerProvider.cs +++ b/src/EditorFeatures/Core/Implementation/Diagnostics/SuggestionAdornmentManagerProvider.cs @@ -17,8 +17,8 @@ namespace Microsoft.CodeAnalysis.Editor.Implementation.Diagnostics [Export(typeof(IWpfTextViewCreationListener))] [ContentType(ContentTypeNames.RoslynContentType)] [TextViewRole(PredefinedTextViewRoles.Document)] - internal class SuggestionLineAdornmentManagerProvider : - AbstractAdornmentManagerProvider + internal class SuggestionAdornmentManagerProvider : + AbstractAdornmentManagerProvider { private const string LayerName = "RoslynSuggestions"; @@ -32,7 +32,7 @@ internal class SuggestionLineAdornmentManagerProvider : #pragma warning restore 0169 [ImportingConstructor] - public SuggestionLineAdornmentManagerProvider( + public SuggestionAdornmentManagerProvider( IViewTagAggregatorFactoryService tagAggregatorFactoryService, [ImportMany] IEnumerable> asyncListeners) : base(tagAggregatorFactoryService, asyncListeners) diff --git a/src/EditorFeatures/Core/Implementation/Diagnostics/SuggestionLineTag.cs b/src/EditorFeatures/Core/Implementation/Diagnostics/SuggestionTag.cs similarity index 95% rename from src/EditorFeatures/Core/Implementation/Diagnostics/SuggestionLineTag.cs rename to src/EditorFeatures/Core/Implementation/Diagnostics/SuggestionTag.cs index 5434bb912aeaa..5372849b46bd6 100644 --- a/src/EditorFeatures/Core/Implementation/Diagnostics/SuggestionLineTag.cs +++ b/src/EditorFeatures/Core/Implementation/Diagnostics/SuggestionTag.cs @@ -10,9 +10,9 @@ namespace Microsoft.CodeAnalysis.Editor.Implementation.Diagnostics /// /// Tag that specifies line separator. /// - internal class SuggestionLineTag : GraphicsTag + internal class SuggestionTag : GraphicsTag { - public static readonly SuggestionLineTag Instance = new SuggestionLineTag(); + public static readonly SuggestionTag Instance = new SuggestionTag(); private static Pen s_pen; From 55502f8db708aa65383b172a63895bcfccddd9f3 Mon Sep 17 00:00:00 2001 From: CyrusNajmabadi Date: Tue, 14 Jun 2016 17:40:52 -0700 Subject: [PATCH 16/36] PR feedback. --- .../Diagnostics/SuggestionAdornmentManagerProvider.cs | 2 +- .../Core/Implementation/Diagnostics/SuggestionTag.cs | 3 --- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/src/EditorFeatures/Core/Implementation/Diagnostics/SuggestionAdornmentManagerProvider.cs b/src/EditorFeatures/Core/Implementation/Diagnostics/SuggestionAdornmentManagerProvider.cs index 9441882b38517..56d4715136803 100644 --- a/src/EditorFeatures/Core/Implementation/Diagnostics/SuggestionAdornmentManagerProvider.cs +++ b/src/EditorFeatures/Core/Implementation/Diagnostics/SuggestionAdornmentManagerProvider.cs @@ -12,7 +12,7 @@ namespace Microsoft.CodeAnalysis.Editor.Implementation.Diagnostics { /// - /// This factory is called to create the view service that will manage line separators. + /// This factory is called to create the view service that will manage suggestion adornments. /// [Export(typeof(IWpfTextViewCreationListener))] [ContentType(ContentTypeNames.RoslynContentType)] diff --git a/src/EditorFeatures/Core/Implementation/Diagnostics/SuggestionTag.cs b/src/EditorFeatures/Core/Implementation/Diagnostics/SuggestionTag.cs index 5372849b46bd6..9c57d753aec23 100644 --- a/src/EditorFeatures/Core/Implementation/Diagnostics/SuggestionTag.cs +++ b/src/EditorFeatures/Core/Implementation/Diagnostics/SuggestionTag.cs @@ -35,9 +35,6 @@ protected override void Initialize(IWpfTextView view) }; } - /// - /// Creates a very long line at the bottom of bounds. - /// public override GraphicsResult GetGraphics(IWpfTextView view, Geometry geometry) { Initialize(view); From db571d7eec9997bf5fb980b20b0cf35fd1bba776 Mon Sep 17 00:00:00 2001 From: CyrusNajmabadi Date: Wed, 15 Jun 2016 16:55:01 -0700 Subject: [PATCH 17/36] Repurpose 'Info' for Suggestions. --- .../Core/Portable/Diagnostic/DiagnosticDescriptor.cs | 2 -- .../Core/Portable/Diagnostic/DiagnosticSeverity.cs | 5 ----- .../Core/Portable/Diagnostic/ReportDiagnostic.cs | 5 ----- src/Compilers/Core/Portable/PublicAPI.Unshipped.txt | 2 -- .../IntroduceVariable/IntroduceVariableTests.cs | 2 +- .../GenerateVariable/GenerateVariableTests.cs | 2 +- .../QualifyMemberAccess/QualifyMemberAccessTests.cs | 2 +- .../QualifyMemberAccessTests_FixAllTests.cs | 2 +- .../SimplifyTypeNamesTests_FixAllTests.cs | 4 ++-- .../UseImplicitOrExplicitType/UseExplicitTypeTests.cs | 4 ++-- .../UseImplicitOrExplicitType/UseImplicitTypeTests.cs | 4 ++-- .../DiagnosticsSuggestionTaggerProvider.cs | 2 +- .../Test2/Simplification/TypeNameSimplifierTest.vb | 4 ++-- .../QualifyMemberAccess/QualifyMemberAccessTests.vb | 2 +- .../QualifyMemberAccessTests_FixAllTests.vb | 2 +- .../SimplifyTypeNamesTests_FixAllTests.vb | 4 ++-- .../CodeFixes/Suppression/SuppressionHelpers.cs | 1 - .../SimplifyTypeNamesDiagnosticAnalyzerBase.cs | 9 --------- .../Diagnostics/BaseDiagnosticIncrementalAnalyzer.cs | 2 -- ...udioBaseDiagnosticListTable.LiveTableDataSource.cs | 1 - .../Impl/Options/AbstractCodeStyleOptionViewModel.cs | 3 +-- .../Style/NamingPreferences/EnforcementLevel.cs | 3 +-- .../NamingPreferences/NamingRuleDialogViewModel.cs | 1 - .../NamingRuleTreeItemViewModel.TreeDisplayItem.cs | 2 -- .../Core/Portable/CodeStyle/CodeStyleOption.cs | 11 +++++++---- .../Core/Portable/CodeStyle/NotificationOption.cs | 3 +-- src/Workspaces/Core/Portable/PublicAPI.Unshipped.txt | 1 - 27 files changed, 27 insertions(+), 58 deletions(-) diff --git a/src/Compilers/Core/Portable/Diagnostic/DiagnosticDescriptor.cs b/src/Compilers/Core/Portable/Diagnostic/DiagnosticDescriptor.cs index 3efe87bc171fc..bd6f6c12d2518 100644 --- a/src/Compilers/Core/Portable/Diagnostic/DiagnosticDescriptor.cs +++ b/src/Compilers/Core/Portable/Diagnostic/DiagnosticDescriptor.cs @@ -227,8 +227,6 @@ private static ReportDiagnostic MapSeverityToReport(DiagnosticSeverity severity) return ReportDiagnostic.Warn; case DiagnosticSeverity.Error: return ReportDiagnostic.Error; - case DiagnosticSeverity.Suggestion: - return ReportDiagnostic.Suggestion; default: throw ExceptionUtilities.Unreachable; } diff --git a/src/Compilers/Core/Portable/Diagnostic/DiagnosticSeverity.cs b/src/Compilers/Core/Portable/Diagnostic/DiagnosticSeverity.cs index d98111fb9fc8c..de98f2acecbd3 100644 --- a/src/Compilers/Core/Portable/Diagnostic/DiagnosticSeverity.cs +++ b/src/Compilers/Core/Portable/Diagnostic/DiagnosticSeverity.cs @@ -28,11 +28,6 @@ public enum DiagnosticSeverity /// Something not allowed by the rules of the language or other authority. /// Error = 3, - - /// - /// Something that can be potentially improved. - /// - Suggestion = 4, } /// diff --git a/src/Compilers/Core/Portable/Diagnostic/ReportDiagnostic.cs b/src/Compilers/Core/Portable/Diagnostic/ReportDiagnostic.cs index 655962d59042c..30b6a20e3c13e 100644 --- a/src/Compilers/Core/Portable/Diagnostic/ReportDiagnostic.cs +++ b/src/Compilers/Core/Portable/Diagnostic/ReportDiagnostic.cs @@ -36,10 +36,5 @@ public enum ReportDiagnostic /// Suppress a diagnostic. /// Suppress = 5, - - /// - /// Report a diagnostic as a suggestion. - /// - Suggestion = 6, } } \ No newline at end of file diff --git a/src/Compilers/Core/Portable/PublicAPI.Unshipped.txt b/src/Compilers/Core/Portable/PublicAPI.Unshipped.txt index 7de6d988fda0c..da45a7f9029ad 100644 --- a/src/Compilers/Core/Portable/PublicAPI.Unshipped.txt +++ b/src/Compilers/Core/Portable/PublicAPI.Unshipped.txt @@ -1,6 +1,5 @@ Microsoft.CodeAnalysis.Compilation.CreateTupleTypeSymbol(Microsoft.CodeAnalysis.INamedTypeSymbol underlyingType, System.Collections.Immutable.ImmutableArray elementNames = default(System.Collections.Immutable.ImmutableArray)) -> Microsoft.CodeAnalysis.INamedTypeSymbol Microsoft.CodeAnalysis.Compilation.CreateTupleTypeSymbol(System.Collections.Immutable.ImmutableArray elementTypes, System.Collections.Immutable.ImmutableArray elementNames = default(System.Collections.Immutable.ImmutableArray)) -> Microsoft.CodeAnalysis.INamedTypeSymbol -Microsoft.CodeAnalysis.DiagnosticSeverity.Suggestion = 4 -> Microsoft.CodeAnalysis.DiagnosticSeverity Microsoft.CodeAnalysis.Diagnostics.AnalysisContext.RegisterOperationAction(System.Action action, params Microsoft.CodeAnalysis.OperationKind[] operationKinds) -> void Microsoft.CodeAnalysis.Diagnostics.AnalysisResult Microsoft.CodeAnalysis.Diagnostics.AnalysisResult.AnalyzerTelemetryInfo.get -> System.Collections.Immutable.ImmutableDictionary @@ -137,7 +136,6 @@ Microsoft.CodeAnalysis.OperationKind.WithStatement = 82 -> Microsoft.CodeAnalysi Microsoft.CodeAnalysis.OperationKind.YieldBreakStatement = 12 -> Microsoft.CodeAnalysis.OperationKind Microsoft.CodeAnalysis.OperationKind.YieldReturnStatement = 16 -> Microsoft.CodeAnalysis.OperationKind Microsoft.CodeAnalysis.PortableExecutableReference.GetMetadata() -> Microsoft.CodeAnalysis.Metadata -Microsoft.CodeAnalysis.ReportDiagnostic.Suggestion = 6 -> Microsoft.CodeAnalysis.ReportDiagnostic Microsoft.CodeAnalysis.SemanticModel.GetOperation(Microsoft.CodeAnalysis.SyntaxNode node, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> Microsoft.CodeAnalysis.IOperation Microsoft.CodeAnalysis.Semantics.ArgumentKind Microsoft.CodeAnalysis.Semantics.ArgumentKind.DefaultValue = 4 -> Microsoft.CodeAnalysis.Semantics.ArgumentKind diff --git a/src/EditorFeatures/CSharpTest/CodeActions/IntroduceVariable/IntroduceVariableTests.cs b/src/EditorFeatures/CSharpTest/CodeActions/IntroduceVariable/IntroduceVariableTests.cs index 31968391aad15..72ded47fce0ee 100644 --- a/src/EditorFeatures/CSharpTest/CodeActions/IntroduceVariable/IntroduceVariableTests.cs +++ b/src/EditorFeatures/CSharpTest/CodeActions/IntroduceVariable/IntroduceVariableTests.cs @@ -21,7 +21,7 @@ protected override CodeRefactoringProvider CreateCodeRefactoringProvider(Workspa return new IntroduceVariableCodeRefactoringProvider(); } - private readonly CodeStyleOption onWithInfo = new CodeStyleOption(true, NotificationOption.Info); + private readonly CodeStyleOption onWithInfo = new CodeStyleOption(true, NotificationOption.Suggestion); // specify all options explicitly to override defaults. private IDictionary ImplicitTypingEverywhere() => diff --git a/src/EditorFeatures/CSharpTest/Diagnostics/GenerateVariable/GenerateVariableTests.cs b/src/EditorFeatures/CSharpTest/Diagnostics/GenerateVariable/GenerateVariableTests.cs index 11aeae912d022..28099af61336a 100644 --- a/src/EditorFeatures/CSharpTest/Diagnostics/GenerateVariable/GenerateVariableTests.cs +++ b/src/EditorFeatures/CSharpTest/Diagnostics/GenerateVariable/GenerateVariableTests.cs @@ -24,7 +24,7 @@ internal override Tuple CreateDiagnosticPro null, new GenerateVariableCodeFixProvider()); } - private readonly CodeStyleOption onWithInfo = new CodeStyleOption(true, NotificationOption.Info); + private readonly CodeStyleOption onWithInfo = new CodeStyleOption(true, NotificationOption.Suggestion); // specify all options explicitly to override defaults. private IDictionary ImplicitTypingEverywhere() => diff --git a/src/EditorFeatures/CSharpTest/Diagnostics/QualifyMemberAccess/QualifyMemberAccessTests.cs b/src/EditorFeatures/CSharpTest/Diagnostics/QualifyMemberAccess/QualifyMemberAccessTests.cs index cfa2b906c7968..6a2e2302fdadd 100644 --- a/src/EditorFeatures/CSharpTest/Diagnostics/QualifyMemberAccess/QualifyMemberAccessTests.cs +++ b/src/EditorFeatures/CSharpTest/Diagnostics/QualifyMemberAccess/QualifyMemberAccessTests.cs @@ -376,7 +376,7 @@ public async Task QualifyMemberAccessOnNotificationOptionInfo() await TestAsyncWithOptionAndNotificationOption( @"class Class { int Property { get; set; }; void M() { [|Property|] = 1; } }", @"class Class { int Property { get; set; }; void M() { this.Property = 1; } }", -CodeStyleOptions.QualifyPropertyAccess, NotificationOption.Info); +CodeStyleOptions.QualifyPropertyAccess, NotificationOption.Suggestion); } [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsQualifyMemberAccess)] diff --git a/src/EditorFeatures/CSharpTest/Diagnostics/QualifyMemberAccess/QualifyMemberAccessTests_FixAllTests.cs b/src/EditorFeatures/CSharpTest/Diagnostics/QualifyMemberAccess/QualifyMemberAccessTests_FixAllTests.cs index 96ec315ce4ee4..b57c96f7f6616 100644 --- a/src/EditorFeatures/CSharpTest/Diagnostics/QualifyMemberAccess/QualifyMemberAccessTests_FixAllTests.cs +++ b/src/EditorFeatures/CSharpTest/Diagnostics/QualifyMemberAccess/QualifyMemberAccessTests_FixAllTests.cs @@ -89,7 +89,7 @@ void N() await TestAsync( initialMarkup: input, expectedMarkup: expected, - options: Option(CodeStyleOptions.QualifyPropertyAccess, true, NotificationOption.Info), + options: Option(CodeStyleOptions.QualifyPropertyAccess, true, NotificationOption.Suggestion), compareTokens: false, fixAllActionEquivalenceKey: FeaturesResources.AddQualification); } diff --git a/src/EditorFeatures/CSharpTest/Diagnostics/SimplifyTypeNames/SimplifyTypeNamesTests_FixAllTests.cs b/src/EditorFeatures/CSharpTest/Diagnostics/SimplifyTypeNames/SimplifyTypeNamesTests_FixAllTests.cs index b966048fdd4d0..6f359b769e1e7 100644 --- a/src/EditorFeatures/CSharpTest/Diagnostics/SimplifyTypeNames/SimplifyTypeNamesTests_FixAllTests.cs +++ b/src/EditorFeatures/CSharpTest/Diagnostics/SimplifyTypeNames/SimplifyTypeNamesTests_FixAllTests.cs @@ -931,8 +931,8 @@ void N() "; var options = OptionsSet( - Tuple.Create(CodeStyleOptions.QualifyPropertyAccess, false, NotificationOption.Info), - Tuple.Create(CodeStyleOptions.QualifyFieldAccess, true, NotificationOption.Info)); + Tuple.Create(CodeStyleOptions.QualifyPropertyAccess, false, NotificationOption.Suggestion), + Tuple.Create(CodeStyleOptions.QualifyFieldAccess, true, NotificationOption.Suggestion)); await TestAsync( initialMarkup: input, expectedMarkup: expected, diff --git a/src/EditorFeatures/CSharpTest/Diagnostics/UseImplicitOrExplicitType/UseExplicitTypeTests.cs b/src/EditorFeatures/CSharpTest/Diagnostics/UseImplicitOrExplicitType/UseExplicitTypeTests.cs index baabf1d0b7f59..1ba4747d9b3a1 100644 --- a/src/EditorFeatures/CSharpTest/Diagnostics/UseImplicitOrExplicitType/UseExplicitTypeTests.cs +++ b/src/EditorFeatures/CSharpTest/Diagnostics/UseImplicitOrExplicitType/UseExplicitTypeTests.cs @@ -24,8 +24,8 @@ internal override Tuple CreateDiagnosticPro private readonly CodeStyleOption onWithNone = new CodeStyleOption(true, NotificationOption.None); private readonly CodeStyleOption offWithNone = new CodeStyleOption(false, NotificationOption.None); - private readonly CodeStyleOption onWithInfo = new CodeStyleOption(true, NotificationOption.Info); - private readonly CodeStyleOption offWithInfo = new CodeStyleOption(false, NotificationOption.Info); + private readonly CodeStyleOption onWithInfo = new CodeStyleOption(true, NotificationOption.Suggestion); + private readonly CodeStyleOption offWithInfo = new CodeStyleOption(false, NotificationOption.Suggestion); private readonly CodeStyleOption onWithWarning = new CodeStyleOption(true, NotificationOption.Warning); private readonly CodeStyleOption offWithWarning = new CodeStyleOption(false, NotificationOption.Warning); private readonly CodeStyleOption onWithError = new CodeStyleOption(true, NotificationOption.Error); diff --git a/src/EditorFeatures/CSharpTest/Diagnostics/UseImplicitOrExplicitType/UseImplicitTypeTests.cs b/src/EditorFeatures/CSharpTest/Diagnostics/UseImplicitOrExplicitType/UseImplicitTypeTests.cs index 96c43bc7efdcc..95bb3f6efa4cf 100644 --- a/src/EditorFeatures/CSharpTest/Diagnostics/UseImplicitOrExplicitType/UseImplicitTypeTests.cs +++ b/src/EditorFeatures/CSharpTest/Diagnostics/UseImplicitOrExplicitType/UseImplicitTypeTests.cs @@ -25,8 +25,8 @@ internal override Tuple CreateDiagnosticPro private readonly CodeStyleOption onWithNone = new CodeStyleOption(true, NotificationOption.None); private readonly CodeStyleOption offWithNone = new CodeStyleOption(false, NotificationOption.None); - private readonly CodeStyleOption onWithInfo = new CodeStyleOption(true, NotificationOption.Info); - private readonly CodeStyleOption offWithInfo = new CodeStyleOption(false, NotificationOption.Info); + private readonly CodeStyleOption onWithInfo = new CodeStyleOption(true, NotificationOption.Suggestion); + private readonly CodeStyleOption offWithInfo = new CodeStyleOption(false, NotificationOption.Suggestion); private readonly CodeStyleOption onWithWarning = new CodeStyleOption(true, NotificationOption.Warning); private readonly CodeStyleOption offWithWarning = new CodeStyleOption(false, NotificationOption.Warning); private readonly CodeStyleOption onWithError = new CodeStyleOption(true, NotificationOption.Error); diff --git a/src/EditorFeatures/Core/Implementation/Diagnostics/DiagnosticsSuggestionTaggerProvider.cs b/src/EditorFeatures/Core/Implementation/Diagnostics/DiagnosticsSuggestionTaggerProvider.cs index b058691c52b8a..479e5d0d03266 100644 --- a/src/EditorFeatures/Core/Implementation/Diagnostics/DiagnosticsSuggestionTaggerProvider.cs +++ b/src/EditorFeatures/Core/Implementation/Diagnostics/DiagnosticsSuggestionTaggerProvider.cs @@ -37,7 +37,7 @@ public DiagnosticsSuggestionTaggerProvider( protected internal override bool IncludeDiagnostic(DiagnosticData diagnostic) { - return diagnostic.Severity == DiagnosticSeverity.Suggestion; + return diagnostic.Severity == DiagnosticSeverity.Info; } protected override SuggestionTag CreateTag(DiagnosticData diagnostic) diff --git a/src/EditorFeatures/Test2/Simplification/TypeNameSimplifierTest.vb b/src/EditorFeatures/Test2/Simplification/TypeNameSimplifierTest.vb index 1b1a0de6632e2..aaad1938bc09b 100644 --- a/src/EditorFeatures/Test2/Simplification/TypeNameSimplifierTest.vb +++ b/src/EditorFeatures/Test2/Simplification/TypeNameSimplifierTest.vb @@ -3116,7 +3116,7 @@ class C } ]]> - Await TestAsync(input, expected, QualifyPropertyAccessOptionWithNotification(LanguageNames.CSharp, NotificationOption.Info)) + Await TestAsync(input, expected, QualifyPropertyAccessOptionWithNotification(LanguageNames.CSharp, NotificationOption.Suggestion)) End Function @@ -5689,7 +5689,7 @@ Class C End Class ]]> - Await TestAsync(input, expected, QualifyPropertyAccessOptionWithNotification(LanguageNames.VisualBasic, NotificationOption.Info)) + Await TestAsync(input, expected, QualifyPropertyAccessOptionWithNotification(LanguageNames.VisualBasic, NotificationOption.Suggestion)) End Function diff --git a/src/EditorFeatures/VisualBasicTest/Diagnostics/QualifyMemberAccess/QualifyMemberAccessTests.vb b/src/EditorFeatures/VisualBasicTest/Diagnostics/QualifyMemberAccess/QualifyMemberAccessTests.vb index 3e64173d67f54..dd697739b0600 100644 --- a/src/EditorFeatures/VisualBasicTest/Diagnostics/QualifyMemberAccess/QualifyMemberAccessTests.vb +++ b/src/EditorFeatures/VisualBasicTest/Diagnostics/QualifyMemberAccess/QualifyMemberAccessTests.vb @@ -443,7 +443,7 @@ CodeStyleOptions.QualifyPropertyAccess, NotificationOption.None) Await TestAsyncWithOptionAndNotification( "Class C : Property I As Integer : Sub M() : [|I|] = 1 : End Sub : End Class", "Class C : Property I As Integer : Sub M() : Me.I = 1 : End Sub : End Class", -CodeStyleOptions.QualifyPropertyAccess, NotificationOption.Info) +CodeStyleOptions.QualifyPropertyAccess, NotificationOption.Suggestion) End Function diff --git a/src/EditorFeatures/VisualBasicTest/Diagnostics/QualifyMemberAccess/QualifyMemberAccessTests_FixAllTests.vb b/src/EditorFeatures/VisualBasicTest/Diagnostics/QualifyMemberAccess/QualifyMemberAccessTests_FixAllTests.vb index a6ae62edd6e4f..8857b16c65f71 100644 --- a/src/EditorFeatures/VisualBasicTest/Diagnostics/QualifyMemberAccess/QualifyMemberAccessTests_FixAllTests.vb +++ b/src/EditorFeatures/VisualBasicTest/Diagnostics/QualifyMemberAccess/QualifyMemberAccessTests_FixAllTests.vb @@ -77,7 +77,7 @@ End Class]]> Await TestAsync( initialMarkup:=input, expectedMarkup:=expected, - options:=[Option](CodeStyleOptions.QualifyPropertyAccess, True, NotificationOption.Info), + options:=[Option](CodeStyleOptions.QualifyPropertyAccess, True, NotificationOption.Suggestion), compareTokens:=False, fixAllActionEquivalenceKey:=FeaturesResources.AddQualification) End Function diff --git a/src/EditorFeatures/VisualBasicTest/Diagnostics/SimplifyTypeNames/SimplifyTypeNamesTests_FixAllTests.vb b/src/EditorFeatures/VisualBasicTest/Diagnostics/SimplifyTypeNames/SimplifyTypeNamesTests_FixAllTests.vb index cc97d49dc2ad7..205549bd024d6 100644 --- a/src/EditorFeatures/VisualBasicTest/Diagnostics/SimplifyTypeNames/SimplifyTypeNamesTests_FixAllTests.vb +++ b/src/EditorFeatures/VisualBasicTest/Diagnostics/SimplifyTypeNames/SimplifyTypeNamesTests_FixAllTests.vb @@ -789,8 +789,8 @@ End Class]]> .ToString() Dim options = OptionsSet( - Tuple.Create(CodeStyleOptions.QualifyPropertyAccess, False, NotificationOption.Info), - Tuple.Create(CodeStyleOptions.QualifyFieldAccess, True, NotificationOption.Info)) + Tuple.Create(CodeStyleOptions.QualifyPropertyAccess, False, NotificationOption.Suggestion), + Tuple.Create(CodeStyleOptions.QualifyFieldAccess, True, NotificationOption.Suggestion)) Await TestAsync( initialMarkup:=input, expectedMarkup:=expected, diff --git a/src/Features/Core/Portable/CodeFixes/Suppression/SuppressionHelpers.cs b/src/Features/Core/Portable/CodeFixes/Suppression/SuppressionHelpers.cs index 430d7361ef26d..51c3527a27fa7 100644 --- a/src/Features/Core/Portable/CodeFixes/Suppression/SuppressionHelpers.cs +++ b/src/Features/Core/Portable/CodeFixes/Suppression/SuppressionHelpers.cs @@ -43,7 +43,6 @@ private static bool CanBeSuppressedOrUnsuppressed(Diagnostic diagnostic, bool ch case DiagnosticSeverity.Error: case DiagnosticSeverity.Warning: case DiagnosticSeverity.Info: - case DiagnosticSeverity.Suggestion: // We allow suppressions for all the remaining configurable, non-hidden diagnostics. // Note that compiler errors are not configurable by default, so only analyzer errors are suppressable. return true; diff --git a/src/Features/Core/Portable/Diagnostics/Analyzers/SimplifyTypeNamesDiagnosticAnalyzerBase.cs b/src/Features/Core/Portable/Diagnostics/Analyzers/SimplifyTypeNamesDiagnosticAnalyzerBase.cs index 9eebce43bc677..1b4193351f303 100644 --- a/src/Features/Core/Portable/Diagnostics/Analyzers/SimplifyTypeNamesDiagnosticAnalyzerBase.cs +++ b/src/Features/Core/Portable/Diagnostics/Analyzers/SimplifyTypeNamesDiagnosticAnalyzerBase.cs @@ -48,13 +48,6 @@ internal abstract class SimplifyTypeNamesDiagnosticAnalyzerBase GetDefaultNotifications() return new List { new NotificationOptionViewModel(NotificationOption.None, KnownMonikers.None), - new NotificationOptionViewModel(NotificationOption.Info, KnownMonikers.StatusInformation), - new NotificationOptionViewModel(NotificationOption.Suggestion, KnownMonikers.Ellipsis), + new NotificationOptionViewModel(NotificationOption.Suggestion, KnownMonikers.StatusInformation), new NotificationOptionViewModel(NotificationOption.Warning, KnownMonikers.StatusWarning), new NotificationOptionViewModel(NotificationOption.Error, KnownMonikers.StatusError) }; diff --git a/src/VisualStudio/Core/Impl/Options/Style/NamingPreferences/EnforcementLevel.cs b/src/VisualStudio/Core/Impl/Options/Style/NamingPreferences/EnforcementLevel.cs index 89d0feb914d7a..f233e9ac7d3f0 100644 --- a/src/VisualStudio/Core/Impl/Options/Style/NamingPreferences/EnforcementLevel.cs +++ b/src/VisualStudio/Core/Impl/Options/Style/NamingPreferences/EnforcementLevel.cs @@ -16,13 +16,12 @@ public EnforcementLevel(DiagnosticSeverity severity) Value = severity; switch (severity) { - case DiagnosticSeverity.Suggestion: case DiagnosticSeverity.Hidden: Name = "None"; Moniker = KnownMonikers.None; return; case DiagnosticSeverity.Info: - Name = "Info"; + Name = "Suggestion"; Moniker = KnownMonikers.StatusInformation; return; case DiagnosticSeverity.Warning: diff --git a/src/VisualStudio/Core/Impl/Options/Style/NamingPreferences/NamingRuleDialogViewModel.cs b/src/VisualStudio/Core/Impl/Options/Style/NamingPreferences/NamingRuleDialogViewModel.cs index 3702843059f0a..5102d656afd6e 100644 --- a/src/VisualStudio/Core/Impl/Options/Style/NamingPreferences/NamingRuleDialogViewModel.cs +++ b/src/VisualStudio/Core/Impl/Options/Style/NamingPreferences/NamingRuleDialogViewModel.cs @@ -47,7 +47,6 @@ public NamingRuleDialogViewModel( { new EnforcementLevel(DiagnosticSeverity.Hidden), new EnforcementLevel(DiagnosticSeverity.Info), - new EnforcementLevel(DiagnosticSeverity.Suggestion), new EnforcementLevel(DiagnosticSeverity.Warning), new EnforcementLevel(DiagnosticSeverity.Error), }; diff --git a/src/VisualStudio/Core/Impl/Options/Style/NamingPreferences/NamingRuleTreeItemViewModel.TreeDisplayItem.cs b/src/VisualStudio/Core/Impl/Options/Style/NamingPreferences/NamingRuleTreeItemViewModel.TreeDisplayItem.cs index df109d439eb20..da967283a2268 100644 --- a/src/VisualStudio/Core/Impl/Options/Style/NamingPreferences/NamingRuleTreeItemViewModel.TreeDisplayItem.cs +++ b/src/VisualStudio/Core/Impl/Options/Style/NamingPreferences/NamingRuleTreeItemViewModel.TreeDisplayItem.cs @@ -44,8 +44,6 @@ private ImageMoniker GetMoniker() return KnownMonikers.None; case CodeAnalysis.DiagnosticSeverity.Info: return KnownMonikers.StatusInformation; - case CodeAnalysis.DiagnosticSeverity.Suggestion: - return KnownMonikers.Ellipsis; case CodeAnalysis.DiagnosticSeverity.Warning: return KnownMonikers.StatusWarning; case CodeAnalysis.DiagnosticSeverity.Error: diff --git a/src/Workspaces/Core/Portable/CodeStyle/CodeStyleOption.cs b/src/Workspaces/Core/Portable/CodeStyle/CodeStyleOption.cs index 84aa39ae3577a..cd71b4bb55b67 100644 --- a/src/Workspaces/Core/Portable/CodeStyle/CodeStyleOption.cs +++ b/src/Workspaces/Core/Portable/CodeStyle/CodeStyleOption.cs @@ -12,7 +12,7 @@ public class CodeStyleOption { public static CodeStyleOption Default => new CodeStyleOption(default(T), NotificationOption.None); - private const int SerializationVersion = 1; + private const int SerializationVersion = 2; public CodeStyleOption(T value, NotificationOption notification) { @@ -36,6 +36,7 @@ public static CodeStyleOption FromXElement(XElement element) var typeAttribute = element.Attribute("Type"); var valueAttribute = element.Attribute(nameof(Value)); var severityAttribute = element.Attribute(nameof(DiagnosticSeverity)); + var version = (int)element.Attribute(nameof(SerializationVersion)); if (typeAttribute == null || valueAttribute == null || severityAttribute == null) { @@ -43,6 +44,11 @@ public static CodeStyleOption FromXElement(XElement element) return Default; } + if (version != SerializationVersion) + { + return Default; + } + var parser = GetParser(typeAttribute.Value); var value = (T)parser(valueAttribute.Value); var severity = (DiagnosticSeverity)Enum.Parse(typeof(DiagnosticSeverity), severityAttribute.Value); @@ -54,9 +60,6 @@ public static CodeStyleOption FromXElement(XElement element) notificationOption = NotificationOption.None; break; case DiagnosticSeverity.Info: - notificationOption = NotificationOption.Info; - break; - case DiagnosticSeverity.Suggestion: notificationOption = NotificationOption.Suggestion; break; case DiagnosticSeverity.Warning: diff --git a/src/Workspaces/Core/Portable/CodeStyle/NotificationOption.cs b/src/Workspaces/Core/Portable/CodeStyle/NotificationOption.cs index 5159076e3f438..7f23e77b0a330 100644 --- a/src/Workspaces/Core/Portable/CodeStyle/NotificationOption.cs +++ b/src/Workspaces/Core/Portable/CodeStyle/NotificationOption.cs @@ -16,8 +16,7 @@ public class NotificationOption public DiagnosticSeverity Value { get; set; } public static readonly NotificationOption None = new NotificationOption(nameof(None), DiagnosticSeverity.Hidden); - public static readonly NotificationOption Info = new NotificationOption(nameof(Info), DiagnosticSeverity.Info); - public static readonly NotificationOption Suggestion = new NotificationOption(nameof(Suggestion), DiagnosticSeverity.Suggestion); + public static readonly NotificationOption Suggestion = new NotificationOption(nameof(Suggestion), DiagnosticSeverity.Info); public static readonly NotificationOption Warning = new NotificationOption(nameof(Warning), DiagnosticSeverity.Warning); public static readonly NotificationOption Error = new NotificationOption(nameof(Error), DiagnosticSeverity.Error); diff --git a/src/Workspaces/Core/Portable/PublicAPI.Unshipped.txt b/src/Workspaces/Core/Portable/PublicAPI.Unshipped.txt index 3de210177382a..efae2f53fbbd7 100644 --- a/src/Workspaces/Core/Portable/PublicAPI.Unshipped.txt +++ b/src/Workspaces/Core/Portable/PublicAPI.Unshipped.txt @@ -43,7 +43,6 @@ static readonly Microsoft.CodeAnalysis.CodeStyle.CodeStyleOptions.QualifyFieldAc static readonly Microsoft.CodeAnalysis.CodeStyle.CodeStyleOptions.QualifyMethodAccess -> Microsoft.CodeAnalysis.Options.PerLanguageOption> static readonly Microsoft.CodeAnalysis.CodeStyle.CodeStyleOptions.QualifyPropertyAccess -> Microsoft.CodeAnalysis.Options.PerLanguageOption> static readonly Microsoft.CodeAnalysis.CodeStyle.NotificationOption.Error -> Microsoft.CodeAnalysis.CodeStyle.NotificationOption -static readonly Microsoft.CodeAnalysis.CodeStyle.NotificationOption.Info -> Microsoft.CodeAnalysis.CodeStyle.NotificationOption static readonly Microsoft.CodeAnalysis.CodeStyle.NotificationOption.None -> Microsoft.CodeAnalysis.CodeStyle.NotificationOption static readonly Microsoft.CodeAnalysis.CodeStyle.NotificationOption.Suggestion -> Microsoft.CodeAnalysis.CodeStyle.NotificationOption static readonly Microsoft.CodeAnalysis.CodeStyle.NotificationOption.Warning -> Microsoft.CodeAnalysis.CodeStyle.NotificationOption From 99d62baf19fd0ab210d0cd81e90d1479e17719dc Mon Sep 17 00:00:00 2001 From: CyrusNajmabadi Date: Thu, 16 Jun 2016 12:26:10 -0700 Subject: [PATCH 18/36] Revert version. --- src/Workspaces/Core/Portable/CodeStyle/CodeStyleOption.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Workspaces/Core/Portable/CodeStyle/CodeStyleOption.cs b/src/Workspaces/Core/Portable/CodeStyle/CodeStyleOption.cs index cd71b4bb55b67..848ec96b721e4 100644 --- a/src/Workspaces/Core/Portable/CodeStyle/CodeStyleOption.cs +++ b/src/Workspaces/Core/Portable/CodeStyle/CodeStyleOption.cs @@ -12,7 +12,7 @@ public class CodeStyleOption { public static CodeStyleOption Default => new CodeStyleOption(default(T), NotificationOption.None); - private const int SerializationVersion = 2; + private const int SerializationVersion = 1; public CodeStyleOption(T value, NotificationOption notification) { From 93ebb390110f8473679255f5125e8f4e4c324eb7 Mon Sep 17 00:00:00 2001 From: Jared Parsons Date: Wed, 15 Jun 2016 15:00:21 -0700 Subject: [PATCH 19/36] Ensure experimental settings run against experimental features While working on another change I noticed that our testing support for experimental features didn't actually verify the underlying feature was experimental. Hence helpers like ParseExperimental(...) would continue to function without error when tuples / ref returns were attached to the /langver switch. This creates maintenance issues in our test framework as it's unclear what we're actually testing. I refactored the code involved to verify the features are actually experimental and removed some troublesome helpers. --- .../Emit/CodeGen/CodeGenAsyncSpillTests.cs | 2 +- .../Test/Emit/CodeGen/CodeGenOperators.cs | 2 +- .../Test/Emit/CodeGen/CodeGenRefLocalTests.cs | 85 ++++++++++++------ .../Emit/CodeGen/CodeGenRefReturnTests.cs | 89 +++++++++++-------- .../CSharp/Test/Emit/Emit/EmitMetadata.cs | 4 +- .../CSharp/Test/Emit/PDB/PDBTests.cs | 12 +-- .../DiagnosticAnalyzerTests.AllInOne.cs | 10 +-- .../Diagnostics/OperationAnalyzerTests.cs | 3 +- .../Test/Semantic/FlowAnalysis/FlowTests.cs | 8 +- .../Test/Semantic/Semantics/BindingTests.cs | 8 +- .../Test/Semantic/Semantics/ConstantTests.cs | 2 +- .../Semantics/InheritanceBindingTests.cs | 18 ++-- .../Test/Semantic/Semantics/LambdaTests.cs | 12 +-- .../Semantic/Semantics/LookupPositionTests.cs | 11 ++- .../Test/Semantic/Semantics/LookupTests.cs | 9 +- .../Semantics/PatternMatchingTests.cs | 5 +- .../Semantic/Semantics/RefLocalsAndReturns.cs | 44 +++++---- .../Semantic/Semantics/SemanticErrorTests.cs | 10 +-- .../Test/Semantic/Semantics/StructsTests.cs | 2 +- .../Compilation/SemanticModelAPITests.cs | 2 +- .../SemanticModelGetSemanticInfoTests.cs | 14 +-- .../Symbols/OverriddenOrHiddenMembersTests.cs | 4 +- .../Symbol/Symbols/Source/DelegateTests.cs | 2 +- .../Source/ExpressionBodiedMethodTests.cs | 2 +- .../Source/ExpressionBodiedPropertyTests.cs | 2 +- .../Test/Symbol/Symbols/Source/MethodTests.cs | 19 ++-- .../Symbol/Symbols/Source/PropertyTests.cs | 24 ++--- .../Test/Syntax/LexicalAndXml/LexicalTests.cs | 9 +- .../Syntax/Parsing/DeclarationParsingTests.cs | 14 +-- .../Syntax/Parsing/ExpressionParsingTests.cs | 8 +- .../Parsing/LocalFunctionParsingTests.cs | 5 +- .../Test/Syntax/Parsing/ParsingTests.cs | 4 +- .../Test/Syntax/Parsing/RoundTrippingTests.cs | 18 ++-- .../Syntax/Parsing/StatementParsingTests.cs | 12 +-- .../Syntax/Syntax/SyntaxAnnotationTests.cs | 16 ++-- ...CSharpCompilerTestUtilities.Desktop.csproj | 4 +- .../CSharp.Desktop/CSharpTestBase.cs | 13 +-- .../CSharp.Desktop/SemanticModelTestBase.cs | 4 +- .../Utilities/CSharp.Desktop/TestOptions.cs | 82 ----------------- .../Test/Utilities/CSharp/CSharpTestBase.cs | 17 ++-- .../Utilities/CSharp/SemanticModelTestBase.cs | 5 +- .../Test/Utilities/CSharp/TestOptions.cs | 48 +++++++--- .../Test/Utilities/VisualBasic/TestOptions.vb | 31 ++++--- .../Semantics/ConditionalAccessTests.vb | 2 +- .../DiagnosticAnalyzerDriverTests.cs | 2 +- 45 files changed, 359 insertions(+), 340 deletions(-) delete mode 100644 src/Compilers/Test/Utilities/CSharp.Desktop/TestOptions.cs diff --git a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncSpillTests.cs b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncSpillTests.cs index 3559bc4982cfe..f43608732191f 100644 --- a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncSpillTests.cs +++ b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncSpillTests.cs @@ -2120,7 +2120,7 @@ public static void Main() var expected = @" > 42 42"; - CompileAndVerifyExperimental(source, expected); + CompileAndVerify(source, expected); } [Fact] diff --git a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenOperators.cs b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenOperators.cs index f5a48ac19bf73..ea390e343e47b 100644 --- a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenOperators.cs +++ b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenOperators.cs @@ -1799,7 +1799,7 @@ static int Main() } }"; // the grammar does not allow a query on the right-hand-side of &&, but we allow it except in strict mode. - CreateCompilationWithMscorlibAndSystemCore(source, parseOptions: TestOptions.Regular.WithFeature("strict", "true")).VerifyDiagnostics( + CreateCompilationWithMscorlibAndSystemCore(source, parseOptions: TestOptions.Regular.WithStrictFeature()).VerifyDiagnostics( // (23,26): error CS1525: Invalid expression term 'from' // var b = false && from x in src select x; // WRN CS0429 Diagnostic(ErrorCode.ERR_InvalidExprTerm, "from x in src").WithArguments("from").WithLocation(23, 26), diff --git a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenRefLocalTests.cs b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenRefLocalTests.cs index 9017473b1e26f..6f42f3ab10156 100644 --- a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenRefLocalTests.cs +++ b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenRefLocalTests.cs @@ -1,6 +1,9 @@ // Copyright (c) Microsoft Open Technologies, Inc. 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.Linq; +using Microsoft.CodeAnalysis.CSharp.Symbols; using Microsoft.CodeAnalysis.CSharp.Test.Utilities; using Microsoft.CodeAnalysis.Test.Utilities; using Xunit; @@ -10,6 +13,32 @@ namespace Microsoft.CodeAnalysis.CSharp.UnitTests [CompilerTrait(CompilerFeature.RefLocalsReturns)] public class RefLocalTests : CompilingTestBase { + private CompilationVerifier CompileAndVerifyRef( + string source, + string expectedOutput = null, + MetadataReference[] additionalRefs = null, + IEnumerable dependencies = null, + Action sourceSymbolValidator = null, + Action assemblyValidator = null, + Action symbolValidator = null, + SignatureDescription[] expectedSignatures = null, + CSharpCompilationOptions options = null, + bool verify = true) + { + return CompileAndVerifyExperimental( + source, + MessageID.IDS_FeatureRefLocalsReturns, + expectedOutput, + additionalRefs, + dependencies, + sourceSymbolValidator, + assemblyValidator, + symbolValidator, + expectedSignatures, + options, + verify); + } + [Fact] public void RefAssignArrayAccess() { @@ -23,7 +52,7 @@ static void M() } "; - CompileAndVerifyExperimental(text, options: TestOptions.DebugDll).VerifyIL("Program.M()", @" + CompileAndVerifyRef(text, options: TestOptions.DebugDll).VerifyIL("Program.M()", @" { // Code size 15 (0xf) .maxstack 2 @@ -51,7 +80,7 @@ static void M(ref int i) } "; - CompileAndVerifyExperimental(text, options: TestOptions.DebugDll).VerifyIL("Program.M(ref int)", @" + CompileAndVerifyRef(text, options: TestOptions.DebugDll).VerifyIL("Program.M(ref int)", @" { // Code size 4 (0x4) .maxstack 1 @@ -77,7 +106,7 @@ static void M(out int i) } "; - CompileAndVerifyExperimental(text, options: TestOptions.DebugDll).VerifyIL("Program.M(out int)", @" + CompileAndVerifyRef(text, options: TestOptions.DebugDll).VerifyIL("Program.M(out int)", @" { // Code size 7 (0x7) .maxstack 2 @@ -106,7 +135,7 @@ static void M(ref int i) } "; - CompileAndVerifyExperimental(text, options: TestOptions.DebugDll).VerifyIL("Program.M(ref int)", @" + CompileAndVerifyRef(text, options: TestOptions.DebugDll).VerifyIL("Program.M(ref int)", @" { // Code size 6 (0x6) .maxstack 1 @@ -137,7 +166,7 @@ static void M() } "; - CompileAndVerifyExperimental(text, options: TestOptions.DebugDll, verify: false).VerifyIL("Program.M()", @" + CompileAndVerifyRef(text, options: TestOptions.DebugDll, verify: false).VerifyIL("Program.M()", @" { // Code size 8 (0x8) .maxstack 1 @@ -170,7 +199,7 @@ void M1() } "; - var comp = CompileAndVerifyExperimental(text, options: TestOptions.DebugDll, verify: false); + var comp = CompileAndVerifyRef(text, options: TestOptions.DebugDll, verify: false); comp.VerifyIL("Program.M()", @" { // Code size 9 (0x9) @@ -241,7 +270,7 @@ void M() } "; - var comp = CompileAndVerifyExperimental(text, options: TestOptions.DebugDll, verify: false); + var comp = CompileAndVerifyRef(text, options: TestOptions.DebugDll, verify: false); comp.VerifyIL("Program.M()", @" { // Code size 9 (0x9) @@ -331,7 +360,7 @@ void M() } "; - var comp = CompileAndVerifyExperimental(text, options: TestOptions.DebugDll); + var comp = CompileAndVerifyRef(text, options: TestOptions.DebugDll); comp.VerifyIL("Program.M()", @" { // Code size 20 (0x14) @@ -393,7 +422,7 @@ void M1() } "; - var comp = CompileAndVerifyExperimental(text, options: TestOptions.DebugDll, verify: false); + var comp = CompileAndVerifyRef(text, options: TestOptions.DebugDll, verify: false); comp.VerifyIL("Program.M()", @" { // Code size 10 (0xa) @@ -460,7 +489,7 @@ void M() } "; - var comp = CompileAndVerifyExperimental(text, options: TestOptions.DebugDll, verify: false); + var comp = CompileAndVerifyRef(text, options: TestOptions.DebugDll, verify: false); comp.VerifyIL("Program.M()", @" { // Code size 10 (0xa) @@ -542,7 +571,7 @@ void M() } "; - var comp = CompileAndVerifyExperimental(text, options: TestOptions.DebugDll); + var comp = CompileAndVerifyRef(text, options: TestOptions.DebugDll); comp.VerifyIL("Program.M()", @" { // Code size 21 (0x15) @@ -603,7 +632,7 @@ static void M() } "; - CompileAndVerifyExperimental(text, options: TestOptions.DebugDll).VerifyIL("Program.M()", @" + CompileAndVerifyRef(text, options: TestOptions.DebugDll).VerifyIL("Program.M()", @" { // Code size 8 (0x8) .maxstack 1 @@ -637,7 +666,7 @@ void M1() } "; - var comp = CompileAndVerifyExperimental(text, options: TestOptions.DebugDll); + var comp = CompileAndVerifyRef(text, options: TestOptions.DebugDll); comp.VerifyIL("Program.M()", @" { // Code size 9 (0x9) @@ -678,7 +707,7 @@ static void M() } "; - CompileAndVerifyExperimental(text, options: TestOptions.DebugDll).VerifyIL("Program.M()", @" + CompileAndVerifyRef(text, options: TestOptions.DebugDll).VerifyIL("Program.M()", @" { // Code size 15 (0xf) .maxstack 2 @@ -713,7 +742,7 @@ void M1() } "; - var comp = CompileAndVerifyExperimental(text, options: TestOptions.DebugDll); + var comp = CompileAndVerifyRef(text, options: TestOptions.DebugDll); comp.VerifyIL("Program.M()", @" { // Code size 9 (0x9) @@ -764,7 +793,7 @@ void M() } "; - var comp = CompileAndVerifyExperimental(text, options: TestOptions.DebugDll); + var comp = CompileAndVerifyRef(text, options: TestOptions.DebugDll); comp.VerifyIL("Program2.M(ref Program)", @" { // Code size 17 (0x11) @@ -808,7 +837,7 @@ static ref int M() } "; - CompileAndVerifyExperimental(text, options: TestOptions.DebugDll, verify: false).VerifyIL("Program.M()", @" + CompileAndVerifyRef(text, options: TestOptions.DebugDll, verify: false).VerifyIL("Program.M()", @" { // Code size 13 (0xd) .maxstack 1 @@ -845,7 +874,7 @@ ref int M1() } "; - var comp = CompileAndVerifyExperimental(text, options: TestOptions.DebugDll, verify: false); + var comp = CompileAndVerifyRef(text, options: TestOptions.DebugDll, verify: false); comp.VerifyIL("Program.M()", @" { // Code size 14 (0xe) @@ -916,7 +945,7 @@ ref int M() } "; - var comp = CompileAndVerifyExperimental(text, options: TestOptions.DebugDll, verify: false); + var comp = CompileAndVerifyRef(text, options: TestOptions.DebugDll, verify: false); comp.VerifyIL("Program.M()", @" { // Code size 14 (0xe) @@ -1013,7 +1042,7 @@ ref int M() } "; - var comp = CompileAndVerifyExperimental(text, options: TestOptions.DebugDll, verify: false); + var comp = CompileAndVerifyRef(text, options: TestOptions.DebugDll, verify: false); comp.VerifyIL("Program.M()", @" { // Code size 25 (0x19) @@ -1083,7 +1112,7 @@ static ref int M(ref int i, ref int j, object o) } "; - CompileAndVerifyExperimental(text, options: TestOptions.DebugDll, verify: false).VerifyIL("Program.M(ref int, ref int, object)", @" + CompileAndVerifyRef(text, options: TestOptions.DebugDll, verify: false).VerifyIL("Program.M(ref int, ref int, object)", @" { // Code size 16 (0x10) .maxstack 3 @@ -1123,7 +1152,7 @@ ref int M1(ref int i, ref int j, object o) } "; - var comp = CompileAndVerifyExperimental(text, options: TestOptions.DebugDll, verify: false); + var comp = CompileAndVerifyRef(text, options: TestOptions.DebugDll, verify: false); comp.VerifyIL("Program.M(ref int, ref int, object)", @" { // Code size 17 (0x11) @@ -1200,7 +1229,7 @@ ref int M(ref int i, ref int j, object o) } "; - var comp = CompileAndVerifyExperimental(text, options: TestOptions.DebugDll, verify: false); + var comp = CompileAndVerifyRef(text, options: TestOptions.DebugDll, verify: false); comp.VerifyIL("Program.M(ref int, ref int, object)", @" { // Code size 17 (0x11) @@ -1306,7 +1335,7 @@ ref int M(ref int i, ref int j, object o) } "; - var comp = CompileAndVerifyExperimental(text, options: TestOptions.DebugDll, verify: false); + var comp = CompileAndVerifyRef(text, options: TestOptions.DebugDll, verify: false); comp.VerifyIL("Program.M(ref int, ref int, object)", @" { // Code size 28 (0x1c) @@ -1386,7 +1415,7 @@ static void M(D d) } "; - CompileAndVerifyExperimental(text, options: TestOptions.DebugDll).VerifyIL("Program.M(D)", @" + CompileAndVerifyRef(text, options: TestOptions.DebugDll).VerifyIL("Program.M(D)", @" { // Code size 9 (0x9) .maxstack 1 @@ -1415,7 +1444,7 @@ static ref int M(D d, ref int i, ref int j, object o) } "; - CompileAndVerifyExperimental(text, options: TestOptions.DebugDll, verify: false).VerifyIL("Program.M(D, ref int, ref int, object)", @" + CompileAndVerifyRef(text, options: TestOptions.DebugDll, verify: false).VerifyIL("Program.M(D, ref int, ref int, object)", @" { // Code size 17 (0x11) .maxstack 4 @@ -1467,7 +1496,7 @@ static unsafe void Main() } "; - CompileAndVerifyExperimental(text, options: TestOptions.UnsafeDebugDll).VerifyIL("Program.Main()", @" + CompileAndVerifyRef(text, options: TestOptions.UnsafeDebugDll).VerifyIL("Program.Main()", @" { // Code size 51 (0x33) .maxstack 3 @@ -1536,7 +1565,7 @@ static unsafe int Main() } "; - CompileAndVerifyExperimental(text, options: TestOptions.UnsafeDebugDll).VerifyIL("Program.Main()", @" + CompileAndVerifyRef(text, options: TestOptions.UnsafeDebugDll).VerifyIL("Program.Main()", @" { // Code size 41 (0x29) .maxstack 2 diff --git a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenRefReturnTests.cs b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenRefReturnTests.cs index c54721d492844..3d1cd52221588 100644 --- a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenRefReturnTests.cs +++ b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenRefReturnTests.cs @@ -14,6 +14,20 @@ namespace Microsoft.CodeAnalysis.CSharp.UnitTests { public class RefReturnTests : CompilingTestBase { + private CompilationVerifier CompileAndVerifyRef( + string source, + string expectedOutput = null, + CSharpCompilationOptions options = null, + bool verify = true) + { + return CompileAndVerifyExperimental( + source, + MessageID.IDS_FeatureRefLocalsReturns, + expectedOutput: expectedOutput, + options: options, + verify: verify); + } + [Fact] public void RefReturnArrayAccess() { @@ -27,7 +41,7 @@ static ref int M() } "; - CompileAndVerifyExperimental(text).VerifyIL("Program.M()", @" + CompileAndVerifyRef(text).VerifyIL("Program.M()", @" { // Code size 13 (0xd) .maxstack 2 @@ -52,7 +66,7 @@ static ref int M(ref int i) } "; - CompileAndVerifyExperimental(text, verify: false).VerifyIL("Program.M(ref int)", @" + CompileAndVerifyRef(text, verify: false).VerifyIL("Program.M(ref int)", @" { // Code size 2 (0x2) .maxstack 1 @@ -75,7 +89,7 @@ static ref int M(out int i) } "; - CompileAndVerifyExperimental(text, verify: false).VerifyIL("Program.M(out int)", @" + CompileAndVerifyRef(text, verify: false).VerifyIL("Program.M(out int)", @" { // Code size 5 (0x5) .maxstack 2 @@ -102,7 +116,7 @@ static ref int M(ref int i) } "; - CompileAndVerifyExperimental(text, verify: false).VerifyIL("Program.M(ref int)", @" + CompileAndVerifyRef(text, verify: false).VerifyIL("Program.M(ref int)", @" { // Code size 5 (0x5) .maxstack 3 @@ -130,7 +144,7 @@ static ref int M() } "; - CompileAndVerifyExperimental(text).VerifyIL("Program.M()", @" + CompileAndVerifyRef(text).VerifyIL("Program.M()", @" { // Code size 6 (0x6) .maxstack 1 @@ -160,7 +174,7 @@ ref int M1() } "; - var compilation = CompileAndVerifyExperimental(text); + var compilation = CompileAndVerifyRef(text); compilation.VerifyIL("Program.M()", @" { // Code size 7 (0x7) @@ -224,7 +238,7 @@ ref int M() } "; - var compilation = CompileAndVerifyExperimental(text, verify: false); + var compilation = CompileAndVerifyRef(text, verify: false); compilation.VerifyIL("Program.M()", @" { // Code size 7 (0x7) @@ -302,7 +316,7 @@ ref int M() } "; - var compilation = CompileAndVerifyExperimental(text); + var compilation = CompileAndVerifyRef(text); compilation.VerifyIL("Program.M()", @" { // Code size 18 (0x12) @@ -355,7 +369,7 @@ ref int M1() } "; - var compilation = CompileAndVerifyExperimental(text); + var compilation = CompileAndVerifyRef(text); compilation.VerifyIL("Program.M()", @" { // Code size 8 (0x8) @@ -416,7 +430,7 @@ ref int M() } "; - var compilation = CompileAndVerifyExperimental(text, verify: false); + var compilation = CompileAndVerifyRef(text, verify: false); compilation.VerifyIL("Program.M()", @" { // Code size 8 (0x8) @@ -489,7 +503,7 @@ ref int M() } "; - var compilation = CompileAndVerifyExperimental(text); + var compilation = CompileAndVerifyRef(text); compilation.VerifyIL("Program.M()", @" { // Code size 19 (0x13) @@ -541,7 +555,7 @@ static ref D M() } "; - CompileAndVerifyExperimental(text).VerifyIL("Program.M()", @" + CompileAndVerifyRef(text).VerifyIL("Program.M()", @" { // Code size 6 (0x6) .maxstack 1 @@ -572,7 +586,7 @@ ref D M1() } "; - var compilation = CompileAndVerifyExperimental(text); + var compilation = CompileAndVerifyRef(text); compilation.VerifyIL("Program.M()", @" { // Code size 7 (0x7) @@ -606,7 +620,7 @@ static ref int M() } "; - CompileAndVerifyExperimental(text).VerifyIL("Program.M()", @" + CompileAndVerifyRef(text).VerifyIL("Program.M()", @" { // Code size 6 (0x6) .maxstack 1 @@ -635,7 +649,7 @@ ref int M1() } "; - var compilation = CompileAndVerifyExperimental(text); + var compilation = CompileAndVerifyRef(text); compilation.VerifyIL("Program.M()", @" { // Code size 7 (0x7) @@ -679,7 +693,7 @@ ref int M() } "; - var compilation = CompileAndVerifyExperimental(text, verify: false); + var compilation = CompileAndVerifyRef(text, verify: false); compilation.VerifyIL("Program2.M(ref Program)", @" { // Code size 7 (0x7) @@ -712,7 +726,7 @@ static ref int M() } "; - CompileAndVerifyExperimental(text).VerifyIL("Program.M()", @" + CompileAndVerifyRef(text).VerifyIL("Program.M()", @" { // Code size 6 (0x6) .maxstack 1 @@ -739,7 +753,7 @@ ref int M1() } "; - var compilation = CompileAndVerifyExperimental(text); + var compilation = CompileAndVerifyRef(text); compilation.VerifyIL("Program.M()", @" { // Code size 7 (0x7) @@ -791,7 +805,7 @@ ref int M() } "; - var compilation = CompileAndVerifyExperimental(text); + var compilation = CompileAndVerifyRef(text); compilation.VerifyIL("Program.M()", @" { // Code size 7 (0x7) @@ -861,7 +875,7 @@ ref int M() } "; - var compilation = CompileAndVerifyExperimental(text); + var compilation = CompileAndVerifyRef(text); compilation.VerifyIL("Program.M()", @" { // Code size 18 (0x12) @@ -906,7 +920,7 @@ static ref int M(ref int i, ref int j, object o) } "; - CompileAndVerifyExperimental(text).VerifyIL("Program.M(ref int, ref int, object)", @" + CompileAndVerifyRef(text).VerifyIL("Program.M(ref int, ref int, object)", @" { // Code size 9 (0x9) .maxstack 3 @@ -936,7 +950,7 @@ ref int M1(ref int i, ref int j, object o) } "; - var compilation = CompileAndVerifyExperimental(text); + var compilation = CompileAndVerifyRef(text); compilation.VerifyIL("Program.M(ref int, ref int, object)", @" { // Code size 10 (0xa) @@ -994,7 +1008,7 @@ ref int M(ref int i, ref int j, object o) } "; - var compilation = CompileAndVerifyExperimental(text); + var compilation = CompileAndVerifyRef(text); compilation.VerifyIL("Program.M(ref int, ref int, object)", @" { // Code size 10 (0xa) @@ -1073,7 +1087,7 @@ ref int M(ref int i, ref int j, object o) } "; - var compilation = CompileAndVerifyExperimental(text); + var compilation = CompileAndVerifyRef(text); compilation.VerifyIL("Program.M(ref int, ref int, object)", @" { // Code size 21 (0x15) @@ -1129,7 +1143,7 @@ static ref int M(D d) } "; - CompileAndVerifyExperimental(text).VerifyIL("Program.M(D)", @" + CompileAndVerifyRef(text).VerifyIL("Program.M(D)", @" { // Code size 7 (0x7) .maxstack 1 @@ -1154,7 +1168,7 @@ static ref int M(D d, ref int i, ref int j, object o) } "; - CompileAndVerifyExperimental(text).VerifyIL("Program.M(D, ref int, ref int, object)", @" + CompileAndVerifyRef(text).VerifyIL("Program.M(D, ref int, ref int, object)", @" { // Code size 10 (0xa) .maxstack 4 @@ -1217,7 +1231,7 @@ static unsafe void Main() } "; - CompileAndVerifyExperimental(text, options: TestOptions.UnsafeReleaseDll).VerifyIL("Program.Main()", @" + CompileAndVerifyRef(text, options: TestOptions.UnsafeReleaseDll).VerifyIL("Program.Main()", @" { // Code size 285 (0x11d) .maxstack 4 @@ -1402,7 +1416,7 @@ static unsafe int Main() } "; - CompileAndVerifyExperimental(text, options: TestOptions.UnsafeReleaseDll).VerifyIL("Program.Main()", @" + CompileAndVerifyRef(text, options: TestOptions.UnsafeReleaseDll).VerifyIL("Program.Main()", @" { // Code size 168 (0xa8) .maxstack 4 @@ -1505,7 +1519,8 @@ ref int N() } "; - CompileAndVerifyExperimental(text).VerifyIL("Program.M()", @" + var options = TestOptions.Regular.WithRefsFeature().WithLocalFunctionsFeature(); + CompileAndVerify(text, parseOptions: options).VerifyIL("Program.M()", @" { // Code size 6 (0x6) .maxstack 1 @@ -1553,7 +1568,8 @@ static void Main() } "; - CompileAndVerifyExperimental(text, expectedOutput: "42", verify: false).VerifyIL("Program.M()", @" + var options = TestOptions.Regular.WithRefsFeature().WithLocalFunctionsFeature(); + CompileAndVerify(text, parseOptions: options, expectedOutput: "42", verify: false).VerifyIL("Program.M()", @" { // Code size 34 (0x22) .maxstack 5 @@ -1628,7 +1644,8 @@ static void Main() } "; - CompileAndVerifyExperimental(text, expectedOutput: "42", verify: false).VerifyIL("Program.M()", @" + var options = TestOptions.Regular.WithRefsFeature().WithLocalFunctionsFeature(); + CompileAndVerify(text, parseOptions: options, expectedOutput: "42", verify: false).VerifyIL("Program.M()", @" { // Code size 36 (0x24) .maxstack 5 @@ -1722,7 +1739,7 @@ public override string ToString() } "; - CompileAndVerifyExperimental(text, expectedOutput: "12") + CompileAndVerifyRef(text, expectedOutput: "12") .VerifyIL("Program.C1.Test()", @" { // Code size 114 (0x72) @@ -1814,7 +1831,7 @@ public override string ToString() } "; - CompileAndVerifyExperimental(text, expectedOutput: "12", verify: false) + CompileAndVerifyRef(text, expectedOutput: "12", verify: false) .VerifyIL("Program.C1.Test()", @" { // Code size 115 (0x73) @@ -1912,7 +1929,7 @@ public override string ToString() } "; - CompileAndVerifyExperimental(text, expectedOutput: "1234", verify: false) + CompileAndVerifyRef(text, expectedOutput: "1234", verify: false) .VerifyIL("Program.C1.Test()", @" { // Code size 129 (0x81) @@ -2017,7 +2034,7 @@ public void Blah(ref Foo arg) } "; - CompileAndVerifyExperimental(text, expectedOutput: "1True", verify: false) + CompileAndVerifyRef(text, expectedOutput: "1True", verify: false) .VerifyIL("Program.C1.Test()", @" { // Code size 84 (0x54) @@ -2115,7 +2132,7 @@ public void Blah(ref Foo arg) } "; - CompileAndVerifyExperimental(text, expectedOutput: "1TrueTrue1TrueTrueTrueTrue1TrueTrue", verify: false) + CompileAndVerifyRef(text, expectedOutput: "1TrueTrue1TrueTrueTrueTrue1TrueTrue", verify: false) .VerifyIL("Program.C1.Test()", @" { // Code size 215 (0xd7) diff --git a/src/Compilers/CSharp/Test/Emit/Emit/EmitMetadata.cs b/src/Compilers/CSharp/Test/Emit/Emit/EmitMetadata.cs index 1d47ea7a9cd65..1700cb841a8d9 100644 --- a/src/Compilers/CSharp/Test/Emit/Emit/EmitMetadata.cs +++ b/src/Compilers/CSharp/Test/Emit/Emit/EmitMetadata.cs @@ -893,7 +893,7 @@ static void Main() Console.Write(c.R); Console.Write(C.S); } -}", parseOptions: TestOptions.ExperimentalParseOptions, +}", parseOptions: TestOptions.Regular, options: TestOptions.ReleaseExe.WithMetadataImportOptions(MetadataImportOptions.Internal)); Action validator = module => { @@ -963,7 +963,7 @@ static void Main() Console.Write(s.R); Console.Write(S.T); } -}", parseOptions: TestOptions.ExperimentalParseOptions, +}", parseOptions: TestOptions.Regular, options: TestOptions.ReleaseExe.WithMetadataImportOptions(MetadataImportOptions.Internal)); Action validator = module => diff --git a/src/Compilers/CSharp/Test/Emit/PDB/PDBTests.cs b/src/Compilers/CSharp/Test/Emit/PDB/PDBTests.cs index 032365c16c1ff..5b03f601839b1 100644 --- a/src/Compilers/CSharp/Test/Emit/PDB/PDBTests.cs +++ b/src/Compilers/CSharp/Test/Emit/PDB/PDBTests.cs @@ -2840,7 +2840,7 @@ public class C public class C { int P { get; } = [|int.Parse(""42"")|]; -}", TestOptions.DebugDll, TestOptions.ExperimentalParseOptions); +}", TestOptions.DebugDll, TestOptions.Regular); } #endregion @@ -4795,7 +4795,7 @@ public static void f(int a) [Fact] public void ExpressionBodiedProperty() { - var comp = CreateExperimentalCompilationWithMscorlib45(@" + var comp = CreateCompilationWithMscorlib45(@" class C { public int P => M(); @@ -4833,7 +4833,7 @@ public int M() [Fact] public void ExpressionBodiedIndexer() { - var comp = CreateExperimentalCompilationWithMscorlib45(@" + var comp = CreateCompilationWithMscorlib45(@" using System; class C @@ -4877,7 +4877,7 @@ public int M() [Fact] public void ExpressionBodiedMethod() { - var comp = CreateExperimentalCompilationWithMscorlib45(@" + var comp = CreateCompilationWithMscorlib45(@" using System; class C @@ -4909,7 +4909,7 @@ class C [Fact] public void ExpressionBodiedOperator() { - var comp = CreateExperimentalCompilationWithMscorlib45(@" + var comp = CreateCompilationWithMscorlib45(@" class C { public static C operator ++(C c) => c; @@ -4936,7 +4936,7 @@ class C [Fact] public void ExpressionBodiedConversion() { - var comp = CreateExperimentalCompilationWithMscorlib45(@" + var comp = CreateCompilationWithMscorlib45(@" using System; class C diff --git a/src/Compilers/CSharp/Test/Semantic/Diagnostics/DiagnosticAnalyzerTests.AllInOne.cs b/src/Compilers/CSharp/Test/Semantic/Diagnostics/DiagnosticAnalyzerTests.AllInOne.cs index 2b553397438c7..6af80aeefae2a 100644 --- a/src/Compilers/CSharp/Test/Semantic/Diagnostics/DiagnosticAnalyzerTests.AllInOne.cs +++ b/src/Compilers/CSharp/Test/Semantic/Diagnostics/DiagnosticAnalyzerTests.AllInOne.cs @@ -37,7 +37,7 @@ public void DiagnosticAnalyzerAllInOne() syntaxKindsPatterns.Add(SyntaxKind.OriginalExpression); var analyzer = new CSharpTrackingDiagnosticAnalyzer(); - CreateExperimentalCompilationWithMscorlib45(source).VerifyAnalyzerDiagnostics(new[] { analyzer }); + CreateCompilationWithMscorlib45(source).VerifyAnalyzerDiagnostics(new[] { analyzer }); analyzer.VerifyAllAnalyzerMembersWereCalled(); analyzer.VerifyAnalyzeSymbolCalledForAllSymbolKinds(); analyzer.VerifyAnalyzeNodeCalledForAllSyntaxKinds(syntaxKindsPatterns); @@ -77,13 +77,13 @@ public string this[int index] } } "; - CreateExperimentalCompilationWithMscorlib45(source).VerifyAnalyzerDiagnostics(new[] { new CSharpTrackingDiagnosticAnalyzer() }); + CreateCompilationWithMscorlib45(source).VerifyAnalyzerDiagnostics(new[] { new CSharpTrackingDiagnosticAnalyzer() }); } [Fact] public void DiagnosticAnalyzerExpressionBodiedProperty() { - var comp = CreateExperimentalCompilationWithMscorlib45(@" + var comp = CreateCompilationWithMscorlib45(@" public class C { public int P => 10; @@ -96,7 +96,7 @@ public class C [WorkItem(759, "https://github.com/dotnet/roslyn/issues/759")] public void AnalyzerDriverIsSafeAgainstAnalyzerExceptions() { - var compilation = CreateExperimentalCompilationWithMscorlib45(TestResource.AllInOneCSharpCode); + var compilation = CreateCompilationWithMscorlib45(TestResource.AllInOneCSharpCode); ThrowingDiagnosticAnalyzer.VerifyAnalyzerEngineIsSafeAgainstExceptions(analyzer => compilation.GetAnalyzerDiagnostics(new[] { analyzer }, null, logAnalyzerExceptionAsDiagnostics: true)); } @@ -110,7 +110,7 @@ public void AnalyzerOptionsArePassedToAllAnalyzers() new[] { new TestAdditionalText("myfilepath", text) }.ToImmutableArray() ); - var compilation = CreateExperimentalCompilationWithMscorlib45(TestResource.AllInOneCSharpCode); + var compilation = CreateCompilationWithMscorlib45(TestResource.AllInOneCSharpCode); var analyzer = new OptionsDiagnosticAnalyzer(options); compilation.GetAnalyzerDiagnostics(new[] { analyzer }, options); analyzer.VerifyAnalyzerOptions(); diff --git a/src/Compilers/CSharp/Test/Semantic/Diagnostics/OperationAnalyzerTests.cs b/src/Compilers/CSharp/Test/Semantic/Diagnostics/OperationAnalyzerTests.cs index 9072e82117c4e..95f593b0df7a7 100644 --- a/src/Compilers/CSharp/Test/Semantic/Diagnostics/OperationAnalyzerTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Diagnostics/OperationAnalyzerTests.cs @@ -14,8 +14,7 @@ namespace Microsoft.CodeAnalysis.CSharp.UnitTests public class OperationAnalyzerTests : CompilingTestBase { private readonly static CSharpParseOptions patternParseOptions = - TestOptions.Regular.WithLanguageVersion(LanguageVersion.CSharp6) - .WithFeature(MessageID.IDS_FeaturePatternMatching.RequiredFeature(), "true"); + TestOptions.Regular.WithLanguageVersion(LanguageVersion.CSharp6).WithPatternsFeature(); [Fact] public void EmptyArrayCSharp() diff --git a/src/Compilers/CSharp/Test/Semantic/FlowAnalysis/FlowTests.cs b/src/Compilers/CSharp/Test/Semantic/FlowAnalysis/FlowTests.cs index 1ea6933e61698..702fcced6df5e 100644 --- a/src/Compilers/CSharp/Test/Semantic/FlowAnalysis/FlowTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/FlowAnalysis/FlowTests.cs @@ -2231,7 +2231,7 @@ static void Main() } } "; - CreateExperimentalCompilationWithMscorlib45(source).VerifyDiagnostics( + CreateCompilationWithMscorlib45(source).VerifyDiagnostics( // (17,34): error CS0165: Use of unassigned local variable 'o' // System.Console.WriteLine(o); Diagnostic(ErrorCode.ERR_UseDefViolation, "o").WithArguments("o").WithLocation(17, 34) @@ -2261,7 +2261,7 @@ static void Main() } } "; - CreateExperimentalCompilationWithMscorlib45(source).VerifyDiagnostics( + CreateCompilationWithMscorlib45(source).VerifyDiagnostics( // (17,34): error CS0165: Use of unassigned local variable 'o' // System.Console.WriteLine(o); Diagnostic(ErrorCode.ERR_UseDefViolation, "o").WithArguments("o").WithLocation(17, 34) @@ -2294,7 +2294,7 @@ static void Main() } } "; - CreateExperimentalCompilationWithMscorlib45(source).VerifyDiagnostics(); + CreateCompilationWithMscorlib45(source).VerifyDiagnostics(); } [Fact] @@ -2323,7 +2323,7 @@ static void Main() } } "; - CreateExperimentalCompilationWithMscorlib45(source).VerifyDiagnostics( + CreateCompilationWithMscorlib45(source).VerifyDiagnostics( ); } } diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/BindingTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/BindingTests.cs index 05bd5225f569e..110664f11c87f 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/BindingTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/BindingTests.cs @@ -2375,7 +2375,7 @@ void Test() } "; - CreateExperimentalCompilationWithMscorlib45(text).VerifyDiagnostics(); + CreateExperimentalCompilationWithMscorlib45(text, MessageID.IDS_FeatureRefLocalsReturns).VerifyDiagnostics(); } [Fact] @@ -2400,7 +2400,7 @@ void Test() } "; - CreateExperimentalCompilationWithMscorlib45(text).VerifyDiagnostics(); + CreateExperimentalCompilationWithMscorlib45(text, MessageID.IDS_FeatureRefLocalsReturns).VerifyDiagnostics(); } [Fact] @@ -2429,7 +2429,7 @@ void Test() } "; - CreateExperimentalCompilationWithMscorlib45(text).VerifyDiagnostics(); + CreateExperimentalCompilationWithMscorlib45(text, MessageID.IDS_FeatureRefLocalsReturns).VerifyDiagnostics(); } [Fact] @@ -2458,7 +2458,7 @@ void Test() } "; - CreateExperimentalCompilationWithMscorlib45(text).VerifyDiagnostics(); + CreateExperimentalCompilationWithMscorlib45(text, MessageID.IDS_FeatureRefLocalsReturns).VerifyDiagnostics(); } [Fact, WorkItem(1078958, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/1078958")] diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/ConstantTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/ConstantTests.cs index 5bb08e4d58bf5..dc2b5610b2f43 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/ConstantTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/ConstantTests.cs @@ -176,7 +176,7 @@ static void Main(string[] args) } } "; - var comp = CreateExperimentalCompilationWithMscorlib45(source); + var comp = CreateCompilationWithMscorlib45(source); comp.VerifyDiagnostics( // (10,12): error CS0568: Structs cannot contain explicit parameterless constructors // public S2() diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/InheritanceBindingTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/InheritanceBindingTests.cs index 237a31444e8bd..1f560f8689baa 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/InheritanceBindingTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/InheritanceBindingTests.cs @@ -1271,7 +1271,7 @@ class Derived : Base } "; - CreateExperimentalCompilationWithMscorlib45(text).VerifyDiagnostics( + CreateExperimentalCompilationWithMscorlib45(text, MessageID.IDS_FeatureRefLocalsReturns).VerifyDiagnostics( // (13,29): error CS8893: 'Derived.Method1()' must not return by reference to match overridden member 'Base.Method1()' // public override ref int Method1() { return ref field; } Diagnostic(ErrorCode.ERR_CantChangeRefReturnOnOverride, "Method1").WithArguments("Derived.Method1()", "Base.Method1()", "not ").WithLocation(13, 29), @@ -1417,7 +1417,7 @@ class Derived : Base public override ref int Property3 { get { return ref field; } } } "; - CreateExperimentalCompilationWithMscorlib45(text).VerifyDiagnostics( + CreateExperimentalCompilationWithMscorlib45(text, MessageID.IDS_FeatureRefLocalsReturns).VerifyDiagnostics( // (15,29): error CS8893: 'Derived.Proprty1' must not return by reference to match overridden member 'Base.Proprty1' // public override ref int Proprty1 { get { return ref field; } } Diagnostic(ErrorCode.ERR_CantChangeRefReturnOnOverride, "Proprty1").WithArguments("Derived.Proprty1", "Base.Proprty1", "not ").WithLocation(15, 29), @@ -1477,7 +1477,7 @@ class Derived : Base public override ref int this[string x, int y] { get { return ref field; } } } "; - CreateExperimentalCompilationWithMscorlib45(text).VerifyDiagnostics( + CreateExperimentalCompilationWithMscorlib45(text, MessageID.IDS_FeatureRefLocalsReturns).VerifyDiagnostics( // (15,29): error CS8893: 'Derived.this[int, int]' must not return by reference to match overridden member 'Base.this[int, int]' // public override ref int this[int x, int y] { get { return ref field; } } Diagnostic(ErrorCode.ERR_CantChangeRefReturnOnOverride, "this").WithArguments("Derived.this[int, int]", "Base.this[int, int]", "not ").WithLocation(15, 29), @@ -2051,7 +2051,7 @@ class Derived : Base public override ref object Method6(ref object o) { return ref o; } //wrong by-ref return } "; - CreateExperimentalCompilationWithMscorlib45(text).VerifyDiagnostics( + CreateExperimentalCompilationWithMscorlib45(text, MessageID.IDS_FeatureRefLocalsReturns).VerifyDiagnostics( // (16,16): warning CS0114: 'Derived.Method3()' hides inherited member 'Base.Method3()'. To make the current member override that implementation, add the override keyword. Otherwise add the new keyword. // public int Method3() { return 0; } //wrong return type Diagnostic(ErrorCode.WRN_NewOrOverrideExpected, "Method3").WithArguments("Derived.Method3()", "Base.Method3()").WithLocation(16, 16), @@ -2120,7 +2120,7 @@ public override object Property7 { set { } } public override object Property11 { get { return null; } } } "; - CreateExperimentalCompilationWithMscorlib45(text).VerifyDiagnostics( + CreateExperimentalCompilationWithMscorlib45(text, MessageID.IDS_FeatureRefLocalsReturns).VerifyDiagnostics( // (23,25): error CS1715: 'Derived.Property3': type must be 'object' to match overridden member 'Base.Property3' // public override int Property3 { get; set; } //wrong type Diagnostic(ErrorCode.ERR_CantChangeTypeOnOverride, "Property3").WithArguments("Derived.Property3", "Base.Property3", "object").WithLocation(23, 25), @@ -2225,7 +2225,7 @@ class Derived : Base public override object this[string w, int x, string y, int z] { get; } } "; - CreateExperimentalCompilationWithMscorlib45(text).VerifyDiagnostics( + CreateExperimentalCompilationWithMscorlib45(text, MessageID.IDS_FeatureRefLocalsReturns).VerifyDiagnostics( // (36,69): error CS0501: 'Derived.this[string, int, string, int].get' must declare a body because it is not marked abstract, extern, or partial // public override object this[string w, int x, string y, int z] { get; } Diagnostic(ErrorCode.ERR_ConcreteMissingBody, "get").WithArguments("Derived.this[string, int, string, int].get").WithLocation(36, 69), @@ -2489,7 +2489,7 @@ class Class : Interface public ref object Method4(ref object o) { return ref o; } //wrong by-ref return } "; - CreateExperimentalCompilationWithMscorlib45(text).VerifyDiagnostics( + CreateExperimentalCompilationWithMscorlib45(text, MessageID.IDS_FeatureRefLocalsReturns).VerifyDiagnostics( // (10,15): error CS8897: 'Class' does not implement interface member 'Interface.Method4(ref object)'. 'Class.Method4(ref object)' cannot implement 'Interface.Method4(ref object)' because it does not return by value // class Class : Interface Diagnostic(ErrorCode.ERR_CloseUnimplementedInterfaceMemberWrongRefReturn, "Interface").WithArguments("Class", "Interface.Method4(ref object)", "Class.Method4(ref object)", "value").WithLocation(10, 15), @@ -2542,7 +2542,7 @@ public object Property5 { set { } } public ref object Property9 { get { return ref o; } } } "; - CreateExperimentalCompilationWithMscorlib45(text).VerifyDiagnostics( + CreateExperimentalCompilationWithMscorlib45(text, MessageID.IDS_FeatureRefLocalsReturns).VerifyDiagnostics( // (17,15): error CS0535: 'Class' does not implement interface member 'Interface.Property2.set' // class Class : Interface Diagnostic(ErrorCode.ERR_UnimplementedInterfaceMember, "Interface").WithArguments("Class", "Interface.Property2.set").WithLocation(17, 15), @@ -2604,7 +2604,7 @@ class Class : Interface public ref object this[string w, int x, int y, string z] { get { return ref o; } } } "; - CreateExperimentalCompilationWithMscorlib45(text).VerifyDiagnostics( + CreateExperimentalCompilationWithMscorlib45(text, MessageID.IDS_FeatureRefLocalsReturns).VerifyDiagnostics( // (17,15): error CS0535: 'Class' does not implement interface member 'Interface.this[int, string, string, string].set' // class Class : Interface Diagnostic(ErrorCode.ERR_UnimplementedInterfaceMember, "Interface").WithArguments("Class", "Interface.this[int, string, string, string].set").WithLocation(17, 15), diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/LambdaTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/LambdaTests.cs index 2f2b5715fbf3b..f2d159a1e078e 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/LambdaTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/LambdaTests.cs @@ -1119,7 +1119,7 @@ static void M() } "; - CreateExperimentalCompilationWithMscorlib45(text).VerifyDiagnostics(); + CreateExperimentalCompilationWithMscorlib45(text, MessageID.IDS_FeatureRefLocalsReturns).VerifyDiagnostics(); } @@ -1141,7 +1141,7 @@ static void M() } "; - CreateExperimentalCompilationWithMscorlib45(text).VerifyDiagnostics(); + CreateExperimentalCompilationWithMscorlib45(text, MessageID.IDS_FeatureRefLocalsReturns).VerifyDiagnostics(); } [Fact] @@ -1169,7 +1169,7 @@ static void M() } "; - CreateExperimentalCompilationWithMscorlib45(text).VerifyDiagnostics(); + CreateExperimentalCompilationWithMscorlib45(text, MessageID.IDS_FeatureRefLocalsReturns).VerifyDiagnostics(); } [Fact] @@ -1192,7 +1192,7 @@ static void M() } "; - CreateExperimentalCompilationWithMscorlib45(text).VerifyDiagnostics( + CreateExperimentalCompilationWithMscorlib45(text, MessageID.IDS_FeatureRefLocalsReturns).VerifyDiagnostics( // (11,22): error CS8083: By-reference returns may only be used in by-reference returning methods. // ME(() => ref i); Diagnostic(ErrorCode.ERR_MustNotHaveRefReturn, "i").WithLocation(11, 22), @@ -1222,7 +1222,7 @@ static void M() } "; - CreateExperimentalCompilationWithMscorlib45(text).VerifyDiagnostics( + CreateExperimentalCompilationWithMscorlib45(text, MessageID.IDS_FeatureRefLocalsReturns).VerifyDiagnostics( // (9,33): error CS8083: By-reference returns may only be used in by-reference returning methods. // var e = new E(() => ref i); Diagnostic(ErrorCode.ERR_MustNotHaveRefReturn, "i").WithLocation(9, 33), @@ -1267,7 +1267,7 @@ static void M() } "; - CreateExperimentalCompilationWithMscorlib45(text).VerifyDiagnostics( + CreateExperimentalCompilationWithMscorlib45(text, MessageID.IDS_FeatureRefLocalsReturns).VerifyDiagnostics( // (18,13): error CS8084: By-value returns may only be used in by-value returning methods. // return i; Diagnostic(ErrorCode.ERR_MustHaveRefReturn, "return").WithLocation(18, 13), diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/LookupPositionTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/LookupPositionTests.cs index 18c7fee03b013..b6fc28a0a30a1 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/LookupPositionTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/LookupPositionTests.cs @@ -9,6 +9,7 @@ using Microsoft.CodeAnalysis.Text; using Roslyn.Test.Utilities; using Xunit; +using Microsoft.CodeAnalysis.CSharp.Test.Utilities; namespace Microsoft.CodeAnalysis.CSharp.UnitTests { @@ -1580,9 +1581,13 @@ private static SemanticModel GetModelAndKeyPositions(string markedText, out int[ keyPositions = keyPositionBuilder.ToArrayAndFree(); var text = textBuilder.ToString(); - var compilation = experimental - ? CreateExperimentalCompilationWithMscorlib45(text) - : CreateCompilationWithMscorlibAndDocumentationComments(text); + var parseOptions = TestOptions.RegularWithDocumentationComments; + if (experimental) + { + parseOptions = parseOptions.WithTuplesFeature().WithRefsFeature(); + } + + var compilation = CreateCompilationWithMscorlib(text, parseOptions: parseOptions); var tree = compilation.SyntaxTrees[0]; return compilation.GetSemanticModel(tree); } diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/LookupTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/LookupTests.cs index f7513f7a75756..365e7c7cdd5ce 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/LookupTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/LookupTests.cs @@ -19,9 +19,12 @@ public class GetSemanticInfoTests : SemanticModelTestBase internal List GetLookupNames(string testSrc, bool experimental = false) { - var compilation = experimental - ? CreateExperimentalCompilationWithMscorlib45(testSrc) - : CreateCompilationWithMscorlib45(testSrc); + var parseOptions = TestOptions.Regular; + if (experimental) + { + parseOptions = parseOptions.WithRefsFeature().WithLocalFunctionsFeature(); + } + var compilation = CreateCompilationWithMscorlib45(testSrc, parseOptions: parseOptions); var tree = compilation.SyntaxTrees.Single(); var model = compilation.GetSemanticModel(tree); var position = testSrc.Contains("/**/") ? GetPositionForBinding(tree) : GetPositionForBinding(testSrc); diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/PatternMatchingTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/PatternMatchingTests.cs index 77398ad29213c..678fdcd1b234a 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/PatternMatchingTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/PatternMatchingTests.cs @@ -15,8 +15,7 @@ namespace Microsoft.CodeAnalysis.CSharp.UnitTests public class PatternMatchingTests : CSharpTestBase { private static CSharpParseOptions patternParseOptions = - TestOptions.Regular.WithLanguageVersion(LanguageVersion.CSharp6) - .WithFeature(MessageID.IDS_FeaturePatternMatching.RequiredFeature(), "true"); + TestOptions.Regular.WithLanguageVersion(LanguageVersion.CSharp6).WithPatternsFeature(); [Fact] public void DemoModes() @@ -382,7 +381,7 @@ private static bool M(object o, bool result) } } "; - var compilation = CreateCompilationWithMscorlib45(source, options: TestOptions.DebugExe, parseOptions: patternParseOptions.WithFeature(MessageID.IDS_FeatureLocalFunctions.RequiredFeature(), "true")); + var compilation = CreateCompilationWithMscorlib45(source, options: TestOptions.DebugExe, parseOptions: patternParseOptions.WithLocalFunctionsFeature()); compilation.VerifyDiagnostics(); var expectedOutput = @"False for 1 diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/RefLocalsAndReturns.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/RefLocalsAndReturns.cs index 92e37329808b2..d6d94867f7445 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/RefLocalsAndReturns.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/RefLocalsAndReturns.cs @@ -1,18 +1,28 @@ // Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +using System.Collections.Generic; using System.IO; +using System.Linq; using Microsoft.CodeAnalysis.Emit; using Roslyn.Test.Utilities; using Xunit; using Microsoft.CodeAnalysis.CSharp.Test.Utilities; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.CSharp.Symbols; -using System.Linq; namespace Microsoft.CodeAnalysis.CSharp.UnitTests.Semantics { public class RefLocalsAndReturnsTeats : CompilingTestBase { + internal static CSharpCompilation CreateCompilationRef( + string text, + IEnumerable references = null, + CSharpCompilationOptions options = null, + string assemblyName = "", + string sourceFileName = "") + { + return CreateExperimentalCompilationWithMscorlib45(text, MessageID.IDS_FeatureRefLocalsReturns, references, options, assemblyName, sourceFileName); + } [Fact] public void RefLocalMissingInitializer() @@ -25,7 +35,7 @@ void A() ref int x; } }"; - var comp = CreateExperimentalCompilationWithMscorlib45(text); + var comp = CreateCompilationRef(text); comp.VerifyDiagnostics( // (6,17): error CS8935: A declaration of a by-reference variable must have an initializer // ref int x; @@ -49,7 +59,7 @@ void A() var y = x; } }"; - var comp = CreateExperimentalCompilationWithMscorlib45(text); + var comp = CreateCompilationRef(text); comp.VerifyDiagnostics( // (7,17): error CS8933: Cannot initialize a by-reference variable with a value // ref int x = a; @@ -70,7 +80,7 @@ void A() var y = ref x; } }"; - var comp = CreateExperimentalCompilationWithMscorlib45(text); + var comp = CreateCompilationRef(text); comp.VerifyDiagnostics( // (8,13): error CS8932: Cannot initialize a by-value variable with a reference // var y = ref x; @@ -114,7 +124,7 @@ ref int E() } }"; - var comp = CreateExperimentalCompilationWithMscorlib45(text); + var comp = CreateCompilationRef(text); comp.VerifyDiagnostics( // (6,20): error CS8910: An expression cannot be used in this context because it may not be returned by reference // return ref 2 + 2; @@ -155,7 +165,7 @@ void Test1() void VoidMethod(){} int P1 {get{return 1;} set{}} }"; - var comp = CreateExperimentalCompilationWithMscorlib45(text); + var comp = CreateCompilationRef(text); comp.VerifyDiagnostics( // (9,27): error CS8910: An expression cannot be used in this context because it may not be returned by reference // D1 d1 = () => ref 2 + 2; @@ -228,7 +238,7 @@ ref char Test1(char arg1, S1 arg2) throw null; } }"; - var comp = CreateExperimentalCompilationWithMscorlib45(text); + var comp = CreateCompilationRef(text); comp.VerifyDiagnostics( // (18,24): error CS8924: Cannot return local 'l' by reference because it is not a ref local // return ref l; @@ -283,7 +293,7 @@ ref char Test1() throw null; } }"; - var comp = CreateExperimentalCompilationWithMscorlib45(text); + var comp = CreateCompilationRef(text); comp.VerifyDiagnostics( // (13,30): error CS1657: Cannot use 'ro' as a ref or out value because it is a 'foreach iteration variable' // ref char r = ref ro; @@ -325,7 +335,7 @@ static void Test1() } }"; - var comp = CreateExperimentalCompilationWithMscorlib45(text, references: new[] { MscorlibRef, SystemCoreRef }); + var comp = CreateCompilationRef(text, references: new[] { MscorlibRef, SystemCoreRef }); comp.VerifyDiagnostics( // (16,34): error CS8913: Cannot return the range variable 'ch' by reference // select(D1)(() => ref ch); @@ -376,7 +386,7 @@ static ref char MR() } }"; - var comp = CreateExperimentalCompilationWithMscorlib45(text, references: new[] { MscorlibRef, SystemCoreRef }); + var comp = CreateCompilationRef(text, references: new[] { MscorlibRef, SystemCoreRef }); comp.VerifyDiagnostics( // (14,26): error CS1657: Cannot use 'M' as a ref or out value because it is a 'method group' // ref char r = ref M; @@ -493,7 +503,7 @@ ref char Test2() } }"; - var comp = CreateExperimentalCompilationWithMscorlib45(text); + var comp = CreateCompilationRef(text); comp.VerifyDiagnostics( // (33,33): error CS0199: A static readonly field cannot be used as a ref or out value (except in a static constructor) // ref char temp = ref s1; @@ -586,7 +596,7 @@ static ref char Test1() }"; - var comp = CreateExperimentalCompilationWithMscorlib45(text); + var comp = CreateCompilationRef(text); comp.VerifyDiagnostics( // (10,24): error CS8927: Struct members cannot return 'this' or other instance members by reference // return ref this; @@ -690,7 +700,7 @@ ref char Test1(char arg1, S1 arg2) throw null; } }"; - var comp = CreateExperimentalCompilationWithMscorlib45(text); + var comp = CreateCompilationRef(text); comp.VerifyDiagnostics( // (18,24): error CS8911: Cannot return 'r' by reference because it was initialized to a value that cannot be returned by reference // return ref r; @@ -774,7 +784,7 @@ ref char Test1(char arg1, S1 arg2) throw null; } }"; - var comp = CreateExperimentalCompilationWithMscorlib45(text); + var comp = CreateCompilationRef(text); comp.VerifyDiagnostics( // (19,24): error CS8911: Cannot return 'r' by reference because it was initialized to a value that cannot be returned by reference // return ref r; //1 @@ -829,7 +839,8 @@ ref char Foo2(ref char c, ref char b) } } }"; - var comp = CreateExperimentalCompilationWithMscorlib45(text); + var options = TestOptions.Regular.WithRefsFeature().WithLocalFunctionsFeature(); + var comp = CreateCompilationWithMscorlib45(text, parseOptions: options); comp.VerifyDiagnostics( // (14,13): error CS8894: By-reference returns may only be used in methods that return by reference // return ref b; @@ -872,7 +883,8 @@ public static void Main() char Moo3(ref char a, ref char b) => r; } }"; - var comp = CreateExperimentalCompilationWithMscorlib45(text); + var options = TestOptions.Regular.WithRefsFeature().WithLocalFunctionsFeature(); + var comp = CreateCompilationWithMscorlib45(text, parseOptions: options); comp.VerifyDiagnostics( // (9,50): error CS8894: By-reference returns may only be used in methods that return by reference // char Foo1(ref char a, ref char b) => ref b; diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/SemanticErrorTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/SemanticErrorTests.cs index 29becf6eec55f..9464c867b8c0f 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/SemanticErrorTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/SemanticErrorTests.cs @@ -22750,7 +22750,7 @@ static void Main(string[] args) } } "; - CreateExperimentalCompilationWithMscorlib45(text).VerifyDiagnostics( + CreateCompilationWithMscorlib45(text).VerifyDiagnostics( // (13,21): error CS0023: Operator '?' cannot be applied to operand of type 'int' // var x = 123 ?.ToString(); Diagnostic(ErrorCode.ERR_BadUnaryOp, "?").WithArguments("?", "int").WithLocation(13, 21), @@ -22819,7 +22819,7 @@ static void Main(string[] args) } } "; - CreateExperimentalCompilationWithMscorlib45(text).VerifyDiagnostics( + CreateCompilationWithMscorlib45(text).VerifyDiagnostics( // (14,23): error CS0023: Operator '?' cannot be applied to operand of type 'method group' // var x1 = p.P1 ?.ToString; Diagnostic(ErrorCode.ERR_BadUnaryOp, "?").WithArguments("?", "method group").WithLocation(14, 23) @@ -22857,7 +22857,7 @@ static void Main(string[] args) } } "; - CreateExperimentalCompilationWithMscorlib45(text).VerifyDiagnostics( + CreateCompilationWithMscorlib45(text).VerifyDiagnostics( // (11,18): error CS0175: Use of keyword 'base' is not valid in this context // var x6 = base?.ToString(); Diagnostic(ErrorCode.ERR_BaseIllegal, "base").WithLocation(11, 18), @@ -22900,7 +22900,7 @@ unsafe static void Main() } "; - CreateExperimentalCompilationWithMscorlib45(text, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics( + CreateCompilationWithMscorlib45(text, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics( // (9,23): error CS0023: Operator '?' cannot be applied to operand of type 'void*' // var p = intPtr?.ToPointer(); Diagnostic(ErrorCode.ERR_BadUnaryOp, "?").WithArguments("?", "void*").WithLocation(9, 23) @@ -23122,7 +23122,7 @@ static void Main() } } "; - CreateExperimentalCompilationWithMscorlib45(text, options: TestOptions.ReleaseDll).VerifyDiagnostics( + CreateCompilationWithMscorlib45(text, options: TestOptions.ReleaseDll).VerifyDiagnostics( // (8,9): error CS0201: Only assignment, call, increment, decrement, and new object expressions can be used as a statement // x?.Length; Diagnostic(ErrorCode.ERR_IllegalStatement, "x?.Length").WithLocation(8, 9), diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/StructsTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/StructsTests.cs index 6163166c71890..959049be692a5 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/StructsTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/StructsTests.cs @@ -602,7 +602,7 @@ public struct X1 } "; - CreateExperimentalCompilationWithMscorlib45(source).VerifyDiagnostics( + CreateCompilationWithMscorlib45(source).VerifyDiagnostics( // (11,5): error CS0568: Structs cannot contain explicit parameterless constructors // X1() Diagnostic(ErrorCode.ERR_StructsCantContainDefaultConstructor, "X1").WithLocation(11, 5), diff --git a/src/Compilers/CSharp/Test/Symbol/Compilation/SemanticModelAPITests.cs b/src/Compilers/CSharp/Test/Symbol/Compilation/SemanticModelAPITests.cs index a39dc2c91dc55..c99d2fe2b8dbc 100644 --- a/src/Compilers/CSharp/Test/Symbol/Compilation/SemanticModelAPITests.cs +++ b/src/Compilers/CSharp/Test/Symbol/Compilation/SemanticModelAPITests.cs @@ -1754,7 +1754,7 @@ public void TestGetSpeculativeSemanticModelInAutoPropInitializer1() int X { get; } = 1; }"; - var comp = CreateCompilationWithMscorlib(source, parseOptions: TestOptions.ExperimentalParseOptions); + var comp = CreateCompilationWithMscorlib(source, parseOptions: TestOptions.Regular); var tree = comp.SyntaxTrees.Single(); var model = comp.GetSemanticModel(tree); diff --git a/src/Compilers/CSharp/Test/Symbol/Compilation/SemanticModelGetSemanticInfoTests.cs b/src/Compilers/CSharp/Test/Symbol/Compilation/SemanticModelGetSemanticInfoTests.cs index e30093d2d426d..eb1ff0b97dd66 100644 --- a/src/Compilers/CSharp/Test/Symbol/Compilation/SemanticModelGetSemanticInfoTests.cs +++ b/src/Compilers/CSharp/Test/Symbol/Compilation/SemanticModelGetSemanticInfoTests.cs @@ -14875,7 +14875,7 @@ static void Main() } } "; - var semanticInfo = GetSemanticInfoForTestExperimental(sourceCode); + var semanticInfo = GetSemanticInfoForTest(sourceCode); Assert.Equal("int", semanticInfo.Type.ToDisplayString()); Assert.Equal(TypeKind.Struct, semanticInfo.Type.TypeKind); @@ -14906,7 +14906,7 @@ static void Main() } } "; - var semanticInfo = GetSemanticInfoForTestExperimental(sourceCode); + var semanticInfo = GetSemanticInfoForTest(sourceCode); Assert.Null(semanticInfo.Type); Assert.Null(semanticInfo.ConvertedType); @@ -14943,7 +14943,7 @@ static void Main() } } "; - var semanticInfo = GetSemanticInfoForTestExperimental(sourceCode); + var semanticInfo = GetSemanticInfoForTest(sourceCode); Assert.Equal("string", semanticInfo.Type.ToDisplayString()); Assert.Equal(TypeKind.Class, semanticInfo.Type.TypeKind); @@ -14974,7 +14974,7 @@ static void Main() } } "; - var semanticInfo = GetSemanticInfoForTestExperimental(sourceCode); + var semanticInfo = GetSemanticInfoForTest(sourceCode); Assert.Equal("int", semanticInfo.Type.ToDisplayString()); Assert.Equal(TypeKind.Struct, semanticInfo.Type.TypeKind); @@ -15036,7 +15036,7 @@ static void Main() } } "; - var semanticInfo = GetSemanticInfoForTestExperimental(sourceCode); + var semanticInfo = GetSemanticInfoForTest(sourceCode); Assert.Equal("int", semanticInfo.Type.ToDisplayString()); Assert.Equal(TypeKind.Struct, semanticInfo.Type.TypeKind); @@ -15067,7 +15067,7 @@ static void Main() } } "; - var semanticInfo = GetSemanticInfoForTestExperimental(sourceCode); + var semanticInfo = GetSemanticInfoForTest(sourceCode); Assert.Equal("char", semanticInfo.Type.ToDisplayString()); Assert.Equal(TypeKind.Struct, semanticInfo.Type.TypeKind); @@ -15097,7 +15097,7 @@ public static BaselineLog Log { } }= new /**/BaselineLog/**/(); -", parseOptions: TestOptions.ExperimentalParseOptions); +", parseOptions: TestOptions.Regular); var semanticInfo = GetSemanticInfoForTest(comp); Assert.Null(semanticInfo.Type); diff --git a/src/Compilers/CSharp/Test/Symbol/Symbols/OverriddenOrHiddenMembersTests.cs b/src/Compilers/CSharp/Test/Symbol/Symbols/OverriddenOrHiddenMembersTests.cs index aefc3b74dc2b7..1db454c66211b 100644 --- a/src/Compilers/CSharp/Test/Symbol/Symbols/OverriddenOrHiddenMembersTests.cs +++ b/src/Compilers/CSharp/Test/Symbol/Symbols/OverriddenOrHiddenMembersTests.cs @@ -530,7 +530,7 @@ public override void Method(int x) { } //this is incorrect, but doesn't break th public override ref int this[int i] { get { return ref field; } } } "; - var comp = CreateExperimentalCompilationWithMscorlib45(text); + var comp = CreateExperimentalCompilationWithMscorlib45(text, MessageID.IDS_FeatureRefLocalsReturns); var global = comp.GlobalNamespace; var baseClass = (NamedTypeSymbol)global.GetMembers("BaseClass").Single(); @@ -838,7 +838,7 @@ class Base4 : Base3 { public override U Property { set { } } }"; - CreateExperimentalCompilationWithMscorlib45(text).VerifyDiagnostics( + CreateExperimentalCompilationWithMscorlib45(text, MessageID.IDS_FeatureRefLocalsReturns).VerifyDiagnostics( Diagnostic(ErrorCode.ERR_UnimplementedAbstractMethod, "Base2").WithArguments("Base2", "Base.Property.get"), Diagnostic(ErrorCode.ERR_UnimplementedAbstractMethod, "Base4").WithArguments("Base4", "Base3.Method(U, V)")); } diff --git a/src/Compilers/CSharp/Test/Symbol/Symbols/Source/DelegateTests.cs b/src/Compilers/CSharp/Test/Symbol/Symbols/Source/DelegateTests.cs index 718436a1f86d3..83de339af1e8a 100644 --- a/src/Compilers/CSharp/Test/Symbol/Symbols/Source/DelegateTests.cs +++ b/src/Compilers/CSharp/Test/Symbol/Symbols/Source/DelegateTests.cs @@ -723,7 +723,7 @@ public void RefReturningDelegate() { var source = @"delegate ref int D();"; - var comp = CreateExperimentalCompilationWithMscorlib45(source); + var comp = CreateExperimentalCompilationWithMscorlib45(source, MessageID.IDS_FeatureRefLocalsReturns); comp.VerifyDiagnostics(); var global = comp.GlobalNamespace; diff --git a/src/Compilers/CSharp/Test/Symbol/Symbols/Source/ExpressionBodiedMethodTests.cs b/src/Compilers/CSharp/Test/Symbol/Symbols/Source/ExpressionBodiedMethodTests.cs index 645b32bc781de..cced4a101a913 100644 --- a/src/Compilers/CSharp/Test/Symbol/Symbols/Source/ExpressionBodiedMethodTests.cs +++ b/src/Compilers/CSharp/Test/Symbol/Symbols/Source/ExpressionBodiedMethodTests.cs @@ -375,7 +375,7 @@ class C { int field = 0; public ref int M() => ref field; -}"); +}", MessageID.IDS_FeatureRefLocalsReturns); comp.VerifyDiagnostics(); } } diff --git a/src/Compilers/CSharp/Test/Symbol/Symbols/Source/ExpressionBodiedPropertyTests.cs b/src/Compilers/CSharp/Test/Symbol/Symbols/Source/ExpressionBodiedPropertyTests.cs index 44841c36f04e7..f110c1e856b25 100644 --- a/src/Compilers/CSharp/Test/Symbol/Symbols/Source/ExpressionBodiedPropertyTests.cs +++ b/src/Compilers/CSharp/Test/Symbol/Symbols/Source/ExpressionBodiedPropertyTests.cs @@ -491,7 +491,7 @@ class C { int field = 0; public ref int P => ref field; -}"); +}", MessageID.IDS_FeatureRefLocalsReturns); comp.VerifyDiagnostics(); var global = comp.GlobalNamespace; diff --git a/src/Compilers/CSharp/Test/Symbol/Symbols/Source/MethodTests.cs b/src/Compilers/CSharp/Test/Symbol/Symbols/Source/MethodTests.cs index bfd5c85528bca..219e78b3ad170 100644 --- a/src/Compilers/CSharp/Test/Symbol/Symbols/Source/MethodTests.cs +++ b/src/Compilers/CSharp/Test/Symbol/Symbols/Source/MethodTests.cs @@ -288,7 +288,7 @@ ref int M(ref int i) } "; - var comp = CreateExperimentalCompilationWithMscorlib45(text); + var comp = CreateExperimentalCompilationWithMscorlib45(text, MessageID.IDS_FeatureRefLocalsReturns); var global = comp.GlobalNamespace; var a = global.GetTypeMembers("A", 0).Single(); var m = a.GetMembers("M").Single() as MethodSymbol; @@ -471,7 +471,7 @@ public override void M0() { } } "; - var comp = CreateExperimentalCompilationWithMscorlib45(new[] { text, text1, text2 }); + var comp = CreateExperimentalCompilationWithMscorlib45(new[] { text, text1, text2 }, MessageID.IDS_FeatureRefLocalsReturns); Assert.Equal(0, comp.GetDiagnostics().Count()); var ns = comp.GlobalNamespace.GetMembers("N1").Single() as NamespaceSymbol; var ns1 = ns.GetMembers("N2").Single() as NamespaceSymbol; @@ -642,16 +642,16 @@ public override void M0() { } } "; - var comp1 = CreateExperimentalCompilationWithMscorlib45(text); + var comp1 = CreateExperimentalCompilationWithMscorlib45(text, MessageID.IDS_FeatureRefLocalsReturns); var compRef1 = new CSharpCompilationReference(comp1); - var comp2 = CreateExperimentalCompilationWithMscorlib45(new string[] { text1 }, new List() { compRef1 }, assemblyName: "Test2"); + var comp2 = CreateExperimentalCompilationWithMscorlib45(new string[] { text1 }, MessageID.IDS_FeatureRefLocalsReturns, new List() { compRef1 }, assemblyName: "Test2"); //Compilation.Create(outputName: "Test2", options: CompilationOptions.Default, // syntaxTrees: new SyntaxTree[] { SyntaxTree.ParseCompilationUnit(text1) }, // references: new MetadataReference[] { compRef1, GetCorlibReference() }); var compRef2 = new CSharpCompilationReference(comp2); - var comp = CreateExperimentalCompilationWithMscorlib45(new string[] { text2 }, new List() { compRef1, compRef2 }, assemblyName: "Test3"); + var comp = CreateExperimentalCompilationWithMscorlib45(new string[] { text2 }, MessageID.IDS_FeatureRefLocalsReturns, new List() { compRef1, compRef2 }, assemblyName: "Test3"); //Compilation.Create(outputName: "Test3", options: CompilationOptions.Default, // syntaxTrees: new SyntaxTree[] { SyntaxTree.ParseCompilationUnit(text2) }, // references: new MetadataReference[] { compRef1, compRef2, GetCorlibReference() }); @@ -1638,7 +1638,7 @@ class C : I } "; - var comp = CreateExperimentalCompilationWithMscorlib45(text); + var comp = CreateExperimentalCompilationWithMscorlib45(text, MessageID.IDS_FeatureRefLocalsReturns); var globalNamespace = comp.GlobalNamespace; @@ -2066,7 +2066,7 @@ static ref void M() { } } "; - CreateExperimentalCompilationWithMscorlib45(source).VerifyDiagnostics( + CreateExperimentalCompilationWithMscorlib45(source, MessageID.IDS_FeatureRefLocalsReturns).VerifyDiagnostics( // (4,12): error CS8088: Void-returning methods cannot return by reference // static ref void M() { } Diagnostic(ErrorCode.ERR_VoidReturningMethodCannotReturnByRef, "ref").WithLocation(4, 12)); @@ -2085,7 +2085,8 @@ ref void M() { } } "; - CreateExperimentalCompilationWithMscorlib45(source).VerifyDiagnostics( + var parseOptions = TestOptions.Regular.WithLocalFunctionsFeature().WithRefsFeature(); + CreateCompilationWithMscorlib45(source, parseOptions: parseOptions).VerifyDiagnostics( // (6,18): error CS8898: Void-returning methods cannot return by reference // ref void M() { } Diagnostic(ErrorCode.ERR_VoidReturningMethodCannotReturnByRef, "M").WithLocation(6, 18), @@ -2104,7 +2105,7 @@ static async ref int M() { } } "; - CreateExperimentalCompilationWithMscorlib45(source).VerifyDiagnostics( + CreateExperimentalCompilationWithMscorlib45(source, MessageID.IDS_FeatureRefLocalsReturns).VerifyDiagnostics( // (4,18): error CS1519: Invalid token 'ref' in class, struct, or interface member declaration // static async ref int M() { } Diagnostic(ErrorCode.ERR_InvalidMemberDecl, "ref").WithArguments("ref").WithLocation(4, 18), diff --git a/src/Compilers/CSharp/Test/Symbol/Symbols/Source/PropertyTests.cs b/src/Compilers/CSharp/Test/Symbol/Symbols/Source/PropertyTests.cs index a31b5365a9fa8..74731225b41b9 100644 --- a/src/Compilers/CSharp/Test/Symbol/Symbols/Source/PropertyTests.cs +++ b/src/Compilers/CSharp/Test/Symbol/Symbols/Source/PropertyTests.cs @@ -32,7 +32,7 @@ class C [Fact] public void SetGetOnlyAutoPropInConstructor() { - CreateExperimentalCompilationWithMscorlib45(@" + CreateCompilationWithMscorlib45(@" class C { public int P { get; } @@ -47,7 +47,7 @@ public C() [Fact] public void GetOnlyAutoPropBadOverride() { - CreateExperimentalCompilationWithMscorlib45(@" + CreateCompilationWithMscorlib45(@" class Base { @@ -81,7 +81,7 @@ public C() [Fact] public void SetGetOnlyAutoPropOutOfConstructor() { - CreateExperimentalCompilationWithMscorlib45(@" + CreateCompilationWithMscorlib45(@" class C { public int P { get; } @@ -203,7 +203,7 @@ struct S public decimal R { get; } = 300; }"; - var comp = CreateCompilationWithMscorlib(text, parseOptions: TestOptions.ExperimentalParseOptions); + var comp = CreateCompilationWithMscorlib(text, parseOptions: TestOptions.Regular); comp.VerifyDiagnostics( // (4,16): error CS0573: 'S': cannot have instance property or field initializers in structs // public int P { get; set; } = 1; @@ -226,7 +226,7 @@ public void AutoWithInitializerInStruct2() public S(int i) : this() {} }"; - var comp = CreateCompilationWithMscorlib(text, parseOptions: TestOptions.ExperimentalParseOptions); + var comp = CreateCompilationWithMscorlib(text, parseOptions: TestOptions.Regular); comp.VerifyDiagnostics( // (3,16): error CS0573: 'S': cannot have instance property or field initializers in structs // public int P { get; set; } = 1; @@ -259,7 +259,7 @@ public void AutoInitializerInInterface() { int P { get; } = 0; }"; - var comp = CreateCompilationWithMscorlib(text, parseOptions: TestOptions.ExperimentalParseOptions); + var comp = CreateCompilationWithMscorlib(text, parseOptions: TestOptions.Regular); comp.VerifyDiagnostics( // (3,9): error CS8035: Auto-implemented properties inside interfaces cannot have initializers. @@ -274,7 +274,7 @@ public void AutoNoSetOrInitializer() { public int P { get; } }"; - var comp = CreateCompilationWithMscorlib(text, parseOptions: TestOptions.ExperimentalParseOptions); + var comp = CreateCompilationWithMscorlib(text, parseOptions: TestOptions.Regular); comp.VerifyDiagnostics(); } @@ -288,7 +288,7 @@ public int P { set {} } public int Q { set; } = 0; public int R { set; } }"; - var comp = CreateCompilationWithMscorlib(text, parseOptions: TestOptions.ExperimentalParseOptions); + var comp = CreateCompilationWithMscorlib(text, parseOptions: TestOptions.Regular); comp.VerifyDiagnostics( // (4,20): error CS8034: Auto-implemented properties must have get accessors. @@ -306,7 +306,7 @@ public void AutoRefReturn() { public ref int P { get; } }"; - var comp = CreateCompilationWithMscorlib(text, parseOptions: TestOptions.ExperimentalParseOptions); + var comp = CreateCompilationWithMscorlib(text, parseOptions: TestOptions.Regular.WithRefsFeature()); comp.VerifyDiagnostics( // (3,20): error CS8080: Auto-implemented properties cannot return by reference @@ -1742,7 +1742,7 @@ class C : I } "; - var comp = CreateExperimentalCompilationWithMscorlib45(text); + var comp = CreateExperimentalCompilationWithMscorlib45(text, MessageID.IDS_FeatureRefLocalsReturns); var globalNamespace = comp.GlobalNamespace; @@ -2875,7 +2875,7 @@ ref int P { set { } } } "; - CreateExperimentalCompilationWithMscorlib45(source).VerifyDiagnostics( + CreateExperimentalCompilationWithMscorlib45(source, MessageID.IDS_FeatureRefLocalsReturns).VerifyDiagnostics( // (4,17): error CS8080: Properties with by-reference returns must have a get accessor. // ref int P { set { } } Diagnostic(ErrorCode.ERR_RefPropertyMustHaveGetAccessor, "P").WithArguments("C.P").WithLocation(4, 17)); @@ -2892,7 +2892,7 @@ class C } "; - CreateExperimentalCompilationWithMscorlib45(source).VerifyDiagnostics( + CreateExperimentalCompilationWithMscorlib45(source, MessageID.IDS_FeatureRefLocalsReturns).VerifyDiagnostics( // (5,47): error CS8081: Properties with by-reference returns cannot have set accessors. // ref int P { get { return ref field; } set { } } Diagnostic(ErrorCode.ERR_RefPropertyCannotHaveSetAccessor, "set").WithArguments("C.P.set").WithLocation(5, 47)); diff --git a/src/Compilers/CSharp/Test/Syntax/LexicalAndXml/LexicalTests.cs b/src/Compilers/CSharp/Test/Syntax/LexicalAndXml/LexicalTests.cs index 7f51fe087e025..e0e15aa060398 100644 --- a/src/Compilers/CSharp/Test/Syntax/LexicalAndXml/LexicalTests.cs +++ b/src/Compilers/CSharp/Test/Syntax/LexicalAndXml/LexicalTests.cs @@ -11,6 +11,7 @@ using Xunit; using InternalSyntax = Microsoft.CodeAnalysis.CSharp.Syntax.InternalSyntax; using Microsoft.CodeAnalysis.CSharp.Syntax.InternalSyntax; +using Microsoft.CodeAnalysis.CSharp.Test.Utilities; namespace Microsoft.CodeAnalysis.CSharp.UnitTests { @@ -24,11 +25,9 @@ public class LexicalTests public LexicalTests() { _options = new CSharpParseOptions(languageVersion: LanguageVersion.CSharp3); - var binaryLiterals = new[] { new KeyValuePair(MessageID.IDS_FeatureBinaryLiteral.RequiredFeature(), "true") }; - var digitSeparators = new[] { new KeyValuePair(MessageID.IDS_FeatureDigitSeparator.RequiredFeature(), "true") }; - _binaryOptions = _options.WithFeatures(binaryLiterals); - _underscoreOptions = _options.WithFeatures(digitSeparators); - _binaryUnderscoreOptions = _options.WithFeatures(binaryLiterals.Concat(digitSeparators)); + _binaryOptions = _options.WithExperimental(MessageID.IDS_FeatureBinaryLiteral); + _underscoreOptions = _options.WithExperimental(MessageID.IDS_FeatureDigitSeparator); + _binaryUnderscoreOptions = _options.WithExperimental(MessageID.IDS_FeatureBinaryLiteral, MessageID.IDS_FeatureDigitSeparator); } private IEnumerable Lex(string text, CSharpParseOptions options = null) diff --git a/src/Compilers/CSharp/Test/Syntax/Parsing/DeclarationParsingTests.cs b/src/Compilers/CSharp/Test/Syntax/Parsing/DeclarationParsingTests.cs index 866671ce508e6..495cbffae7a98 100644 --- a/src/Compilers/CSharp/Test/Syntax/Parsing/DeclarationParsingTests.cs +++ b/src/Compilers/CSharp/Test/Syntax/Parsing/DeclarationParsingTests.cs @@ -1950,7 +1950,7 @@ public void TestDelegate() public void TestDelegateWithRefReturnType() { var text = "delegate ref a b();"; - var file = this.ParseFileExperimental(text); + var file = this.ParseFileExperimental(text, MessageID.IDS_FeatureRefLocalsReturns); Assert.NotNull(file); Assert.Equal(1, file.Members.Count); @@ -2422,7 +2422,7 @@ public void TestClassMethod() public void TestClassMethodWithRefReturn() { var text = "class a { ref b X() { } }"; - var file = this.ParseFileExperimental(text); + var file = this.ParseFileExperimental(text, MessageID.IDS_FeatureRefLocalsReturns); Assert.NotNull(file); Assert.Equal(1, file.Members.Count); @@ -2468,7 +2468,7 @@ public void TestClassMethodWithRefReturn() public void TestClassMethodWithRef() { var text = "class a { ref }"; - var file = this.ParseFileExperimental(text); + var file = this.ParseFileExperimental(text, MessageID.IDS_FeatureRefLocalsReturns); Assert.NotNull(file); Assert.Equal(1, file.Members.Count); @@ -3787,7 +3787,7 @@ public void TestClassProperty() public void TestClassPropertyWithRefReturn() { var text = "class a { ref b c { get; set; } }"; - var file = this.ParseFileExperimental(text); + var file = this.ParseFileExperimental(text, MessageID.IDS_FeatureRefLocalsReturns); Assert.NotNull(file); Assert.Equal(1, file.Members.Count); @@ -3969,7 +3969,7 @@ public void TestClassPropertyWithBodies() public void TestClassAutoPropertyWithInitializer() { var text = "class a { b c { get; set; } = d; }"; - var file = this.ParseFile(text, TestOptions.ExperimentalParseOptions); + var file = this.ParseFile(text); Assert.NotNull(file); Assert.Equal(1, file.Members.Count); @@ -4025,7 +4025,7 @@ public void TestClassAutoPropertyWithInitializer() public void InitializerOnNonAutoProp() { var text = "class C { int P { set {} } = 0; }"; - var file = this.ParseFile(text, TestOptions.ExperimentalParseOptions); + var file = this.ParseFile(text); Assert.NotNull(file); Assert.Equal(0, file.Errors().Length); @@ -4663,7 +4663,7 @@ public void TestClassIndexer() public void TestClassIndexerWithRefReturn() { var text = "class a { ref b this[c d] { get; set; } }"; - var file = this.ParseFileExperimental(text); + var file = this.ParseFileExperimental(text, MessageID.IDS_FeatureRefLocalsReturns); Assert.NotNull(file); Assert.Equal(1, file.Members.Count); diff --git a/src/Compilers/CSharp/Test/Syntax/Parsing/ExpressionParsingTests.cs b/src/Compilers/CSharp/Test/Syntax/Parsing/ExpressionParsingTests.cs index 9ad6e3fe50854..6e2282c032389 100644 --- a/src/Compilers/CSharp/Test/Syntax/Parsing/ExpressionParsingTests.cs +++ b/src/Compilers/CSharp/Test/Syntax/Parsing/ExpressionParsingTests.cs @@ -22,9 +22,9 @@ private ExpressionSyntax ParseExpression(string text, ParseOptions options = nul return SyntaxFactory.ParseExpression(text, options: options); } - private ExpressionSyntax ParseExpressionExperimental(string text) + private ExpressionSyntax ParseExpressionExperimental(string text, MessageID feature) { - return SyntaxFactory.ParseExpression(text, options: TestOptions.ExperimentalParseOptions); + return SyntaxFactory.ParseExpression(text, options: TestOptions.Regular.WithExperimental(feature)); } [Fact] @@ -1181,7 +1181,7 @@ public void TestSimpleLambda() public void TestSimpleLambdaWithRefReturn() { var text = "a => ref b"; - var expr = this.ParseExpressionExperimental(text); + var expr = this.ParseExpressionExperimental(text, MessageID.IDS_FeatureRefLocalsReturns); Assert.NotNull(expr); Assert.Equal(SyntaxKind.SimpleLambdaExpression, expr.Kind()); @@ -1240,7 +1240,7 @@ public void TestLambdaWithNoParameters() public void TestLambdaWithNoParametersAndRefReturn() { var text = "() => ref b"; - var expr = this.ParseExpressionExperimental(text); + var expr = this.ParseExpressionExperimental(text, MessageID.IDS_FeatureRefLocalsReturns); Assert.NotNull(expr); Assert.Equal(SyntaxKind.ParenthesizedLambdaExpression, expr.Kind()); diff --git a/src/Compilers/CSharp/Test/Syntax/Parsing/LocalFunctionParsingTests.cs b/src/Compilers/CSharp/Test/Syntax/Parsing/LocalFunctionParsingTests.cs index f5e7874325259..8795f75ef5c70 100644 --- a/src/Compilers/CSharp/Test/Syntax/Parsing/LocalFunctionParsingTests.cs +++ b/src/Compilers/CSharp/Test/Syntax/Parsing/LocalFunctionParsingTests.cs @@ -70,7 +70,8 @@ int local() return 0; } } -}"); +}", MessageID.IDS_FeatureLocalFunctions); + Assert.NotNull(file); Assert.False(file.HasErrors); Assert.Equal(0, file.SyntaxTree.Options.Features.Count); @@ -108,7 +109,7 @@ public void LocalFunctionsWithAwait() void m2() { await () => new await(); } async void m3() { await () => new await(); } void m4() { async await() => new await(); } -}"); +}", MessageID.IDS_FeatureLocalFunctions); Assert.NotNull(file); var c = (ClassDeclarationSyntax)file.Members.Single(); diff --git a/src/Compilers/CSharp/Test/Syntax/Parsing/ParsingTests.cs b/src/Compilers/CSharp/Test/Syntax/Parsing/ParsingTests.cs index 18d1325a87cfd..7f3d7b7ebda5e 100644 --- a/src/Compilers/CSharp/Test/Syntax/Parsing/ParsingTests.cs +++ b/src/Compilers/CSharp/Test/Syntax/Parsing/ParsingTests.cs @@ -17,8 +17,8 @@ public abstract class ParsingTests : CSharpTestBase public CompilationUnitSyntax ParseFile(string text, CSharpParseOptions parseOptions = null) => SyntaxFactory.ParseCompilationUnit(text, options: parseOptions); - public CompilationUnitSyntax ParseFileExperimental(string text) => - ParseFile(text, parseOptions: TestOptions.ExperimentalParseOptions); + internal CompilationUnitSyntax ParseFileExperimental(string text, MessageID feature) => + ParseFile(text, parseOptions: TestOptions.Regular.WithExperimental(feature)); protected virtual CSharpSyntaxNode ParseNode(string text, CSharpParseOptions options) => ParseTree(text, options).GetCompilationUnitRoot(); diff --git a/src/Compilers/CSharp/Test/Syntax/Parsing/RoundTrippingTests.cs b/src/Compilers/CSharp/Test/Syntax/Parsing/RoundTrippingTests.cs index fba0b3916447e..5055eea5ad76a 100644 --- a/src/Compilers/CSharp/Test/Syntax/Parsing/RoundTrippingTests.cs +++ b/src/Compilers/CSharp/Test/Syntax/Parsing/RoundTrippingTests.cs @@ -70,15 +70,15 @@ public static void ParseAndCheckTerminalSpans(string text) [Fact] public void AutoPropInitializers() { - var experimental = TestOptions.ExperimentalParseOptions; - ParseAndRoundTripping("class C { int GetInt { get; } = 0; }", experimental, memberCount: 1); - ParseAndRoundTripping("class C { int GetInt { get; } = 0 }", experimental, 1, 1); - ParseAndRoundTripping("class C { public int GetInt { get; } = 0; }", experimental, memberCount: 1); - ParseAndRoundTripping("class C { int GetInt { get; } = 0;; }", experimental, 1, 1); - ParseAndRoundTripping("class C { int GetInt { get;; } = 0;; }", experimental, 2, 1); - ParseAndRoundTripping("interface I { int GetInt { get; } = 0; }", experimental, memberCount: 1); - ParseAndRoundTripping("interface I { int GetInt { get; } = 0 }", experimental, 1, 1); - ParseAndRoundTripping("interface I { public int GetInt { get; } = 0; }", experimental, memberCount: 1); + var parseOptions = TestOptions.Regular; + ParseAndRoundTripping("class C { int GetInt { get; } = 0; }", parseOptions, memberCount: 1); + ParseAndRoundTripping("class C { int GetInt { get; } = 0 }", parseOptions, 1, 1); + ParseAndRoundTripping("class C { public int GetInt { get; } = 0; }", parseOptions, memberCount: 1); + ParseAndRoundTripping("class C { int GetInt { get; } = 0;; }", parseOptions, 1, 1); + ParseAndRoundTripping("class C { int GetInt { get;; } = 0;; }", parseOptions, 2, 1); + ParseAndRoundTripping("interface I { int GetInt { get; } = 0; }", parseOptions, memberCount: 1); + ParseAndRoundTripping("interface I { int GetInt { get; } = 0 }", parseOptions, 1, 1); + ParseAndRoundTripping("interface I { public int GetInt { get; } = 0; }", parseOptions, memberCount: 1); } [Fact()] diff --git a/src/Compilers/CSharp/Test/Syntax/Parsing/StatementParsingTests.cs b/src/Compilers/CSharp/Test/Syntax/Parsing/StatementParsingTests.cs index 24f829ed1adf1..153223074ee4c 100644 --- a/src/Compilers/CSharp/Test/Syntax/Parsing/StatementParsingTests.cs +++ b/src/Compilers/CSharp/Test/Syntax/Parsing/StatementParsingTests.cs @@ -16,9 +16,9 @@ private StatementSyntax ParseStatement(string text, int offset = 0, ParseOptions return SyntaxFactory.ParseStatement(text, offset, options); } - private StatementSyntax ParseStatementExperimental(string text) + private StatementSyntax ParseStatementExperimental(string text, MessageID feature) { - return ParseStatement(text, offset: 0, options: TestOptions.ExperimentalParseOptions); + return ParseStatement(text, offset: 0, options: TestOptions.Regular.WithExperimental(feature)); } [Fact] @@ -738,7 +738,7 @@ public void TestVolatileLocalDeclarationStatement() public void TestRefLocalDeclarationStatement() { var text = "ref T a;"; - var statement = this.ParseStatementExperimental(text); + var statement = this.ParseStatementExperimental(text, MessageID.IDS_FeatureRefLocalsReturns); Assert.NotNull(statement); Assert.Equal(SyntaxKind.LocalDeclarationStatement, statement.Kind()); @@ -765,7 +765,7 @@ public void TestRefLocalDeclarationStatement() public void TestRefLocalDeclarationStatementWithInitializer() { var text = "ref T a = ref b;"; - var statement = this.ParseStatementExperimental(text); + var statement = this.ParseStatementExperimental(text, MessageID.IDS_FeatureRefLocalsReturns); Assert.NotNull(statement); Assert.Equal(SyntaxKind.LocalDeclarationStatement, statement.Kind()); @@ -798,7 +798,7 @@ public void TestRefLocalDeclarationStatementWithInitializer() public void TestRefLocalDeclarationStatementWithMultipleInitializers() { var text = "ref T a = ref b, c = ref d;"; - var statement = this.ParseStatementExperimental(text); + var statement = this.ParseStatementExperimental(text, MessageID.IDS_FeatureRefLocalsReturns); Assert.NotNull(statement); Assert.Equal(SyntaxKind.LocalDeclarationStatement, statement.Kind()); @@ -1639,7 +1639,7 @@ public void TestForWithMultipleVariableDeclarations() public void TestForWithRefVariableDeclaration() { var text = "for(ref T a = ref b, c = ref d;;) { }"; - var statement = this.ParseStatementExperimental(text); + var statement = this.ParseStatementExperimental(text, MessageID.IDS_FeatureRefLocalsReturns); Assert.NotNull(statement); Assert.Equal(SyntaxKind.ForStatement, statement.Kind()); diff --git a/src/Compilers/CSharp/Test/Syntax/Syntax/SyntaxAnnotationTests.cs b/src/Compilers/CSharp/Test/Syntax/Syntax/SyntaxAnnotationTests.cs index 342a3de8918ac..4e07d6fb4b63e 100644 --- a/src/Compilers/CSharp/Test/Syntax/Syntax/SyntaxAnnotationTests.cs +++ b/src/Compilers/CSharp/Test/Syntax/Syntax/SyntaxAnnotationTests.cs @@ -125,7 +125,7 @@ public void TestCopyAnnotationOfZeroLengthToSyntaxTrivia() public void TestMissingAnnotationsOnNodesOrTokens() { SyntaxAnnotation annotation = new SyntaxAnnotation(); - var tree = SyntaxFactory.ParseSyntaxTree(_allInOneCSharpCode, options: Test.Utilities.TestOptions.ExperimentalParseOptions); + var tree = SyntaxFactory.ParseSyntaxTree(_allInOneCSharpCode, options: Test.Utilities.TestOptions.Regular); var matchingNodesOrTokens = tree.GetCompilationUnitRoot().GetAnnotatedNodesAndTokens(annotation); Assert.Empty(matchingNodesOrTokens); @@ -135,7 +135,7 @@ public void TestMissingAnnotationsOnNodesOrTokens() public void TestMissingAnnotationsOnTrivia() { SyntaxAnnotation annotation = new SyntaxAnnotation(); - var tree = SyntaxFactory.ParseSyntaxTree(_allInOneCSharpCode, options: Test.Utilities.TestOptions.ExperimentalParseOptions); + var tree = SyntaxFactory.ParseSyntaxTree(_allInOneCSharpCode, options: Test.Utilities.TestOptions.Regular); var matchingTrivia = tree.GetCompilationUnitRoot().GetAnnotatedTrivia(annotation); Assert.Empty(matchingTrivia); @@ -298,7 +298,7 @@ public void TestIfNodeHasAnnotations() [Fact] public void TestCSharpAllInOne() { - var tree = SyntaxFactory.ParseSyntaxTree(_allInOneCSharpCode, options: Test.Utilities.TestOptions.ExperimentalParseOptions); + var tree = SyntaxFactory.ParseSyntaxTree(_allInOneCSharpCode, options: Test.Utilities.TestOptions.Regular); TestAnnotation(tree); } @@ -306,7 +306,7 @@ public void TestCSharpAllInOne() [Fact] public void TestCSharpAllInOneRandom() { - var tree = SyntaxFactory.ParseSyntaxTree(_allInOneCSharpCode, options: Test.Utilities.TestOptions.ExperimentalParseOptions); + var tree = SyntaxFactory.ParseSyntaxTree(_allInOneCSharpCode, options: Test.Utilities.TestOptions.Regular); TestRandomAnnotations(tree); } @@ -314,7 +314,7 @@ public void TestCSharpAllInOneRandom() [Fact] public void TestCSharpAllInOneManyRandom() { - var tree = SyntaxFactory.ParseSyntaxTree(_allInOneCSharpCode, options: Test.Utilities.TestOptions.ExperimentalParseOptions); + var tree = SyntaxFactory.ParseSyntaxTree(_allInOneCSharpCode, options: Test.Utilities.TestOptions.Regular); TestManyRandomAnnotations(tree); } @@ -322,7 +322,7 @@ public void TestCSharpAllInOneManyRandom() [Fact] public void TestCSharpAllInOneTrivia() { - var tree = SyntaxFactory.ParseSyntaxTree(_allInOneCSharpCode, options: Test.Utilities.TestOptions.ExperimentalParseOptions); + var tree = SyntaxFactory.ParseSyntaxTree(_allInOneCSharpCode, options: Test.Utilities.TestOptions.Regular); TestTriviaAnnotation(tree); } @@ -330,8 +330,8 @@ public void TestCSharpAllInOneTrivia() [Fact] public void TestCopyAnnotations1() { - var tree1 = SyntaxFactory.ParseSyntaxTree(_allInOneCSharpCode, options: Test.Utilities.TestOptions.ExperimentalParseOptions); - var tree2 = SyntaxFactory.ParseSyntaxTree(_allInOneCSharpCode, options: Test.Utilities.TestOptions.ExperimentalParseOptions); + var tree1 = SyntaxFactory.ParseSyntaxTree(_allInOneCSharpCode, options: Test.Utilities.TestOptions.Regular); + var tree2 = SyntaxFactory.ParseSyntaxTree(_allInOneCSharpCode, options: Test.Utilities.TestOptions.Regular); TestCopyAnnotations(tree1, tree2); } diff --git a/src/Compilers/Test/Utilities/CSharp.Desktop/CSharpCompilerTestUtilities.Desktop.csproj b/src/Compilers/Test/Utilities/CSharp.Desktop/CSharpCompilerTestUtilities.Desktop.csproj index c8d93765fe0d0..d2450d1b7be63 100644 --- a/src/Compilers/Test/Utilities/CSharp.Desktop/CSharpCompilerTestUtilities.Desktop.csproj +++ b/src/Compilers/Test/Utilities/CSharp.Desktop/CSharpCompilerTestUtilities.Desktop.csproj @@ -60,6 +60,9 @@ + + TestOptions.cs + @@ -75,7 +78,6 @@ - diff --git a/src/Compilers/Test/Utilities/CSharp.Desktop/CSharpTestBase.cs b/src/Compilers/Test/Utilities/CSharp.Desktop/CSharpTestBase.cs index 1388e94efc7c2..2025879fa52bb 100644 --- a/src/Compilers/Test/Utilities/CSharp.Desktop/CSharpTestBase.cs +++ b/src/Compilers/Test/Utilities/CSharp.Desktop/CSharpTestBase.cs @@ -119,6 +119,7 @@ internal CompilationVerifier CompileAndVerify( internal CompilationVerifier CompileAndVerifyExperimental( string source, + MessageID feature, string expectedOutput = null, MetadataReference[] additionalRefs = null, IEnumerable dependencies = null, @@ -132,7 +133,7 @@ internal CompilationVerifier CompileAndVerifyExperimental( options = options ?? ((expectedOutput != null) ? TestOptions.ReleaseExe : TestOptions.ReleaseDll); - var compilation = CreateExperimentalCompilationWithMscorlib45(source, additionalRefs, options); + var compilation = CreateExperimentalCompilationWithMscorlib45(source, feature, additionalRefs, options); return CompileAndVerify( compilation: compilation, @@ -405,8 +406,9 @@ public static CSharpCompilation CreateCompilationWithMscorlib( assemblyName: assemblyName); } - public static CSharpCompilation CreateExperimentalCompilationWithMscorlib45( + internal static CSharpCompilation CreateExperimentalCompilationWithMscorlib45( string text, + MessageID feature, IEnumerable references = null, CSharpCompilationOptions options = null, string assemblyName = "", @@ -418,11 +420,12 @@ public static CSharpCompilation CreateExperimentalCompilationWithMscorlib45( refs.AddRange(references); } refs.Add(MscorlibRef_v4_0_30316_17626); - return CreateCompilation(new[] { Parse(text, sourceFileName, TestOptions.ExperimentalParseOptions) }, refs, options, assemblyName); + return CreateCompilation(new[] { Parse(text, sourceFileName, TestOptions.Regular.WithExperimental(feature)) }, refs, options, assemblyName); } - public static CSharpCompilation CreateExperimentalCompilationWithMscorlib45( + internal static CSharpCompilation CreateExperimentalCompilationWithMscorlib45( string[] texts, + MessageID feature, IEnumerable references = null, CSharpCompilationOptions options = null, string assemblyName = "", @@ -434,7 +437,7 @@ public static CSharpCompilation CreateExperimentalCompilationWithMscorlib45( refs.AddRange(references); } refs.Add(MscorlibRef_v4_0_30316_17626); - return CreateCompilation((from text in texts select Parse(text, sourceFileName, TestOptions.ExperimentalParseOptions)).ToArray(), refs, options, assemblyName); + return CreateCompilation((from text in texts select Parse(text, sourceFileName, TestOptions.Regular.WithExperimental(feature))).ToArray(), refs, options, assemblyName); } public static CSharpCompilation CreateCompilationWithMscorlib45AndCSruntime( diff --git a/src/Compilers/Test/Utilities/CSharp.Desktop/SemanticModelTestBase.cs b/src/Compilers/Test/Utilities/CSharp.Desktop/SemanticModelTestBase.cs index 452d325e2f7f2..39446f987307f 100644 --- a/src/Compilers/Test/Utilities/CSharp.Desktop/SemanticModelTestBase.cs +++ b/src/Compilers/Test/Utilities/CSharp.Desktop/SemanticModelTestBase.cs @@ -176,9 +176,9 @@ protected CompilationUtils.SemanticInfoSummary GetSemanticInfoForTest(str return GetSemanticInfoForTest(compilation); } - protected CompilationUtils.SemanticInfoSummary GetSemanticInfoForTestExperimental(string testSrc) where TNode : SyntaxNode + internal CompilationUtils.SemanticInfoSummary GetSemanticInfoForTestExperimental(string testSrc, MessageID feature) where TNode : SyntaxNode { - var compilation = CreateExperimentalCompilationWithMscorlib45(testSrc, new[] { SystemCoreRef }); + var compilation = CreateExperimentalCompilationWithMscorlib45(testSrc, feature, new[] { SystemCoreRef }); return GetSemanticInfoForTest(compilation); } diff --git a/src/Compilers/Test/Utilities/CSharp.Desktop/TestOptions.cs b/src/Compilers/Test/Utilities/CSharp.Desktop/TestOptions.cs deleted file mode 100644 index 7409411602413..0000000000000 --- a/src/Compilers/Test/Utilities/CSharp.Desktop/TestOptions.cs +++ /dev/null @@ -1,82 +0,0 @@ -// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Linq; - -namespace Microsoft.CodeAnalysis.CSharp.Test.Utilities -{ - public static class TestOptions - { - // Disable documentation comments by default so that we don't need to - // document every public member of every test input. - public static readonly CSharpParseOptions Script = new CSharpParseOptions(kind: SourceCodeKind.Script, documentationMode: DocumentationMode.None); - public static readonly CSharpParseOptions Regular = new CSharpParseOptions(kind: SourceCodeKind.Regular, documentationMode: DocumentationMode.None); - public static readonly CSharpParseOptions RegularWithDocumentationComments = new CSharpParseOptions(kind: SourceCodeKind.Regular, documentationMode: DocumentationMode.Diagnose); - - private static readonly SmallDictionary s_experimentalFeatures = new SmallDictionary { { MessageID.IDS_FeatureLocalFunctions.RequiredFeature(), "true" }, { MessageID.IDS_FeatureRefLocalsReturns.RequiredFeature(), "true" } }; - public static readonly CSharpParseOptions ExperimentalParseOptions = - new CSharpParseOptions(kind: SourceCodeKind.Regular, documentationMode: DocumentationMode.None, languageVersion: LanguageVersion.CSharp6).WithFeatures(s_experimentalFeatures); - - public static readonly CSharpCompilationOptions ReleaseDll = new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary, optimizationLevel: OptimizationLevel.Release).WithExtendedCustomDebugInformation(true); - public static readonly CSharpCompilationOptions ReleaseExe = new CSharpCompilationOptions(OutputKind.ConsoleApplication, optimizationLevel: OptimizationLevel.Release).WithExtendedCustomDebugInformation(true); - - public static readonly CSharpCompilationOptions ReleaseDebugDll = new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary, optimizationLevel: OptimizationLevel.Release). - WithExtendedCustomDebugInformation(true). - WithDebugPlusMode(true); - - public static readonly CSharpCompilationOptions ReleaseDebugExe = new CSharpCompilationOptions(OutputKind.ConsoleApplication, optimizationLevel: OptimizationLevel.Release). - WithExtendedCustomDebugInformation(true). - WithDebugPlusMode(true); - - public static readonly CSharpCompilationOptions DebugDll = new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary, optimizationLevel: OptimizationLevel.Debug).WithExtendedCustomDebugInformation(true); - public static readonly CSharpCompilationOptions DebugExe = new CSharpCompilationOptions(OutputKind.ConsoleApplication, optimizationLevel: OptimizationLevel.Debug).WithExtendedCustomDebugInformation(true); - - public static readonly CSharpCompilationOptions ReleaseWinMD = new CSharpCompilationOptions(OutputKind.WindowsRuntimeMetadata, optimizationLevel: OptimizationLevel.Release).WithExtendedCustomDebugInformation(true); - public static readonly CSharpCompilationOptions DebugWinMD = new CSharpCompilationOptions(OutputKind.WindowsRuntimeMetadata, optimizationLevel: OptimizationLevel.Debug).WithExtendedCustomDebugInformation(true); - - public static readonly CSharpCompilationOptions ReleaseModule = new CSharpCompilationOptions(OutputKind.NetModule, optimizationLevel: OptimizationLevel.Release); - public static readonly CSharpCompilationOptions DebugModule = new CSharpCompilationOptions(OutputKind.NetModule, optimizationLevel: OptimizationLevel.Debug); - - public static readonly CSharpCompilationOptions UnsafeReleaseDll = ReleaseDll.WithAllowUnsafe(true); - public static readonly CSharpCompilationOptions UnsafeReleaseExe = ReleaseExe.WithAllowUnsafe(true); - - public static readonly CSharpCompilationOptions UnsafeDebugDll = DebugDll.WithAllowUnsafe(true); - public static readonly CSharpCompilationOptions UnsafeDebugExe = DebugExe.WithAllowUnsafe(true); - - public static CSharpParseOptions WithFeature(this CSharpParseOptions options, string feature, string value) - { - return options.WithFeatures(options.Features.Concat(new[] { new KeyValuePair(feature, value) })); - } - - public static CSharpParseOptions WithLocalFunctionsFeature(this CSharpParseOptions options) - { - return options.WithFeature(MessageID.IDS_FeatureLocalFunctions.RequiredFeature(), "true"); - } - - public static CSharpParseOptions WithRefsFeature(this CSharpParseOptions options) - { - return options.WithFeature(MessageID.IDS_FeatureRefLocalsReturns.RequiredFeature(), "true"); - } - - public static CSharpParseOptions WithStrictFeature(this CSharpParseOptions options) - { - return options.WithFeature("strict", "true"); - } - - public static CSharpParseOptions WithTuplesFeature(this CSharpParseOptions options) - { - return options.WithFeature("tuples", "true"); - } - - public static CSharpParseOptions WithPatternsFeature(this CSharpParseOptions options) - { - return options.WithFeature("patterns", "true"); - } - - public static CSharpParseOptions WithReplaceFeature(this CSharpParseOptions options) - { - return options.WithFeature("replace", "true"); - } - } -} diff --git a/src/Compilers/Test/Utilities/CSharp/CSharpTestBase.cs b/src/Compilers/Test/Utilities/CSharp/CSharpTestBase.cs index 55ba206338cc0..983d57f759216 100644 --- a/src/Compilers/Test/Utilities/CSharp/CSharpTestBase.cs +++ b/src/Compilers/Test/Utilities/CSharp/CSharpTestBase.cs @@ -120,6 +120,7 @@ internal ICompilationVerifier CompileAndVerify( internal ICompilationVerifier CompileAndVerifyExperimental( string source, + MessageID feature, string expectedOutput = null, MetadataReference[] additionalRefs = null, IEnumerable dependencies = null, @@ -133,7 +134,7 @@ internal ICompilationVerifier CompileAndVerifyExperimental( options = options ?? ((expectedOutput != null) ? TestOptions.ReleaseExe : TestOptions.ReleaseDll); - var compilation = CreateExperimentalCompilationWithMscorlib45(source, additionalRefs, options); + var compilation = CreateExperimentalCompilationWithMscorlib45(source, feature, additionalRefs, options); return CompileAndVerify( compilation: compilation, @@ -376,8 +377,9 @@ public static CSharpCompilation CreateCompilationWithMscorlib( assemblyName: assemblyName); } - public static CSharpCompilation CreateExperimentalCompilationWithMscorlib45( + internal static CSharpCompilation CreateExperimentalCompilationWithMscorlib45( string text, + MessageID feature, IEnumerable references = null, CSharpCompilationOptions options = null, string assemblyName = "", @@ -389,11 +391,14 @@ public static CSharpCompilation CreateExperimentalCompilationWithMscorlib45( refs.AddRange(references); } refs.Add(MscorlibRef_v4_0_30316_17626); - return CreateCompilation(new[] { Parse(text, sourceFileName, TestOptions.ExperimentalParseOptions) }, refs, options, assemblyName); + + var parseOptions = TestOptions.Regular.WithExperimental(feature); + return CreateCompilation(new[] { Parse(text, sourceFileName, parseOptions) }, refs, options, assemblyName); } - public static CSharpCompilation CreateExperimentalCompilationWithMscorlib45( + internal static CSharpCompilation CreateExperimentalCompilationWithMscorlib45( string[] texts, + MessageID feature, IEnumerable references = null, CSharpCompilationOptions options = null, string assemblyName = "", @@ -405,7 +410,9 @@ public static CSharpCompilation CreateExperimentalCompilationWithMscorlib45( refs.AddRange(references); } refs.Add(MscorlibRef_v4_0_30316_17626); - return CreateCompilation((from text in texts select Parse(text, sourceFileName, TestOptions.ExperimentalParseOptions)).ToArray(), refs, options, assemblyName); + + var parseOptions = TestOptions.Regular.WithExperimental(feature); + return CreateCompilation((from text in texts select Parse(text, sourceFileName, parseOptions)).ToArray(), refs, options, assemblyName); } public static CSharpCompilation CreateCompilationWithMscorlib45AndCSruntime( diff --git a/src/Compilers/Test/Utilities/CSharp/SemanticModelTestBase.cs b/src/Compilers/Test/Utilities/CSharp/SemanticModelTestBase.cs index 452d325e2f7f2..6a19130acd9aa 100644 --- a/src/Compilers/Test/Utilities/CSharp/SemanticModelTestBase.cs +++ b/src/Compilers/Test/Utilities/CSharp/SemanticModelTestBase.cs @@ -169,16 +169,15 @@ private static ConstructorInitializerSyntax GetFirstConstructorInitializer(Synta return (ConstructorInitializerSyntax)constructorInitializers.FirstOrDefault(); } - protected CompilationUtils.SemanticInfoSummary GetSemanticInfoForTest(string testSrc) where TNode : SyntaxNode { var compilation = CreateCompilationWithMscorlib(testSrc, new[] { SystemCoreRef }); return GetSemanticInfoForTest(compilation); } - protected CompilationUtils.SemanticInfoSummary GetSemanticInfoForTestExperimental(string testSrc) where TNode : SyntaxNode + internal CompilationUtils.SemanticInfoSummary GetSemanticInfoForTestExperimental(string testSrc, MessageID feature) where TNode : SyntaxNode { - var compilation = CreateExperimentalCompilationWithMscorlib45(testSrc, new[] { SystemCoreRef }); + var compilation = CreateExperimentalCompilationWithMscorlib45(testSrc, feature, new[] { SystemCoreRef }); return GetSemanticInfoForTest(compilation); } diff --git a/src/Compilers/Test/Utilities/CSharp/TestOptions.cs b/src/Compilers/Test/Utilities/CSharp/TestOptions.cs index bc4d708e749ae..473f4ceccc315 100644 --- a/src/Compilers/Test/Utilities/CSharp/TestOptions.cs +++ b/src/Compilers/Test/Utilities/CSharp/TestOptions.cs @@ -1,5 +1,6 @@ // Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +using System; using System.Collections.Generic; using System.Collections.Immutable; using System.Linq; @@ -14,10 +15,6 @@ public static class TestOptions public static readonly CSharpParseOptions Regular = new CSharpParseOptions(kind: SourceCodeKind.Regular, documentationMode: DocumentationMode.None); public static readonly CSharpParseOptions RegularWithDocumentationComments = new CSharpParseOptions(kind: SourceCodeKind.Regular, documentationMode: DocumentationMode.Diagnose); - private static readonly SmallDictionary s_experimentalFeatures = new SmallDictionary { { MessageID.IDS_FeatureLocalFunctions.RequiredFeature(), "true" }, { MessageID.IDS_FeatureRefLocalsReturns.RequiredFeature(), "true" } }; - public static readonly CSharpParseOptions ExperimentalParseOptions = - new CSharpParseOptions(kind: SourceCodeKind.Regular, documentationMode: DocumentationMode.None, languageVersion: LanguageVersion.CSharp6).WithFeatures(s_experimentalFeatures); - public static readonly CSharpCompilationOptions ReleaseDll = new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary, optimizationLevel: OptimizationLevel.Release).WithExtendedCustomDebugInformation(true); public static readonly CSharpCompilationOptions ReleaseExe = new CSharpCompilationOptions(OutputKind.ConsoleApplication, optimizationLevel: OptimizationLevel.Release).WithExtendedCustomDebugInformation(true); @@ -44,34 +41,57 @@ public static class TestOptions public static readonly CSharpCompilationOptions UnsafeDebugDll = DebugDll.WithAllowUnsafe(true); public static readonly CSharpCompilationOptions UnsafeDebugExe = DebugExe.WithAllowUnsafe(true); - public static CSharpParseOptions WithFeature(this CSharpParseOptions options, string feature, string value) - { - return options.WithFeatures(options.Features.Concat(new[] { new KeyValuePair(feature, value) })); - } - public static CSharpParseOptions WithStrictFeature(this CSharpParseOptions options) { - return options.WithFeature("strict", "true"); + return options.WithFeatures(options.Features.Concat(new[] { new KeyValuePair("strict", "true") })); } public static CSharpParseOptions WithLocalFunctionsFeature(this CSharpParseOptions options) { - return options.WithFeature(MessageID.IDS_FeatureLocalFunctions.RequiredFeature(), "true"); + return WithExperimental(options, MessageID.IDS_FeatureLocalFunctions); } public static CSharpParseOptions WithRefsFeature(this CSharpParseOptions options) { - return options.WithFeature(MessageID.IDS_FeatureRefLocalsReturns.RequiredFeature(), "true"); + return WithExperimental(options, MessageID.IDS_FeatureRefLocalsReturns); } public static CSharpParseOptions WithTuplesFeature(this CSharpParseOptions options) { - return options.WithFeature("tuples", "true"); + return WithExperimental(options, MessageID.IDS_FeatureTuples); } public static CSharpParseOptions WithReplaceFeature(this CSharpParseOptions options) { - return options.WithFeature("replace", "true"); + return WithExperimental(options, MessageID.IDS_FeatureReplace); + } + + public static CSharpParseOptions WithPatternsFeature(this CSharpParseOptions options) + { + return WithExperimental(options, MessageID.IDS_FeaturePatternMatching); + } + + internal static CSharpParseOptions WithExperimental(this CSharpParseOptions options, params MessageID[] features) + { + if (features.Length == 0) + { + throw new InvalidOperationException("Need at least one feature to enable"); + } + + var list = new List>(); + foreach (var feature in features) + { + var name = feature.RequiredFeature(); + if (name == null) + { + throw new InvalidOperationException($"{feature} is not a valid experimental feature"); + } + + list.Add(new KeyValuePair(name, "true")); + } + + return options.WithFeatures(options.Features.Concat(list)); } } } + diff --git a/src/Compilers/Test/Utilities/VisualBasic/TestOptions.vb b/src/Compilers/Test/Utilities/VisualBasic/TestOptions.vb index 65c857af7e8ee..ffa5d57329bae 100644 --- a/src/Compilers/Test/Utilities/VisualBasic/TestOptions.vb +++ b/src/Compilers/Test/Utilities/VisualBasic/TestOptions.vb @@ -1,6 +1,7 @@ ' Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. Imports System.Runtime.CompilerServices +Imports Microsoft.CodeAnalysis.VisualBasic.Syntax.InternalSyntax Public Class TestOptions Public Shared ReadOnly Script As New VisualBasicParseOptions(kind:=SourceCodeKind.Script) @@ -17,11 +18,6 @@ Public Class TestOptions WithExtendedCustomDebugInformation(True). WithDebugPlusMode(True) - Private Shared ReadOnly s_features As New Dictionary(Of String, String) ' No experimental features to enable at this time - Public Shared ReadOnly ExperimentalReleaseExe As New VisualBasicCompilationOptions(OutputKind.ConsoleApplication, - optimizationLevel:=OptimizationLevel.Release, - parseOptions:=New VisualBasicParseOptions(kind:=SourceCodeKind.Regular).WithFeatures(s_features)) - Public Shared ReadOnly DebugDll As VisualBasicCompilationOptions = New VisualBasicCompilationOptions(OutputKind.DynamicallyLinkedLibrary, optimizationLevel:=OptimizationLevel.Debug).WithExtendedCustomDebugInformation(True) Public Shared ReadOnly DebugExe As VisualBasicCompilationOptions = New VisualBasicCompilationOptions(OutputKind.ConsoleApplication, optimizationLevel:=OptimizationLevel.Debug).WithExtendedCustomDebugInformation(True) @@ -31,18 +27,27 @@ Public Class TestOptions End Class Friend Module TestOptionExtensions - - Public Function WithFeature(options As VisualBasicParseOptions, feature As String, value As String) As VisualBasicParseOptions - Return options.WithFeatures(options.Features.Concat({New KeyValuePair(Of String, String)(feature, value)})) - End Function - Public Function WithStrictFeature(options As VisualBasicParseOptions) As VisualBasicParseOptions - Return options.WithFeature("Strict", "true") + Return options.WithFeatures(options.Features.Concat(New KeyValuePair(Of String, String)() {New KeyValuePair(Of String, String)("Strict", "true")})) End Function - Public Function WithDeterministicFeature(options As VisualBasicParseOptions) As VisualBasicParseOptions - Return options.WithFeature("Deterministic", "true") + Friend Function WithExperimental(options As VisualBasicParseOptions, ParamArray features As Feature()) As VisualBasicParseOptions + If features.Length = 0 Then + Throw New InvalidOperationException("Need at least one feature to enable") + End If + + Dim list As New List(Of KeyValuePair(Of String, String)) + For Each feature In features + Dim flagName = feature.GetFeatureFlag() + If flagName Is Nothing Then + Throw New InvalidOperationException($"{feature} is not an experimental feature") + End If + + list.Add(New KeyValuePair(Of String, String)(flagName, "True")) + Next + + Return options.WithFeatures(options.Features.Concat(list)) End Function End Module diff --git a/src/Compilers/VisualBasic/Test/Semantic/Semantics/ConditionalAccessTests.vb b/src/Compilers/VisualBasic/Test/Semantic/Semantics/ConditionalAccessTests.vb index 570a0b6275db1..feb07a9912617 100644 --- a/src/Compilers/VisualBasic/Test/Semantic/Semantics/ConditionalAccessTests.vb +++ b/src/Compilers/VisualBasic/Test/Semantic/Semantics/ConditionalAccessTests.vb @@ -2207,7 +2207,7 @@ End Structure - Dim compilation = CompilationUtils.CreateCompilationWithMscorlibAndVBRuntime(compilationDef, TestOptions.ExperimentalReleaseExe) + Dim compilation = CompilationUtils.CreateCompilationWithMscorlibAndVBRuntime(compilationDef, TestOptions.ReleaseExe) Dim verifier = CompileAndVerify(compilation, expectedOutput:= Date: Thu, 16 Jun 2016 15:32:46 -0700 Subject: [PATCH 20/36] loose up a bit on what happens when build error report on us errors on a file that doesn't exist in roslyn solution but have error code we own. we used to treat them as project level errors but that made us to sometime show one entry for multiple error reports. now, we will at least show all of them, but things like double click will not work since we don't know which document it is. --- .../ExternalErrorDiagnosticUpdateSource.cs | 23 ++++++++++++++----- 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/src/VisualStudio/Core/Def/Implementation/TaskList/ExternalErrorDiagnosticUpdateSource.cs b/src/VisualStudio/Core/Def/Implementation/TaskList/ExternalErrorDiagnosticUpdateSource.cs index ad753f44031fb..25d5a03d944b1 100644 --- a/src/VisualStudio/Core/Def/Implementation/TaskList/ExternalErrorDiagnosticUpdateSource.cs +++ b/src/VisualStudio/Core/Def/Implementation/TaskList/ExternalErrorDiagnosticUpdateSource.cs @@ -561,11 +561,11 @@ public bool Equals(DiagnosticData item1, DiagnosticData item2) return false; } + var lineColumn1 = GetOriginalOrMappedLineColumn(item1); + var lineColumn2 = GetOriginalOrMappedLineColumn(item2); + if (item1.DocumentId != null && item2.DocumentId != null) { - var lineColumn1 = GetOriginalOrMappedLineColumn(item1); - var lineColumn2 = GetOriginalOrMappedLineColumn(item2); - return item1.Id == item2.Id && item1.Message == item2.Message && item1.ProjectId == item2.ProjectId && @@ -578,15 +578,18 @@ public bool Equals(DiagnosticData item1, DiagnosticData item2) return item1.Id == item2.Id && item1.Message == item2.Message && item1.ProjectId == item2.ProjectId && + item1.DataLocation?.OriginalFilePath == item2.DataLocation?.OriginalFilePath && + lineColumn1.Item1 == lineColumn2.Item1 && + lineColumn1.Item2 == lineColumn2.Item2 && item1.Severity == item2.Severity; } public int GetHashCode(DiagnosticData obj) { + var lineColumn = GetOriginalOrMappedLineColumn(obj); + if (obj.DocumentId != null) { - var lineColumn = GetOriginalOrMappedLineColumn(obj); - return Hash.Combine(obj.Id, Hash.Combine(obj.Message, Hash.Combine(obj.ProjectId, @@ -597,7 +600,10 @@ public int GetHashCode(DiagnosticData obj) return Hash.Combine(obj.Id, Hash.Combine(obj.Message, - Hash.Combine(obj.ProjectId, (int)obj.Severity))); + Hash.Combine(obj.ProjectId, + Hash.Combine(obj.DataLocation?.OriginalFilePath?.GetHashCode() ?? 0, + Hash.Combine(lineColumn.Item1, + Hash.Combine(lineColumn.Item2, (int)obj.Severity)))))); } private static ValueTuple GetOriginalOrMappedLineColumn(DiagnosticData data) @@ -608,6 +614,11 @@ private static ValueTuple GetOriginalOrMappedLineColumn(DiagnosticData return ValueTuple.Create(data.DataLocation?.MappedStartLine ?? 0, data.DataLocation?.MappedStartColumn ?? 0); } + if (data.DocumentId == null) + { + return ValueTuple.Create(data.DataLocation?.MappedStartLine ?? 0, data.DataLocation?.MappedStartColumn ?? 0); + } + var containedDocument = workspace.GetHostDocument(data.DocumentId) as ContainedDocument; if (containedDocument == null) { From db3e3cab3c9343c120746f50db997a05d8c2040e Mon Sep 17 00:00:00 2001 From: VSadov Date: Thu, 16 Jun 2016 16:47:29 -0700 Subject: [PATCH 21/36] Added initial support for explicit tuple conversions --- .../Portable/Binder/Binder_Conversions.cs | 15 +- .../Portable/Binder/Binder_Expressions.cs | 177 +++- .../Portable/Binder/Binder_Statements.cs | 32 +- .../Semantics/Conversions/Conversion.cs | 34 +- .../Semantics/Conversions/Conversions.cs | 33 +- .../Semantics/Conversions/ConversionsBase.cs | 133 ++- .../CSharp/Portable/BoundTree/Formatting.cs | 22 +- .../LocalRewriter/LocalRewriter_Conversion.cs | 17 +- .../Test/Emit/CodeGen/CodeGenTupleTest.cs | 960 ++++++++++++++++-- 9 files changed, 1217 insertions(+), 206 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Conversions.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Conversions.cs index 015775a43cc9c..9e6e257a48637 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Conversions.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Conversions.cs @@ -364,7 +364,20 @@ private BoundExpression CreateTupleLiteralConversion(CSharpSyntaxNode syntax, Bo { var argument = arguments[i]; var destType = targetElementTypes[i]; - convertedArguments.Add(CreateConversion(argument, destType, diagnostics)); + + HashSet useSiteDiagnostics = null; + Conversion elementConversion; + if (isCast) + { + elementConversion = this.Conversions.ClassifyConversionForCast(argument, destType, ref useSiteDiagnostics); + } + else + { + elementConversion = this.Conversions.ClassifyConversionFromExpression(argument, destType, ref useSiteDiagnostics); + } + + diagnostics.Add(syntax, useSiteDiagnostics); + convertedArguments.Add(CreateConversion(argument.Syntax, argument, elementConversion, isCast, destType, diagnostics)); } BoundExpression result = new BoundConvertedTupleLiteral( diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs index 71b826bb9ed6d..e9e69fe8aea24 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs @@ -1716,60 +1716,9 @@ private BoundExpression BindCastCore(ExpressionSyntax node, BoundExpression oper HashSet useSiteDiagnostics = null; Conversion conversion = this.Conversions.ClassifyConversionForCast(operand, targetType, ref useSiteDiagnostics); diagnostics.Add(node, useSiteDiagnostics); - bool targetTypeIsStatic = targetType.IsStatic; - if (operand.HasAnyErrors || targetType.IsErrorType() || !conversion.IsValid || targetTypeIsStatic) + if (operand.HasAnyErrors || targetType.IsErrorType() || !conversion.IsValid || targetType.IsStatic) { - // Make sure that errors within the unbound lambda don't get lost. - if (operand.Kind == BoundKind.UnboundLambda) - { - GenerateAnonymousFunctionConversionError(diagnostics, operand.Syntax, (UnboundLambda)operand, targetType); - } - else if (operand.HasAnyErrors || targetType.IsErrorType()) - { - // an error has already been reported elsewhere - } - else if (targetTypeIsStatic) - { - // The specification states in the section titled "Referencing Static - // Class Types" that it is always illegal to have a static class in a - // cast operator. - diagnostics.Add(ErrorCode.ERR_ConvertToStaticClass, node.Location, targetType); - } - else if (!targetType.IsReferenceType && !targetType.IsNullableType() && operand.IsLiteralNull()) - { - diagnostics.Add(ErrorCode.ERR_ValueCantBeNull, node.Location, targetType); - } - else if (conversion.ResultKind == LookupResultKind.OverloadResolutionFailure) - { - Debug.Assert(conversion.IsUserDefined); - - ImmutableArray originalUserDefinedConversions = conversion.OriginalUserDefinedConversions; - if (originalUserDefinedConversions.Length > 1) - { - diagnostics.Add(ErrorCode.ERR_AmbigUDConv, node.Location, originalUserDefinedConversions[0], originalUserDefinedConversions[1], operand.Type, targetType); - } - else - { - Debug.Assert(originalUserDefinedConversions.Length == 0, - "How can there be exactly one applicable user-defined conversion if the conversion doesn't exist?"); - SymbolDistinguisher distinguisher = new SymbolDistinguisher(this.Compilation, operand.Type, targetType); - diagnostics.Add(ErrorCode.ERR_NoExplicitConv, node.Location, distinguisher.First, distinguisher.Second); - } - } - else - { - // TODO: report more specific diagnostics here for failed method group conversions - if (operand.Kind == BoundKind.MethodGroup) - { - diagnostics.Add(ErrorCode.ERR_NoExplicitConv, node.Location, MessageID.IDS_SK_METHOD.Localize(), targetType); - } - else - { - Debug.Assert((object)operand.Type != null); - SymbolDistinguisher distinguisher = new SymbolDistinguisher(this.Compilation, operand.Type, targetType); - diagnostics.Add(ErrorCode.ERR_NoExplicitConv, node.Location, distinguisher.First, distinguisher.Second); - } - } + GenerateExplicitConversionErrors(diagnostics, node, conversion, operand, targetType); return new BoundConversion( node, @@ -1785,6 +1734,128 @@ private BoundExpression BindCastCore(ExpressionSyntax node, BoundExpression oper return CreateConversion(node, operand, conversion, isCast: true, wasCompilerGenerated: wasCompilerGenerated, destination: targetType, diagnostics: diagnostics); } + private void GenerateExplicitConversionErrors( + DiagnosticBag diagnostics, + CSharpSyntaxNode syntax, + Conversion conversion, + BoundExpression operand, + TypeSymbol targetType) + { + // Make sure that errors within the unbound lambda don't get lost. + if (operand.Kind == BoundKind.UnboundLambda) + { + GenerateAnonymousFunctionConversionError(diagnostics, operand.Syntax, (UnboundLambda)operand, targetType); + return; + } + + if (operand.HasAnyErrors || targetType.IsErrorType()) + { + // an error has already been reported elsewhere + return; + } + + if (targetType.IsStatic) + { + // The specification states in the section titled "Referencing Static + // Class Types" that it is always illegal to have a static class in a + // cast operator. + diagnostics.Add(ErrorCode.ERR_ConvertToStaticClass, syntax.Location, targetType); + return; + } + + if (!targetType.IsReferenceType && !targetType.IsNullableType() && operand.IsLiteralNull()) + { + diagnostics.Add(ErrorCode.ERR_ValueCantBeNull, syntax.Location, targetType); + return; + } + + if (conversion.ResultKind == LookupResultKind.OverloadResolutionFailure) + { + Debug.Assert(conversion.IsUserDefined); + + ImmutableArray originalUserDefinedConversions = conversion.OriginalUserDefinedConversions; + if (originalUserDefinedConversions.Length > 1) + { + diagnostics.Add(ErrorCode.ERR_AmbigUDConv, syntax.Location, originalUserDefinedConversions[0], originalUserDefinedConversions[1], operand.Type, targetType); + } + else + { + Debug.Assert(originalUserDefinedConversions.Length == 0, + "How can there be exactly one applicable user-defined conversion if the conversion doesn't exist?"); + SymbolDistinguisher distinguisher1 = new SymbolDistinguisher(this.Compilation, operand.Type, targetType); + diagnostics.Add(ErrorCode.ERR_NoExplicitConv, syntax.Location, distinguisher1.First, distinguisher1.Second); + } + + return; + } + + // TODO: report more specific diagnostics here for failed method group conversions + if (operand.Kind == BoundKind.MethodGroup) + { + diagnostics.Add(ErrorCode.ERR_NoExplicitConv, syntax.Location, MessageID.IDS_SK_METHOD.Localize(), targetType); + return; + } + + if (operand.Kind == BoundKind.TupleLiteral) + { + var tuple = (BoundTupleLiteral)operand; + var targetElementTypes = default(ImmutableArray); + + // source does not have a type. + // Such conversions could only happen via explicit tuple literal conversions + // which are currently not supported. + // See: https://github.com/dotnet/roslyn/issues/11804 + if ((object)tuple.Type == null) + { + Error(diagnostics, ErrorCode.ERR_NoExplicitConv, syntax, tuple.Display, targetType); + return; + } + + // If target is a tuple or compatible type with the same number of elements, + // report errors for tuple arguments that failed to convert, which would be more useful. + if (targetType.TryGetElementTypesIfTupleOrCompatible(out targetElementTypes) && + targetElementTypes.Length == tuple.Arguments.Length) + { + GenerateExplicitConversionErrorsForTupleLiteralArguments(diagnostics, syntax, tuple.Arguments, targetElementTypes); + return; + } + + // Otherwise it is just a regular conversion failure from T1 to T2. + } + + Debug.Assert((object)operand.Type != null); + SymbolDistinguisher distinguisher = new SymbolDistinguisher(this.Compilation, operand.Type, targetType); + diagnostics.Add(ErrorCode.ERR_NoExplicitConv, syntax.Location, distinguisher.First, distinguisher.Second); + } + + private void GenerateExplicitConversionErrorsForTupleLiteralArguments( + DiagnosticBag diagnostics, + CSharpSyntaxNode syntax, + ImmutableArray tupleArguments, + ImmutableArray targetElementTypes) + { + var argLength = tupleArguments.Length; + + // report all leaf elements of the tuple literal that failed to convert + // NOTE: we are not responsible for reporting use site errors here, just the failed leaf conversions. + // By the time we get here we have done analysis and know we have failed the cast in general, and diagnostics collected in the process is already in the bag. + // The only thing left is to form a diagnostics about the actually failing conversion(s). + // This whole method does not itself collect any usesite diagnostics. Its only purpose is to produce an error better than "conversion failed here" + HashSet usDiagsUnused = null; + + for (int i = 0; i < targetElementTypes.Length; i++) + { + var argument = tupleArguments[i]; + var targetElementType = targetElementTypes[i]; + + var elementConversion = Conversions.ClassifyConversionFromType(argument.Type, targetElementType, ref usDiagsUnused); + if (!elementConversion.IsValid) + { + GenerateExplicitConversionErrors(diagnostics, argument.Syntax, elementConversion, argument, targetElementType); + } + } + } + private BoundExpression BindOriginal(OriginalExpressionSyntax syntax, DiagnosticBag diagnostics) { var containingMethod = (MethodSymbol)this.ContainingMember(); diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs index 5f4a01894d91a..1488fa3df292e 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs @@ -2646,10 +2646,14 @@ protected static void GenerateImplicitConversionError(DiagnosticBag diagnostics, } } - protected void GenerateImplicitConversionError(DiagnosticBag diagnostics, CSharpSyntaxNode syntax, - Conversion conversion, BoundExpression expression, TypeSymbol targetType) + protected void GenerateImplicitConversionError( + DiagnosticBag diagnostics, + CSharpSyntaxNode syntax, + Conversion conversion, + BoundExpression operand, + TypeSymbol targetType) { - Debug.Assert(expression != null); + Debug.Assert(operand != null); Debug.Assert((object)targetType != null); if (targetType.TypeKind == TypeKind.Error) @@ -2657,20 +2661,20 @@ protected void GenerateImplicitConversionError(DiagnosticBag diagnostics, CSharp return; } - if (expression.Kind == BoundKind.BadExpression) + if (operand.Kind == BoundKind.BadExpression) { return; } - if (expression.Kind == BoundKind.UnboundLambda) + if (operand.Kind == BoundKind.UnboundLambda) { - GenerateAnonymousFunctionConversionError(diagnostics, syntax, (UnboundLambda)expression, targetType); + GenerateAnonymousFunctionConversionError(diagnostics, syntax, (UnboundLambda)operand, targetType); return; } - if (expression.Kind == BoundKind.TupleLiteral) + if (operand.Kind == BoundKind.TupleLiteral) { - var tuple = (BoundTupleLiteral)expression; + var tuple = (BoundTupleLiteral)operand; var targetElementTypes = default(ImmutableArray); // If target is a tuple or compatible type with the same number of elements, @@ -2692,14 +2696,14 @@ protected void GenerateImplicitConversionError(DiagnosticBag diagnostics, CSharp // Otherwise it is just a regular conversion failure from T1 to T2. } - var sourceType = expression.Type; + var sourceType = operand.Type; if ((object)sourceType != null) { - GenerateImplicitConversionError(diagnostics, this.Compilation, syntax, conversion, sourceType, targetType, expression.ConstantValue); + GenerateImplicitConversionError(diagnostics, this.Compilation, syntax, conversion, sourceType, targetType, operand.ConstantValue); return; } - if (expression.IsLiteralNull()) + if (operand.IsLiteralNull()) { if (targetType.TypeKind == TypeKind.TypeParameter) { @@ -2713,9 +2717,9 @@ protected void GenerateImplicitConversionError(DiagnosticBag diagnostics, CSharp } } - if (expression.Kind == BoundKind.MethodGroup) + if (operand.Kind == BoundKind.MethodGroup) { - var methodGroup = (BoundMethodGroup)expression; + var methodGroup = (BoundMethodGroup)operand; if (!Conversions.ReportDelegateMethodGroupDiagnostics(this, methodGroup, targetType, diagnostics)) { var nodeForSquiggle = syntax; @@ -2744,7 +2748,7 @@ protected void GenerateImplicitConversionError(DiagnosticBag diagnostics, CSharp return; } - Debug.Assert(expression.HasAnyErrors && expression.Kind != BoundKind.UnboundLambda, "Missing a case in implicit conversion error reporting"); + Debug.Assert(operand.HasAnyErrors && operand.Kind != BoundKind.UnboundLambda, "Missing a case in implicit conversion error reporting"); } private void GenerateImplicitConversionErrorsForTupleLiteralArguments( diff --git a/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/Conversion.cs b/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/Conversion.cs index c06ea33c85c71..3849c4221a1ae 100644 --- a/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/Conversion.cs +++ b/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/Conversion.cs @@ -7,6 +7,7 @@ using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Text; using Roslyn.Utilities; +using System.Linq; namespace Microsoft.CodeAnalysis.CSharp { @@ -53,7 +54,10 @@ public struct Conversion : IEquatable private const byte IsExtensionMethodMask = 1 << 0; private const byte IsArrayIndexMask = 1 << 1; + private readonly Conversion[] _nestedConversionsOpt; + private Conversion(ConversionKind kind, bool isExtensionMethod, bool isArrayIndex, UserDefinedConversionResult conversionResult, MethodSymbol methodGroupConversionMethod) + : this() { _kind = kind; _conversionResult = conversionResult; @@ -81,6 +85,13 @@ internal Conversion(ConversionKind kind) this._kind = kind; } + internal Conversion(ConversionKind kind, Conversion[] nestedConversions) + : this() + { + this._kind = kind; + this._nestedConversionsOpt = nestedConversions; + } + internal ConversionKind Kind { get @@ -128,7 +139,28 @@ internal bool IsValid { get { - return this.Exists && (!this.IsUserDefined || (object)this.Method != null || _conversionResult.Kind == UserDefinedConversionResultKind.Valid); + if (!this.Exists) + { + return false; + } + + if (_nestedConversionsOpt != null) + { + foreach (var conv in _nestedConversionsOpt) + { + if (!conv.IsValid) + { + return false; + } + } + + Debug.Assert(!this.IsUserDefined); + return true; + } + + return !this.IsUserDefined || + (object)this.Method != null || + _conversionResult.Kind == UserDefinedConversionResultKind.Valid; } } diff --git a/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/Conversions.cs b/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/Conversions.cs index 1d644089d23c9..fa8189495f57c 100644 --- a/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/Conversions.cs +++ b/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/Conversions.cs @@ -49,7 +49,7 @@ public Conversion ClassifyConversionFromExpression(BoundExpression sourceExpress return result; } - return ClassifyExplicitOnlyConversionFromExpression(sourceExpression, source, destination, ref useSiteDiagnostics); + return ClassifyExplicitOnlyConversionFromExpression(sourceExpression, source, destination, ref useSiteDiagnostics, forCast: false); } /// @@ -71,12 +71,12 @@ public Conversion ClassifyConversionForCast(BoundExpression source, TypeSymbol d Debug.Assert((object)destination != null); Conversion implicitConversion = ClassifyImplicitConversionFromExpression(source, destination, ref useSiteDiagnostics); - if (implicitConversion.Exists && !implicitConversion.IsUserDefined && !implicitConversion.IsDynamic) + if (implicitConversion.Exists && !ExplicitConversionMayDifferFromImplicit(implicitConversion)) { return implicitConversion; } - Conversion explicitConversion = ClassifyExplicitOnlyConversionFromExpression(source, source.Type, destination, ref useSiteDiagnostics); + Conversion explicitConversion = ClassifyExplicitOnlyConversionFromExpression(source, source.Type, destination, ref useSiteDiagnostics, forCast: true); if (explicitConversion.Exists) { return explicitConversion; @@ -109,6 +109,24 @@ public Conversion ClassifyConversionForCast(BoundExpression source, TypeSymbol d return implicitConversion.Exists ? implicitConversion : Conversion.NoConversion; } + /// + /// returns true when implicit conversion is not necessarily the same as explicit conversion + /// + private static bool ExplicitConversionMayDifferFromImplicit(Conversion implicitConversion) + { + switch (implicitConversion.Kind) + { + case ConversionKind.ImplicitUserDefined: + case ConversionKind.ImplicitDynamic: + case ConversionKind.ImplicitTuple: + case ConversionKind.ImplicitNullable: + return true; + + default: + return false; + } + } + private Conversion ClassifyImplicitBuiltInConversionFromExpression(BoundExpression sourceExpression, TypeSymbol source, TypeSymbol destination, ref HashSet useSiteDiagnostics) { Debug.Assert(sourceExpression != null || (object)source != null); @@ -369,7 +387,12 @@ internal static bool HasImplicitConstantExpressionConversion(BoundExpression sou return false; } - private Conversion ClassifyExplicitOnlyConversionFromExpression(BoundExpression sourceExpression, TypeSymbol source, TypeSymbol destination, ref HashSet useSiteDiagnostics) + private Conversion ClassifyExplicitOnlyConversionFromExpression( + BoundExpression sourceExpression, + TypeSymbol source, + TypeSymbol destination, + ref HashSet useSiteDiagnostics, + bool forCast) { Debug.Assert(sourceExpression != null || (object)source != null); Debug.Assert(sourceExpression == null || (object)sourceExpression.Type == (object)source); @@ -385,7 +408,7 @@ private Conversion ClassifyExplicitOnlyConversionFromExpression(BoundExpression } else { - Conversion conversion = ClassifyExplicitBuiltInOnlyConversion(source, destination, ref useSiteDiagnostics); + Conversion conversion = ClassifyExplicitBuiltInOnlyConversion(source, destination, ref useSiteDiagnostics, forCast); if (conversion.Exists) { return conversion; diff --git a/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/ConversionsBase.cs b/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/ConversionsBase.cs index 63757699a3c50..d52bb27cda399 100644 --- a/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/ConversionsBase.cs +++ b/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/ConversionsBase.cs @@ -157,7 +157,7 @@ public Conversion ClassifyConversion(TypeSymbol source, TypeSymbol destination, } } - var conversion = ClassifyExplicitBuiltInOnlyConversion(source, destination, ref useSiteDiagnostics); + var conversion = ClassifyExplicitBuiltInOnlyConversion(source, destination, ref useSiteDiagnostics, forCast: false); if (conversion.Exists) { return conversion; @@ -186,19 +186,22 @@ public Conversion ClassifyConversionForCast(TypeSymbol source, TypeSymbol destin { return fastConversion; } - else + + Conversion implicitBuiltInConversion = ClassifyImplicitBuiltInConversionSlow(source, destination, ref useSiteDiagnostics); + if (implicitBuiltInConversion.Exists && !ExplicitConversionMayDifferFromImplicit(implicitBuiltInConversion)) { - Conversion conversion1 = ClassifyImplicitBuiltInConversionSlow(source, destination, ref useSiteDiagnostics); - if (conversion1.Exists) - { - return conversion1; - } + return implicitBuiltInConversion; } - Conversion conversion = ClassifyExplicitBuiltInOnlyConversion(source, destination, ref useSiteDiagnostics); - if (conversion.Exists) + Conversion explicitBuiltInConversion = ClassifyExplicitBuiltInOnlyConversion(source, destination, ref useSiteDiagnostics, forCast: true); + if (explicitBuiltInConversion.Exists) { - return conversion; + return explicitBuiltInConversion; + } + + if (implicitBuiltInConversion.Exists) + { + return implicitBuiltInConversion; } // It is possible for a user-defined conversion to be unambiguous when considered as @@ -217,7 +220,7 @@ public Conversion ClassifyConversionForCast(TypeSymbol source, TypeSymbol destin // // fail. - conversion = GetExplicitUserDefinedConversion(null, source, destination, ref useSiteDiagnostics); + var conversion = GetExplicitUserDefinedConversion(null, source, destination, ref useSiteDiagnostics); if (conversion.Exists) { return conversion; @@ -379,9 +382,10 @@ internal Conversion ClassifyStandardImplicitConversion(TypeSymbol source, TypeSy return Conversion.PointerToVoid; } - if (HasImplicitTupleConversion(source, destination, ref useSiteDiagnostics)) + var tupleConversion = ClassifyImplicitTupleConversion(source, destination, ref useSiteDiagnostics); + if (tupleConversion.Exists) { - return Conversion.ImplicitTuple; + return tupleConversion; } return Conversion.NoConversion; @@ -453,20 +457,20 @@ public Conversion ClassifyImplicitConversion(TypeSymbol source, TypeSymbol desti return GetImplicitUserDefinedConversion(null, source, destination, ref useSiteDiagnostics); } - private Conversion ClassifyExplicitBuiltInOnlyConversion(TypeSymbol source, TypeSymbol destination, ref HashSet useSiteDiagnostics) + private Conversion ClassifyExplicitBuiltInOnlyConversion(TypeSymbol source, TypeSymbol destination, ref HashSet useSiteDiagnostics, bool forCast) { Debug.Assert((object)source != null); Debug.Assert((object)destination != null); - // The call to HasExplicitNumericConversion isn't necessary, because it is always tested - // already by the "FastConversion" code. - Debug.Assert(!HasExplicitNumericConversion(source, destination)); - if (source.SpecialType == SpecialType.System_Void || destination.SpecialType == SpecialType.System_Void) { return Conversion.NoConversion; } + // The call to HasExplicitNumericConversion isn't necessary, because it is always tested + // already by the "FastConversion" code. + Debug.Assert(!HasExplicitNumericConversion(source, destination)); + //if (HasExplicitNumericConversion(source, specialTypeSource, destination, specialTypeDest)) //{ // return Conversion.ExplicitNumeric; @@ -482,9 +486,10 @@ private Conversion ClassifyExplicitBuiltInOnlyConversion(TypeSymbol source, Type return Conversion.ExplicitEnumeration; } - if (HasExplicitNullableConversion(source, destination)) + var nullableConversion = ClassifyExplicitNullableConversion(source, destination, ref useSiteDiagnostics, forCast); + if (nullableConversion.Exists) { - return Conversion.ExplicitNullable; + return nullableConversion; } if (HasExplicitReferenceConversion(source, destination, ref useSiteDiagnostics)) @@ -497,6 +502,12 @@ private Conversion ClassifyExplicitBuiltInOnlyConversion(TypeSymbol source, Type return Conversion.Unboxing; } + var tupleConversion = ClassifyExplicitTupleConversion(source, destination, ref useSiteDiagnostics, forCast); + if (tupleConversion.Exists) + { + return tupleConversion; + } + if (HasPointerToPointerConversion(source, destination)) { return Conversion.PointerToPointer; @@ -914,7 +925,8 @@ private bool HasImplicitNullableConversion(TypeSymbol source, TypeSymbol destina return true; } - if (HasImplicitTupleConversion(unwrappedSource, unwrappedDestination, ref useSiteDiagnostics)) + var tupleConversion = ClassifyImplicitTupleConversion(unwrappedSource, unwrappedDestination, ref useSiteDiagnostics); + if (tupleConversion.Exists) { return true; } @@ -922,7 +934,7 @@ private bool HasImplicitNullableConversion(TypeSymbol source, TypeSymbol destina return false; } - private bool HasImplicitTupleConversion(TypeSymbol source, TypeSymbol destination, ref HashSet useSiteDiagnostics) + private Conversion ClassifyImplicitTupleConversion(TypeSymbol source, TypeSymbol destination, ref HashSet useSiteDiagnostics) { ImmutableArray sourceTypes; ImmutableArray destTypes; @@ -931,22 +943,57 @@ private bool HasImplicitTupleConversion(TypeSymbol source, TypeSymbol destinatio !destination.TryGetElementTypesIfTupleOrCompatible(out destTypes) || sourceTypes.Length != destTypes.Length) { - return false; + return Conversion.NoConversion; } + var nestedConversions = ArrayBuilder.GetInstance(sourceTypes.Length); for (int i = 0; i < sourceTypes.Length; i++) { var conversion = ClassifyImplicitConversion(sourceTypes[i], destTypes[i], ref useSiteDiagnostics); if (!conversion.Exists) { - return false; + nestedConversions.Free(); + return Conversion.NoConversion; } + + nestedConversions.Add(conversion); } - return true; + return new Conversion(ConversionKind.ImplicitTuple, nestedConversions.ToArrayAndFree()); } - private static bool HasExplicitNullableConversion(TypeSymbol source, TypeSymbol destination) + private Conversion ClassifyExplicitTupleConversion(TypeSymbol source, TypeSymbol destination, ref HashSet useSiteDiagnostics, bool forCast) + { + ImmutableArray sourceTypes; + ImmutableArray destTypes; + + if (!source.TryGetElementTypesIfTupleOrCompatible(out sourceTypes) || + !destination.TryGetElementTypesIfTupleOrCompatible(out destTypes) || + sourceTypes.Length != destTypes.Length) + { + return Conversion.NoConversion; + } + + var nestedConversions = ArrayBuilder.GetInstance(sourceTypes.Length); + for (int i = 0; i < sourceTypes.Length; i++) + { + var conversion = forCast ? + ClassifyConversionForCast(sourceTypes[i], destTypes[i], ref useSiteDiagnostics) : + ClassifyConversionFromType(sourceTypes[i], destTypes[i], ref useSiteDiagnostics); + + if (!conversion.Exists) + { + nestedConversions.Free(); + return Conversion.NoConversion; + } + + nestedConversions.Add(conversion); + } + + return new Conversion(ConversionKind.ExplicitTuple, nestedConversions.ToArrayAndFree()); + } + + private Conversion ClassifyExplicitNullableConversion(TypeSymbol source, TypeSymbol destination, ref HashSet useSiteDiagnostics, bool forCast) { Debug.Assert((object)source != null); Debug.Assert((object)destination != null); @@ -961,38 +1008,44 @@ private static bool HasExplicitNullableConversion(TypeSymbol source, TypeSymbol if (!source.IsNullableType() && !destination.IsNullableType()) { - return false; + return Conversion.NoConversion; } - TypeSymbol sourceUnderlying = source.StrippedType(); - TypeSymbol destinationUnderlying = destination.StrippedType(); + TypeSymbol unwrappedSource = source.StrippedType(); + TypeSymbol unwrappedDestination = destination.StrippedType(); - if (HasIdentityConversion(sourceUnderlying, destinationUnderlying)) + if (HasIdentityConversion(unwrappedSource, unwrappedDestination)) { - return true; + return Conversion.ExplicitNullable; } - if (HasImplicitNumericConversion(sourceUnderlying, destinationUnderlying)) + if (HasImplicitNumericConversion(unwrappedSource, unwrappedDestination)) { - return true; + return Conversion.ExplicitNullable; } - if (HasExplicitNumericConversion(sourceUnderlying, destinationUnderlying)) + if (HasExplicitNumericConversion(unwrappedSource, unwrappedDestination)) { - return true; + return Conversion.ExplicitNullable; } - if (HasExplicitEnumerationConversion(sourceUnderlying, destinationUnderlying)) + var tupleConversion = ClassifyExplicitTupleConversion(unwrappedSource, unwrappedDestination, ref useSiteDiagnostics, forCast); + if (tupleConversion.Exists) { - return true; + return new Conversion(ConversionKind.ExplicitNullable, new[] {tupleConversion}); } - if (HasPointerToIntegerConversion(sourceUnderlying, destinationUnderlying)) + if (HasExplicitEnumerationConversion(unwrappedSource, unwrappedDestination)) { - return true; + return Conversion.ExplicitNullable; } - return false; + if (HasPointerToIntegerConversion(unwrappedSource, unwrappedDestination)) + { + return Conversion.ExplicitNullable; + } + + return Conversion.NoConversion; } private bool HasCovariantArrayConversion(TypeSymbol source, TypeSymbol destination, ref HashSet useSiteDiagnostics) diff --git a/src/Compilers/CSharp/Portable/BoundTree/Formatting.cs b/src/Compilers/CSharp/Portable/BoundTree/Formatting.cs index a51da0578be39..da6cefc7b3783 100644 --- a/src/Compilers/CSharp/Portable/BoundTree/Formatting.cs +++ b/src/Compilers/CSharp/Portable/BoundTree/Formatting.cs @@ -1,5 +1,6 @@ // Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +using Microsoft.CodeAnalysis.Collections; using Roslyn.Utilities; using System.Diagnostics; @@ -64,7 +65,26 @@ internal partial class BoundTupleExpression { public override object Display { - get { return (object)this.Type ?? ""; } + get + { + var pooledBuilder = PooledStringBuilder.GetInstance(); + var builder = pooledBuilder.Builder; + var arguments = this.Arguments; + + + builder.Append('('); + builder.Append(arguments[0].Display); + + for(int i = 1; i < arguments.Length; i++) + { + builder.Append(", "); + builder.Append(arguments[i].Display); + } + + builder.Append(')'); + + return pooledBuilder.ToStringAndFree(); + } } } diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_Conversion.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_Conversion.cs index fdccf5ac49267..f7ab8c545c7ca 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_Conversion.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_Conversion.cs @@ -326,6 +326,7 @@ private BoundExpression MakeConversion( return _dynamicFactory.MakeDynamicConversion(rewrittenOperand, explicitCastInCode || conversionKind == ConversionKind.ExplicitDynamic, isArrayIndex, @checked, rewrittenType).ToExpression(); case ConversionKind.ImplicitTuple: + case ConversionKind.ExplicitTuple: return RewriteTupleConversion( syntax: syntax, rewrittenOperand: rewrittenOperand, @@ -673,7 +674,21 @@ private BoundExpression RewriteTupleConversion( Symbol.ReportUseSiteDiagnostic(useSiteInfo, _diagnostics, syntax.Location); } var fieldAccess = MakeTupleFieldAccess(syntax, field, savedTuple, null, LookupResultKind.Empty); - var convertedFieldAccess = MakeConversion(fieldAccess, destElementTypes[i], @checked); + + Conversion conversion; + + HashSet useSiteDiagnostics = null; + if (explicitCastInCode) + { + conversion = this._compilation.Conversions.ClassifyConversionForCast(fieldAccess.Type, destElementTypes[i], ref useSiteDiagnostics); + } + else + { + conversion = this._compilation.Conversions.ClassifyConversion(fieldAccess.Type, destElementTypes[i], ref useSiteDiagnostics); + } + + var convertedFieldAccess = MakeConversion(syntax, fieldAccess, conversion, destElementTypes[i], @checked, explicitCastInCode); + fieldAccessorsBuilder.Add(convertedFieldAccess); } diff --git a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenTupleTest.cs b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenTupleTest.cs index 38b8694d0c04e..b0e1a28ef3242 100644 --- a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenTupleTest.cs +++ b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenTupleTest.cs @@ -4397,12 +4397,12 @@ static void Main() var comp = CreateCompilationWithMscorlib(source, parseOptions: TestOptions.Regular.WithTuplesFeature()); comp.VerifyDiagnostics( - // (10,30): error CS0029: Cannot implicitly convert type '(long, string)' to '(short, string)' + // (10,30): error CS0266: Cannot implicitly convert type '(long, string)' to '(short, string)'. An explicit conversion exists (are you missing a cast?) // (short, string) x2 = ((long, string))(1, "hello"); - Diagnostic(ErrorCode.ERR_NoImplicitConv, @"((long, string))(1, ""hello"")").WithArguments("(long, string)", "(short, string)").WithLocation(10, 30), - // (13,34): error CS0029: Cannot implicitly convert type '(long c, string d)' to '(short a, string b)' + Diagnostic(ErrorCode.ERR_NoImplicitConvCast, @"((long, string))(1, ""hello"")").WithArguments("(long, string)", "(short, string)").WithLocation(10, 30), + // (13,34): error CS0266: Cannot implicitly convert type '(long c, string d)' to '(short a, string b)'. An explicit conversion exists (are you missing a cast?) // (short a, string b) x3 = ((long c, string d))(1, "hello"); - Diagnostic(ErrorCode.ERR_NoImplicitConv, @"((long c, string d))(1, ""hello"")").WithArguments("(long c, string d)", "(short a, string b)").WithLocation(13, 34) + Diagnostic(ErrorCode.ERR_NoImplicitConvCast, @"((long c, string d))(1, ""hello"")").WithArguments("(long c, string d)", "(short a, string b)").WithLocation(13, 34) ); } @@ -5159,15 +5159,15 @@ static void Main() " + trivial2uple; CreateCompilationWithMscorlib(source, references: new[] { ValueTupleRef, SystemRuntimeFacadeRef }, parseOptions: TestOptions.Regular.WithTuplesFeature()).VerifyDiagnostics( - // (7,29): error CS0029: Cannot implicitly convert type '(long c, long d)' to '(int a, int b)' + // (7,29): error CS0266: Cannot implicitly convert type '(long c, long d)' to '(int a, int b)'. An explicit conversion exists (are you missing a cast?) // (int a, int b) x1 = ((long c, long d))(e: 1, f:2); - Diagnostic(ErrorCode.ERR_NoImplicitConv, "((long c, long d))(e: 1, f:2)").WithArguments("(long c, long d)", "(int a, int b)").WithLocation(7, 29), - // (9,33): error CS0029: Cannot implicitly convert type '(int c, int d)' to '(short a, short b)' + Diagnostic(ErrorCode.ERR_NoImplicitConvCast, "((long c, long d))(e: 1, f:2)").WithArguments("(long c, long d)", "(int a, int b)").WithLocation(7, 29), + // (9,33): error CS0266: Cannot implicitly convert type '(int c, int d)' to '(short a, short b)'. An explicit conversion exists (are you missing a cast?) // (short a, short b) x2 = ((int c, int d))(e: 1, f:2); - Diagnostic(ErrorCode.ERR_NoImplicitConv, "((int c, int d))(e: 1, f:2)").WithArguments("(int c, int d)", "(short a, short b)").WithLocation(9, 33), - // (12,29): error CS0030: Cannot convert type '(int e, string f)' to '(long c, long d)' + Diagnostic(ErrorCode.ERR_NoImplicitConvCast, "((int c, int d))(e: 1, f:2)").WithArguments("(int c, int d)", "(short a, short b)").WithLocation(9, 33), + // (12,56): error CS0030: Cannot convert type 'string' to 'long' // (int a, int b) x3 = ((long c, long d))(e: 1, f:"qq"); - Diagnostic(ErrorCode.ERR_NoExplicitConv, @"((long c, long d))(e: 1, f:""qq"")").WithArguments("(int e, string f)", "(long c, long d)").WithLocation(12, 29) + Diagnostic(ErrorCode.ERR_NoExplicitConv, @"""qq""").WithArguments("string", "long").WithLocation(12, 56) ); } @@ -5226,6 +5226,7 @@ class C { static void Main() { + // explicit conversion exists, cast in the code picks that. (short a, string b)? x = ((short c, string d)?)(e: 1, f: ""hello""); short? y = (short?)11; } @@ -5249,14 +5250,14 @@ static void Main() Assert.Equal(@"(e: 1, f: ""hello"")", node.ToString()); Assert.Equal("(System.Int32 e, System.String f)", model.GetTypeInfo(node).Type.ToTestDisplayString()); - Assert.Equal("(System.Int16 e, System.String f)", model.GetTypeInfo(node).ConvertedType.ToTestDisplayString()); - Assert.Equal(Conversion.ImplicitTupleLiteral, model.GetConversion(node)); + Assert.Equal("(System.Int32 e, System.String f)", model.GetTypeInfo(node).ConvertedType.ToTestDisplayString()); + Assert.Equal(Conversion.Identity, model.GetConversion(node)); // semantic model returns topmost conversion from the sequence of conversions for // ((short c, string d)?)(e: 1, f: ""hello"") - Assert.Equal("(System.Int16 c, System.String d)", model.GetTypeInfo(node.Parent).Type.ToTestDisplayString()); + Assert.Equal("(System.Int16 c, System.String d)?", model.GetTypeInfo(node.Parent).Type.ToTestDisplayString()); Assert.Equal("(System.Int16 a, System.String b)?", model.GetTypeInfo(node.Parent).ConvertedType.ToTestDisplayString()); - Assert.Equal(Conversion.ImplicitNullable, model.GetConversion(node.Parent)); + Assert.Equal(Conversion.Identity, model.GetConversion(node.Parent)); var x = nodes.OfType().First(); Assert.Equal("(System.Int16 a, System.String b)? x", model.GetDeclaredSymbol(x).ToTestDisplayString()); @@ -5480,9 +5481,9 @@ static void Main() // semantic model returns topmost conversion from the sequence of conversions for // ((int c, string d)?)(e: 1, f: ""hello"") - Assert.Equal("(System.Int32 c, System.String d)", model.GetTypeInfo(node.Parent).Type.ToTestDisplayString()); + Assert.Equal("(System.Int32 c, System.String d)?", model.GetTypeInfo(node.Parent).Type.ToTestDisplayString()); Assert.Equal("(System.Int32 a, System.String b)?", model.GetTypeInfo(node.Parent).ConvertedType.ToTestDisplayString()); - Assert.Equal(Conversion.ImplicitNullable, model.GetConversion(node.Parent)); + Assert.Equal(Conversion.Identity, model.GetConversion(node.Parent)); var x = nodes.OfType().First(); Assert.Equal("(System.Int32 a, System.String b)? x", model.GetDeclaredSymbol(x).ToTestDisplayString()); @@ -12038,6 +12039,55 @@ static public implicit operator (byte, byte)(C1 arg) "); } + [Fact] + public void ExplicitConversions01() + { + var source = @" +using System; +class C +{ + static void Main() + { + // long tuple + var x1 = (1,2,3,4,5,6,7,8,9,10,11,12); + (byte, byte, byte, byte, byte, byte, byte, byte,byte, byte, byte, byte) y1 = ((byte, byte, byte, byte, byte, byte, byte, byte,byte, byte, byte, byte))x1; + System.Console.WriteLine(y1); + + // long nested tuple + var x2 = (1,2,3,4,5,6,7,8,9,10,11,(1,2,3,4,5,6,7,8,9,10,11,12)); + (byte, byte, byte, byte, byte, byte, byte, byte,byte, byte, byte, (byte, byte, byte, byte, byte, byte, byte, byte,byte, byte, byte, byte)) y2 = + ((byte, byte, byte, byte, byte, byte, byte, byte,byte, byte, byte, (byte, byte, byte, byte, byte, byte, byte, byte,byte, byte, byte, byte)))x2; + System.Console.WriteLine(y2); + + // user defined conversion + var x3 = (1,1); + C1 y3 = (C1)x3; + x3 = ((int, int))y3; + System.Console.WriteLine(x3); + } + + class C1 + { + static public explicit operator C1((long, long) arg) + { + return new C1(); + } + + static public explicit operator (byte, byte)(C1 arg) + { + return (2, 2); + } + } +} +"; + + var comp = CompileAndVerify(source, additionalRefs: new[] { ValueTupleRef, SystemRuntimeFacadeRef }, parseOptions: TestOptions.Regular.WithTuplesFeature(), expectedOutput: @" +(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12) +(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, (1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12)) +(2, 2) +"); + } + [Fact] public void ImplicitConversions02() { @@ -12126,6 +12176,94 @@ .locals init (System.ValueTuple V_0, "); ; } + [Fact] + public void ExplicitConversions02() + { + var source = @" +using System; +class C +{ + static void Main() + { + var x = (a:1, b:1); + C1 y = (C1)x; + x = ((int a, int b))y; + System.Console.WriteLine(x); + + x = ((int, int))(C1)x; + System.Console.WriteLine(x); + } + + class C1 + { + static public explicit operator C1((long, long) arg) + { + return new C1(); + } + + static public explicit operator (byte c, byte d)(C1 arg) + { + return (2, 2); + } + } +} +" + trivial2uple; + + var comp = CompileAndVerify(source, parseOptions: TestOptions.Regular.WithTuplesFeature(), expectedOutput: @" +{2, 2} +{2, 2}"); + + comp.VerifyIL("C.Main", @" +{ + // Code size 125 (0x7d) + .maxstack 2 + .locals init (System.ValueTuple V_0, + System.ValueTuple V_1) + IL_0000: ldc.i4.1 + IL_0001: ldc.i4.1 + IL_0002: newobj ""System.ValueTuple..ctor(int, int)"" + IL_0007: stloc.0 + IL_0008: ldloc.0 + IL_0009: ldfld ""int System.ValueTuple.Item1"" + IL_000e: conv.i8 + IL_000f: ldloc.0 + IL_0010: ldfld ""int System.ValueTuple.Item2"" + IL_0015: conv.i8 + IL_0016: newobj ""System.ValueTuple..ctor(long, long)"" + IL_001b: call ""C.C1 C.C1.op_Explicit((long, long))"" + IL_0020: call ""(byte c, byte d) C.C1.op_Explicit(C.C1)"" + IL_0025: stloc.1 + IL_0026: ldloc.1 + IL_0027: ldfld ""byte System.ValueTuple.Item1"" + IL_002c: ldloc.1 + IL_002d: ldfld ""byte System.ValueTuple.Item2"" + IL_0032: newobj ""System.ValueTuple..ctor(int, int)"" + IL_0037: dup + IL_0038: box ""System.ValueTuple"" + IL_003d: call ""void System.Console.WriteLine(object)"" + IL_0042: stloc.0 + IL_0043: ldloc.0 + IL_0044: ldfld ""int System.ValueTuple.Item1"" + IL_0049: conv.i8 + IL_004a: ldloc.0 + IL_004b: ldfld ""int System.ValueTuple.Item2"" + IL_0050: conv.i8 + IL_0051: newobj ""System.ValueTuple..ctor(long, long)"" + IL_0056: call ""C.C1 C.C1.op_Explicit((long, long))"" + IL_005b: call ""(byte c, byte d) C.C1.op_Explicit(C.C1)"" + IL_0060: stloc.1 + IL_0061: ldloc.1 + IL_0062: ldfld ""byte System.ValueTuple.Item1"" + IL_0067: ldloc.1 + IL_0068: ldfld ""byte System.ValueTuple.Item2"" + IL_006d: newobj ""System.ValueTuple..ctor(int, int)"" + IL_0072: box ""System.ValueTuple"" + IL_0077: call ""void System.Console.WriteLine(object)"" + IL_007c: ret +} +"); + } + [Fact] public void ImplicitConversions03() { @@ -12140,7 +12278,7 @@ static void Main() (int, int)? x1 = y; System.Console.WriteLine(x1); - x1 = ((int, int))(C1)x; + x1 = ((int, int)?)(C1)x; System.Console.WriteLine(x1); } @@ -12165,6 +12303,45 @@ static public implicit operator (byte c, byte d)(C1 arg) ") ; } + [Fact] + public void ExplicitConversions03() + { + var source = @" +using System; +class C +{ + static void Main() + { + var x = (a:1, b:1); + C1 y = (C1)x; + (int, int)? x1 = ((int, int)?)y; + System.Console.WriteLine(x1); + + x1 = ((int, int)?)(C1)x; + System.Console.WriteLine(x1); + } + + class C1 + { + static public explicit operator C1((long, long)? arg) + { + return new C1(); + } + + static public explicit operator (byte c, byte d)(C1 arg) + { + return (2, 2); + } + } +} +" + trivial2uple; + + var comp = CompileAndVerify(source, parseOptions: TestOptions.Regular.WithTuplesFeature(), expectedOutput: @" +{2, 2} +{2, 2} +"); + } + [Fact] public void ImplicitConversions04() { @@ -12225,45 +12402,56 @@ static public implicit operator (byte c, byte d)(C1 arg) } [Fact] - public void ImplicitConversions05() + public void ExplicitConversions04Err() { + + // explicit conversion case similar to ImplicitConversions04 + // is a compiler error since explicit tuple conversions do not stack up like + // implicit tuple conversions which are in standard implicit set. + var source = @" using System; class C { static void Main() { - var x = (1, (1, (1, (1, (1, 1))))); - C1 y = x; + var x = (1, (1, (1, (1, (1, (1, 1)))))); + C1 y = (C1)x; + + (int, int) x2 = ((int, int))y; + System.Console.WriteLine(x2); + + (int, (int, int)) x3 = ((int, (int, int)))y; + System.Console.WriteLine(x3); - (int, (object, (byte, (int?, (long, IComparable)?))?))? x1 = y; - System.Console.WriteLine(x1); + (int, (int, (int, (int, (int, (int, (int, (int, (int, (int, (int, int))))))))))) x12 = ((int, (int, (int, (int, (int, (int, (int, (int, (int, (int, (int, int))))))))))))y; + System.Console.WriteLine(x12); } class C1 { private byte x; - static public implicit operator C1((long, C1) arg) + static public explicit operator C1((long, C1) arg) { var result = new C1(); result.x = arg.Item2.x; return result; } - static public implicit operator C1((long, long) arg) + static public explicit operator C1((long, long) arg) { var result = new C1(); result.x = (byte)(arg.Item2); return result; } - static public implicit operator (byte, C1)(C1 arg) + static public explicit operator (byte, C1)(C1 arg) { return ((byte)(arg.x++), arg); } - static public implicit operator (byte c, byte d)(C1 arg) + static public explicit operator (byte c, byte d)(C1 arg) { return ((byte)(arg.x++), (byte)(arg.x++)); } @@ -12271,12 +12459,22 @@ static public implicit operator (byte c, byte d)(C1 arg) } " + trivial2uple; - var comp = CompileAndVerify(source, parseOptions: TestOptions.Regular.WithTuplesFeature(), expectedOutput: @" -{1, {2, {3, {4, {5, 6}}}}} -"); + var comp = CreateCompilationWithMscorlib(source, parseOptions: TestOptions.Regular.WithTuplesFeature(), assemblyName: "ImplicitConversions06Err"); + comp.VerifyEmitDiagnostics( + // (8,16): error CS0030: Cannot convert type '(int, (int, (int, (int, (int, (int, int))))))' to 'C.C1' + // C1 y = (C1)x; + Diagnostic(ErrorCode.ERR_NoExplicitConv, "(C1)x").WithArguments("(int, (int, (int, (int, (int, (int, int))))))", "C.C1").WithLocation(8, 16), + // (13,32): error CS0030: Cannot convert type 'C.C1' to '(int, (int, int))' + // (int, (int, int)) x3 = ((int, (int, int)))y; + Diagnostic(ErrorCode.ERR_NoExplicitConv, "((int, (int, int)))y").WithArguments("C.C1", "(int, (int, int))").WithLocation(13, 32), + // (16,96): error CS0030: Cannot convert type 'C.C1' to '(int, (int, (int, (int, (int, (int, (int, (int, (int, (int, (int, int)))))))))))' + // (int, (int, (int, (int, (int, (int, (int, (int, (int, (int, (int, int))))))))))) x12 = ((int, (int, (int, (int, (int, (int, (int, (int, (int, (int, (int, int))))))))))))y; + Diagnostic(ErrorCode.ERR_NoExplicitConv, "((int, (int, (int, (int, (int, (int, (int, (int, (int, (int, (int, int))))))))))))y").WithArguments("C.C1", "(int, (int, (int, (int, (int, (int, (int, (int, (int, (int, (int, int)))))))))))").WithLocation(16, 96) + ); } - public void ImplicitConversions05Err() + [Fact] + public void ImplicitConversions05() { var source = @" using System; @@ -12308,73 +12506,276 @@ static public implicit operator C1((long, long) arg) result.x = (byte)(arg.Item2); return result; } + + static public implicit operator (byte, C1)(C1 arg) + { + return ((byte)(arg.x++), arg); + } + + static public implicit operator (byte c, byte d)(C1 arg) + { + return ((byte)(arg.x++), (byte)(arg.x++)); + } } } " + trivial2uple; - var comp = CreateCompilationWithMscorlib(source, parseOptions: TestOptions.Regular.WithTuplesFeature()); - comp.VerifyDiagnostics( - // (10,70): error CS0029: Cannot implicitly convert type 'C.C1' to '(int, (object, (byte, (int?, (long, System.IComparable)?))?))?' - // (int, (object, (byte, (int?, (long, IComparable)?))?))? x1 = y; - Diagnostic(ErrorCode.ERR_NoImplicitConv, "y").WithArguments("C.C1", "(int, (object, (byte, (int?, (long, System.IComparable)?))?))?").WithLocation(10, 70) - ); - + var comp = CompileAndVerify(source, parseOptions: TestOptions.Regular.WithTuplesFeature(), expectedOutput: @" +{1, {2, {3, {4, {5, 6}}}}} +"); } - public void ImplicitConversions06Err() + [Fact] + public void ExplicitConversions05() { var source = @" +using System; class C { static void Main() { - var x = (1, 1); - (long, long) y = x; + var x = (1, (1, (1, (1, (1, 1))))); + C1 y = (C1)((int, C1))((int, (int, C1)))((int, (int, (int, C1))))((int, (int, (int, (int, C1)))))x; - System.Console.WriteLine(y); + (int, (object, (byte, (int?, (long, IComparable)?))?))? x1 = ((int, (object, (byte, (int?, (long, IComparable)?))?))?) + ((int, (object, (byte, (int?, C1))?))?) + ((int, (object, (byte, C1)?))?) + ((int, (object, C1))?) + ((int, C1)?) + (C1)y; + System.Console.WriteLine(x1); } -} - -namespace System -{ - // struct with two values (missing a field) - public struct ValueTuple + class C1 { - public T1 Item1; + private byte x; - public ValueTuple(T1 item1, T2 item2) + static public explicit operator C1((long, C1) arg) { - this.Item1 = item1; + var result = new C1(); + result.x = arg.Item2.x; + return result; } - } -} -"; - - var comp = CreateCompilationWithMscorlib(source, parseOptions: TestOptions.Regular.WithTuplesFeature(), assemblyName: "ImplicitConversions06Err"); - comp.VerifyEmitDiagnostics( - // (7,26): error CS8205: Member 'Item2' was not found on type 'ValueTuple' from assembly 'ImplicitConversions06Err, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'. - // (long, long) y = x; - Diagnostic(ErrorCode.ERR_PredefinedTypeMemberNotFoundInAssembly, "x").WithArguments("Item2", "System.ValueTuple", "ImplicitConversions06Err, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null").WithLocation(7, 26) - ); + static public explicit operator C1((long, long) arg) + { + var result = new C1(); + result.x = (byte)(arg.Item2); + return result; } - [Fact] - public void ImplicitConversions07() + static public explicit operator (byte, C1)(C1 arg) { - var source = @" -using System; + return ((byte)(arg.x++), arg); + } - internal class Program - { - static void Main(string[] args) + static public explicit operator (byte c, byte d)(C1 arg) { - var t = (1, 1); - (C2, C2) aa = t; - System.Console.WriteLine(aa); + return ((byte)(arg.x++), (byte)(arg.x++)); + } + } +} +" + trivial2uple; + + var comp = CompileAndVerify(source, parseOptions: TestOptions.Regular.WithTuplesFeature(), expectedOutput: @" +{1, {2, {3, {4, {5, 6}}}}} +"); + } + + [Fact] + public void ImplicitConversions05Err() + { + var source = @" +using System; +class C +{ + static void Main() + { + var x = (1, (1, (1, (1, (1, 1))))); + C1 y = x; + + (int, (object, (byte, (int?, (long, IComparable)?))?))? x1 = y; + System.Console.WriteLine(x1); + } + + class C1 + { + private byte x; + + static public implicit operator C1((long, C1) arg) + { + var result = new C1(); + result.x = arg.Item2.x; + return result; + } + + static public implicit operator C1((long, long) arg) + { + var result = new C1(); + result.x = (byte)(arg.Item2); + return result; + } + } +} +" + trivial2uple; + + var comp = CreateCompilationWithMscorlib(source, parseOptions: TestOptions.Regular.WithTuplesFeature()); + comp.VerifyDiagnostics( + // (10,70): error CS0029: Cannot implicitly convert type 'C.C1' to '(int, (object, (byte, (int?, (long, System.IComparable)?))?))?' + // (int, (object, (byte, (int?, (long, IComparable)?))?))? x1 = y; + Diagnostic(ErrorCode.ERR_NoImplicitConv, "y").WithArguments("C.C1", "(int, (object, (byte, (int?, (long, System.IComparable)?))?))?").WithLocation(10, 70) + ); + + } + + [Fact] + public void ExplicitConversions05Err() + { + var source = @" +using System; +class C +{ + static void Main() + { + var x = (1, (1, (1, (1, (1, 1))))); + C1 y = (C1)((int, C1))((int, (int, C1)))((int, (int, (int, C1))))((int, (int, (int, (int, C1)))))x; + + (int, (object, (byte, (int?, (long, IComparable)?))?))? x1 = ((int, (object, (byte, (int?, (long, IComparable)?))?))?) + ((int, (object, (byte, (int?, C1))?))?) + ((int, (object, (byte, C1)?))?) + ((int, (object, C1))?) + ((int, C1)?) + (C1)y; + System.Console.WriteLine(x1); + } + + class C1 + { + private byte x; + + static public explicit operator C1((long, C1) arg) + { + var result = new C1(); + result.x = arg.Item2.x; + return result; } + static public explicit operator C1((long, long) arg) + { + var result = new C1(); + result.x = (byte)(arg.Item2); + return result; + } + } +} +" + trivial2uple; + + var comp = CreateCompilationWithMscorlib(source, parseOptions: TestOptions.Regular.WithTuplesFeature()); + comp.VerifyDiagnostics( + // (14,70): error CS0030: Cannot convert type 'C.C1' to '(int, C.C1)?' + // ((int, C1)?) + Diagnostic(ErrorCode.ERR_NoExplicitConv, @"((int, C1)?) + (C1)y").WithArguments("C.C1", "(int, C.C1)?").WithLocation(14, 70) + ); + + } + + [Fact] + public void ImplicitConversions06Err() + { + var source = @" +class C +{ + static void Main() + { + var x = (1, 1); + (long, long) y = x; + + System.Console.WriteLine(y); + } + +} + +namespace System +{ + // struct with two values (missing a field) + public struct ValueTuple + { + public T1 Item1; + + public ValueTuple(T1 item1, T2 item2) + { + this.Item1 = item1; + } + } +} +"; + + var comp = CreateCompilationWithMscorlib(source, parseOptions: TestOptions.Regular.WithTuplesFeature(), assemblyName: "ImplicitConversions06Err"); + comp.VerifyEmitDiagnostics( + // (7,26): error CS8205: Member 'Item2' was not found on type 'ValueTuple' from assembly 'ImplicitConversions06Err, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'. + // (long, long) y = x; + Diagnostic(ErrorCode.ERR_PredefinedTypeMemberNotFoundInAssembly, "x").WithArguments("Item2", "System.ValueTuple", "ImplicitConversions06Err, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null").WithLocation(7, 26) + ); + + } + + [Fact] + public void ExplicitConversions06Err() + { + var source = @" +class C +{ + static void Main() + { + var x = (1, 1); + (byte, byte) y = ((byte, byte))x; + + System.Console.WriteLine(y); + } + +} + +namespace System +{ + // struct with two values (missing a field) + public struct ValueTuple + { + public T1 Item1; + + public ValueTuple(T1 item1, T2 item2) + { + this.Item1 = item1; + } + } +} +"; + + var comp = CreateCompilationWithMscorlib(source, parseOptions: TestOptions.Regular.WithTuplesFeature(), assemblyName: "ImplicitConversions06Err"); + comp.VerifyEmitDiagnostics( + // (7,26): error CS8205: Member 'Item2' was not found on type 'ValueTuple' from assembly 'ImplicitConversions06Err, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'. + // (byte, byte) y = ((byte, byte))x; + Diagnostic(ErrorCode.ERR_PredefinedTypeMemberNotFoundInAssembly, "((byte, byte))x").WithArguments("Item2", "System.ValueTuple", "ImplicitConversions06Err, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null").WithLocation(7, 26) + ); + + } + + [Fact] + public void ImplicitConversions07() + { + var source = @" +using System; + + internal class Program + { + static void Main(string[] args) + { + var t = (1, 1); + (C2, C2) aa = t; + System.Console.WriteLine(aa); + } + + // accessibility of operators. Notice Private private class C2 { public static implicit operator C2(int arg) @@ -12391,6 +12792,385 @@ public static implicit operator C2(int arg) "); } + [Fact] + public void ExplicitConversions07() + { + var source = @" +using System; + + internal class Program + { + static void Main(string[] args) + { + var t = (1, 1); + (C2, C2) aa = ((C2, C2))t; + System.Console.WriteLine(aa); + } + + // accessibility of operators. Notice Private + private class C2 + { + public static explicit operator C2(int arg) + { + return new C2(); + } + } + } + +" + trivial2uple; + + var comp = CompileAndVerify(source, parseOptions: TestOptions.Regular.WithTuplesFeature(), expectedOutput: @" +{Program+C2, Program+C2} +"); + } + + [Fact] + public void ExplicitConversionsSimple01() + { + var source = @" +using System; +class C +{ + static void Main() + { + var x = (a:1, b:2); + var y = ((byte, byte))x; + System.Console.WriteLine(y); + + var z = ((int, int))((long)3, (object)4); + System.Console.WriteLine(z); + } +} +" + trivial2uple; + + var comp = CompileAndVerify(source, parseOptions: TestOptions.Regular.WithTuplesFeature(), expectedOutput: @" +{1, 2} +{3, 4}"); + + comp.VerifyIL("C.Main", @" +{ + // Code size 86 (0x56) + .maxstack 3 + .locals init (System.ValueTuple V_0, + System.ValueTuple V_1) + IL_0000: ldc.i4.1 + IL_0001: ldc.i4.2 + IL_0002: newobj ""System.ValueTuple..ctor(int, int)"" + IL_0007: stloc.0 + IL_0008: ldloc.0 + IL_0009: ldfld ""int System.ValueTuple.Item1"" + IL_000e: conv.u1 + IL_000f: ldloc.0 + IL_0010: ldfld ""int System.ValueTuple.Item2"" + IL_0015: conv.u1 + IL_0016: newobj ""System.ValueTuple..ctor(byte, byte)"" + IL_001b: box ""System.ValueTuple"" + IL_0020: call ""void System.Console.WriteLine(object)"" + IL_0025: ldloca.s V_1 + IL_0027: ldc.i4.3 + IL_0028: conv.i8 + IL_0029: ldc.i4.4 + IL_002a: box ""int"" + IL_002f: call ""System.ValueTuple..ctor(long, object)"" + IL_0034: ldloc.1 + IL_0035: ldfld ""long System.ValueTuple.Item1"" + IL_003a: conv.i4 + IL_003b: ldloc.1 + IL_003c: ldfld ""object System.ValueTuple.Item2"" + IL_0041: unbox.any ""int"" + IL_0046: newobj ""System.ValueTuple..ctor(int, int)"" + IL_004b: box ""System.ValueTuple"" + IL_0050: call ""void System.Console.WriteLine(object)"" + IL_0055: ret +} +"); ; + } + + [Fact] + public void ExplicitConversionsSimple02() + { + var source = @" +using System; +class C +{ + static void Main() + { + var x = (a:1, b:2); + var y = ((C2, C2))x; + System.Console.WriteLine(y); + + var z = ((C2, C2))((object)null, 4); + System.Console.WriteLine(z); + } + + private class C2 + { + private int x; + + public static explicit operator C2(int arg) + { + var result = new C2(); + result.x = arg + 1; + return result; + } + + public override string ToString() + { + return x.ToString(); + } + } +} +" + trivial2uple; + + var comp = CompileAndVerify(source, parseOptions: TestOptions.Regular.WithTuplesFeature(), expectedOutput: @" +{2, 3} +{, 5}"); + + comp.VerifyIL("C.Main", @" +{ + // Code size 92 (0x5c) + .maxstack 3 + .locals init (System.ValueTuple V_0, + System.ValueTuple V_1) + IL_0000: ldc.i4.1 + IL_0001: ldc.i4.2 + IL_0002: newobj ""System.ValueTuple..ctor(int, int)"" + IL_0007: stloc.0 + IL_0008: ldloc.0 + IL_0009: ldfld ""int System.ValueTuple.Item1"" + IL_000e: call ""C.C2 C.C2.op_Explicit(int)"" + IL_0013: ldloc.0 + IL_0014: ldfld ""int System.ValueTuple.Item2"" + IL_0019: call ""C.C2 C.C2.op_Explicit(int)"" + IL_001e: newobj ""System.ValueTuple..ctor(C.C2, C.C2)"" + IL_0023: box ""System.ValueTuple"" + IL_0028: call ""void System.Console.WriteLine(object)"" + IL_002d: ldloca.s V_1 + IL_002f: ldnull + IL_0030: ldc.i4.4 + IL_0031: call ""System.ValueTuple..ctor(object, int)"" + IL_0036: ldloc.1 + IL_0037: ldfld ""object System.ValueTuple.Item1"" + IL_003c: castclass ""C.C2"" + IL_0041: ldloc.1 + IL_0042: ldfld ""int System.ValueTuple.Item2"" + IL_0047: call ""C.C2 C.C2.op_Explicit(int)"" + IL_004c: newobj ""System.ValueTuple..ctor(C.C2, C.C2)"" + IL_0051: box ""System.ValueTuple"" + IL_0056: call ""void System.Console.WriteLine(object)"" + IL_005b: ret +} +"); ; + } + + [Fact] + [WorkItem(11804, "https://github.com/dotnet/roslyn/issues/11804")] + public void ExplicitConversionsSimple02Expr() + { + var source = @" +using System; +class C +{ + static void Main() + { + var x = (a:1, b:2); + var y = ((C2, C2))x; + System.Console.WriteLine(y); + + // this is likely to become legal upon resolution of https://github.com/dotnet/roslyn/issues/11804 + // for now just check that we can handle this case. + var z = ((C2, C2))(null, 4); + + System.Console.WriteLine(z); + } + + private class C2 + { + private int x; + + public static explicit operator C2(int arg) + { + var result = new C2(); + result.x = arg + 1; + return result; + } + + public override string ToString() + { + return x.ToString(); + } + } +} +" + trivial2uple; + + var comp = CreateCompilationWithMscorlib(source, parseOptions: TestOptions.Regular.WithTuplesFeature(), assemblyName: "ImplicitConversions06Err"); + comp.VerifyEmitDiagnostics( + // (13,17): error CS0030: Cannot convert type '(, int)' to '(C.C2, C.C2)' + // var z = ((C2, C2))(null, 4); + Diagnostic(ErrorCode.ERR_NoExplicitConv, "((C2, C2))(null, 4)").WithArguments("(, int)", "(C.C2, C.C2)").WithLocation(13, 17) + + ); + + } + + [WorkItem(12064, "https://github.com/dotnet/roslyn/issues/12064")] + [Fact] + public void ExplicitConversionsPreference01() + { + var source = @" + +using System; + +class B +{ + static void Main() + { + C x = new D(""original""); + + D y = (D)x; // explicit builtin downcast is preferred + Console.WriteLine(""explicit""); + Console.WriteLine(y); + Console.WriteLine(); + + y = x; // implicit user defined implicit conversion + Console.WriteLine(""implicit""); + Console.WriteLine(y); + + Console.WriteLine(); + Console.WriteLine(); + + (C, C) xt = (new D(""original1""), new D(""original2"")); + var xtxt = (xt, xt); + + Console.WriteLine(""explicit""); + (D, D) yt = ((D, D))xt; // explicit builtin downcast is preferred + Console.WriteLine(yt); + + ((D, D),(D, D)) ytyt = (((D, D),(D, D)))(xt, xt); // explicit builtin downcast is preferred + Console.WriteLine(ytyt); + + ytyt = (((D, D),(D, D)))xtxt; // explicit builtin downcast is preferred + Console.WriteLine(ytyt); + + (D, D)? ytn = ((D, D)?)xt; // BUG https://github.com/dotnet/roslyn/issues/12064 explicit builtin downcast is NOT preferred (nullable) + Console.WriteLine(ytn); + Console.WriteLine(); + + + Console.WriteLine(""implicit""); + yt = xt; // implicit user defined implicit conversion + Console.WriteLine(yt); + + ytyt = xtxt; // implicit user defined implicit conversion + Console.WriteLine(ytyt); + + ytyt = (xt, xt); // implicit user defined implicit conversion + Console.WriteLine(ytyt); + + ytn = xt; // implicit user defined implicit conversion + Console.WriteLine(ytn); + } +} + +class C { } +class D : C +{ + private readonly string val; + + public D(string val) + { + this.val = val; + } + + public override string ToString() + { + return val; + } + + public static implicit operator D(C x) + { + return new D(""converted""); + } +} +" + trivial2uple; + + var comp = CompileAndVerify(source, parseOptions: TestOptions.Regular.WithTuplesFeature(), expectedOutput: @" +explicit +original + +implicit +converted + + +explicit +{original1, original2} +{{original1, original2}, {original1, original2}} +{{original1, original2}, {original1, original2}} +{converted, converted} + +implicit +{converted, converted} +{{converted, converted}, {converted, converted}} +{{converted, converted}, {converted, converted}} +{converted, converted} +"); + + } + + [Fact] + public void ExplicitConversionsPreference02() + { + var source = @" + +using System; + +class A +{ + public static implicit operator A(int d) { return null; } +} + +class B : A +{ + public static implicit operator B(long d) { return null; } +} + +class C +{ + static void Main() + { + var x = 1; + var xt = (x, x); + + // implicit conversions are unambiguous + B b = x; + (B, B) bt = xt; + + // the following is an error for compat reasons + // explicit conversion is ambiguous and implicit conversion exists, but ignored + b = (B)x; + + // SHOULD THIS BE AN ERROR TOO? + bt = ((B, B))xt; + + // SHOULD THIS BE AN ERROR TOO? + bt = ((B, B)?)xt; +} +} +" + trivial2uple; + + var comp = CreateCompilationWithMscorlib(source, parseOptions: TestOptions.Regular.WithTuplesFeature(), assemblyName: "ImplicitConversions06Err"); + comp.VerifyEmitDiagnostics( + // (28,13): error CS0457: Ambiguous user defined conversions 'B.implicit operator B(long)' and 'A.implicit operator A(int)' when converting from 'int' to 'B' + // b = (B)x; + Diagnostic(ErrorCode.ERR_AmbigUDConv, "(B)x").WithArguments("B.implicit operator B(long)", "A.implicit operator A(int)", "int", "B").WithLocation(28, 13), + // (31,14): error CS0030: Cannot convert type '(int, int)' to '(B, B)' + // bt = ((B, B))xt; + Diagnostic(ErrorCode.ERR_NoExplicitConv, "((B, B))xt").WithArguments("(int, int)", "(B, B)").WithLocation(31, 14), + // (34,14): error CS0030: Cannot convert type '(int, int)' to '(B, B)?' + // bt = ((B, B)?)xt; + Diagnostic(ErrorCode.ERR_NoExplicitConv, "((B, B)?)xt").WithArguments("(int, int)", "(B, B)?").WithLocation(34, 14) + ); + } [Fact] public void ConversionsOverload02() @@ -12625,7 +13405,7 @@ public void ClassifyConversionImplicit01() var int_object = comp.CreateTupleTypeSymbol(ImmutableArray.Create(intType, objectType)); Assert.Equal(ConversionKind.ImplicitTuple, comp.ClassifyConversion(int_string, int_object).Kind); - Assert.Equal(ConversionKind.NoConversion, comp.ClassifyConversion(int_object, int_string).Kind); + Assert.Equal(ConversionKind.ExplicitTuple, comp.ClassifyConversion(int_object, int_string).Kind); } [Fact] @@ -12649,9 +13429,9 @@ public void ClassifyConversionImplicit02() Assert.Equal(ConversionKind.ImplicitTuple, comp.ClassifyConversion(int_string2, int_object1).Kind); Assert.Equal(ConversionKind.ImplicitTuple, comp.ClassifyConversion(int_string2, int_object2).Kind); - Assert.Equal(ConversionKind.NoConversion, comp.ClassifyConversion(int_object2, int_string1).Kind); - Assert.Equal(ConversionKind.NoConversion, comp.ClassifyConversion(int_object1, int_string2).Kind); - Assert.Equal(ConversionKind.NoConversion, comp.ClassifyConversion(int_object2, int_string2).Kind); + Assert.Equal(ConversionKind.ExplicitTuple, comp.ClassifyConversion(int_object2, int_string1).Kind); + Assert.Equal(ConversionKind.ExplicitTuple, comp.ClassifyConversion(int_object1, int_string2).Kind); + Assert.Equal(ConversionKind.ExplicitTuple, comp.ClassifyConversion(int_object2, int_string2).Kind); } [Fact] @@ -12675,8 +13455,8 @@ public void ClassifyConversionImplicit03() Assert.Equal(ConversionKind.ImplicitTuple, comp.ClassifyConversion(int_string1, int_object).Kind); Assert.Equal(ConversionKind.ImplicitTuple, comp.ClassifyConversion(int_string2, int_object).Kind); - Assert.Equal(ConversionKind.NoConversion, comp.ClassifyConversion(int_object, int_string1).Kind); - Assert.Equal(ConversionKind.NoConversion, comp.ClassifyConversion(int_object, int_string2).Kind); + Assert.Equal(ConversionKind.ExplicitTuple, comp.ClassifyConversion(int_object, int_string1).Kind); + Assert.Equal(ConversionKind.ExplicitTuple, comp.ClassifyConversion(int_object, int_string2).Kind); } [Fact] @@ -12700,8 +13480,8 @@ public void ClassifyConversionImplicit03u() Assert.Equal(ConversionKind.ImplicitTuple, comp.ClassifyConversion(int_string1, int_object).Kind); Assert.Equal(ConversionKind.ImplicitTuple, comp.ClassifyConversion(int_string2, int_object).Kind); - Assert.Equal(ConversionKind.NoConversion, comp.ClassifyConversion(int_object, int_string1).Kind); - Assert.Equal(ConversionKind.NoConversion, comp.ClassifyConversion(int_object, int_string2).Kind); + Assert.Equal(ConversionKind.ExplicitTuple, comp.ClassifyConversion(int_object, int_string1).Kind); + Assert.Equal(ConversionKind.ExplicitTuple, comp.ClassifyConversion(int_object, int_string2).Kind); } [Fact] @@ -12725,8 +13505,8 @@ public void ClassifyConversionImplicit03uu() Assert.Equal(ConversionKind.ImplicitTuple, comp.ClassifyConversion(int_string1, int_object).Kind); Assert.Equal(ConversionKind.ImplicitTuple, comp.ClassifyConversion(int_string2, int_object).Kind); - Assert.Equal(ConversionKind.NoConversion, comp.ClassifyConversion(int_object, int_string1).Kind); - Assert.Equal(ConversionKind.NoConversion, comp.ClassifyConversion(int_object, int_string2).Kind); + Assert.Equal(ConversionKind.ExplicitTuple, comp.ClassifyConversion(int_object, int_string1).Kind); + Assert.Equal(ConversionKind.ExplicitTuple, comp.ClassifyConversion(int_object, int_string2).Kind); } [Fact] @@ -12750,8 +13530,8 @@ public void ClassifyConversionImplicit03uuu() Assert.Equal(ConversionKind.ImplicitTuple, comp.ClassifyConversion(int_string1, int_object).Kind); Assert.Equal(ConversionKind.ImplicitTuple, comp.ClassifyConversion(int_string2, int_object).Kind); - Assert.Equal(ConversionKind.NoConversion, comp.ClassifyConversion(int_object, int_string1).Kind); - Assert.Equal(ConversionKind.NoConversion, comp.ClassifyConversion(int_object, int_string2).Kind); + Assert.Equal(ConversionKind.ExplicitTuple, comp.ClassifyConversion(int_object, int_string1).Kind); + Assert.Equal(ConversionKind.ExplicitTuple, comp.ClassifyConversion(int_object, int_string2).Kind); } [Fact] @@ -12785,9 +13565,9 @@ public C() var int_object_object = tupleComp1.CreateTupleTypeSymbol(ImmutableArray.Create(intType, objectType, objectType)); Assert.Equal(ConversionKind.Identity, model.ClassifyConversion(expr1, int_string1).Kind); - Assert.Equal(ConversionKind.ImplicitTuple, model.ClassifyConversion(expr1, int_object).Kind); Assert.Equal(ConversionKind.Identity, model.ClassifyConversion(expr1, int_string1, isExplicitInSource: true).Kind); - Assert.Equal(ConversionKind.ImplicitTuple, model.ClassifyConversion(expr1, int_object, isExplicitInSource: true).Kind); + Assert.Equal(ConversionKind.ImplicitTuple, model.ClassifyConversion(expr1, int_object).Kind); + Assert.Equal(ConversionKind.ExplicitTuple, model.ClassifyConversion(expr1, int_object, isExplicitInSource: true).Kind); Assert.Equal(ConversionKind.NoConversion, model.ClassifyConversion(expr1, int_object_object).Kind); Assert.Equal(ConversionKind.NoConversion, model.ClassifyConversion(expr1, int_object_object, isExplicitInSource: true).Kind); @@ -12823,9 +13603,9 @@ public C() var int_object = tupleComp1.CreateTupleTypeSymbol(ImmutableArray.Create(intType, objectType)); var int_object_object = tupleComp1.CreateTupleTypeSymbol(ImmutableArray.Create(intType, objectType, objectType)); - Assert.Equal(ConversionKind.NoConversion, model.ClassifyConversion(expr1, int_string1).Kind); + Assert.Equal(ConversionKind.ExplicitTuple, model.ClassifyConversion(expr1, int_string1).Kind); Assert.Equal(ConversionKind.Identity, model.ClassifyConversion(expr1, int_object).Kind); - Assert.Equal(ConversionKind.NoConversion, model.ClassifyConversion(expr1, int_string1, isExplicitInSource: true).Kind); + Assert.Equal(ConversionKind.ExplicitTuple, model.ClassifyConversion(expr1, int_string1, isExplicitInSource: true).Kind); Assert.Equal(ConversionKind.Identity, model.ClassifyConversion(expr1, int_object, isExplicitInSource: true).Kind); Assert.Equal(ConversionKind.NoConversion, model.ClassifyConversion(expr1, int_object_object).Kind); @@ -12863,9 +13643,9 @@ public C() var int_object_object = comp.GetWellKnownType(WellKnownType.System_ValueTuple_T3).Construct((TypeSymbol)intType, (TypeSymbol)objectType, (TypeSymbol)objectType); Assert.Equal(ConversionKind.Identity, model.ClassifyConversion(expr1, int_string1).Kind); - Assert.Equal(ConversionKind.ImplicitTuple, model.ClassifyConversion(expr1, int_object).Kind); Assert.Equal(ConversionKind.Identity, model.ClassifyConversion(expr1, int_string1, isExplicitInSource: true).Kind); - Assert.Equal(ConversionKind.ImplicitTuple, model.ClassifyConversion(expr1, int_object, isExplicitInSource: true).Kind); + Assert.Equal(ConversionKind.ImplicitTuple, model.ClassifyConversion(expr1, int_object).Kind); + Assert.Equal(ConversionKind.ExplicitTuple, model.ClassifyConversion(expr1, int_object, isExplicitInSource: true).Kind); Assert.Equal(ConversionKind.NoConversion, model.ClassifyConversion(expr1, int_object_object).Kind); Assert.Equal(ConversionKind.NoConversion, model.ClassifyConversion(expr1, int_object_object, isExplicitInSource: true).Kind); @@ -12901,9 +13681,9 @@ public C() var int_object = comp.GetWellKnownType(WellKnownType.System_ValueTuple_T2).Construct((TypeSymbol)intType, (TypeSymbol)objectType); var int_object_object = comp.GetWellKnownType(WellKnownType.System_ValueTuple_T3).Construct((TypeSymbol)intType, (TypeSymbol)objectType, (TypeSymbol)objectType); - Assert.Equal(ConversionKind.NoConversion, model.ClassifyConversion(expr1, int_string1).Kind); + Assert.Equal(ConversionKind.ExplicitTuple, model.ClassifyConversion(expr1, int_string1).Kind); Assert.Equal(ConversionKind.Identity, model.ClassifyConversion(expr1, int_object).Kind); - Assert.Equal(ConversionKind.NoConversion, model.ClassifyConversion(expr1, int_string1, isExplicitInSource: true).Kind); + Assert.Equal(ConversionKind.ExplicitTuple, model.ClassifyConversion(expr1, int_string1, isExplicitInSource: true).Kind); Assert.Equal(ConversionKind.Identity, model.ClassifyConversion(expr1, int_object, isExplicitInSource: true).Kind); Assert.Equal(ConversionKind.NoConversion, model.ClassifyConversion(expr1, int_object_object).Kind); From d29375cea12dd261a017afd2f45527c156932e3b Mon Sep 17 00:00:00 2001 From: Artur Spychaj Date: Fri, 17 Jun 2016 10:14:33 -0700 Subject: [PATCH 22/36] Revert "Start plumbing InvocationReasons through SolutionCrawler events..." This reverts commit 7357c7ba7b0b5f2398bcfb337481f50cbfa81278. --- .../AbstractTodoCommentIncrementalAnalyzer.cs | 6 +- ...eviewSolutionCrawlerRegistrationService.cs | 4 +- .../BaseDiagnosticIncrementalAnalyzer.cs | 9 +- .../DefaultDiagnosticAnalyzerService.cs | 6 +- ...sticAnalyzerService_IncrementalAnalyzer.cs | 12 +- .../EngineV1/DiagnosticIncrementalAnalyzer.cs | 1151 +++++++++++++++++ ...IncrementalAnalyzer_IncrementalAnalyzer.cs | 7 +- ...mbolTreeInfoIncrementalAnalyzerProvider.cs | 4 +- ...ntaxTreeInfoIncrementalAnalyzerProvider.cs | 2 +- .../SemanticChangeNotificationService.cs | 6 +- .../AggregateIncrementalAnalyzer.cs | 12 +- .../IncrementalAnalyzerBase.cs | 6 +- ...oordinator.IncrementalAnalyzerProcessor.cs | 16 +- .../WorkCoordinator.LowPriorityProcessor.cs | 7 +- ...WorkCoordinator.NormalPriorityProcessor.cs | 7 +- ...actDesignerAttributeIncrementalAnalyzer.cs | 7 +- .../SolutionSize/SolutionSizeTracker.cs | 6 +- .../SolutionCrawler/IIncrementalAnalyzer.cs | 6 +- 18 files changed, 1208 insertions(+), 66 deletions(-) create mode 100644 src/Features/Core/Portable/Diagnostics/EngineV1/DiagnosticIncrementalAnalyzer.cs diff --git a/src/EditorFeatures/Core/Implementation/TodoComment/AbstractTodoCommentIncrementalAnalyzer.cs b/src/EditorFeatures/Core/Implementation/TodoComment/AbstractTodoCommentIncrementalAnalyzer.cs index 057294982b576..78b39c27e671a 100644 --- a/src/EditorFeatures/Core/Implementation/TodoComment/AbstractTodoCommentIncrementalAnalyzer.cs +++ b/src/EditorFeatures/Core/Implementation/TodoComment/AbstractTodoCommentIncrementalAnalyzer.cs @@ -42,7 +42,7 @@ public Task DocumentResetAsync(Document document, CancellationToken cancellation return _state.PersistAsync(document, new Data(VersionStamp.Default, VersionStamp.Default, ImmutableArray.Empty), cancellationToken); } - public async Task AnalyzeSyntaxAsync(Document document, InvocationReasons reasons, CancellationToken cancellationToken) + public async Task AnalyzeSyntaxAsync(Document document, CancellationToken cancellationToken) { // it has an assumption that this will not be called concurrently for same document. // in fact, in current design, it won't be even called concurrently for different documents. @@ -220,12 +220,12 @@ public Task DocumentCloseAsync(Document document, CancellationToken cancellation return SpecializedTasks.EmptyTask; } - public Task AnalyzeDocumentAsync(Document document, SyntaxNode bodyOpt, InvocationReasons reasons, CancellationToken cancellationToken) + public Task AnalyzeDocumentAsync(Document document, SyntaxNode bodyOpt, CancellationToken cancellationToken) { return SpecializedTasks.EmptyTask; } - public Task AnalyzeProjectAsync(Project project, bool semanticsChanged, InvocationReasons reasons, CancellationToken cancellationToken) + public Task AnalyzeProjectAsync(Project project, bool semanticsChanged, CancellationToken cancellationToken) { return SpecializedTasks.EmptyTask; } diff --git a/src/EditorFeatures/Core/Shared/Preview/PreviewSolutionCrawlerRegistrationService.cs b/src/EditorFeatures/Core/Shared/Preview/PreviewSolutionCrawlerRegistrationService.cs index 6ee60a79c27f4..c6cdfe76b04f8 100644 --- a/src/EditorFeatures/Core/Shared/Preview/PreviewSolutionCrawlerRegistrationService.cs +++ b/src/EditorFeatures/Core/Shared/Preview/PreviewSolutionCrawlerRegistrationService.cs @@ -49,8 +49,8 @@ public async void Register(Workspace workspace) await Task.Delay(workerBackOffTimeSpanInMS).ConfigureAwait(false); // do actual analysis - await analyzer.AnalyzeSyntaxAsync(document, InvocationReasons.Empty, source.Token).ConfigureAwait(false); - await analyzer.AnalyzeDocumentAsync(document, bodyOpt: null, reasons: InvocationReasons.Empty, cancellationToken: source.Token).ConfigureAwait(false); + await analyzer.AnalyzeSyntaxAsync(document, source.Token).ConfigureAwait(false); + await analyzer.AnalyzeDocumentAsync(document, bodyOpt: null, cancellationToken: source.Token).ConfigureAwait(false); // don't call project one. } diff --git a/src/Features/Core/Portable/Diagnostics/BaseDiagnosticIncrementalAnalyzer.cs b/src/Features/Core/Portable/Diagnostics/BaseDiagnosticIncrementalAnalyzer.cs index affd488b5e8c3..d26897848c12c 100644 --- a/src/Features/Core/Portable/Diagnostics/BaseDiagnosticIncrementalAnalyzer.cs +++ b/src/Features/Core/Portable/Diagnostics/BaseDiagnosticIncrementalAnalyzer.cs @@ -35,10 +35,9 @@ protected BaseDiagnosticIncrementalAnalyzer(DiagnosticAnalyzerService owner, Wor /// /// The document to analyze. /// If present, indicates a portion (e.g. a method body) of the document to analyze. - /// The reason(s) this analysis was triggered. /// /// - public abstract Task AnalyzeDocumentAsync(Document document, SyntaxNode bodyOpt, InvocationReasons reasons, CancellationToken cancellationToken); + public abstract Task AnalyzeDocumentAsync(Document document, SyntaxNode bodyOpt, CancellationToken cancellationToken); /// /// Analyze a single project such that diagnostics for the entire project become available. /// Calls for each @@ -46,20 +45,18 @@ protected BaseDiagnosticIncrementalAnalyzer(DiagnosticAnalyzerService owner, Wor /// /// The project to analyze. /// Indicates a change to the declarative semantics of the project. - /// The reason(s) this analysis was triggered. /// /// - public abstract Task AnalyzeProjectAsync(Project project, bool semanticsChanged, InvocationReasons reasons, CancellationToken cancellationToken); + public abstract Task AnalyzeProjectAsync(Project project, bool semanticsChanged, CancellationToken cancellationToken); /// /// Apply syntax tree actions (that have not already been applied) to a document. /// Calls for each /// unique group of diagnostics, where a group is identified by analysis classification (syntax), document, and analyzer. /// /// The document to analyze. - /// The reason(s) this analysis was triggered. /// /// - public abstract Task AnalyzeSyntaxAsync(Document document, InvocationReasons reasons, CancellationToken cancellationToken); + public abstract Task AnalyzeSyntaxAsync(Document document, CancellationToken cancellationToken); /// /// Respond to a document being opened for editing in the host. /// diff --git a/src/Features/Core/Portable/Diagnostics/DefaultDiagnosticAnalyzerService.cs b/src/Features/Core/Portable/Diagnostics/DefaultDiagnosticAnalyzerService.cs index 4c06deeb74a39..5257e1c6f9765 100644 --- a/src/Features/Core/Portable/Diagnostics/DefaultDiagnosticAnalyzerService.cs +++ b/src/Features/Core/Portable/Diagnostics/DefaultDiagnosticAnalyzerService.cs @@ -82,7 +82,7 @@ public bool NeedsReanalysisOnOptionChanged(object sender, OptionChangedEventArgs return false; } - public async Task AnalyzeSyntaxAsync(Document document, InvocationReasons reasons, CancellationToken cancellationToken) + public async Task AnalyzeSyntaxAsync(Document document, CancellationToken cancellationToken) { // right now, there is no way to observe diagnostics for closed file. if (!_workspace.IsDocumentOpen(document.Id) || @@ -103,7 +103,7 @@ public async Task AnalyzeSyntaxAsync(Document document, InvocationReasons reason _workspace, document.Project.Solution, document.Project.Id, document.Id, diagnosticData)); } - public async Task AnalyzeDocumentAsync(Document document, SyntaxNode bodyOpt, InvocationReasons reasons, CancellationToken cancellationToken) + public async Task AnalyzeDocumentAsync(Document document, SyntaxNode bodyOpt, CancellationToken cancellationToken) { // right now, there is no way to observe diagnostics for closed file. if (!_workspace.IsDocumentOpen(document.Id) || @@ -150,7 +150,7 @@ private void RaiseEmptyDiagnosticUpdated(int kind, DocumentId documentId) new DefaultUpdateArgsId(_workspace.Kind, kind, documentId), _workspace, null, documentId.ProjectId, documentId)); } - public Task AnalyzeProjectAsync(Project project, bool semanticsChanged, InvocationReasons reasons, CancellationToken cancellationToken) + public Task AnalyzeProjectAsync(Project project, bool semanticsChanged, CancellationToken cancellationToken) { return SpecializedTasks.EmptyTask; } diff --git a/src/Features/Core/Portable/Diagnostics/DiagnosticAnalyzerService_IncrementalAnalyzer.cs b/src/Features/Core/Portable/Diagnostics/DiagnosticAnalyzerService_IncrementalAnalyzer.cs index 0738cbcea6f89..8bb68389c5361 100644 --- a/src/Features/Core/Portable/Diagnostics/DiagnosticAnalyzerService_IncrementalAnalyzer.cs +++ b/src/Features/Core/Portable/Diagnostics/DiagnosticAnalyzerService_IncrementalAnalyzer.cs @@ -70,19 +70,19 @@ public IncrementalAnalyzerDelegatee(DiagnosticAnalyzerService owner, Workspace w } #region IIncrementalAnalyzer - public override Task AnalyzeSyntaxAsync(Document document, InvocationReasons reasons, CancellationToken cancellationToken) + public override Task AnalyzeSyntaxAsync(Document document, CancellationToken cancellationToken) { - return Analyzer.AnalyzeSyntaxAsync(document, reasons, cancellationToken); + return Analyzer.AnalyzeSyntaxAsync(document, cancellationToken); } - public override Task AnalyzeDocumentAsync(Document document, SyntaxNode bodyOpt, InvocationReasons reasons, CancellationToken cancellationToken) + public override Task AnalyzeDocumentAsync(Document document, SyntaxNode bodyOpt, CancellationToken cancellationToken) { - return Analyzer.AnalyzeDocumentAsync(document, bodyOpt, reasons, cancellationToken); + return Analyzer.AnalyzeDocumentAsync(document, bodyOpt, cancellationToken); } - public override Task AnalyzeProjectAsync(Project project, bool semanticsChanged, InvocationReasons reasons, CancellationToken cancellationToken) + public override Task AnalyzeProjectAsync(Project project, bool semanticsChanged, CancellationToken cancellationToken) { - return Analyzer.AnalyzeProjectAsync(project, semanticsChanged, reasons, cancellationToken); + return Analyzer.AnalyzeProjectAsync(project, semanticsChanged, cancellationToken); } public override Task DocumentOpenAsync(Document document, CancellationToken cancellationToken) diff --git a/src/Features/Core/Portable/Diagnostics/EngineV1/DiagnosticIncrementalAnalyzer.cs b/src/Features/Core/Portable/Diagnostics/EngineV1/DiagnosticIncrementalAnalyzer.cs new file mode 100644 index 0000000000000..7ce5ee89d5090 --- /dev/null +++ b/src/Features/Core/Portable/Diagnostics/EngineV1/DiagnosticIncrementalAnalyzer.cs @@ -0,0 +1,1151 @@ +// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Diagnostics; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.Diagnostics.Log; +using Microsoft.CodeAnalysis.ErrorReporting; +using Microsoft.CodeAnalysis.Internal.Log; +using Microsoft.CodeAnalysis.LanguageServices; +using Microsoft.CodeAnalysis.Shared.Extensions; +using Microsoft.CodeAnalysis.Shared.Options; +using Microsoft.CodeAnalysis.Text; +using Microsoft.CodeAnalysis.Versions; +using Roslyn.Utilities; + +namespace Microsoft.CodeAnalysis.Diagnostics.EngineV1 +{ + internal partial class DiagnosticIncrementalAnalyzer : BaseDiagnosticIncrementalAnalyzer + { + private readonly int _correlationId; + private readonly MemberRangeMap _memberRangeMap; + private readonly AnalyzerExecutor _executor; + private readonly StateManager _stateManager; + + /// + /// PERF: Always run analyzers sequentially for background analysis. + /// + private const bool ConcurrentAnalysis = false; + + /// + /// Always compute suppressed diagnostics - diagnostic clients may or may not request for suppressed diagnostics. + /// + private const bool ReportSuppressedDiagnostics = true; + + public DiagnosticIncrementalAnalyzer( + DiagnosticAnalyzerService owner, + int correlationId, + Workspace workspace, + HostAnalyzerManager analyzerManager, + AbstractHostDiagnosticUpdateSource hostDiagnosticUpdateSource) + : base(owner, workspace, analyzerManager, hostDiagnosticUpdateSource) + { + _correlationId = correlationId; + _memberRangeMap = new MemberRangeMap(); + _executor = new AnalyzerExecutor(this); + + _stateManager = new StateManager(analyzerManager); + _stateManager.ProjectAnalyzerReferenceChanged += OnProjectAnalyzerReferenceChanged; + } + + private void OnProjectAnalyzerReferenceChanged(object sender, ProjectAnalyzerReferenceChangedEventArgs e) + { + if (e.Removed.Length == 0) + { + // nothing to refresh + return; + } + + // events will be automatically serialized. + var project = e.Project; + var stateSets = e.Removed; + + // first remove all states + foreach (var stateSet in stateSets) + { + foreach (var document in project.Documents) + { + stateSet.Remove(document.Id); + } + + stateSet.Remove(project.Id); + } + + // now raise events + Owner.RaiseBulkDiagnosticsUpdated(raiseEvents => + { + foreach (var document in project.Documents) + { + RaiseDocumentDiagnosticsRemoved(document, stateSets, includeProjectState: true, raiseEvents: raiseEvents); + } + + RaiseProjectDiagnosticsRemoved(project, stateSets, raiseEvents); + }); + } + + public override Task DocumentOpenAsync(Document document, CancellationToken cancellationToken) + { + using (Logger.LogBlock(FunctionId.Diagnostics_DocumentOpen, GetOpenLogMessage, document, cancellationToken)) + { + return ClearOnlyDocumentStates(document); + } + } + + public override Task DocumentCloseAsync(Document document, CancellationToken cancellationToken) + { + using (Logger.LogBlock(FunctionId.Diagnostics_DocumentClose, GetResetLogMessage, document, cancellationToken)) + { + // we don't need the info for closed file + _memberRangeMap.Remove(document.Id); + + return ClearOnlyDocumentStates(document); + } + } + + public override Task DocumentResetAsync(Document document, CancellationToken cancellationToken) + { + using (Logger.LogBlock(FunctionId.Diagnostics_DocumentReset, GetResetLogMessage, document, cancellationToken)) + { + // clear states for re-analysis and raise events about it. otherwise, some states might not updated on re-analysis + // due to our build-live de-duplication logic where we put all state in Documents state. + return ClearOnlyDocumentStates(document); + } + } + + private Task ClearOnlyDocumentStates(Document document) + { + // since managing states and raising events are separated, it can't be cancelled. + var stateSets = _stateManager.GetStateSets(document.Project); + + // we remove whatever information we used to have on document open/close and re-calculate diagnostics + // we had to do this since some diagnostic analyzer changes its behavior based on whether the document is opened or not. + // so we can't use cached information. + + // clean up states + foreach (var stateSet in stateSets) + { + stateSet.Remove(document.Id, onlyDocumentStates: true); + } + + // raise events + Owner.RaiseBulkDiagnosticsUpdated(raiseEvents => + { + RaiseDocumentDiagnosticsRemoved(document, stateSets, includeProjectState: false, raiseEvents: raiseEvents); + }); + + return SpecializedTasks.EmptyTask; + } + + private bool CheckOptions(Project project, bool forceAnalysis) + { + var workspace = project.Solution.Workspace; + if (ServiceFeatureOnOffOptions.IsClosedFileDiagnosticsEnabled(project) && + workspace.Options.GetOption(RuntimeOptions.FullSolutionAnalysis)) + { + return true; + } + + if (forceAnalysis) + { + return true; + } + + return false; + } + + internal CompilationWithAnalyzers GetCompilationWithAnalyzers( + Project project, + IEnumerable analyzers, + Compilation compilation, + bool concurrentAnalysis, + bool reportSuppressedDiagnostics) + { + Contract.ThrowIfFalse(project.SupportsCompilation); + Contract.ThrowIfNull(compilation); + + Func analyzerExceptionFilter = ex => + { + if (project.Solution.Options.GetOption(InternalDiagnosticsOptions.CrashOnAnalyzerException)) + { + // if option is on, crash the host to get crash dump. + FatalError.ReportUnlessCanceled(ex); + } + + return true; + }; + + var analysisOptions = new CompilationWithAnalyzersOptions( + new WorkspaceAnalyzerOptions(project.AnalyzerOptions, project.Solution.Workspace), + GetOnAnalyzerException(project.Id), + analyzerExceptionFilter: analyzerExceptionFilter, + concurrentAnalysis: concurrentAnalysis, + logAnalyzerExecutionTime: true, + reportSuppressedDiagnostics: reportSuppressedDiagnostics); + + var filteredAnalyzers = analyzers + .Where(a => !CompilationWithAnalyzers.IsDiagnosticAnalyzerSuppressed(a, compilation.Options, analysisOptions.OnAnalyzerException)) + .Distinct() + .ToImmutableArray(); + if (filteredAnalyzers.IsEmpty) + { + return null; + } + + return new CompilationWithAnalyzers(compilation, filteredAnalyzers, analysisOptions); + } + + public override async Task AnalyzeSyntaxAsync(Document document, CancellationToken cancellationToken) + { + await AnalyzeSyntaxAsync(document, diagnosticIds: null, cancellationToken: cancellationToken).ConfigureAwait(false); + } + + private async Task AnalyzeSyntaxAsync(Document document, ImmutableHashSet diagnosticIds, CancellationToken cancellationToken) + { + try + { + if (!CheckOptions(document.Project, document.IsOpen())) + { + return; + } + + var textVersion = await document.GetTextVersionAsync(cancellationToken).ConfigureAwait(false); + var dataVersion = await document.GetSyntaxVersionAsync(cancellationToken).ConfigureAwait(false); + var versions = new VersionArgument(textVersion, dataVersion); + + var root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false); + var fullSpan = root == null ? null : (TextSpan?)root.FullSpan; + + var openedDocument = document.IsOpen(); + + var stateSets = _stateManager.GetOrUpdateStateSets(document.Project); + var analyzers = stateSets.Select(s => s.Analyzer); + + var userDiagnosticDriver = new DiagnosticAnalyzerDriver( + document, fullSpan, root, this, analyzers, ConcurrentAnalysis, ReportSuppressedDiagnostics, cancellationToken); + + foreach (var stateSet in stateSets) + { + if (await SkipRunningAnalyzerAsync(document.Project, stateSet.Analyzer, openedDocument, skipClosedFileCheck: false, cancellationToken: cancellationToken).ConfigureAwait(false)) + { + await ClearExistingDiagnostics(document, stateSet, StateType.Syntax, cancellationToken).ConfigureAwait(false); + continue; + } + + if (ShouldRunAnalyzerForStateType(stateSet.Analyzer, StateType.Syntax, diagnosticIds)) + { + var data = await _executor.GetSyntaxAnalysisDataAsync(userDiagnosticDriver, stateSet, versions).ConfigureAwait(false); + if (data.FromCache) + { + RaiseDiagnosticsCreatedFromCacheIfNeeded(StateType.Syntax, document, stateSet, data.Items); + continue; + } + + var state = stateSet.GetState(StateType.Syntax); + await state.PersistAsync(document, data.ToPersistData(), cancellationToken).ConfigureAwait(false); + + RaiseDocumentDiagnosticsUpdatedIfNeeded(StateType.Syntax, document, stateSet, data.OldItems, data.Items); + } + } + } + catch (Exception e) when (FatalError.ReportUnlessCanceled(e)) + { + throw ExceptionUtilities.Unreachable; + } + } + + public override async Task AnalyzeDocumentAsync(Document document, SyntaxNode bodyOpt, CancellationToken cancellationToken) + { + await AnalyzeDocumentAsync(document, bodyOpt, diagnosticIds: null, cancellationToken: cancellationToken).ConfigureAwait(false); + } + + private async Task AnalyzeDocumentAsync(Document document, SyntaxNode bodyOpt, ImmutableHashSet diagnosticIds, CancellationToken cancellationToken) + { + try + { + if (!CheckOptions(document.Project, document.IsOpen())) + { + return; + } + + var textVersion = await document.GetTextVersionAsync(cancellationToken).ConfigureAwait(false); + var projectVersion = await document.Project.GetDependentVersionAsync(cancellationToken).ConfigureAwait(false); + var dataVersion = await document.Project.GetDependentSemanticVersionAsync(cancellationToken).ConfigureAwait(false); + + var versions = new VersionArgument(textVersion, dataVersion, projectVersion); + if (bodyOpt == null) + { + await AnalyzeDocumentAsync(document, versions, diagnosticIds, cancellationToken).ConfigureAwait(false); + } + else + { + // only open file can go this route + await AnalyzeBodyDocumentAsync(document, bodyOpt, versions, cancellationToken).ConfigureAwait(false); + } + } + catch (Exception e) when (FatalError.ReportUnlessCanceled(e)) + { + throw ExceptionUtilities.Unreachable; + } + } + + private async Task AnalyzeBodyDocumentAsync(Document document, SyntaxNode member, VersionArgument versions, CancellationToken cancellationToken) + { + try + { + // syntax facts service must exist, otherwise, this method won't have called. + var syntaxFacts = document.Project.LanguageServices.GetService(); + var root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false); + var memberId = syntaxFacts.GetMethodLevelMemberId(root, member); + + var stateSets = _stateManager.GetOrUpdateStateSets(document.Project); + var analyzers = stateSets.Select(s => s.Analyzer); + + var spanBasedDriver = new DiagnosticAnalyzerDriver( + document, member.FullSpan, root, this, analyzers, ConcurrentAnalysis, ReportSuppressedDiagnostics, cancellationToken); + + var documentBasedDriver = new DiagnosticAnalyzerDriver( + document, root.FullSpan, root, this, analyzers, ConcurrentAnalysis, ReportSuppressedDiagnostics, cancellationToken); + + foreach (var stateSet in stateSets) + { + if (Owner.IsAnalyzerSuppressed(stateSet.Analyzer, document.Project)) + { + await ClearExistingDiagnostics(document, stateSet, StateType.Document, cancellationToken).ConfigureAwait(false); + continue; + } + + if (ShouldRunAnalyzerForStateType(stateSet.Analyzer, StateType.Document)) + { + var supportsSemanticInSpan = stateSet.Analyzer.SupportsSpanBasedSemanticDiagnosticAnalysis(); + var userDiagnosticDriver = supportsSemanticInSpan ? spanBasedDriver : documentBasedDriver; + + var ranges = _memberRangeMap.GetSavedMemberRange(stateSet.Analyzer, document); + var data = await _executor.GetDocumentBodyAnalysisDataAsync( + stateSet, versions, userDiagnosticDriver, root, member, memberId, supportsSemanticInSpan, ranges).ConfigureAwait(false); + + _memberRangeMap.UpdateMemberRange(stateSet.Analyzer, document, versions.TextVersion, memberId, member.FullSpan, ranges); + + var state = stateSet.GetState(StateType.Document); + await state.PersistAsync(document, data.ToPersistData(), cancellationToken).ConfigureAwait(false); + + if (data.FromCache) + { + RaiseDiagnosticsCreatedFromCacheIfNeeded(StateType.Document, document, stateSet, data.Items); + continue; + } + + RaiseDocumentDiagnosticsUpdatedIfNeeded(StateType.Document, document, stateSet, data.OldItems, data.Items); + } + } + } + catch (Exception e) when (FatalError.ReportUnlessCanceled(e)) + { + throw ExceptionUtilities.Unreachable; + } + } + + private async Task AnalyzeDocumentAsync(Document document, VersionArgument versions, ImmutableHashSet diagnosticIds, CancellationToken cancellationToken) + { + try + { + var root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false); + var fullSpan = root == null ? null : (TextSpan?)root.FullSpan; + + bool openedDocument = document.IsOpen(); + + var stateSets = _stateManager.GetOrUpdateStateSets(document.Project); + var analyzers = stateSets.Select(s => s.Analyzer); + + var userDiagnosticDriver = new DiagnosticAnalyzerDriver( + document, fullSpan, root, this, analyzers, ConcurrentAnalysis, ReportSuppressedDiagnostics, cancellationToken); + + foreach (var stateSet in stateSets) + { + if (await SkipRunningAnalyzerAsync(document.Project, stateSet.Analyzer, openedDocument, skipClosedFileCheck: false, cancellationToken: cancellationToken).ConfigureAwait(false)) + { + await ClearExistingDiagnostics(document, stateSet, StateType.Document, cancellationToken).ConfigureAwait(false); + continue; + } + + if (ShouldRunAnalyzerForStateType(stateSet.Analyzer, StateType.Document, diagnosticIds)) + { + var data = await _executor.GetDocumentAnalysisDataAsync(userDiagnosticDriver, stateSet, versions).ConfigureAwait(false); + if (data.FromCache) + { + RaiseDiagnosticsCreatedFromCacheIfNeeded(StateType.Document, document, stateSet, data.Items); + continue; + } + + if (openedDocument) + { + _memberRangeMap.Touch(stateSet.Analyzer, document, versions.TextVersion); + } + + var state = stateSet.GetState(StateType.Document); + await state.PersistAsync(document, data.ToPersistData(), cancellationToken).ConfigureAwait(false); + + RaiseDocumentDiagnosticsUpdatedIfNeeded(StateType.Document, document, stateSet, data.OldItems, data.Items); + } + } + } + catch (Exception e) when (FatalError.ReportUnlessCanceled(e)) + { + throw ExceptionUtilities.Unreachable; + } + } + + public override async Task AnalyzeProjectAsync(Project project, bool semanticsChanged, CancellationToken cancellationToken) + { + await AnalyzeProjectAsync(project, cancellationToken).ConfigureAwait(false); + } + + private async Task AnalyzeProjectAsync(Project project, CancellationToken cancellationToken) + { + try + { + if (!CheckOptions(project, forceAnalysis: false)) + { + return; + } + + // PERF: Ensure that we explicitly ignore the skipped analyzers while creating the analyzer driver, otherwise we might end up running hidden analyzers on closed files. + var stateSets = _stateManager.GetOrUpdateStateSets(project).ToImmutableArray(); + var skipAnalyzersMap = new Dictionary(stateSets.Length); + var shouldRunAnalyzersMap = new Dictionary(stateSets.Length); + var analyzersBuilder = ImmutableArray.CreateBuilder(stateSets.Length); + foreach (var stateSet in stateSets) + { + var skip = await SkipRunningAnalyzerAsync(project, stateSet.Analyzer, openedDocument: false, skipClosedFileCheck: true, cancellationToken: cancellationToken).ConfigureAwait(false); + skipAnalyzersMap.Add(stateSet.Analyzer, skip); + + var shouldRun = !skip && ShouldRunAnalyzerForStateType(stateSet.Analyzer, StateType.Project, diagnosticIds: null); + shouldRunAnalyzersMap.Add(stateSet.Analyzer, shouldRun); + + if (shouldRun) + { + analyzersBuilder.Add(stateSet.Analyzer); + } + } + + var analyzerDriver = new DiagnosticAnalyzerDriver(project, this, analyzersBuilder.ToImmutable(), ConcurrentAnalysis, ReportSuppressedDiagnostics, cancellationToken); + + var projectTextVersion = await project.GetLatestDocumentVersionAsync(cancellationToken).ConfigureAwait(false); + var semanticVersion = await project.GetDependentSemanticVersionAsync(cancellationToken).ConfigureAwait(false); + var projectVersion = await project.GetDependentVersionAsync(cancellationToken).ConfigureAwait(false); + var versions = new VersionArgument(projectTextVersion, semanticVersion, projectVersion); + + foreach (var stateSet in stateSets) + { + // Compilation actions can report diagnostics on open files, so we skipClosedFileChecks. + if (skipAnalyzersMap[stateSet.Analyzer]) + { + await ClearExistingDiagnostics(project, stateSet, cancellationToken).ConfigureAwait(false); + continue; + } + + if (shouldRunAnalyzersMap[stateSet.Analyzer]) + { + var data = await _executor.GetProjectAnalysisDataAsync(analyzerDriver, stateSet, versions).ConfigureAwait(false); + if (data.FromCache) + { + RaiseProjectDiagnosticsUpdatedIfNeeded(project, stateSet, ImmutableArray.Empty, data.Items); + continue; + } + + var state = stateSet.GetState(StateType.Project); + await PersistProjectData(project, state, data).ConfigureAwait(false); + + RaiseProjectDiagnosticsUpdatedIfNeeded(project, stateSet, data.OldItems, data.Items); + } + } + } + catch (Exception e) when (FatalError.ReportUnlessCanceled(e)) + { + throw ExceptionUtilities.Unreachable; + } + } + + private async Task SkipRunningAnalyzerAsync(Project project, DiagnosticAnalyzer analyzer, bool openedDocument, bool skipClosedFileCheck, CancellationToken cancellationToken) + { + // this project is not in a good state. only continue if it is opened document. + if (!await project.HasSuccessfullyLoadedAsync(cancellationToken).ConfigureAwait(false) && !openedDocument) + { + return true; + } + + if (Owner.IsAnalyzerSuppressed(analyzer, project)) + { + return true; + } + + if (skipClosedFileCheck || ShouldRunAnalyzerForClosedFile(analyzer, project.CompilationOptions, openedDocument)) + { + return false; + } + + return true; + } + + private static async Task PersistProjectData(Project project, DiagnosticState state, AnalysisData data) + { + // TODO: Cancellation is not allowed here to prevent data inconsistency. But there is still a possibility of data inconsistency due to + // things like exception. For now, I am letting it go and let v2 engine take care of it properly. If v2 doesn't come online soon enough + // more refactoring is required on project state. + + // clear all existing data + state.Remove(project.Id); + foreach (var document in project.Documents) + { + state.Remove(document.Id); + } + + // quick bail out + if (data.Items.Length == 0) + { + return; + } + + // save new data + var group = data.Items.GroupBy(d => d.DocumentId); + foreach (var kv in group) + { + if (kv.Key == null) + { + // save project scope diagnostics + await state.PersistAsync(project, new AnalysisData(data.TextVersion, data.DataVersion, kv.ToImmutableArrayOrEmpty()), CancellationToken.None).ConfigureAwait(false); + continue; + } + + // save document scope diagnostics + var document = project.GetDocument(kv.Key); + if (document == null) + { + continue; + } + + await state.PersistAsync(document, new AnalysisData(data.TextVersion, data.DataVersion, kv.ToImmutableArrayOrEmpty()), CancellationToken.None).ConfigureAwait(false); + } + } + + public override void RemoveDocument(DocumentId documentId) + { + using (Logger.LogBlock(FunctionId.Diagnostics_RemoveDocument, GetRemoveLogMessage, documentId, CancellationToken.None)) + { + _memberRangeMap.Remove(documentId); + + var stateSets = _stateManager.GetStateSets(documentId.ProjectId); + + foreach (var stateSet in stateSets) + { + stateSet.Remove(documentId); + } + + Owner.RaiseBulkDiagnosticsUpdated(raiseEvents => + { + foreach (var stateSet in stateSets) + { + var solutionArgs = new SolutionArgument(null, documentId.ProjectId, documentId); + for (var stateType = 0; stateType < s_stateTypeCount; stateType++) + { + RaiseDiagnosticsRemoved((StateType)stateType, documentId, stateSet, solutionArgs, raiseEvents); + } + } + }); + } + } + + public override void RemoveProject(ProjectId projectId) + { + using (Logger.LogBlock(FunctionId.Diagnostics_RemoveProject, GetRemoveLogMessage, projectId, CancellationToken.None)) + { + var stateSets = _stateManager.GetStateSets(projectId); + + foreach (var stateSet in stateSets) + { + stateSet.Remove(projectId); + } + + _stateManager.RemoveStateSet(projectId); + + Owner.RaiseBulkDiagnosticsUpdated(raiseEvents => + { + foreach (var stateSet in stateSets) + { + var solutionArgs = new SolutionArgument(null, projectId, null); + RaiseDiagnosticsRemoved(StateType.Project, projectId, stateSet, solutionArgs, raiseEvents); + } + }); + } + } + + public override async Task TryAppendDiagnosticsForSpanAsync(Document document, TextSpan range, List diagnostics, bool includeSuppressedDiagnostics = false, CancellationToken cancellationToken = default(CancellationToken)) + { + var root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false); + var getter = new LatestDiagnosticsForSpanGetter(this, document, root, range, blockForData: false, diagnostics: diagnostics, includeSuppressedDiagnostics: includeSuppressedDiagnostics, cancellationToken: cancellationToken); + return await getter.TryGetAsync().ConfigureAwait(false); + } + + public override async Task> GetDiagnosticsForSpanAsync(Document document, TextSpan range, bool includeSuppressedDiagnostics = false, CancellationToken cancellationToken = default(CancellationToken)) + { + var root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false); + var getter = new LatestDiagnosticsForSpanGetter(this, document, root, range, blockForData: true, includeSuppressedDiagnostics: includeSuppressedDiagnostics, cancellationToken: cancellationToken); + + var result = await getter.TryGetAsync().ConfigureAwait(false); + Contract.Requires(result); + + return getter.Diagnostics; + } + + public override bool ContainsDiagnostics(Workspace workspace, ProjectId projectId) + { + // need to improve perf in v2. + foreach (var stateSet in _stateManager.GetStateSets(projectId)) + { + for (var stateType = 0; stateType < s_stateTypeCount; stateType++) + { + var state = stateSet.GetState((StateType)stateType); + if (state.Count == 0) + { + continue; + } + + if (state.GetDataCount(projectId) > 0) + { + return true; + } + + var project = workspace.CurrentSolution.GetProject(projectId); + if (project == null) + { + continue; + } + + foreach (var documentId in project.DocumentIds) + { + if (state.GetDataCount(documentId) > 0) + { + return true; + } + } + } + } + + return false; + } + + private bool ShouldRunAnalyzerForClosedFile(DiagnosticAnalyzer analyzer, CompilationOptions options, bool openedDocument) + { + // we have opened document, doesn't matter + // PERF: Don't query descriptors for compiler analyzer, always execute it. + if (openedDocument || analyzer.IsCompilerAnalyzer()) + { + return true; + } + + // most of analyzers, number of descriptor is quite small, so this should be cheap. + return Owner.GetDiagnosticDescriptors(analyzer).Any(d => GetEffectiveSeverity(d, options) != ReportDiagnostic.Hidden); + } + + private bool ShouldRunAnalyzerForStateType(DiagnosticAnalyzer analyzer, StateType stateTypeId, ImmutableHashSet diagnosticIds) + { + return ShouldRunAnalyzerForStateType(analyzer, stateTypeId, diagnosticIds, Owner.GetDiagnosticDescriptors); + } + + private static bool ShouldRunAnalyzerForStateType(DiagnosticAnalyzer analyzer, StateType stateTypeId, + ImmutableHashSet diagnosticIds = null, Func> getDescriptors = null) + { + // PERF: Don't query descriptors for compiler analyzer, always execute it for all state types. + if (analyzer.IsCompilerAnalyzer()) + { + return true; + } + + if (diagnosticIds != null && getDescriptors(analyzer).All(d => !diagnosticIds.Contains(d.Id))) + { + return false; + } + + switch (stateTypeId) + { + case StateType.Syntax: + return analyzer.SupportsSyntaxDiagnosticAnalysis(); + + case StateType.Document: + return analyzer.SupportsSemanticDiagnosticAnalysis(); + + case StateType.Project: + return analyzer.SupportsProjectDiagnosticAnalysis(); + + default: + throw ExceptionUtilities.Unreachable; + } + } + + public override void LogAnalyzerCountSummary() + { + DiagnosticAnalyzerLogger.LogAnalyzerCrashCountSummary(_correlationId, DiagnosticLogAggregator); + DiagnosticAnalyzerLogger.LogAnalyzerTypeCountSummary(_correlationId, DiagnosticLogAggregator); + + // reset the log aggregator + ResetDiagnosticLogAggregator(); + } + + private static bool CheckSyntaxVersions(Document document, AnalysisData existingData, VersionArgument versions) + { + if (existingData == null) + { + return false; + } + + return document.CanReusePersistedTextVersion(versions.TextVersion, existingData.TextVersion) && + document.CanReusePersistedSyntaxTreeVersion(versions.DataVersion, existingData.DataVersion); + } + + private static bool CheckSemanticVersions(Document document, AnalysisData existingData, VersionArgument versions) + { + if (existingData == null) + { + return false; + } + + return document.CanReusePersistedTextVersion(versions.TextVersion, existingData.TextVersion) && + document.Project.CanReusePersistedDependentSemanticVersion(versions.ProjectVersion, versions.DataVersion, existingData.DataVersion); + } + + private static bool CheckSemanticVersions(Project project, AnalysisData existingData, VersionArgument versions) + { + if (existingData == null) + { + return false; + } + + return VersionStamp.CanReusePersistedVersion(versions.TextVersion, existingData.TextVersion) && + project.CanReusePersistedDependentSemanticVersion(versions.ProjectVersion, versions.DataVersion, existingData.DataVersion); + } + + private void RaiseDiagnosticsCreatedFromCacheIfNeeded(StateType type, Document document, StateSet stateSet, ImmutableArray items) + { + RaiseDocumentDiagnosticsUpdatedIfNeeded(type, document, stateSet, ImmutableArray.Empty, items); + } + + private void RaiseDocumentDiagnosticsUpdatedIfNeeded( + StateType type, Document document, StateSet stateSet, ImmutableArray existingItems, ImmutableArray newItems) + { + var noItems = existingItems.Length == 0 && newItems.Length == 0; + if (noItems) + { + return; + } + + RaiseDiagnosticsCreated(type, document.Id, stateSet, new SolutionArgument(document), newItems); + } + + private void RaiseProjectDiagnosticsUpdatedIfNeeded( + Project project, StateSet stateSet, ImmutableArray existingItems, ImmutableArray newItems) + { + var noItems = existingItems.Length == 0 && newItems.Length == 0; + if (noItems) + { + return; + } + + RaiseProjectDiagnosticsRemovedIfNeeded(project, stateSet, existingItems, newItems); + RaiseProjectDiagnosticsUpdated(project, stateSet, newItems); + } + + private void RaiseProjectDiagnosticsRemovedIfNeeded( + Project project, StateSet stateSet, ImmutableArray existingItems, ImmutableArray newItems) + { + if (existingItems.Length == 0) + { + return; + } + + Owner.RaiseBulkDiagnosticsUpdated(raiseEvents => + { + var removedItems = existingItems.GroupBy(d => d.DocumentId).Select(g => g.Key).Except(newItems.GroupBy(d => d.DocumentId).Select(g => g.Key)); + foreach (var documentId in removedItems) + { + if (documentId == null) + { + RaiseDiagnosticsRemoved(StateType.Project, project.Id, stateSet, new SolutionArgument(project), raiseEvents); + continue; + } + + var document = project.GetDocument(documentId); + var argument = document == null ? new SolutionArgument(null, documentId.ProjectId, documentId) : new SolutionArgument(document); + RaiseDiagnosticsRemoved(StateType.Project, documentId, stateSet, argument, raiseEvents); + } + }); + } + + private void RaiseProjectDiagnosticsUpdated(Project project, StateSet stateSet, ImmutableArray diagnostics) + { + Owner.RaiseBulkDiagnosticsUpdated(raiseEvents => + { + var group = diagnostics.GroupBy(d => d.DocumentId); + foreach (var kv in group) + { + if (kv.Key == null) + { + RaiseDiagnosticsCreated(StateType.Project, project.Id, stateSet, new SolutionArgument(project), kv.ToImmutableArrayOrEmpty(), raiseEvents); + continue; + } + + RaiseDiagnosticsCreated(StateType.Project, kv.Key, stateSet, new SolutionArgument(project.GetDocument(kv.Key)), kv.ToImmutableArrayOrEmpty(), raiseEvents); + } + }); + } + + private static ImmutableArray GetDiagnosticData(ILookup lookup, DocumentId documentId) + { + return lookup.Contains(documentId) ? lookup[documentId].ToImmutableArrayOrEmpty() : ImmutableArray.Empty; + } + + private void RaiseDiagnosticsCreated( + StateType type, object key, StateSet stateSet, SolutionArgument solution, ImmutableArray diagnostics) + { + RaiseDiagnosticsCreated(type, key, stateSet, solution, diagnostics, Owner.RaiseDiagnosticsUpdated); + } + + private void RaiseDiagnosticsCreated( + StateType type, object key, StateSet stateSet, SolutionArgument solution, ImmutableArray diagnostics, Action raiseEvents) + { + // get right arg id for the given analyzer + var id = new LiveDiagnosticUpdateArgsId(stateSet.Analyzer, key, (int)type, stateSet.ErrorSourceName); + raiseEvents(DiagnosticsUpdatedArgs.DiagnosticsCreated(id, Workspace, solution.Solution, solution.ProjectId, solution.DocumentId, diagnostics)); + } + + private void RaiseDiagnosticsRemoved( + StateType type, object key, StateSet stateSet, SolutionArgument solution) + { + RaiseDiagnosticsRemoved(type, key, stateSet, solution, Owner.RaiseDiagnosticsUpdated); + } + + private void RaiseDiagnosticsRemoved( + StateType type, object key, StateSet stateSet, SolutionArgument solution, Action raiseEvents) + { + // get right arg id for the given analyzer + var id = new LiveDiagnosticUpdateArgsId(stateSet.Analyzer, key, (int)type, stateSet.ErrorSourceName); + raiseEvents(DiagnosticsUpdatedArgs.DiagnosticsRemoved(id, Workspace, solution.Solution, solution.ProjectId, solution.DocumentId)); + } + + private void RaiseDocumentDiagnosticsRemoved(Document document, IEnumerable stateSets, bool includeProjectState, Action raiseEvents) + { + foreach (var stateSet in stateSets) + { + for (var stateType = 0; stateType < s_stateTypeCount; stateType++) + { + if (!includeProjectState && stateType == (int)StateType.Project) + { + // don't re-set project state type + continue; + } + + // raise diagnostic updated event + RaiseDiagnosticsRemoved((StateType)stateType, document.Id, stateSet, new SolutionArgument(document), raiseEvents); + } + } + } + + private void RaiseProjectDiagnosticsRemoved(Project project, IEnumerable stateSets, Action raiseEvents) + { + foreach (var stateSet in stateSets) + { + RaiseDiagnosticsRemoved(StateType.Project, project.Id, stateSet, new SolutionArgument(project), raiseEvents); + } + } + + private ImmutableArray UpdateDocumentDiagnostics( + AnalysisData existingData, ImmutableArray range, ImmutableArray memberDiagnostics, + SyntaxTree tree, SyntaxNode member, int memberId) + { + // get old span + var oldSpan = range[memberId]; + + // get old diagnostics + var diagnostics = existingData.Items; + + // check quick exit cases + if (diagnostics.Length == 0 && memberDiagnostics.Length == 0) + { + return diagnostics; + } + + // simple case + if (diagnostics.Length == 0 && memberDiagnostics.Length > 0) + { + return memberDiagnostics; + } + + // regular case + var result = new List(); + + // update member location + Contract.Requires(member.FullSpan.Start == oldSpan.Start); + var delta = member.FullSpan.End - oldSpan.End; + + var replaced = false; + foreach (var diagnostic in diagnostics) + { + if (diagnostic.TextSpan.Start < oldSpan.Start) + { + result.Add(diagnostic); + continue; + } + + if (!replaced) + { + result.AddRange(memberDiagnostics); + replaced = true; + } + + if (oldSpan.End <= diagnostic.TextSpan.Start) + { + result.Add(UpdatePosition(diagnostic, tree, delta)); + continue; + } + } + + // if it haven't replaced, replace it now + if (!replaced) + { + result.AddRange(memberDiagnostics); + replaced = true; + } + + return result.ToImmutableArray(); + } + + private DiagnosticData UpdatePosition(DiagnosticData diagnostic, SyntaxTree tree, int delta) + { + Debug.Assert(diagnostic.AdditionalLocations == null || diagnostic.AdditionalLocations.Count == 0); + var newDiagnosticLocation = UpdateDiagnosticLocation(diagnostic.DataLocation, tree, delta); + + return new DiagnosticData( + diagnostic.Id, + diagnostic.Category, + diagnostic.Message, + diagnostic.ENUMessageForBingSearch, + diagnostic.Severity, + diagnostic.DefaultSeverity, + diagnostic.IsEnabledByDefault, + diagnostic.WarningLevel, + diagnostic.CustomTags, + diagnostic.Properties, + diagnostic.Workspace, + diagnostic.ProjectId, + newDiagnosticLocation, + additionalLocations: diagnostic.AdditionalLocations, + description: diagnostic.Description, + helpLink: diagnostic.HelpLink, + isSuppressed: diagnostic.IsSuppressed); + } + + private DiagnosticDataLocation UpdateDiagnosticLocation( + DiagnosticDataLocation dataLocation, SyntaxTree tree, int delta) + { + var span = dataLocation.SourceSpan.Value; + var start = Math.Min(Math.Max(span.Start + delta, 0), tree.Length); + var newSpan = new TextSpan(start, start >= tree.Length ? 0 : span.Length); + + var mappedLineInfo = tree.GetMappedLineSpan(newSpan); + var originalLineInfo = tree.GetLineSpan(newSpan); + + return new DiagnosticDataLocation(dataLocation.DocumentId, newSpan, + originalFilePath: originalLineInfo.Path, + originalStartLine: originalLineInfo.StartLinePosition.Line, + originalStartColumn: originalLineInfo.StartLinePosition.Character, + originalEndLine: originalLineInfo.EndLinePosition.Line, + originalEndColumn: originalLineInfo.EndLinePosition.Character, + mappedFilePath: mappedLineInfo.GetMappedFilePathIfExist(), + mappedStartLine: mappedLineInfo.StartLinePosition.Line, + mappedStartColumn: mappedLineInfo.StartLinePosition.Character, + mappedEndLine: mappedLineInfo.EndLinePosition.Line, + mappedEndColumn: mappedLineInfo.EndLinePosition.Character); + } + + private static IEnumerable GetDiagnosticData(Document document, SyntaxTree tree, TextSpan? span, IEnumerable diagnostics) + { + return diagnostics != null ? diagnostics.Where(dx => ShouldIncludeDiagnostic(dx, tree, span)).Select(d => DiagnosticData.Create(document, d)) : null; + } + + private static bool ShouldIncludeDiagnostic(Diagnostic diagnostic, SyntaxTree tree, TextSpan? span) + { + if (diagnostic == null) + { + return false; + } + + if (diagnostic.Location == null || diagnostic.Location == Location.None) + { + return false; + } + + if (diagnostic.Location.SourceTree != tree) + { + return false; + } + + if (span == null) + { + return true; + } + + return span.Value.Contains(diagnostic.Location.SourceSpan); + } + + private static IEnumerable GetDiagnosticData(Project project, IEnumerable diagnostics) + { + if (diagnostics == null) + { + yield break; + } + + foreach (var diagnostic in diagnostics) + { + if (diagnostic.Location == null || diagnostic.Location == Location.None) + { + yield return DiagnosticData.Create(project, diagnostic); + continue; + } + + var document = project.GetDocument(diagnostic.Location.SourceTree); + if (document == null) + { + continue; + } + + yield return DiagnosticData.Create(document, diagnostic); + } + } + + private static async Task> GetSyntaxDiagnosticsAsync(DiagnosticAnalyzerDriver userDiagnosticDriver, DiagnosticAnalyzer analyzer) + { + using (Logger.LogBlock(FunctionId.Diagnostics_SyntaxDiagnostic, GetSyntaxLogMessage, userDiagnosticDriver.Document, userDiagnosticDriver.Span, analyzer, userDiagnosticDriver.CancellationToken)) + { + try + { + Contract.ThrowIfNull(analyzer); + + var tree = await userDiagnosticDriver.Document.GetSyntaxTreeAsync(userDiagnosticDriver.CancellationToken).ConfigureAwait(false); + var diagnostics = await userDiagnosticDriver.GetSyntaxDiagnosticsAsync(analyzer).ConfigureAwait(false); + return GetDiagnosticData(userDiagnosticDriver.Document, tree, userDiagnosticDriver.Span, diagnostics); + } + catch (Exception e) when (FatalError.ReportUnlessCanceled(e)) + { + throw ExceptionUtilities.Unreachable; + } + } + } + + private static async Task> GetSemanticDiagnosticsAsync(DiagnosticAnalyzerDriver userDiagnosticDriver, DiagnosticAnalyzer analyzer) + { + using (Logger.LogBlock(FunctionId.Diagnostics_SemanticDiagnostic, GetSemanticLogMessage, userDiagnosticDriver.Document, userDiagnosticDriver.Span, analyzer, userDiagnosticDriver.CancellationToken)) + { + try + { + Contract.ThrowIfNull(analyzer); + + var tree = await userDiagnosticDriver.Document.GetSyntaxTreeAsync(userDiagnosticDriver.CancellationToken).ConfigureAwait(false); + var diagnostics = await userDiagnosticDriver.GetSemanticDiagnosticsAsync(analyzer).ConfigureAwait(false); + return GetDiagnosticData(userDiagnosticDriver.Document, tree, userDiagnosticDriver.Span, diagnostics); + } + catch (Exception e) when (FatalError.ReportUnlessCanceled(e)) + { + throw ExceptionUtilities.Unreachable; + } + } + } + + private static async Task> GetProjectDiagnosticsAsync(DiagnosticAnalyzerDriver userDiagnosticDriver, DiagnosticAnalyzer analyzer) + { + using (Logger.LogBlock(FunctionId.Diagnostics_ProjectDiagnostic, GetProjectLogMessage, userDiagnosticDriver.Project, analyzer, userDiagnosticDriver.CancellationToken)) + { + try + { + Contract.ThrowIfNull(analyzer); + + var diagnostics = await userDiagnosticDriver.GetProjectDiagnosticsAsync(analyzer).ConfigureAwait(false); + return GetDiagnosticData(userDiagnosticDriver.Project, diagnostics); + } + catch (Exception e) when (FatalError.ReportUnlessCanceled(e)) + { + throw ExceptionUtilities.Unreachable; + } + } + } + + private async Task ClearExistingDiagnostics(Document document, StateSet stateSet, StateType type, CancellationToken cancellationToken) + { + var state = stateSet.GetState(type); + var existingData = await state.TryGetExistingDataAsync(document, cancellationToken).ConfigureAwait(false); + if (existingData?.Items.Length > 0) + { + // remove saved info + state.Remove(document.Id); + + // raise diagnostic updated event + RaiseDiagnosticsRemoved(type, document.Id, stateSet, new SolutionArgument(document)); + } + } + + private async Task ClearExistingDiagnostics(Project project, StateSet stateSet, CancellationToken cancellationToken) + { + var state = stateSet.GetState(StateType.Project); + var existingData = await state.TryGetExistingDataAsync(project, cancellationToken).ConfigureAwait(false); + if (existingData?.Items.Length > 0) + { + // remove saved cache + state.Remove(project.Id); + + // raise diagnostic updated event + RaiseDiagnosticsRemoved(StateType.Project, project.Id, stateSet, new SolutionArgument(project)); + } + } + + private static string GetSyntaxLogMessage(Document document, TextSpan? span, DiagnosticAnalyzer analyzer) + { + return string.Format("syntax: {0}, {1}, {2}", document.FilePath ?? document.Name, span.HasValue ? span.Value.ToString() : "Full", analyzer.ToString()); + } + + private static string GetSemanticLogMessage(Document document, TextSpan? span, DiagnosticAnalyzer analyzer) + { + return string.Format("semantic: {0}, {1}, {2}", document.FilePath ?? document.Name, span.HasValue ? span.Value.ToString() : "Full", analyzer.ToString()); + } + + private static string GetProjectLogMessage(Project project, DiagnosticAnalyzer analyzer) + { + return string.Format("project: {0}, {1}", project.FilePath ?? project.Name, analyzer.ToString()); + } + + private static string GetResetLogMessage(Document document) + { + return string.Format("document reset: {0}", document.FilePath ?? document.Name); + } + + private static string GetOpenLogMessage(Document document) + { + return string.Format("document open: {0}", document.FilePath ?? document.Name); + } + + private static string GetRemoveLogMessage(DocumentId id) + { + return string.Format("document remove: {0}", id.ToString()); + } + + private static string GetRemoveLogMessage(ProjectId id) + { + return string.Format("project remove: {0}", id.ToString()); + } + + public override Task NewSolutionSnapshotAsync(Solution newSolution, CancellationToken cancellationToken) + { + return SpecializedTasks.EmptyTask; + } + } +} diff --git a/src/Features/Core/Portable/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs b/src/Features/Core/Portable/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs index 6000726900224..c68e6eb47d8d9 100644 --- a/src/Features/Core/Portable/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs +++ b/src/Features/Core/Portable/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs @@ -10,7 +10,6 @@ using Microsoft.CodeAnalysis.Internal.Log; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Shared.Options; -using Microsoft.CodeAnalysis.SolutionCrawler; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Diagnostics.EngineV2 @@ -18,12 +17,12 @@ namespace Microsoft.CodeAnalysis.Diagnostics.EngineV2 // TODO: make it to use cache internal partial class DiagnosticIncrementalAnalyzer : BaseDiagnosticIncrementalAnalyzer { - public override Task AnalyzeSyntaxAsync(Document document, InvocationReasons reasons, CancellationToken cancellationToken) + public override Task AnalyzeSyntaxAsync(Document document, CancellationToken cancellationToken) { return AnalyzeDocumentForKindAsync(document, AnalysisKind.Syntax, cancellationToken); } - public override Task AnalyzeDocumentAsync(Document document, SyntaxNode bodyOpt, InvocationReasons reasons, CancellationToken cancellationToken) + public override Task AnalyzeDocumentAsync(Document document, SyntaxNode bodyOpt, CancellationToken cancellationToken) { return AnalyzeDocumentForKindAsync(document, AnalysisKind.Semantic, cancellationToken); } @@ -66,7 +65,7 @@ private async Task AnalyzeDocumentForKindAsync(Document document, AnalysisKind k } } - public override async Task AnalyzeProjectAsync(Project project, bool semanticsChanged, InvocationReasons reasons, CancellationToken cancellationToken) + public override async Task AnalyzeProjectAsync(Project project, bool semanticsChanged, CancellationToken cancellationToken) { try { diff --git a/src/Features/Core/Portable/IncrementalCaches/SymbolTreeInfoIncrementalAnalyzerProvider.cs b/src/Features/Core/Portable/IncrementalCaches/SymbolTreeInfoIncrementalAnalyzerProvider.cs index 48c6609510eb7..51fa02af65159 100644 --- a/src/Features/Core/Portable/IncrementalCaches/SymbolTreeInfoIncrementalAnalyzerProvider.cs +++ b/src/Features/Core/Portable/IncrementalCaches/SymbolTreeInfoIncrementalAnalyzerProvider.cs @@ -190,7 +190,7 @@ public IncrementalAnalyzer( _metadataPathToInfo = metadataPathToInfo; } - public override Task AnalyzeDocumentAsync(Document document, SyntaxNode bodyOpt, InvocationReasons reasons, CancellationToken cancellationToken) + public override Task AnalyzeDocumentAsync(Document document, SyntaxNode bodyOpt, CancellationToken cancellationToken) { if (!document.SupportsSyntaxTree) { @@ -208,7 +208,7 @@ public override Task AnalyzeDocumentAsync(Document document, SyntaxNode bodyOpt, return UpdateSymbolTreeInfoAsync(document.Project, cancellationToken); } - public override Task AnalyzeProjectAsync(Project project, bool semanticsChanged, InvocationReasons reasons, CancellationToken cancellationToken) + public override Task AnalyzeProjectAsync(Project project, bool semanticsChanged, CancellationToken cancellationToken) { return UpdateSymbolTreeInfoAsync(project, cancellationToken); } diff --git a/src/Features/Core/Portable/IncrementalCaches/SyntaxTreeInfoIncrementalAnalyzerProvider.cs b/src/Features/Core/Portable/IncrementalCaches/SyntaxTreeInfoIncrementalAnalyzerProvider.cs index b88d84c26cada..79a23ff9a3197 100644 --- a/src/Features/Core/Portable/IncrementalCaches/SyntaxTreeInfoIncrementalAnalyzerProvider.cs +++ b/src/Features/Core/Portable/IncrementalCaches/SyntaxTreeInfoIncrementalAnalyzerProvider.cs @@ -18,7 +18,7 @@ public IIncrementalAnalyzer CreateIncrementalAnalyzer(Workspace workspace) private class IncrementalAnalyzer : IncrementalAnalyzerBase { - public override Task AnalyzeSyntaxAsync(Document document, InvocationReasons reasons, CancellationToken cancellationToken) + public override Task AnalyzeSyntaxAsync(Document document, CancellationToken cancellationToken) { return SyntaxTreeInfo.PrecalculateAsync(document, cancellationToken); } diff --git a/src/Features/Core/Portable/Notification/SemanticChangeNotificationService.cs b/src/Features/Core/Portable/Notification/SemanticChangeNotificationService.cs index 1306196bc3a74..b854538cb7537 100644 --- a/src/Features/Core/Portable/Notification/SemanticChangeNotificationService.cs +++ b/src/Features/Core/Portable/Notification/SemanticChangeNotificationService.cs @@ -72,7 +72,7 @@ public bool NeedsReanalysisOnOptionChanged(object sender, OptionChangedEventArgs return false; } - public async Task AnalyzeDocumentAsync(Document document, SyntaxNode bodyOpt, InvocationReasons reasons, CancellationToken cancellationToken) + public async Task AnalyzeDocumentAsync(Document document, SyntaxNode bodyOpt, CancellationToken cancellationToken) { // method body change if (bodyOpt != null || !document.IsOpen()) @@ -106,12 +106,12 @@ public Task NewSolutionSnapshotAsync(Solution solution, CancellationToken cancel return SpecializedTasks.EmptyTask; } - public Task AnalyzeSyntaxAsync(Document document, InvocationReasons reasons, CancellationToken cancellationToken) + public Task AnalyzeSyntaxAsync(Document document, CancellationToken cancellationToken) { return SpecializedTasks.EmptyTask; } - public Task AnalyzeProjectAsync(Project project, bool semanticsChanged, InvocationReasons reasons, CancellationToken cancellationToken) + public Task AnalyzeProjectAsync(Project project, bool semanticsChanged, CancellationToken cancellationToken) { return SpecializedTasks.EmptyTask; } diff --git a/src/Features/Core/Portable/SolutionCrawler/AggregateIncrementalAnalyzer.cs b/src/Features/Core/Portable/SolutionCrawler/AggregateIncrementalAnalyzer.cs index 16adc1bcffb75..d2ab985b629c9 100644 --- a/src/Features/Core/Portable/SolutionCrawler/AggregateIncrementalAnalyzer.cs +++ b/src/Features/Core/Portable/SolutionCrawler/AggregateIncrementalAnalyzer.cs @@ -64,30 +64,30 @@ public bool NeedsReanalysisOnOptionChanged(object sender, OptionChangedEventArgs return false; } - public async Task AnalyzeSyntaxAsync(Document document, InvocationReasons reasons, CancellationToken cancellationToken) + public async Task AnalyzeSyntaxAsync(Document document, CancellationToken cancellationToken) { IIncrementalAnalyzer analyzer; if (TryGetAnalyzer(document.Project, out analyzer)) { - await analyzer.AnalyzeSyntaxAsync(document, reasons, cancellationToken).ConfigureAwait(false); + await analyzer.AnalyzeSyntaxAsync(document, cancellationToken).ConfigureAwait(false); } } - public async Task AnalyzeDocumentAsync(Document document, SyntaxNode bodyOpt, InvocationReasons reasons, CancellationToken cancellationToken) + public async Task AnalyzeDocumentAsync(Document document, SyntaxNode bodyOpt, CancellationToken cancellationToken) { IIncrementalAnalyzer analyzer; if (TryGetAnalyzer(document.Project, out analyzer)) { - await analyzer.AnalyzeDocumentAsync(document, bodyOpt, reasons, cancellationToken).ConfigureAwait(false); + await analyzer.AnalyzeDocumentAsync(document, bodyOpt, cancellationToken).ConfigureAwait(false); } } - public async Task AnalyzeProjectAsync(Project project, bool semanticsChanged, InvocationReasons reasons, CancellationToken cancellationToken) + public async Task AnalyzeProjectAsync(Project project, bool semanticsChanged, CancellationToken cancellationToken) { IIncrementalAnalyzer analyzer; if (TryGetAnalyzer(project, out analyzer)) { - await analyzer.AnalyzeProjectAsync(project, semanticsChanged, reasons, cancellationToken).ConfigureAwait(false); + await analyzer.AnalyzeProjectAsync(project, semanticsChanged, cancellationToken).ConfigureAwait(false); } } diff --git a/src/Features/Core/Portable/SolutionCrawler/IncrementalAnalyzerBase.cs b/src/Features/Core/Portable/SolutionCrawler/IncrementalAnalyzerBase.cs index 470d0beffad5b..9e999bf07ec26 100644 --- a/src/Features/Core/Portable/SolutionCrawler/IncrementalAnalyzerBase.cs +++ b/src/Features/Core/Portable/SolutionCrawler/IncrementalAnalyzerBase.cs @@ -39,17 +39,17 @@ public bool NeedsReanalysisOnOptionChanged(object sender, OptionChangedEventArgs return false; } - public virtual Task AnalyzeSyntaxAsync(Document document, InvocationReasons reasons, CancellationToken cancellationToken) + public virtual Task AnalyzeSyntaxAsync(Document document, CancellationToken cancellationToken) { return SpecializedTasks.EmptyTask; } - public virtual Task AnalyzeDocumentAsync(Document document, SyntaxNode bodyOpt, InvocationReasons reasons, CancellationToken cancellationToken) + public virtual Task AnalyzeDocumentAsync(Document document, SyntaxNode bodyOpt, CancellationToken cancellationToken) { return SpecializedTasks.EmptyTask; } - public virtual Task AnalyzeProjectAsync(Project project, bool semanticsChanged, InvocationReasons reasons, CancellationToken cancellationToken) + public virtual Task AnalyzeProjectAsync(Project project, bool semanticsChanged, CancellationToken cancellationToken) { return SpecializedTasks.EmptyTask; } diff --git a/src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.IncrementalAnalyzerProcessor.cs b/src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.IncrementalAnalyzerProcessor.cs index 88ed250fe7068..2410b799bfc83 100644 --- a/src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.IncrementalAnalyzerProcessor.cs +++ b/src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.IncrementalAnalyzerProcessor.cs @@ -166,15 +166,14 @@ private static async Task ProcessDocumentAnalyzersAsync( Document document, ImmutableArray analyzers, WorkItem workItem, CancellationToken cancellationToken) { // process all analyzers for each categories in this order - syntax, body, document - var reasons = workItem.InvocationReasons; - if (workItem.MustRefresh || reasons.Contains(PredefinedInvocationReasons.SyntaxChanged)) + if (workItem.MustRefresh || workItem.InvocationReasons.Contains(PredefinedInvocationReasons.SyntaxChanged)) { - await RunAnalyzersAsync(analyzers, document, (a, d, c) => a.AnalyzeSyntaxAsync(d, reasons, c), cancellationToken).ConfigureAwait(false); + await RunAnalyzersAsync(analyzers, document, (a, d, c) => a.AnalyzeSyntaxAsync(d, c), cancellationToken).ConfigureAwait(false); } - if (workItem.MustRefresh || reasons.Contains(PredefinedInvocationReasons.SemanticChanged)) + if (workItem.MustRefresh || workItem.InvocationReasons.Contains(PredefinedInvocationReasons.SemanticChanged)) { - await RunAnalyzersAsync(analyzers, document, (a, d, c) => a.AnalyzeDocumentAsync(d, null, reasons, c), cancellationToken).ConfigureAwait(false); + await RunAnalyzersAsync(analyzers, document, (a, d, c) => a.AnalyzeDocumentAsync(d, null, c), cancellationToken).ConfigureAwait(false); } else { @@ -208,11 +207,10 @@ private static async Task RunBodyAnalyzersAsync(ImmutableArray d.GetSyntaxRootAsync(c), cancellationToken).ConfigureAwait(false); var syntaxFactsService = document.Project.LanguageServices.GetService(); - var reasons = workItem.InvocationReasons; if (root == null || syntaxFactsService == null) { // as a fallback mechanism, if we can't run one method body due to some missing service, run whole document analyzer. - await RunAnalyzersAsync(analyzers, document, (a, d, c) => a.AnalyzeDocumentAsync(d, null, reasons, c), cancellationToken).ConfigureAwait(false); + await RunAnalyzersAsync(analyzers, document, (a, d, c) => a.AnalyzeDocumentAsync(d, null, c), cancellationToken).ConfigureAwait(false); return; } @@ -223,12 +221,12 @@ private static async Task RunBodyAnalyzersAsync(ImmutableArray a.AnalyzeDocumentAsync(d, null, reasons, c), cancellationToken).ConfigureAwait(false); + await RunAnalyzersAsync(analyzers, document, (a, d, c) => a.AnalyzeDocumentAsync(d, null, c), cancellationToken).ConfigureAwait(false); return; } // re-run just the body - await RunAnalyzersAsync(analyzers, document, (a, d, c) => a.AnalyzeDocumentAsync(d, activeMember, reasons, c), cancellationToken).ConfigureAwait(false); + await RunAnalyzersAsync(analyzers, document, (a, d, c) => a.AnalyzeDocumentAsync(d, activeMember, c), cancellationToken).ConfigureAwait(false); } catch (Exception e) when (FatalError.ReportUnlessCanceled(e)) { diff --git a/src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.LowPriorityProcessor.cs b/src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.LowPriorityProcessor.cs index ebe8c1f13f8a9..8b9c2ff84436d 100644 --- a/src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.LowPriorityProcessor.cs +++ b/src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.LowPriorityProcessor.cs @@ -145,13 +145,12 @@ private async Task ProcessProjectAsync(ImmutableArray anal var project = processingSolution.GetProject(projectId); if (project != null) { - var reasons = workItem.InvocationReasons; - var semanticsChanged = reasons.Contains(PredefinedInvocationReasons.SemanticChanged) || - reasons.Contains(PredefinedInvocationReasons.SolutionRemoved); + var semanticsChanged = workItem.InvocationReasons.Contains(PredefinedInvocationReasons.SemanticChanged) || + workItem.InvocationReasons.Contains(PredefinedInvocationReasons.SolutionRemoved); using (Processor.EnableCaching(project.Id)) { - await RunAnalyzersAsync(analyzers, project, (a, p, c) => a.AnalyzeProjectAsync(p, semanticsChanged, reasons, c), cancellationToken).ConfigureAwait(false); + await RunAnalyzersAsync(analyzers, project, (a, p, c) => a.AnalyzeProjectAsync(p, semanticsChanged, c), cancellationToken).ConfigureAwait(false); } } else diff --git a/src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.NormalPriorityProcessor.cs b/src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.NormalPriorityProcessor.cs index 89ae3d5e1be75..61ab301897089 100644 --- a/src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.NormalPriorityProcessor.cs +++ b/src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.NormalPriorityProcessor.cs @@ -472,16 +472,15 @@ private async Task ProcessReanalyzeDocumentAsync(WorkItem workItem, Document doc await RunAnalyzersAsync(reanalyzers, document, (a, d, c) => a.DocumentResetAsync(d, c), cancellationToken).ConfigureAwait(false); // no request to re-run syntax change analysis. run it here - var reasons = workItem.InvocationReasons; - if (!reasons.Contains(PredefinedInvocationReasons.SyntaxChanged)) + if (!workItem.InvocationReasons.Contains(PredefinedInvocationReasons.SyntaxChanged)) { - await RunAnalyzersAsync(reanalyzers, document, (a, d, c) => a.AnalyzeSyntaxAsync(d, reasons, c), cancellationToken).ConfigureAwait(false); + await RunAnalyzersAsync(reanalyzers, document, (a, d, c) => a.AnalyzeSyntaxAsync(d, c), cancellationToken).ConfigureAwait(false); } // no request to re-run semantic change analysis. run it here if (!workItem.InvocationReasons.Contains(PredefinedInvocationReasons.SemanticChanged)) { - await RunAnalyzersAsync(reanalyzers, document, (a, d, c) => a.AnalyzeDocumentAsync(d, null, reasons, c), cancellationToken).ConfigureAwait(false); + await RunAnalyzersAsync(reanalyzers, document, (a, d, c) => a.AnalyzeDocumentAsync(d, null, c), cancellationToken).ConfigureAwait(false); } } catch (Exception e) when (FatalError.ReportUnlessCanceled(e)) diff --git a/src/VisualStudio/Core/Def/Implementation/DesignerAttribute/AbstractDesignerAttributeIncrementalAnalyzer.cs b/src/VisualStudio/Core/Def/Implementation/DesignerAttribute/AbstractDesignerAttributeIncrementalAnalyzer.cs index c00a57b264224..c3d73b74c31ff 100644 --- a/src/VisualStudio/Core/Def/Implementation/DesignerAttribute/AbstractDesignerAttributeIncrementalAnalyzer.cs +++ b/src/VisualStudio/Core/Def/Implementation/DesignerAttribute/AbstractDesignerAttributeIncrementalAnalyzer.cs @@ -11,7 +11,6 @@ using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Shared.TestHooks; -using Microsoft.CodeAnalysis.SolutionCrawler; using Microsoft.CodeAnalysis.Versions; using Microsoft.VisualStudio.Designer.Interfaces; using Microsoft.VisualStudio.LanguageServices.Implementation.ProjectSystem; @@ -64,7 +63,7 @@ public bool NeedsReanalysisOnOptionChanged(object sender, OptionChangedEventArgs return false; } - public async System.Threading.Tasks.Task AnalyzeDocumentAsync(Document document, SyntaxNode bodyOpt, InvocationReasons reasons, CancellationToken cancellationToken) + public async System.Threading.Tasks.Task AnalyzeDocumentAsync(Document document, SyntaxNode bodyOpt, CancellationToken cancellationToken) { Contract.ThrowIfFalse(document.IsFromPrimaryBranch()); @@ -311,12 +310,12 @@ public System.Threading.Tasks.Task DocumentCloseAsync(Document document, Cancell return SpecializedTasks.EmptyTask; } - public System.Threading.Tasks.Task AnalyzeSyntaxAsync(Document document, InvocationReasons reasons, CancellationToken cancellationToken) + public System.Threading.Tasks.Task AnalyzeSyntaxAsync(Document document, CancellationToken cancellationToken) { return SpecializedTasks.EmptyTask; } - public System.Threading.Tasks.Task AnalyzeProjectAsync(Project project, bool semanticsChanged, InvocationReasons reasons, CancellationToken cancellationToken) + public System.Threading.Tasks.Task AnalyzeProjectAsync(Project project, bool semanticsChanged, CancellationToken cancellationToken) { return SpecializedTasks.EmptyTask; } diff --git a/src/VisualStudio/Core/Def/Implementation/SolutionSize/SolutionSizeTracker.cs b/src/VisualStudio/Core/Def/Implementation/SolutionSize/SolutionSizeTracker.cs index 1acbe0d662ea8..dfff1d26b8002 100644 --- a/src/VisualStudio/Core/Def/Implementation/SolutionSize/SolutionSizeTracker.cs +++ b/src/VisualStudio/Core/Def/Implementation/SolutionSize/SolutionSizeTracker.cs @@ -65,7 +65,7 @@ public Task NewSolutionSnapshotAsync(Solution solution, CancellationToken cancel return SpecializedTasks.EmptyTask; } - public async Task AnalyzeSyntaxAsync(Document document, InvocationReasons reasons, CancellationToken cancellationToken) + public async Task AnalyzeSyntaxAsync(Document document, CancellationToken cancellationToken) { if (!document.SupportsSyntaxTree) { @@ -111,7 +111,7 @@ public void RemoveDocument(DocumentId documentId) } #region Not Used - public Task AnalyzeDocumentAsync(Document document, SyntaxNode bodyOpt, InvocationReasons reasons, CancellationToken cancellationToken) + public Task AnalyzeDocumentAsync(Document document, SyntaxNode bodyOpt, CancellationToken cancellationToken) { return SpecializedTasks.EmptyTask; } @@ -136,7 +136,7 @@ public bool NeedsReanalysisOnOptionChanged(object sender, OptionChangedEventArgs return false; } - public Task AnalyzeProjectAsync(Project project, bool semanticsChanged, InvocationReasons reasons, CancellationToken cancellationToken) + public Task AnalyzeProjectAsync(Project project, bool semanticsChanged, CancellationToken cancellationToken) { return SpecializedTasks.EmptyTask; } diff --git a/src/Workspaces/Core/Portable/SolutionCrawler/IIncrementalAnalyzer.cs b/src/Workspaces/Core/Portable/SolutionCrawler/IIncrementalAnalyzer.cs index 5458362b67715..87880876e6cfa 100644 --- a/src/Workspaces/Core/Portable/SolutionCrawler/IIncrementalAnalyzer.cs +++ b/src/Workspaces/Core/Portable/SolutionCrawler/IIncrementalAnalyzer.cs @@ -18,9 +18,9 @@ internal interface IIncrementalAnalyzer /// Task DocumentResetAsync(Document document, CancellationToken cancellationToken); - Task AnalyzeSyntaxAsync(Document document, InvocationReasons reasons, CancellationToken cancellationToken); - Task AnalyzeDocumentAsync(Document document, SyntaxNode bodyOpt, InvocationReasons reasons, CancellationToken cancellationToken); - Task AnalyzeProjectAsync(Project project, bool semanticsChanged, InvocationReasons reasons, CancellationToken cancellationToken); + Task AnalyzeSyntaxAsync(Document document, CancellationToken cancellationToken); + Task AnalyzeDocumentAsync(Document document, SyntaxNode bodyOpt, CancellationToken cancellationToken); + Task AnalyzeProjectAsync(Project project, bool semanticsChanged, CancellationToken cancellationToken); void RemoveDocument(DocumentId documentId); void RemoveProject(ProjectId projectId); From 50c22bcccb5c0fd99872fe239754adee33115162 Mon Sep 17 00:00:00 2001 From: Artur Spychaj Date: Fri, 17 Jun 2016 10:15:41 -0700 Subject: [PATCH 23/36] Revert "Make use of InvocationReason in tests and analyzers" This reverts commit 1b6a83f9dcd6d768414f2cca62cc8ff8aeebb4e8. --- .../TodoComment/TodoCommentTests.cs | 2 +- .../DiagnosticAnalyzerServiceTests.cs | 13 +- .../SolutionCrawler/WorkCoordinatorTests.cs | 6 +- .../Diagnostics/DiagnosticServiceTests.vb | 3 +- .../TodoComment/TodoCommentTests.vb | 2 +- .../EngineV1/DiagnosticIncrementalAnalyzer.cs | 1151 ----------------- src/Features/Core/Portable/Features.csproj | 3 + .../SolutionCrawler/InvocationReasons.cs | 0 .../InvocationReasons_Constants.cs | 0 .../PredefinedInvocationReasons.cs | 0 ...WorkCoordinator.NormalPriorityProcessor.cs | 2 +- .../CSharp/Test/CSharpVisualStudioTest.csproj | 3 +- .../PersistentStorage/SolutionSizeTests.cs | 5 +- .../SolutionSize/SolutionSizeTracker.cs | 1 - .../CodeModel/CodeModelIncrementalAnalyzer.cs | 6 +- .../DefaultDiagnosticUpdateSourceTests.vb | 23 +- .../Core/Portable/Workspaces.csproj | 3 - 17 files changed, 34 insertions(+), 1189 deletions(-) delete mode 100644 src/Features/Core/Portable/Diagnostics/EngineV1/DiagnosticIncrementalAnalyzer.cs rename src/{Workspaces => Features}/Core/Portable/SolutionCrawler/InvocationReasons.cs (100%) rename src/{Workspaces => Features}/Core/Portable/SolutionCrawler/InvocationReasons_Constants.cs (100%) rename src/{Workspaces => Features}/Core/Portable/SolutionCrawler/PredefinedInvocationReasons.cs (100%) diff --git a/src/EditorFeatures/CSharpTest/TodoComment/TodoCommentTests.cs b/src/EditorFeatures/CSharpTest/TodoComment/TodoCommentTests.cs index 051338d4ee915..eeb9a8f3683b0 100644 --- a/src/EditorFeatures/CSharpTest/TodoComment/TodoCommentTests.cs +++ b/src/EditorFeatures/CSharpTest/TodoComment/TodoCommentTests.cs @@ -176,7 +176,7 @@ private static async Task TestAsync(string codeWithMarker) var document = workspace.Documents.First(); var documentId = document.Id; var reasons = new InvocationReasons(PredefinedInvocationReasons.DocumentAdded); - await worker.AnalyzeSyntaxAsync(workspace.CurrentSolution.GetDocument(documentId), InvocationReasons.Empty, CancellationToken.None); + await worker.AnalyzeSyntaxAsync(workspace.CurrentSolution.GetDocument(documentId), CancellationToken.None); var todoLists = worker.GetItems_TestingOnly(documentId); var expectedLists = document.SelectedSpans; diff --git a/src/EditorFeatures/Test/Diagnostics/DiagnosticAnalyzerServiceTests.cs b/src/EditorFeatures/Test/Diagnostics/DiagnosticAnalyzerServiceTests.cs index 507ca5528bb1e..7b2659674e731 100644 --- a/src/EditorFeatures/Test/Diagnostics/DiagnosticAnalyzerServiceTests.cs +++ b/src/EditorFeatures/Test/Diagnostics/DiagnosticAnalyzerServiceTests.cs @@ -6,7 +6,6 @@ using System.Threading.Tasks; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Shared.TestHooks; -using Microsoft.CodeAnalysis.SolutionCrawler; using Microsoft.CodeAnalysis.Text; using Roslyn.Test.Utilities; using Roslyn.Utilities; @@ -32,9 +31,9 @@ public async Task TestHasSuccessfullyLoadedBeingFalse() service.DiagnosticsUpdated += (s, a) => Assert.Empty(a.Diagnostics); // now call each analyze method. none of them should run. - await analyzer.AnalyzeSyntaxAsync(document, InvocationReasons.Empty, CancellationToken.None).ConfigureAwait(false); - await analyzer.AnalyzeDocumentAsync(document, bodyOpt: null, reasons: InvocationReasons.Empty, cancellationToken: CancellationToken.None).ConfigureAwait(false); - await analyzer.AnalyzeProjectAsync(document.Project, semanticsChanged: true, reasons: InvocationReasons.Empty, cancellationToken: CancellationToken.None).ConfigureAwait(false); + await analyzer.AnalyzeSyntaxAsync(document, CancellationToken.None).ConfigureAwait(false); + await analyzer.AnalyzeDocumentAsync(document, bodyOpt: null, cancellationToken: CancellationToken.None).ConfigureAwait(false); + await analyzer.AnalyzeProjectAsync(document.Project, semanticsChanged: true, cancellationToken: CancellationToken.None).ConfigureAwait(false); // wait for all events to raised await listener.CreateWaitTask().ConfigureAwait(false); @@ -75,9 +74,9 @@ public async Task TestHasSuccessfullyLoadedBeingFalseWhenFileOpened() }; // now call each analyze method. none of them should run. - await analyzer.AnalyzeSyntaxAsync(document, InvocationReasons.Empty, CancellationToken.None).ConfigureAwait(false); - await analyzer.AnalyzeDocumentAsync(document, bodyOpt: null, reasons: InvocationReasons.Empty, cancellationToken: CancellationToken.None).ConfigureAwait(false); - await analyzer.AnalyzeProjectAsync(document.Project, semanticsChanged: true, reasons: InvocationReasons.Empty, cancellationToken: CancellationToken.None).ConfigureAwait(false); + await analyzer.AnalyzeSyntaxAsync(document, CancellationToken.None).ConfigureAwait(false); + await analyzer.AnalyzeDocumentAsync(document, bodyOpt: null, cancellationToken: CancellationToken.None).ConfigureAwait(false); + await analyzer.AnalyzeProjectAsync(document.Project, semanticsChanged: true, cancellationToken: CancellationToken.None).ConfigureAwait(false); // wait for all events to raised await listener.CreateWaitTask().ConfigureAwait(false); diff --git a/src/EditorFeatures/Test/SolutionCrawler/WorkCoordinatorTests.cs b/src/EditorFeatures/Test/SolutionCrawler/WorkCoordinatorTests.cs index 99120e0943102..d0c18e8c72c4c 100644 --- a/src/EditorFeatures/Test/SolutionCrawler/WorkCoordinatorTests.cs +++ b/src/EditorFeatures/Test/SolutionCrawler/WorkCoordinatorTests.cs @@ -1039,13 +1039,13 @@ public Analyzer(bool waitForCancellation = false, bool blockedRun = false) this.RunningEvent = new ManualResetEventSlim(initialState: false); } - public Task AnalyzeProjectAsync(Project project, bool semanticsChanged, InvocationReasons reasons, CancellationToken cancellationToken) + public Task AnalyzeProjectAsync(Project project, bool semanticsChanged, CancellationToken cancellationToken) { this.ProjectIds.Add(project.Id); return SpecializedTasks.EmptyTask; } - public Task AnalyzeDocumentAsync(Document document, SyntaxNode bodyOpt, InvocationReasons reasons, CancellationToken cancellationToken) + public Task AnalyzeDocumentAsync(Document document, SyntaxNode bodyOpt, CancellationToken cancellationToken) { if (bodyOpt == null) { @@ -1055,7 +1055,7 @@ public Task AnalyzeDocumentAsync(Document document, SyntaxNode bodyOpt, Invocati return SpecializedTasks.EmptyTask; } - public Task AnalyzeSyntaxAsync(Document document, InvocationReasons reasons, CancellationToken cancellationToken) + public Task AnalyzeSyntaxAsync(Document document, CancellationToken cancellationToken) { this.SyntaxDocumentIds.Add(document.Id); Process(document.Id, cancellationToken); diff --git a/src/EditorFeatures/Test2/Diagnostics/DiagnosticServiceTests.vb b/src/EditorFeatures/Test2/Diagnostics/DiagnosticServiceTests.vb index c12c85b7eaadc..0d14e41893d39 100644 --- a/src/EditorFeatures/Test2/Diagnostics/DiagnosticServiceTests.vb +++ b/src/EditorFeatures/Test2/Diagnostics/DiagnosticServiceTests.vb @@ -11,7 +11,6 @@ Imports Microsoft.CodeAnalysis.Editor.UnitTests.Diagnostics Imports Microsoft.CodeAnalysis.Editor.UnitTests.Workspaces Imports Microsoft.CodeAnalysis.Options Imports Microsoft.CodeAnalysis.Shared.Options -Imports Microsoft.CodeAnalysis.SolutionCrawler Imports Microsoft.CodeAnalysis.Test.Utilities Imports Microsoft.CodeAnalysis.Text Imports Microsoft.CodeAnalysis.UnitTests.Diagnostics @@ -1935,7 +1934,7 @@ class MyClass Assert.Equal(HiddenDiagnosticsCompilationAnalyzer.Descriptor.Id, descriptors.Single().Id) ' Force project analysis - incrementalAnalyzer.AnalyzeProjectAsync(project, semanticsChanged:=True, reasons:=InvocationReasons.Empty, cancellationToken:=CancellationToken.None).Wait() + incrementalAnalyzer.AnalyzeProjectAsync(project, semanticsChanged:=True, cancellationToken:=CancellationToken.None).Wait() ' Get cached project diagnostics. Dim diagnostics = diagnosticService.GetCachedDiagnosticsAsync(workspace, project.Id).WaitAndGetResult(CancellationToken.None) diff --git a/src/EditorFeatures/VisualBasicTest/TodoComment/TodoCommentTests.vb b/src/EditorFeatures/VisualBasicTest/TodoComment/TodoCommentTests.vb index 9f3d4cdc8d804..540a485159c19 100644 --- a/src/EditorFeatures/VisualBasicTest/TodoComment/TodoCommentTests.vb +++ b/src/EditorFeatures/VisualBasicTest/TodoComment/TodoCommentTests.vb @@ -185,7 +185,7 @@ Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.UnitTests.TodoComment Dim document = workspace.Documents.First() Dim documentId = document.Id - Await worker.AnalyzeSyntaxAsync(workspace.CurrentSolution.GetDocument(documentId), InvocationReasons.Empty, CancellationToken.None) + Await worker.AnalyzeSyntaxAsync(workspace.CurrentSolution.GetDocument(documentId), CancellationToken.None) Dim todoLists = worker.GetItems_TestingOnly(documentId) diff --git a/src/Features/Core/Portable/Diagnostics/EngineV1/DiagnosticIncrementalAnalyzer.cs b/src/Features/Core/Portable/Diagnostics/EngineV1/DiagnosticIncrementalAnalyzer.cs deleted file mode 100644 index 7ce5ee89d5090..0000000000000 --- a/src/Features/Core/Portable/Diagnostics/EngineV1/DiagnosticIncrementalAnalyzer.cs +++ /dev/null @@ -1,1151 +0,0 @@ -// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System; -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Diagnostics; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; -using Microsoft.CodeAnalysis.Diagnostics.Log; -using Microsoft.CodeAnalysis.ErrorReporting; -using Microsoft.CodeAnalysis.Internal.Log; -using Microsoft.CodeAnalysis.LanguageServices; -using Microsoft.CodeAnalysis.Shared.Extensions; -using Microsoft.CodeAnalysis.Shared.Options; -using Microsoft.CodeAnalysis.Text; -using Microsoft.CodeAnalysis.Versions; -using Roslyn.Utilities; - -namespace Microsoft.CodeAnalysis.Diagnostics.EngineV1 -{ - internal partial class DiagnosticIncrementalAnalyzer : BaseDiagnosticIncrementalAnalyzer - { - private readonly int _correlationId; - private readonly MemberRangeMap _memberRangeMap; - private readonly AnalyzerExecutor _executor; - private readonly StateManager _stateManager; - - /// - /// PERF: Always run analyzers sequentially for background analysis. - /// - private const bool ConcurrentAnalysis = false; - - /// - /// Always compute suppressed diagnostics - diagnostic clients may or may not request for suppressed diagnostics. - /// - private const bool ReportSuppressedDiagnostics = true; - - public DiagnosticIncrementalAnalyzer( - DiagnosticAnalyzerService owner, - int correlationId, - Workspace workspace, - HostAnalyzerManager analyzerManager, - AbstractHostDiagnosticUpdateSource hostDiagnosticUpdateSource) - : base(owner, workspace, analyzerManager, hostDiagnosticUpdateSource) - { - _correlationId = correlationId; - _memberRangeMap = new MemberRangeMap(); - _executor = new AnalyzerExecutor(this); - - _stateManager = new StateManager(analyzerManager); - _stateManager.ProjectAnalyzerReferenceChanged += OnProjectAnalyzerReferenceChanged; - } - - private void OnProjectAnalyzerReferenceChanged(object sender, ProjectAnalyzerReferenceChangedEventArgs e) - { - if (e.Removed.Length == 0) - { - // nothing to refresh - return; - } - - // events will be automatically serialized. - var project = e.Project; - var stateSets = e.Removed; - - // first remove all states - foreach (var stateSet in stateSets) - { - foreach (var document in project.Documents) - { - stateSet.Remove(document.Id); - } - - stateSet.Remove(project.Id); - } - - // now raise events - Owner.RaiseBulkDiagnosticsUpdated(raiseEvents => - { - foreach (var document in project.Documents) - { - RaiseDocumentDiagnosticsRemoved(document, stateSets, includeProjectState: true, raiseEvents: raiseEvents); - } - - RaiseProjectDiagnosticsRemoved(project, stateSets, raiseEvents); - }); - } - - public override Task DocumentOpenAsync(Document document, CancellationToken cancellationToken) - { - using (Logger.LogBlock(FunctionId.Diagnostics_DocumentOpen, GetOpenLogMessage, document, cancellationToken)) - { - return ClearOnlyDocumentStates(document); - } - } - - public override Task DocumentCloseAsync(Document document, CancellationToken cancellationToken) - { - using (Logger.LogBlock(FunctionId.Diagnostics_DocumentClose, GetResetLogMessage, document, cancellationToken)) - { - // we don't need the info for closed file - _memberRangeMap.Remove(document.Id); - - return ClearOnlyDocumentStates(document); - } - } - - public override Task DocumentResetAsync(Document document, CancellationToken cancellationToken) - { - using (Logger.LogBlock(FunctionId.Diagnostics_DocumentReset, GetResetLogMessage, document, cancellationToken)) - { - // clear states for re-analysis and raise events about it. otherwise, some states might not updated on re-analysis - // due to our build-live de-duplication logic where we put all state in Documents state. - return ClearOnlyDocumentStates(document); - } - } - - private Task ClearOnlyDocumentStates(Document document) - { - // since managing states and raising events are separated, it can't be cancelled. - var stateSets = _stateManager.GetStateSets(document.Project); - - // we remove whatever information we used to have on document open/close and re-calculate diagnostics - // we had to do this since some diagnostic analyzer changes its behavior based on whether the document is opened or not. - // so we can't use cached information. - - // clean up states - foreach (var stateSet in stateSets) - { - stateSet.Remove(document.Id, onlyDocumentStates: true); - } - - // raise events - Owner.RaiseBulkDiagnosticsUpdated(raiseEvents => - { - RaiseDocumentDiagnosticsRemoved(document, stateSets, includeProjectState: false, raiseEvents: raiseEvents); - }); - - return SpecializedTasks.EmptyTask; - } - - private bool CheckOptions(Project project, bool forceAnalysis) - { - var workspace = project.Solution.Workspace; - if (ServiceFeatureOnOffOptions.IsClosedFileDiagnosticsEnabled(project) && - workspace.Options.GetOption(RuntimeOptions.FullSolutionAnalysis)) - { - return true; - } - - if (forceAnalysis) - { - return true; - } - - return false; - } - - internal CompilationWithAnalyzers GetCompilationWithAnalyzers( - Project project, - IEnumerable analyzers, - Compilation compilation, - bool concurrentAnalysis, - bool reportSuppressedDiagnostics) - { - Contract.ThrowIfFalse(project.SupportsCompilation); - Contract.ThrowIfNull(compilation); - - Func analyzerExceptionFilter = ex => - { - if (project.Solution.Options.GetOption(InternalDiagnosticsOptions.CrashOnAnalyzerException)) - { - // if option is on, crash the host to get crash dump. - FatalError.ReportUnlessCanceled(ex); - } - - return true; - }; - - var analysisOptions = new CompilationWithAnalyzersOptions( - new WorkspaceAnalyzerOptions(project.AnalyzerOptions, project.Solution.Workspace), - GetOnAnalyzerException(project.Id), - analyzerExceptionFilter: analyzerExceptionFilter, - concurrentAnalysis: concurrentAnalysis, - logAnalyzerExecutionTime: true, - reportSuppressedDiagnostics: reportSuppressedDiagnostics); - - var filteredAnalyzers = analyzers - .Where(a => !CompilationWithAnalyzers.IsDiagnosticAnalyzerSuppressed(a, compilation.Options, analysisOptions.OnAnalyzerException)) - .Distinct() - .ToImmutableArray(); - if (filteredAnalyzers.IsEmpty) - { - return null; - } - - return new CompilationWithAnalyzers(compilation, filteredAnalyzers, analysisOptions); - } - - public override async Task AnalyzeSyntaxAsync(Document document, CancellationToken cancellationToken) - { - await AnalyzeSyntaxAsync(document, diagnosticIds: null, cancellationToken: cancellationToken).ConfigureAwait(false); - } - - private async Task AnalyzeSyntaxAsync(Document document, ImmutableHashSet diagnosticIds, CancellationToken cancellationToken) - { - try - { - if (!CheckOptions(document.Project, document.IsOpen())) - { - return; - } - - var textVersion = await document.GetTextVersionAsync(cancellationToken).ConfigureAwait(false); - var dataVersion = await document.GetSyntaxVersionAsync(cancellationToken).ConfigureAwait(false); - var versions = new VersionArgument(textVersion, dataVersion); - - var root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false); - var fullSpan = root == null ? null : (TextSpan?)root.FullSpan; - - var openedDocument = document.IsOpen(); - - var stateSets = _stateManager.GetOrUpdateStateSets(document.Project); - var analyzers = stateSets.Select(s => s.Analyzer); - - var userDiagnosticDriver = new DiagnosticAnalyzerDriver( - document, fullSpan, root, this, analyzers, ConcurrentAnalysis, ReportSuppressedDiagnostics, cancellationToken); - - foreach (var stateSet in stateSets) - { - if (await SkipRunningAnalyzerAsync(document.Project, stateSet.Analyzer, openedDocument, skipClosedFileCheck: false, cancellationToken: cancellationToken).ConfigureAwait(false)) - { - await ClearExistingDiagnostics(document, stateSet, StateType.Syntax, cancellationToken).ConfigureAwait(false); - continue; - } - - if (ShouldRunAnalyzerForStateType(stateSet.Analyzer, StateType.Syntax, diagnosticIds)) - { - var data = await _executor.GetSyntaxAnalysisDataAsync(userDiagnosticDriver, stateSet, versions).ConfigureAwait(false); - if (data.FromCache) - { - RaiseDiagnosticsCreatedFromCacheIfNeeded(StateType.Syntax, document, stateSet, data.Items); - continue; - } - - var state = stateSet.GetState(StateType.Syntax); - await state.PersistAsync(document, data.ToPersistData(), cancellationToken).ConfigureAwait(false); - - RaiseDocumentDiagnosticsUpdatedIfNeeded(StateType.Syntax, document, stateSet, data.OldItems, data.Items); - } - } - } - catch (Exception e) when (FatalError.ReportUnlessCanceled(e)) - { - throw ExceptionUtilities.Unreachable; - } - } - - public override async Task AnalyzeDocumentAsync(Document document, SyntaxNode bodyOpt, CancellationToken cancellationToken) - { - await AnalyzeDocumentAsync(document, bodyOpt, diagnosticIds: null, cancellationToken: cancellationToken).ConfigureAwait(false); - } - - private async Task AnalyzeDocumentAsync(Document document, SyntaxNode bodyOpt, ImmutableHashSet diagnosticIds, CancellationToken cancellationToken) - { - try - { - if (!CheckOptions(document.Project, document.IsOpen())) - { - return; - } - - var textVersion = await document.GetTextVersionAsync(cancellationToken).ConfigureAwait(false); - var projectVersion = await document.Project.GetDependentVersionAsync(cancellationToken).ConfigureAwait(false); - var dataVersion = await document.Project.GetDependentSemanticVersionAsync(cancellationToken).ConfigureAwait(false); - - var versions = new VersionArgument(textVersion, dataVersion, projectVersion); - if (bodyOpt == null) - { - await AnalyzeDocumentAsync(document, versions, diagnosticIds, cancellationToken).ConfigureAwait(false); - } - else - { - // only open file can go this route - await AnalyzeBodyDocumentAsync(document, bodyOpt, versions, cancellationToken).ConfigureAwait(false); - } - } - catch (Exception e) when (FatalError.ReportUnlessCanceled(e)) - { - throw ExceptionUtilities.Unreachable; - } - } - - private async Task AnalyzeBodyDocumentAsync(Document document, SyntaxNode member, VersionArgument versions, CancellationToken cancellationToken) - { - try - { - // syntax facts service must exist, otherwise, this method won't have called. - var syntaxFacts = document.Project.LanguageServices.GetService(); - var root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false); - var memberId = syntaxFacts.GetMethodLevelMemberId(root, member); - - var stateSets = _stateManager.GetOrUpdateStateSets(document.Project); - var analyzers = stateSets.Select(s => s.Analyzer); - - var spanBasedDriver = new DiagnosticAnalyzerDriver( - document, member.FullSpan, root, this, analyzers, ConcurrentAnalysis, ReportSuppressedDiagnostics, cancellationToken); - - var documentBasedDriver = new DiagnosticAnalyzerDriver( - document, root.FullSpan, root, this, analyzers, ConcurrentAnalysis, ReportSuppressedDiagnostics, cancellationToken); - - foreach (var stateSet in stateSets) - { - if (Owner.IsAnalyzerSuppressed(stateSet.Analyzer, document.Project)) - { - await ClearExistingDiagnostics(document, stateSet, StateType.Document, cancellationToken).ConfigureAwait(false); - continue; - } - - if (ShouldRunAnalyzerForStateType(stateSet.Analyzer, StateType.Document)) - { - var supportsSemanticInSpan = stateSet.Analyzer.SupportsSpanBasedSemanticDiagnosticAnalysis(); - var userDiagnosticDriver = supportsSemanticInSpan ? spanBasedDriver : documentBasedDriver; - - var ranges = _memberRangeMap.GetSavedMemberRange(stateSet.Analyzer, document); - var data = await _executor.GetDocumentBodyAnalysisDataAsync( - stateSet, versions, userDiagnosticDriver, root, member, memberId, supportsSemanticInSpan, ranges).ConfigureAwait(false); - - _memberRangeMap.UpdateMemberRange(stateSet.Analyzer, document, versions.TextVersion, memberId, member.FullSpan, ranges); - - var state = stateSet.GetState(StateType.Document); - await state.PersistAsync(document, data.ToPersistData(), cancellationToken).ConfigureAwait(false); - - if (data.FromCache) - { - RaiseDiagnosticsCreatedFromCacheIfNeeded(StateType.Document, document, stateSet, data.Items); - continue; - } - - RaiseDocumentDiagnosticsUpdatedIfNeeded(StateType.Document, document, stateSet, data.OldItems, data.Items); - } - } - } - catch (Exception e) when (FatalError.ReportUnlessCanceled(e)) - { - throw ExceptionUtilities.Unreachable; - } - } - - private async Task AnalyzeDocumentAsync(Document document, VersionArgument versions, ImmutableHashSet diagnosticIds, CancellationToken cancellationToken) - { - try - { - var root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false); - var fullSpan = root == null ? null : (TextSpan?)root.FullSpan; - - bool openedDocument = document.IsOpen(); - - var stateSets = _stateManager.GetOrUpdateStateSets(document.Project); - var analyzers = stateSets.Select(s => s.Analyzer); - - var userDiagnosticDriver = new DiagnosticAnalyzerDriver( - document, fullSpan, root, this, analyzers, ConcurrentAnalysis, ReportSuppressedDiagnostics, cancellationToken); - - foreach (var stateSet in stateSets) - { - if (await SkipRunningAnalyzerAsync(document.Project, stateSet.Analyzer, openedDocument, skipClosedFileCheck: false, cancellationToken: cancellationToken).ConfigureAwait(false)) - { - await ClearExistingDiagnostics(document, stateSet, StateType.Document, cancellationToken).ConfigureAwait(false); - continue; - } - - if (ShouldRunAnalyzerForStateType(stateSet.Analyzer, StateType.Document, diagnosticIds)) - { - var data = await _executor.GetDocumentAnalysisDataAsync(userDiagnosticDriver, stateSet, versions).ConfigureAwait(false); - if (data.FromCache) - { - RaiseDiagnosticsCreatedFromCacheIfNeeded(StateType.Document, document, stateSet, data.Items); - continue; - } - - if (openedDocument) - { - _memberRangeMap.Touch(stateSet.Analyzer, document, versions.TextVersion); - } - - var state = stateSet.GetState(StateType.Document); - await state.PersistAsync(document, data.ToPersistData(), cancellationToken).ConfigureAwait(false); - - RaiseDocumentDiagnosticsUpdatedIfNeeded(StateType.Document, document, stateSet, data.OldItems, data.Items); - } - } - } - catch (Exception e) when (FatalError.ReportUnlessCanceled(e)) - { - throw ExceptionUtilities.Unreachable; - } - } - - public override async Task AnalyzeProjectAsync(Project project, bool semanticsChanged, CancellationToken cancellationToken) - { - await AnalyzeProjectAsync(project, cancellationToken).ConfigureAwait(false); - } - - private async Task AnalyzeProjectAsync(Project project, CancellationToken cancellationToken) - { - try - { - if (!CheckOptions(project, forceAnalysis: false)) - { - return; - } - - // PERF: Ensure that we explicitly ignore the skipped analyzers while creating the analyzer driver, otherwise we might end up running hidden analyzers on closed files. - var stateSets = _stateManager.GetOrUpdateStateSets(project).ToImmutableArray(); - var skipAnalyzersMap = new Dictionary(stateSets.Length); - var shouldRunAnalyzersMap = new Dictionary(stateSets.Length); - var analyzersBuilder = ImmutableArray.CreateBuilder(stateSets.Length); - foreach (var stateSet in stateSets) - { - var skip = await SkipRunningAnalyzerAsync(project, stateSet.Analyzer, openedDocument: false, skipClosedFileCheck: true, cancellationToken: cancellationToken).ConfigureAwait(false); - skipAnalyzersMap.Add(stateSet.Analyzer, skip); - - var shouldRun = !skip && ShouldRunAnalyzerForStateType(stateSet.Analyzer, StateType.Project, diagnosticIds: null); - shouldRunAnalyzersMap.Add(stateSet.Analyzer, shouldRun); - - if (shouldRun) - { - analyzersBuilder.Add(stateSet.Analyzer); - } - } - - var analyzerDriver = new DiagnosticAnalyzerDriver(project, this, analyzersBuilder.ToImmutable(), ConcurrentAnalysis, ReportSuppressedDiagnostics, cancellationToken); - - var projectTextVersion = await project.GetLatestDocumentVersionAsync(cancellationToken).ConfigureAwait(false); - var semanticVersion = await project.GetDependentSemanticVersionAsync(cancellationToken).ConfigureAwait(false); - var projectVersion = await project.GetDependentVersionAsync(cancellationToken).ConfigureAwait(false); - var versions = new VersionArgument(projectTextVersion, semanticVersion, projectVersion); - - foreach (var stateSet in stateSets) - { - // Compilation actions can report diagnostics on open files, so we skipClosedFileChecks. - if (skipAnalyzersMap[stateSet.Analyzer]) - { - await ClearExistingDiagnostics(project, stateSet, cancellationToken).ConfigureAwait(false); - continue; - } - - if (shouldRunAnalyzersMap[stateSet.Analyzer]) - { - var data = await _executor.GetProjectAnalysisDataAsync(analyzerDriver, stateSet, versions).ConfigureAwait(false); - if (data.FromCache) - { - RaiseProjectDiagnosticsUpdatedIfNeeded(project, stateSet, ImmutableArray.Empty, data.Items); - continue; - } - - var state = stateSet.GetState(StateType.Project); - await PersistProjectData(project, state, data).ConfigureAwait(false); - - RaiseProjectDiagnosticsUpdatedIfNeeded(project, stateSet, data.OldItems, data.Items); - } - } - } - catch (Exception e) when (FatalError.ReportUnlessCanceled(e)) - { - throw ExceptionUtilities.Unreachable; - } - } - - private async Task SkipRunningAnalyzerAsync(Project project, DiagnosticAnalyzer analyzer, bool openedDocument, bool skipClosedFileCheck, CancellationToken cancellationToken) - { - // this project is not in a good state. only continue if it is opened document. - if (!await project.HasSuccessfullyLoadedAsync(cancellationToken).ConfigureAwait(false) && !openedDocument) - { - return true; - } - - if (Owner.IsAnalyzerSuppressed(analyzer, project)) - { - return true; - } - - if (skipClosedFileCheck || ShouldRunAnalyzerForClosedFile(analyzer, project.CompilationOptions, openedDocument)) - { - return false; - } - - return true; - } - - private static async Task PersistProjectData(Project project, DiagnosticState state, AnalysisData data) - { - // TODO: Cancellation is not allowed here to prevent data inconsistency. But there is still a possibility of data inconsistency due to - // things like exception. For now, I am letting it go and let v2 engine take care of it properly. If v2 doesn't come online soon enough - // more refactoring is required on project state. - - // clear all existing data - state.Remove(project.Id); - foreach (var document in project.Documents) - { - state.Remove(document.Id); - } - - // quick bail out - if (data.Items.Length == 0) - { - return; - } - - // save new data - var group = data.Items.GroupBy(d => d.DocumentId); - foreach (var kv in group) - { - if (kv.Key == null) - { - // save project scope diagnostics - await state.PersistAsync(project, new AnalysisData(data.TextVersion, data.DataVersion, kv.ToImmutableArrayOrEmpty()), CancellationToken.None).ConfigureAwait(false); - continue; - } - - // save document scope diagnostics - var document = project.GetDocument(kv.Key); - if (document == null) - { - continue; - } - - await state.PersistAsync(document, new AnalysisData(data.TextVersion, data.DataVersion, kv.ToImmutableArrayOrEmpty()), CancellationToken.None).ConfigureAwait(false); - } - } - - public override void RemoveDocument(DocumentId documentId) - { - using (Logger.LogBlock(FunctionId.Diagnostics_RemoveDocument, GetRemoveLogMessage, documentId, CancellationToken.None)) - { - _memberRangeMap.Remove(documentId); - - var stateSets = _stateManager.GetStateSets(documentId.ProjectId); - - foreach (var stateSet in stateSets) - { - stateSet.Remove(documentId); - } - - Owner.RaiseBulkDiagnosticsUpdated(raiseEvents => - { - foreach (var stateSet in stateSets) - { - var solutionArgs = new SolutionArgument(null, documentId.ProjectId, documentId); - for (var stateType = 0; stateType < s_stateTypeCount; stateType++) - { - RaiseDiagnosticsRemoved((StateType)stateType, documentId, stateSet, solutionArgs, raiseEvents); - } - } - }); - } - } - - public override void RemoveProject(ProjectId projectId) - { - using (Logger.LogBlock(FunctionId.Diagnostics_RemoveProject, GetRemoveLogMessage, projectId, CancellationToken.None)) - { - var stateSets = _stateManager.GetStateSets(projectId); - - foreach (var stateSet in stateSets) - { - stateSet.Remove(projectId); - } - - _stateManager.RemoveStateSet(projectId); - - Owner.RaiseBulkDiagnosticsUpdated(raiseEvents => - { - foreach (var stateSet in stateSets) - { - var solutionArgs = new SolutionArgument(null, projectId, null); - RaiseDiagnosticsRemoved(StateType.Project, projectId, stateSet, solutionArgs, raiseEvents); - } - }); - } - } - - public override async Task TryAppendDiagnosticsForSpanAsync(Document document, TextSpan range, List diagnostics, bool includeSuppressedDiagnostics = false, CancellationToken cancellationToken = default(CancellationToken)) - { - var root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false); - var getter = new LatestDiagnosticsForSpanGetter(this, document, root, range, blockForData: false, diagnostics: diagnostics, includeSuppressedDiagnostics: includeSuppressedDiagnostics, cancellationToken: cancellationToken); - return await getter.TryGetAsync().ConfigureAwait(false); - } - - public override async Task> GetDiagnosticsForSpanAsync(Document document, TextSpan range, bool includeSuppressedDiagnostics = false, CancellationToken cancellationToken = default(CancellationToken)) - { - var root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false); - var getter = new LatestDiagnosticsForSpanGetter(this, document, root, range, blockForData: true, includeSuppressedDiagnostics: includeSuppressedDiagnostics, cancellationToken: cancellationToken); - - var result = await getter.TryGetAsync().ConfigureAwait(false); - Contract.Requires(result); - - return getter.Diagnostics; - } - - public override bool ContainsDiagnostics(Workspace workspace, ProjectId projectId) - { - // need to improve perf in v2. - foreach (var stateSet in _stateManager.GetStateSets(projectId)) - { - for (var stateType = 0; stateType < s_stateTypeCount; stateType++) - { - var state = stateSet.GetState((StateType)stateType); - if (state.Count == 0) - { - continue; - } - - if (state.GetDataCount(projectId) > 0) - { - return true; - } - - var project = workspace.CurrentSolution.GetProject(projectId); - if (project == null) - { - continue; - } - - foreach (var documentId in project.DocumentIds) - { - if (state.GetDataCount(documentId) > 0) - { - return true; - } - } - } - } - - return false; - } - - private bool ShouldRunAnalyzerForClosedFile(DiagnosticAnalyzer analyzer, CompilationOptions options, bool openedDocument) - { - // we have opened document, doesn't matter - // PERF: Don't query descriptors for compiler analyzer, always execute it. - if (openedDocument || analyzer.IsCompilerAnalyzer()) - { - return true; - } - - // most of analyzers, number of descriptor is quite small, so this should be cheap. - return Owner.GetDiagnosticDescriptors(analyzer).Any(d => GetEffectiveSeverity(d, options) != ReportDiagnostic.Hidden); - } - - private bool ShouldRunAnalyzerForStateType(DiagnosticAnalyzer analyzer, StateType stateTypeId, ImmutableHashSet diagnosticIds) - { - return ShouldRunAnalyzerForStateType(analyzer, stateTypeId, diagnosticIds, Owner.GetDiagnosticDescriptors); - } - - private static bool ShouldRunAnalyzerForStateType(DiagnosticAnalyzer analyzer, StateType stateTypeId, - ImmutableHashSet diagnosticIds = null, Func> getDescriptors = null) - { - // PERF: Don't query descriptors for compiler analyzer, always execute it for all state types. - if (analyzer.IsCompilerAnalyzer()) - { - return true; - } - - if (diagnosticIds != null && getDescriptors(analyzer).All(d => !diagnosticIds.Contains(d.Id))) - { - return false; - } - - switch (stateTypeId) - { - case StateType.Syntax: - return analyzer.SupportsSyntaxDiagnosticAnalysis(); - - case StateType.Document: - return analyzer.SupportsSemanticDiagnosticAnalysis(); - - case StateType.Project: - return analyzer.SupportsProjectDiagnosticAnalysis(); - - default: - throw ExceptionUtilities.Unreachable; - } - } - - public override void LogAnalyzerCountSummary() - { - DiagnosticAnalyzerLogger.LogAnalyzerCrashCountSummary(_correlationId, DiagnosticLogAggregator); - DiagnosticAnalyzerLogger.LogAnalyzerTypeCountSummary(_correlationId, DiagnosticLogAggregator); - - // reset the log aggregator - ResetDiagnosticLogAggregator(); - } - - private static bool CheckSyntaxVersions(Document document, AnalysisData existingData, VersionArgument versions) - { - if (existingData == null) - { - return false; - } - - return document.CanReusePersistedTextVersion(versions.TextVersion, existingData.TextVersion) && - document.CanReusePersistedSyntaxTreeVersion(versions.DataVersion, existingData.DataVersion); - } - - private static bool CheckSemanticVersions(Document document, AnalysisData existingData, VersionArgument versions) - { - if (existingData == null) - { - return false; - } - - return document.CanReusePersistedTextVersion(versions.TextVersion, existingData.TextVersion) && - document.Project.CanReusePersistedDependentSemanticVersion(versions.ProjectVersion, versions.DataVersion, existingData.DataVersion); - } - - private static bool CheckSemanticVersions(Project project, AnalysisData existingData, VersionArgument versions) - { - if (existingData == null) - { - return false; - } - - return VersionStamp.CanReusePersistedVersion(versions.TextVersion, existingData.TextVersion) && - project.CanReusePersistedDependentSemanticVersion(versions.ProjectVersion, versions.DataVersion, existingData.DataVersion); - } - - private void RaiseDiagnosticsCreatedFromCacheIfNeeded(StateType type, Document document, StateSet stateSet, ImmutableArray items) - { - RaiseDocumentDiagnosticsUpdatedIfNeeded(type, document, stateSet, ImmutableArray.Empty, items); - } - - private void RaiseDocumentDiagnosticsUpdatedIfNeeded( - StateType type, Document document, StateSet stateSet, ImmutableArray existingItems, ImmutableArray newItems) - { - var noItems = existingItems.Length == 0 && newItems.Length == 0; - if (noItems) - { - return; - } - - RaiseDiagnosticsCreated(type, document.Id, stateSet, new SolutionArgument(document), newItems); - } - - private void RaiseProjectDiagnosticsUpdatedIfNeeded( - Project project, StateSet stateSet, ImmutableArray existingItems, ImmutableArray newItems) - { - var noItems = existingItems.Length == 0 && newItems.Length == 0; - if (noItems) - { - return; - } - - RaiseProjectDiagnosticsRemovedIfNeeded(project, stateSet, existingItems, newItems); - RaiseProjectDiagnosticsUpdated(project, stateSet, newItems); - } - - private void RaiseProjectDiagnosticsRemovedIfNeeded( - Project project, StateSet stateSet, ImmutableArray existingItems, ImmutableArray newItems) - { - if (existingItems.Length == 0) - { - return; - } - - Owner.RaiseBulkDiagnosticsUpdated(raiseEvents => - { - var removedItems = existingItems.GroupBy(d => d.DocumentId).Select(g => g.Key).Except(newItems.GroupBy(d => d.DocumentId).Select(g => g.Key)); - foreach (var documentId in removedItems) - { - if (documentId == null) - { - RaiseDiagnosticsRemoved(StateType.Project, project.Id, stateSet, new SolutionArgument(project), raiseEvents); - continue; - } - - var document = project.GetDocument(documentId); - var argument = document == null ? new SolutionArgument(null, documentId.ProjectId, documentId) : new SolutionArgument(document); - RaiseDiagnosticsRemoved(StateType.Project, documentId, stateSet, argument, raiseEvents); - } - }); - } - - private void RaiseProjectDiagnosticsUpdated(Project project, StateSet stateSet, ImmutableArray diagnostics) - { - Owner.RaiseBulkDiagnosticsUpdated(raiseEvents => - { - var group = diagnostics.GroupBy(d => d.DocumentId); - foreach (var kv in group) - { - if (kv.Key == null) - { - RaiseDiagnosticsCreated(StateType.Project, project.Id, stateSet, new SolutionArgument(project), kv.ToImmutableArrayOrEmpty(), raiseEvents); - continue; - } - - RaiseDiagnosticsCreated(StateType.Project, kv.Key, stateSet, new SolutionArgument(project.GetDocument(kv.Key)), kv.ToImmutableArrayOrEmpty(), raiseEvents); - } - }); - } - - private static ImmutableArray GetDiagnosticData(ILookup lookup, DocumentId documentId) - { - return lookup.Contains(documentId) ? lookup[documentId].ToImmutableArrayOrEmpty() : ImmutableArray.Empty; - } - - private void RaiseDiagnosticsCreated( - StateType type, object key, StateSet stateSet, SolutionArgument solution, ImmutableArray diagnostics) - { - RaiseDiagnosticsCreated(type, key, stateSet, solution, diagnostics, Owner.RaiseDiagnosticsUpdated); - } - - private void RaiseDiagnosticsCreated( - StateType type, object key, StateSet stateSet, SolutionArgument solution, ImmutableArray diagnostics, Action raiseEvents) - { - // get right arg id for the given analyzer - var id = new LiveDiagnosticUpdateArgsId(stateSet.Analyzer, key, (int)type, stateSet.ErrorSourceName); - raiseEvents(DiagnosticsUpdatedArgs.DiagnosticsCreated(id, Workspace, solution.Solution, solution.ProjectId, solution.DocumentId, diagnostics)); - } - - private void RaiseDiagnosticsRemoved( - StateType type, object key, StateSet stateSet, SolutionArgument solution) - { - RaiseDiagnosticsRemoved(type, key, stateSet, solution, Owner.RaiseDiagnosticsUpdated); - } - - private void RaiseDiagnosticsRemoved( - StateType type, object key, StateSet stateSet, SolutionArgument solution, Action raiseEvents) - { - // get right arg id for the given analyzer - var id = new LiveDiagnosticUpdateArgsId(stateSet.Analyzer, key, (int)type, stateSet.ErrorSourceName); - raiseEvents(DiagnosticsUpdatedArgs.DiagnosticsRemoved(id, Workspace, solution.Solution, solution.ProjectId, solution.DocumentId)); - } - - private void RaiseDocumentDiagnosticsRemoved(Document document, IEnumerable stateSets, bool includeProjectState, Action raiseEvents) - { - foreach (var stateSet in stateSets) - { - for (var stateType = 0; stateType < s_stateTypeCount; stateType++) - { - if (!includeProjectState && stateType == (int)StateType.Project) - { - // don't re-set project state type - continue; - } - - // raise diagnostic updated event - RaiseDiagnosticsRemoved((StateType)stateType, document.Id, stateSet, new SolutionArgument(document), raiseEvents); - } - } - } - - private void RaiseProjectDiagnosticsRemoved(Project project, IEnumerable stateSets, Action raiseEvents) - { - foreach (var stateSet in stateSets) - { - RaiseDiagnosticsRemoved(StateType.Project, project.Id, stateSet, new SolutionArgument(project), raiseEvents); - } - } - - private ImmutableArray UpdateDocumentDiagnostics( - AnalysisData existingData, ImmutableArray range, ImmutableArray memberDiagnostics, - SyntaxTree tree, SyntaxNode member, int memberId) - { - // get old span - var oldSpan = range[memberId]; - - // get old diagnostics - var diagnostics = existingData.Items; - - // check quick exit cases - if (diagnostics.Length == 0 && memberDiagnostics.Length == 0) - { - return diagnostics; - } - - // simple case - if (diagnostics.Length == 0 && memberDiagnostics.Length > 0) - { - return memberDiagnostics; - } - - // regular case - var result = new List(); - - // update member location - Contract.Requires(member.FullSpan.Start == oldSpan.Start); - var delta = member.FullSpan.End - oldSpan.End; - - var replaced = false; - foreach (var diagnostic in diagnostics) - { - if (diagnostic.TextSpan.Start < oldSpan.Start) - { - result.Add(diagnostic); - continue; - } - - if (!replaced) - { - result.AddRange(memberDiagnostics); - replaced = true; - } - - if (oldSpan.End <= diagnostic.TextSpan.Start) - { - result.Add(UpdatePosition(diagnostic, tree, delta)); - continue; - } - } - - // if it haven't replaced, replace it now - if (!replaced) - { - result.AddRange(memberDiagnostics); - replaced = true; - } - - return result.ToImmutableArray(); - } - - private DiagnosticData UpdatePosition(DiagnosticData diagnostic, SyntaxTree tree, int delta) - { - Debug.Assert(diagnostic.AdditionalLocations == null || diagnostic.AdditionalLocations.Count == 0); - var newDiagnosticLocation = UpdateDiagnosticLocation(diagnostic.DataLocation, tree, delta); - - return new DiagnosticData( - diagnostic.Id, - diagnostic.Category, - diagnostic.Message, - diagnostic.ENUMessageForBingSearch, - diagnostic.Severity, - diagnostic.DefaultSeverity, - diagnostic.IsEnabledByDefault, - diagnostic.WarningLevel, - diagnostic.CustomTags, - diagnostic.Properties, - diagnostic.Workspace, - diagnostic.ProjectId, - newDiagnosticLocation, - additionalLocations: diagnostic.AdditionalLocations, - description: diagnostic.Description, - helpLink: diagnostic.HelpLink, - isSuppressed: diagnostic.IsSuppressed); - } - - private DiagnosticDataLocation UpdateDiagnosticLocation( - DiagnosticDataLocation dataLocation, SyntaxTree tree, int delta) - { - var span = dataLocation.SourceSpan.Value; - var start = Math.Min(Math.Max(span.Start + delta, 0), tree.Length); - var newSpan = new TextSpan(start, start >= tree.Length ? 0 : span.Length); - - var mappedLineInfo = tree.GetMappedLineSpan(newSpan); - var originalLineInfo = tree.GetLineSpan(newSpan); - - return new DiagnosticDataLocation(dataLocation.DocumentId, newSpan, - originalFilePath: originalLineInfo.Path, - originalStartLine: originalLineInfo.StartLinePosition.Line, - originalStartColumn: originalLineInfo.StartLinePosition.Character, - originalEndLine: originalLineInfo.EndLinePosition.Line, - originalEndColumn: originalLineInfo.EndLinePosition.Character, - mappedFilePath: mappedLineInfo.GetMappedFilePathIfExist(), - mappedStartLine: mappedLineInfo.StartLinePosition.Line, - mappedStartColumn: mappedLineInfo.StartLinePosition.Character, - mappedEndLine: mappedLineInfo.EndLinePosition.Line, - mappedEndColumn: mappedLineInfo.EndLinePosition.Character); - } - - private static IEnumerable GetDiagnosticData(Document document, SyntaxTree tree, TextSpan? span, IEnumerable diagnostics) - { - return diagnostics != null ? diagnostics.Where(dx => ShouldIncludeDiagnostic(dx, tree, span)).Select(d => DiagnosticData.Create(document, d)) : null; - } - - private static bool ShouldIncludeDiagnostic(Diagnostic diagnostic, SyntaxTree tree, TextSpan? span) - { - if (diagnostic == null) - { - return false; - } - - if (diagnostic.Location == null || diagnostic.Location == Location.None) - { - return false; - } - - if (diagnostic.Location.SourceTree != tree) - { - return false; - } - - if (span == null) - { - return true; - } - - return span.Value.Contains(diagnostic.Location.SourceSpan); - } - - private static IEnumerable GetDiagnosticData(Project project, IEnumerable diagnostics) - { - if (diagnostics == null) - { - yield break; - } - - foreach (var diagnostic in diagnostics) - { - if (diagnostic.Location == null || diagnostic.Location == Location.None) - { - yield return DiagnosticData.Create(project, diagnostic); - continue; - } - - var document = project.GetDocument(diagnostic.Location.SourceTree); - if (document == null) - { - continue; - } - - yield return DiagnosticData.Create(document, diagnostic); - } - } - - private static async Task> GetSyntaxDiagnosticsAsync(DiagnosticAnalyzerDriver userDiagnosticDriver, DiagnosticAnalyzer analyzer) - { - using (Logger.LogBlock(FunctionId.Diagnostics_SyntaxDiagnostic, GetSyntaxLogMessage, userDiagnosticDriver.Document, userDiagnosticDriver.Span, analyzer, userDiagnosticDriver.CancellationToken)) - { - try - { - Contract.ThrowIfNull(analyzer); - - var tree = await userDiagnosticDriver.Document.GetSyntaxTreeAsync(userDiagnosticDriver.CancellationToken).ConfigureAwait(false); - var diagnostics = await userDiagnosticDriver.GetSyntaxDiagnosticsAsync(analyzer).ConfigureAwait(false); - return GetDiagnosticData(userDiagnosticDriver.Document, tree, userDiagnosticDriver.Span, diagnostics); - } - catch (Exception e) when (FatalError.ReportUnlessCanceled(e)) - { - throw ExceptionUtilities.Unreachable; - } - } - } - - private static async Task> GetSemanticDiagnosticsAsync(DiagnosticAnalyzerDriver userDiagnosticDriver, DiagnosticAnalyzer analyzer) - { - using (Logger.LogBlock(FunctionId.Diagnostics_SemanticDiagnostic, GetSemanticLogMessage, userDiagnosticDriver.Document, userDiagnosticDriver.Span, analyzer, userDiagnosticDriver.CancellationToken)) - { - try - { - Contract.ThrowIfNull(analyzer); - - var tree = await userDiagnosticDriver.Document.GetSyntaxTreeAsync(userDiagnosticDriver.CancellationToken).ConfigureAwait(false); - var diagnostics = await userDiagnosticDriver.GetSemanticDiagnosticsAsync(analyzer).ConfigureAwait(false); - return GetDiagnosticData(userDiagnosticDriver.Document, tree, userDiagnosticDriver.Span, diagnostics); - } - catch (Exception e) when (FatalError.ReportUnlessCanceled(e)) - { - throw ExceptionUtilities.Unreachable; - } - } - } - - private static async Task> GetProjectDiagnosticsAsync(DiagnosticAnalyzerDriver userDiagnosticDriver, DiagnosticAnalyzer analyzer) - { - using (Logger.LogBlock(FunctionId.Diagnostics_ProjectDiagnostic, GetProjectLogMessage, userDiagnosticDriver.Project, analyzer, userDiagnosticDriver.CancellationToken)) - { - try - { - Contract.ThrowIfNull(analyzer); - - var diagnostics = await userDiagnosticDriver.GetProjectDiagnosticsAsync(analyzer).ConfigureAwait(false); - return GetDiagnosticData(userDiagnosticDriver.Project, diagnostics); - } - catch (Exception e) when (FatalError.ReportUnlessCanceled(e)) - { - throw ExceptionUtilities.Unreachable; - } - } - } - - private async Task ClearExistingDiagnostics(Document document, StateSet stateSet, StateType type, CancellationToken cancellationToken) - { - var state = stateSet.GetState(type); - var existingData = await state.TryGetExistingDataAsync(document, cancellationToken).ConfigureAwait(false); - if (existingData?.Items.Length > 0) - { - // remove saved info - state.Remove(document.Id); - - // raise diagnostic updated event - RaiseDiagnosticsRemoved(type, document.Id, stateSet, new SolutionArgument(document)); - } - } - - private async Task ClearExistingDiagnostics(Project project, StateSet stateSet, CancellationToken cancellationToken) - { - var state = stateSet.GetState(StateType.Project); - var existingData = await state.TryGetExistingDataAsync(project, cancellationToken).ConfigureAwait(false); - if (existingData?.Items.Length > 0) - { - // remove saved cache - state.Remove(project.Id); - - // raise diagnostic updated event - RaiseDiagnosticsRemoved(StateType.Project, project.Id, stateSet, new SolutionArgument(project)); - } - } - - private static string GetSyntaxLogMessage(Document document, TextSpan? span, DiagnosticAnalyzer analyzer) - { - return string.Format("syntax: {0}, {1}, {2}", document.FilePath ?? document.Name, span.HasValue ? span.Value.ToString() : "Full", analyzer.ToString()); - } - - private static string GetSemanticLogMessage(Document document, TextSpan? span, DiagnosticAnalyzer analyzer) - { - return string.Format("semantic: {0}, {1}, {2}", document.FilePath ?? document.Name, span.HasValue ? span.Value.ToString() : "Full", analyzer.ToString()); - } - - private static string GetProjectLogMessage(Project project, DiagnosticAnalyzer analyzer) - { - return string.Format("project: {0}, {1}", project.FilePath ?? project.Name, analyzer.ToString()); - } - - private static string GetResetLogMessage(Document document) - { - return string.Format("document reset: {0}", document.FilePath ?? document.Name); - } - - private static string GetOpenLogMessage(Document document) - { - return string.Format("document open: {0}", document.FilePath ?? document.Name); - } - - private static string GetRemoveLogMessage(DocumentId id) - { - return string.Format("document remove: {0}", id.ToString()); - } - - private static string GetRemoveLogMessage(ProjectId id) - { - return string.Format("project remove: {0}", id.ToString()); - } - - public override Task NewSolutionSnapshotAsync(Solution newSolution, CancellationToken cancellationToken) - { - return SpecializedTasks.EmptyTask; - } - } -} diff --git a/src/Features/Core/Portable/Features.csproj b/src/Features/Core/Portable/Features.csproj index d99daf6b36c5e..ed00ed6879acc 100644 --- a/src/Features/Core/Portable/Features.csproj +++ b/src/Features/Core/Portable/Features.csproj @@ -577,9 +577,12 @@ + + + diff --git a/src/Workspaces/Core/Portable/SolutionCrawler/InvocationReasons.cs b/src/Features/Core/Portable/SolutionCrawler/InvocationReasons.cs similarity index 100% rename from src/Workspaces/Core/Portable/SolutionCrawler/InvocationReasons.cs rename to src/Features/Core/Portable/SolutionCrawler/InvocationReasons.cs diff --git a/src/Workspaces/Core/Portable/SolutionCrawler/InvocationReasons_Constants.cs b/src/Features/Core/Portable/SolutionCrawler/InvocationReasons_Constants.cs similarity index 100% rename from src/Workspaces/Core/Portable/SolutionCrawler/InvocationReasons_Constants.cs rename to src/Features/Core/Portable/SolutionCrawler/InvocationReasons_Constants.cs diff --git a/src/Workspaces/Core/Portable/SolutionCrawler/PredefinedInvocationReasons.cs b/src/Features/Core/Portable/SolutionCrawler/PredefinedInvocationReasons.cs similarity index 100% rename from src/Workspaces/Core/Portable/SolutionCrawler/PredefinedInvocationReasons.cs rename to src/Features/Core/Portable/SolutionCrawler/PredefinedInvocationReasons.cs diff --git a/src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.NormalPriorityProcessor.cs b/src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.NormalPriorityProcessor.cs index 61ab301897089..f6faaa4e70f7a 100644 --- a/src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.NormalPriorityProcessor.cs +++ b/src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.NormalPriorityProcessor.cs @@ -530,7 +530,7 @@ private async Task ResetStatesAsync() _currentSnapshotVersionTrackingSet.Clear(); _processingSolution = currentSolution; - + await RunAnalyzersAsync(this.Analyzers, currentSolution, (a, s, c) => a.NewSolutionSnapshotAsync(s, c), this.CancellationToken).ConfigureAwait(false); foreach (var id in this.Processor.GetOpenDocumentIds()) diff --git a/src/VisualStudio/CSharp/Test/CSharpVisualStudioTest.csproj b/src/VisualStudio/CSharp/Test/CSharpVisualStudioTest.csproj index 1f3e9a7c1f85f..68a4432990438 100644 --- a/src/VisualStudio/CSharp/Test/CSharpVisualStudioTest.csproj +++ b/src/VisualStudio/CSharp/Test/CSharpVisualStudioTest.csproj @@ -103,8 +103,9 @@ false - {edc68a0e-c68d-4a74-91b7-bf38ec909888} + {EDC68A0E-C68D-4A74-91B7-BF38EC909888} Features + false diff --git a/src/VisualStudio/CSharp/Test/PersistentStorage/SolutionSizeTests.cs b/src/VisualStudio/CSharp/Test/PersistentStorage/SolutionSizeTests.cs index d9867a3694704..c857b32d3bd21 100644 --- a/src/VisualStudio/CSharp/Test/PersistentStorage/SolutionSizeTests.cs +++ b/src/VisualStudio/CSharp/Test/PersistentStorage/SolutionSizeTests.cs @@ -3,7 +3,6 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; -using Microsoft.CodeAnalysis.SolutionCrawler; using Microsoft.CodeAnalysis.Text; using Microsoft.VisualStudio.LanguageServices.Implementation.SolutionSize; using Xunit; @@ -47,7 +46,7 @@ public async Task Test_SolutionSize_Update() var text = SourceText.From(new string('2', 1000)); var newDocument = document.WithText(text); - await analyzer.AnalyzeSyntaxAsync(newDocument, InvocationReasons.DocumentChanged, CancellationToken.None); + await analyzer.AnalyzeSyntaxAsync(newDocument, CancellationToken.None); var size = analyzer.GetSolutionSize(solution.Id); Assert.Equal(expected - length + text.Length, size); @@ -79,7 +78,7 @@ private static async Task AddSolutionAsync(SolutionSizeTracker.IncrementalAnalyz { foreach (var document in solution.Projects.SelectMany(p => p.Documents)) { - await analyzer.AnalyzeSyntaxAsync(document, InvocationReasons.Empty, CancellationToken.None); + await analyzer.AnalyzeSyntaxAsync(document, CancellationToken.None); } } diff --git a/src/VisualStudio/Core/Def/Implementation/SolutionSize/SolutionSizeTracker.cs b/src/VisualStudio/Core/Def/Implementation/SolutionSize/SolutionSizeTracker.cs index dfff1d26b8002..31a2f6ec1e1a1 100644 --- a/src/VisualStudio/Core/Def/Implementation/SolutionSize/SolutionSizeTracker.cs +++ b/src/VisualStudio/Core/Def/Implementation/SolutionSize/SolutionSizeTracker.cs @@ -1,6 +1,5 @@ // Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. -using System; using System.Collections.Concurrent; using System.Composition; using System.Threading; diff --git a/src/VisualStudio/Core/Impl/CodeModel/CodeModelIncrementalAnalyzer.cs b/src/VisualStudio/Core/Impl/CodeModel/CodeModelIncrementalAnalyzer.cs index 39c821a2747f4..6c84b918867a1 100644 --- a/src/VisualStudio/Core/Impl/CodeModel/CodeModelIncrementalAnalyzer.cs +++ b/src/VisualStudio/Core/Impl/CodeModel/CodeModelIncrementalAnalyzer.cs @@ -55,7 +55,7 @@ public Analyzer(IForegroundNotificationService notificationService, IAsynchronou _workspace = workspace; } - public Task AnalyzeSyntaxAsync(Document document, InvocationReasons reasons, CancellationToken cancellationToken) + public Task AnalyzeSyntaxAsync(Document document, CancellationToken cancellationToken) { FireEvents(document.Id, cancellationToken); @@ -124,12 +124,12 @@ public bool NeedsReanalysisOnOptionChanged(object sender, OptionChangedEventArgs return false; } - public Task AnalyzeDocumentAsync(Document document, SyntaxNode bodyOpt, InvocationReasons reasons, CancellationToken cancellationToken) + public Task AnalyzeDocumentAsync(Document document, SyntaxNode bodyOpt, CancellationToken cancellationToken) { return SpecializedTasks.EmptyTask; } - public Task AnalyzeProjectAsync(Project project, bool semanticsChanged, InvocationReasons reasons, CancellationToken cancellationToken) + public Task AnalyzeProjectAsync(Project project, bool semanticsChanged, CancellationToken cancellationToken) { return SpecializedTasks.EmptyTask; } diff --git a/src/VisualStudio/Core/Test/Diagnostics/DefaultDiagnosticUpdateSourceTests.vb b/src/VisualStudio/Core/Test/Diagnostics/DefaultDiagnosticUpdateSourceTests.vb index 337ce583b73a8..95c9bac1882aa 100644 --- a/src/VisualStudio/Core/Test/Diagnostics/DefaultDiagnosticUpdateSourceTests.vb +++ b/src/VisualStudio/Core/Test/Diagnostics/DefaultDiagnosticUpdateSourceTests.vb @@ -11,7 +11,6 @@ Imports Microsoft.CodeAnalysis.Editor.UnitTests.Diagnostics Imports Microsoft.CodeAnalysis.Editor.UnitTests.Workspaces Imports Microsoft.CodeAnalysis.Options Imports Microsoft.CodeAnalysis.Shared.TestHooks -Imports Microsoft.CodeAnalysis.SolutionCrawler Imports Microsoft.CodeAnalysis.Text.Shared.Extensions Imports Microsoft.VisualStudio.LanguageServices.Implementation.Diagnostics Imports Microsoft.VisualStudio.Text.Tagging @@ -48,7 +47,7 @@ class 123 { } Dim tagger = provider.CreateTagger(Of IErrorTag)(buffer) Using disposable = TryCast(tagger, IDisposable) Dim analyzer = miscService.CreateIncrementalAnalyzer(workspace) - Await analyzer.AnalyzeSyntaxAsync(workspace.CurrentSolution.Projects.First().Documents.First(), InvocationReasons.Empty, CancellationToken.None) + Await analyzer.AnalyzeSyntaxAsync(workspace.CurrentSolution.Projects.First().Documents.First(), CancellationToken.None) Await listener.CreateWaitTask() @@ -88,8 +87,8 @@ class A Dim document = workspace.CurrentSolution.Projects.First().Documents.First() Dim analyzer = miscService.CreateIncrementalAnalyzer(workspace) - Await analyzer.AnalyzeSyntaxAsync(document, InvocationReasons.Empty, CancellationToken.None) - Await analyzer.AnalyzeDocumentAsync(document, Nothing, InvocationReasons.Empty, CancellationToken.None) + Await analyzer.AnalyzeSyntaxAsync(document, CancellationToken.None) + Await analyzer.AnalyzeDocumentAsync(document, Nothing, CancellationToken.None) Await listener.CreateWaitTask() @@ -125,8 +124,8 @@ class A Dim document = workspace.CurrentSolution.Projects.First().Documents.First() Dim analyzer = miscService.CreateIncrementalAnalyzer(workspace) - Await analyzer.AnalyzeSyntaxAsync(document, InvocationReasons.Empty, CancellationToken.None) - Await analyzer.AnalyzeDocumentAsync(document, Nothing, InvocationReasons.Empty, CancellationToken.None) + Await analyzer.AnalyzeSyntaxAsync(document, CancellationToken.None) + Await analyzer.AnalyzeDocumentAsync(document, Nothing, CancellationToken.None) Await listener.CreateWaitTask() @@ -162,8 +161,8 @@ class A Dim document = workspace.CurrentSolution.Projects.First().Documents.First() Dim analyzer = miscService.CreateIncrementalAnalyzer(workspace) - Await analyzer.AnalyzeSyntaxAsync(document, InvocationReasons.Empty, CancellationToken.None) - Await analyzer.AnalyzeDocumentAsync(document, Nothing, InvocationReasons.Empty, CancellationToken.None) + Await analyzer.AnalyzeSyntaxAsync(document, CancellationToken.None) + Await analyzer.AnalyzeDocumentAsync(document, Nothing, CancellationToken.None) Await listener.CreateWaitTask() @@ -199,8 +198,8 @@ class A Dim document = workspace.CurrentSolution.Projects.First().Documents.First() Dim analyzer = miscService.CreateIncrementalAnalyzer(workspace) - Await analyzer.AnalyzeSyntaxAsync(document, InvocationReasons.Empty, CancellationToken.None) - Await analyzer.AnalyzeDocumentAsync(document, Nothing, InvocationReasons.Empty, CancellationToken.None) + Await analyzer.AnalyzeSyntaxAsync(document, CancellationToken.None) + Await analyzer.AnalyzeDocumentAsync(document, Nothing, CancellationToken.None) analyzer.RemoveDocument(document.Id) Await listener.CreateWaitTask() @@ -228,7 +227,7 @@ class 123 { } End Sub Dim analyzer = miscService.CreateIncrementalAnalyzer(workspace) - Await analyzer.AnalyzeSyntaxAsync(workspace.CurrentSolution.Projects.First().Documents.First(), InvocationReasons.Empty, CancellationToken.None) + Await analyzer.AnalyzeSyntaxAsync(workspace.CurrentSolution.Projects.First().Documents.First(), CancellationToken.None) Assert.Equal(PredefinedBuildTools.Live, buildTool) End Using @@ -253,7 +252,7 @@ End Class End Sub Dim analyzer = miscService.CreateIncrementalAnalyzer(workspace) - Await analyzer.AnalyzeSyntaxAsync(workspace.CurrentSolution.Projects.First().Documents.First(), InvocationReasons.Empty, CancellationToken.None) + Await analyzer.AnalyzeSyntaxAsync(workspace.CurrentSolution.Projects.First().Documents.First(), CancellationToken.None) Assert.Equal(PredefinedBuildTools.Live, buildTool) End Using diff --git a/src/Workspaces/Core/Portable/Workspaces.csproj b/src/Workspaces/Core/Portable/Workspaces.csproj index 850118da5630d..f260d2c7cb71f 100644 --- a/src/Workspaces/Core/Portable/Workspaces.csproj +++ b/src/Workspaces/Core/Portable/Workspaces.csproj @@ -365,8 +365,6 @@ - - @@ -431,7 +429,6 @@ - From 43cf3cb8390eda3360703f5f225d1a860fbd6a51 Mon Sep 17 00:00:00 2001 From: Jason Malinowski Date: Fri, 17 Jun 2016 13:53:19 -0700 Subject: [PATCH 24/36] Bump package versions to beta4 --- build/Targets/VSL.Versions.targets | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/Targets/VSL.Versions.targets b/build/Targets/VSL.Versions.targets index ab66a0006c758..d46286d7daa8d 100644 --- a/build/Targets/VSL.Versions.targets +++ b/build/Targets/VSL.Versions.targets @@ -85,7 +85,7 @@ $(RoslynSemanticVersion) - $(RoslynSemanticVersion)-beta3 + $(RoslynSemanticVersion)-beta4 $(NuGetPreReleaseVersion)-$(BuildNumberPart1.Trim())-$(BuildNumberPart2.Trim()) From 754bf605c9c1c0f23b8ed6a9fbc72572fe1ecd01 Mon Sep 17 00:00:00 2001 From: Jason Malinowski Date: Fri, 17 Jun 2016 15:00:06 -0700 Subject: [PATCH 25/36] Remove supression of StyleCop bug We don't use StyleCop anymore. --- .../Solution/Solution.CompilationTranslationAction.Actions.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/Solution.CompilationTranslationAction.Actions.cs b/src/Workspaces/Core/Portable/Workspace/Solution/Solution.CompilationTranslationAction.Actions.cs index 3cbdb43e8ef03..b480b7abf0565 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/Solution.CompilationTranslationAction.Actions.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/Solution.CompilationTranslationAction.Actions.cs @@ -51,7 +51,6 @@ public RemoveDocumentAction(DocumentState document) private class AddDocumentAction : SimpleCompilationTranslationAction { - [SuppressMessage("Microsoft.StyleCop.CSharp.SpacingRules", "SA1008:OpeningParenthesisMustBeSpacedCorrectly", Justification = "Working around StyleCop bug 7080")] private static readonly Func> s_action = async (o, d, c) => o.AddSyntaxTrees(await d.GetSyntaxTreeAsync(c).ConfigureAwait(false)); From eef6afb41e5cac629537c217927d5426e682f304 Mon Sep 17 00:00:00 2001 From: Jason Malinowski Date: Fri, 17 Jun 2016 15:01:22 -0700 Subject: [PATCH 26/36] Make pure function static --- src/Workspaces/Core/Portable/Workspace/Workspace.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/Workspaces/Core/Portable/Workspace/Workspace.cs b/src/Workspaces/Core/Portable/Workspace/Workspace.cs index d9df2e21776bd..190f1e6489bad 100644 --- a/src/Workspaces/Core/Portable/Workspace/Workspace.cs +++ b/src/Workspaces/Core/Portable/Workspace/Workspace.cs @@ -902,7 +902,7 @@ protected void UpdateReferencesAfterAdd() using (_serializationLock.DisposableWait()) { var oldSolution = this.CurrentSolution; - var newSolution = this.UpdateReferencesAfterAdd(oldSolution); + var newSolution = UpdateReferencesAfterAdd(oldSolution); if (newSolution != oldSolution) { @@ -912,7 +912,8 @@ protected void UpdateReferencesAfterAdd() } } - private Solution UpdateReferencesAfterAdd(Solution solution) + [System.Diagnostics.Contracts.Pure] + private static Solution UpdateReferencesAfterAdd(Solution solution) { // Build map from output assembly path to ProjectId // Use explicit loop instead of ToDictionary so we don't throw if multiple projects have same output assembly path. From b4e5f0a8428ec82355f5c3d20044ce519412ecee Mon Sep 17 00:00:00 2001 From: Jason Malinowski Date: Fri, 17 Jun 2016 15:02:03 -0700 Subject: [PATCH 27/36] Use the simpler methods for adding and removing references --- src/Workspaces/Core/Portable/Workspace/Workspace.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Workspaces/Core/Portable/Workspace/Workspace.cs b/src/Workspaces/Core/Portable/Workspace/Workspace.cs index 190f1e6489bad..5d110221ccfdc 100644 --- a/src/Workspaces/Core/Portable/Workspace/Workspace.cs +++ b/src/Workspaces/Core/Portable/Workspace/Workspace.cs @@ -948,10 +948,10 @@ private static Solution UpdateReferencesAfterAdd(Solution solution) if (!project.ProjectReferences.Contains(newProjRef)) { - project = project.WithProjectReferences(project.ProjectReferences.Concat(newProjRef)); + project = project.AddProjectReference(newProjRef); } - project = project.WithMetadataReferences(project.MetadataReferences.Where(mr => mr != meta)); + project = project.RemoveMetadataReference(meta); } } } From 5e9a9a47fd5b8b18f38b1b12eced33e7a81bbb1e Mon Sep 17 00:00:00 2001 From: Jason Malinowski Date: Fri, 17 Jun 2016 15:10:17 -0700 Subject: [PATCH 28/36] Delete some dead helpers --- .../Core/Portable/Workspace/Solution/Project.cs | 8 -------- .../Core/Portable/WorkspacesResources.Designer.cs | 9 --------- src/Workspaces/Core/Portable/WorkspacesResources.resx | 3 --- 3 files changed, 20 deletions(-) diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/Project.cs b/src/Workspaces/Core/Portable/Workspace/Solution/Project.cs index 0a7662f0a4a12..6350e4a074ddc 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/Project.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/Project.cs @@ -281,14 +281,6 @@ public ProjectChanges GetChanges(Project oldProject) return new ProjectChanges(this, oldProject); } - private void CheckContainsDocument(DocumentId documentId) - { - if (!this.ContainsDocument(documentId)) - { - throw new InvalidOperationException(WorkspacesResources.DocumentNotInProject); - } - } - /// /// The project version. This equates to the version of the project file. /// diff --git a/src/Workspaces/Core/Portable/WorkspacesResources.Designer.cs b/src/Workspaces/Core/Portable/WorkspacesResources.Designer.cs index d751975ac3af5..cfe2b45ec3999 100644 --- a/src/Workspaces/Core/Portable/WorkspacesResources.Designer.cs +++ b/src/Workspaces/Core/Portable/WorkspacesResources.Designer.cs @@ -376,15 +376,6 @@ internal static string DocumentIsOpen { } } - /// - /// Looks up a localized string similar to The project does not contain the specified document.. - /// - internal static string DocumentNotInProject { - get { - return ResourceManager.GetString("DocumentNotInProject", resourceCulture); - } - } - /// /// Looks up a localized string similar to The solution does not contain the specified document.. /// diff --git a/src/Workspaces/Core/Portable/WorkspacesResources.resx b/src/Workspaces/Core/Portable/WorkspacesResources.resx index b060e8d1201b1..2afefa8662ba3 100644 --- a/src/Workspaces/Core/Portable/WorkspacesResources.resx +++ b/src/Workspaces/Core/Portable/WorkspacesResources.resx @@ -198,9 +198,6 @@ The specified document is not a version of this document. - - The project does not contain the specified document. - The language '{0}' is not supported. From 0a86188ef16c911cf70c63e41847a97cf5f0fe3b Mon Sep 17 00:00:00 2001 From: VSadov Date: Fri, 17 Jun 2016 16:07:52 -0700 Subject: [PATCH 29/36] removed unnecessary usings. --- .../Portable/Binder/Semantics/Conversions/Conversion.cs | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/Conversion.cs b/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/Conversion.cs index 3849c4221a1ae..626c2b41a35aa 100644 --- a/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/Conversion.cs +++ b/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/Conversion.cs @@ -1,13 +1,10 @@ // Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +using Microsoft.CodeAnalysis.CSharp.Symbols; +using Roslyn.Utilities; using System; using System.Collections.Immutable; using System.Diagnostics; -using Microsoft.CodeAnalysis.CSharp.Symbols; -using Microsoft.CodeAnalysis.CSharp.Syntax; -using Microsoft.CodeAnalysis.Text; -using Roslyn.Utilities; -using System.Linq; namespace Microsoft.CodeAnalysis.CSharp { From e3283df6737d4dc1c3be1feb72984eeef6c2eb68 Mon Sep 17 00:00:00 2001 From: CyrusNajmabadi Date: Fri, 17 Jun 2016 17:37:33 -0700 Subject: [PATCH 30/36] Allow languages to specify if they want to support completion on backspace or not. --- .../Completion/CSharpCompletionService.cs | 12 ++------- .../Portable/Completion/CompletionOptions.cs | 4 ++- .../CompletionServiceWithProviders.cs | 14 +++++++++- .../VisualBasicCompletionService.vb | 21 +++++---------- .../CSharp/Impl/CSharpVSResources.Designer.cs | 27 ++++++++++++------- .../CSharp/Impl/CSharpVSResources.resx | 9 ++++--- .../CSharpSettingsManagerOptionSerializer.cs | 23 ++++++++++++++++ .../IntelliSenseOptionPageControl.xaml | 11 +++++--- .../IntelliSenseOptionPageControl.xaml.cs | 20 +++++++++++++- .../Options/IntelliSenseOptionPageStrings.cs | 9 ++++--- .../Impl/BasicVSResources.Designer.vb | 18 +++++++++++++ .../VisualBasic/Impl/BasicVSResources.resx | 6 +++++ .../IntelliSenseOptionPageControl.xaml | 11 ++++++++ .../IntelliSenseOptionPageControl.xaml.vb | 17 ++++++++++++ .../Options/IntelliSenseOptionPageStrings.vb | 6 +++++ ...ualBasicSettingsManagerOptionSerializer.vb | 21 ++++++++++++++- 16 files changed, 181 insertions(+), 48 deletions(-) diff --git a/src/Features/CSharp/Portable/Completion/CSharpCompletionService.cs b/src/Features/CSharp/Portable/Completion/CSharpCompletionService.cs index 2844ef145b3dd..87218bb9ae642 100644 --- a/src/Features/CSharp/Portable/Completion/CSharpCompletionService.cs +++ b/src/Features/CSharp/Portable/Completion/CSharpCompletionService.cs @@ -1,18 +1,13 @@ // Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. -using System; -using System.Collections.Generic; using System.Collections.Immutable; using System.Composition; using System.Threading; -using System.Threading.Tasks; using Microsoft.CodeAnalysis.Completion; -using Microsoft.CodeAnalysis.Completion.Providers; using Microsoft.CodeAnalysis.CSharp.Completion.Providers; using Microsoft.CodeAnalysis.CSharp.Completion.SuggestionMode; using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.Host.Mef; -using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.Text; namespace Microsoft.CodeAnalysis.CSharp.Completion @@ -57,10 +52,7 @@ public CSharpCompletionService( _workspace = workspace; } - public override string Language - { - get { return LanguageNames.CSharp; } - } + public override string Language => LanguageNames.CSharp; protected override ImmutableArray GetBuiltInProviders() { @@ -95,4 +87,4 @@ public override CompletionRules GetRules() return newRules; } } -} +} \ No newline at end of file diff --git a/src/Features/Core/Portable/Completion/CompletionOptions.cs b/src/Features/Core/Portable/Completion/CompletionOptions.cs index 907da69a6914a..5aafb2597191a 100644 --- a/src/Features/Core/Portable/Completion/CompletionOptions.cs +++ b/src/Features/Core/Portable/Completion/CompletionOptions.cs @@ -12,7 +12,9 @@ internal static class CompletionOptions public static readonly PerLanguageOption HideAdvancedMembers = new PerLanguageOption(FeatureName, "HideAdvancedMembers", defaultValue: false); public static readonly PerLanguageOption IncludeKeywords = new PerLanguageOption(FeatureName, "IncludeKeywords", defaultValue: true); public static readonly PerLanguageOption TriggerOnTyping = new PerLanguageOption(FeatureName, "TriggerOnTyping", defaultValue: true); - public static readonly PerLanguageOption TriggerOnTypingLetters = new PerLanguageOption(FeatureName, "TriggerOnTypingLetters", defaultValue: true); + + public static readonly PerLanguageOption TriggerOnTypingLetters = new PerLanguageOption(FeatureName, nameof(TriggerOnTypingLetters), defaultValue: true); + public static readonly PerLanguageOption TriggerOnDeletion = new PerLanguageOption(FeatureName, nameof(TriggerOnDeletion), defaultValue: null); public static readonly PerLanguageOption EnterKeyBehavior = new PerLanguageOption(FeatureName, nameof(EnterKeyBehavior), defaultValue: EnterKeyRule.Default); diff --git a/src/Features/Core/Portable/Completion/CompletionServiceWithProviders.cs b/src/Features/Core/Portable/Completion/CompletionServiceWithProviders.cs index 8f02612b213e4..eb5fd893548e1 100644 --- a/src/Features/Core/Portable/Completion/CompletionServiceWithProviders.cs +++ b/src/Features/Core/Portable/Completion/CompletionServiceWithProviders.cs @@ -401,7 +401,8 @@ private async Task GetContextAsync( } } - public override bool ShouldTriggerCompletion(SourceText text, int caretPosition, CompletionTrigger trigger, ImmutableHashSet roles = null, OptionSet options = null) + public override bool ShouldTriggerCompletion( + SourceText text, int caretPosition, CompletionTrigger trigger, ImmutableHashSet roles = null, OptionSet options = null) { options = options ?? _workspace.Options; if (!options.GetOption(CompletionOptions.TriggerOnTyping, this.Language)) @@ -409,10 +410,21 @@ public override bool ShouldTriggerCompletion(SourceText text, int caretPosition, return false; } + if (trigger.Kind == CompletionTriggerKind.Deletion && this.SupportsTriggerOnDeletion(options)) + { + return Char.IsLetterOrDigit(trigger.Character) || trigger.Character == '.'; + } + var providers = this.GetProviders(roles, CompletionTrigger.Default); return providers.Any(p => p.ShouldTriggerCompletion(text, caretPosition, trigger, options)); } + internal virtual bool SupportsTriggerOnDeletion(OptionSet options) + { + var opt = options.GetOption(CompletionOptions.TriggerOnDeletion, this.Language); + return opt == true; + } + public override async Task GetChangeAsync( Document document, CompletionItem item, char? commitKey, CancellationToken cancellationToken) { diff --git a/src/Features/VisualBasic/Portable/Completion/VisualBasicCompletionService.vb b/src/Features/VisualBasic/Portable/Completion/VisualBasicCompletionService.vb index 1fa284df94934..ed66986a83554 100644 --- a/src/Features/VisualBasic/Portable/Completion/VisualBasicCompletionService.vb +++ b/src/Features/VisualBasic/Portable/Completion/VisualBasicCompletionService.vb @@ -2,10 +2,8 @@ Imports System.Collections.Immutable Imports System.Composition -Imports System.Globalization Imports System.Threading Imports Microsoft.CodeAnalysis.Completion -Imports Microsoft.CodeAnalysis.Completion.Providers Imports Microsoft.CodeAnalysis.Host.Mef Imports Microsoft.CodeAnalysis.Options Imports Microsoft.CodeAnalysis.Text @@ -151,18 +149,11 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Completion Return CompletionUtilities.GetCompletionItemSpan(text, caretPosition) End Function - Public Overrides Function ShouldTriggerCompletion(text As SourceText, position As Integer, trigger As CompletionTrigger, Optional roles As ImmutableHashSet(Of String) = Nothing, Optional options As OptionSet = Nothing) As Boolean - options = If(options, _workspace.Options) - - If Not options.GetOption(CompletionOptions.TriggerOnTyping, Me.Language) Then - Return False - End If - - If trigger.Kind = CompletionTriggerKind.Deletion AndAlso (Char.IsLetterOrDigit(trigger.Character) OrElse trigger.Character = "."c) Then - Return True - Else - Return MyBase.ShouldTriggerCompletion(text, position, trigger, roles, options) - End If + Friend Overrides Function SupportsTriggerOnDeletion(options As OptionSet) As Boolean + ' If the option is null (i.e. default) or 'true', then we want to trigger completion. + ' Only if the option is false do we not want to trigger. + Dim opt = options.GetOption(CompletionOptions.TriggerOnDeletion, Me.Language) + Return If(opt = False, False, True) End Function End Class -End Namespace +End Namespace \ No newline at end of file diff --git a/src/VisualStudio/CSharp/Impl/CSharpVSResources.Designer.cs b/src/VisualStudio/CSharp/Impl/CSharpVSResources.Designer.cs index e124ac3be1a91..c18cdec807096 100644 --- a/src/VisualStudio/CSharp/Impl/CSharpVSResources.Designer.cs +++ b/src/VisualStudio/CSharp/Impl/CSharpVSResources.Designer.cs @@ -402,15 +402,6 @@ internal static string Option_Always_add_new_line_on_enter { } } - /// - /// Looks up a localized string similar to _Show completion list after a character is typed. - /// - internal static string Option_BringUpOnIdentifier { - get { - return ResourceManager.GetString("Option_BringUpOnIdentifier", resourceCulture); - } - } - /// /// Looks up a localized string similar to Enable full solution _analysis. /// @@ -636,6 +627,24 @@ internal static string Option_Show_completion_item_filters { } } + /// + /// Looks up a localized string similar to Show completion list after a character is _deleted. + /// + internal static string Option_Show_completion_list_after_a_character_is_deleted { + get { + return ResourceManager.GetString("Option_Show_completion_list_after_a_character_is_deleted", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to _Show completion list after a character is typed. + /// + internal static string Option_Show_completion_list_after_a_character_is_typed { + get { + return ResourceManager.GetString("Option_Show_completion_list_after_a_character_is_typed", resourceCulture); + } + } + /// /// Looks up a localized string similar to Place _keywords in completion lists. /// diff --git a/src/VisualStudio/CSharp/Impl/CSharpVSResources.resx b/src/VisualStudio/CSharp/Impl/CSharpVSResources.resx index 2886264b8e516..50dced64eac93 100644 --- a/src/VisualStudio/CSharp/Impl/CSharpVSResources.resx +++ b/src/VisualStudio/CSharp/Impl/CSharpVSResources.resx @@ -378,7 +378,7 @@ _Place 'System' directives first when sorting usings - + _Show completion list after a character is typed @@ -468,10 +468,13 @@ _Only add new line on enter after end of fully typed word - + _Always add new line on enter - + _Never add new line on enter + + Show completion list after a character is _deleted + \ No newline at end of file diff --git a/src/VisualStudio/CSharp/Impl/Options/CSharpSettingsManagerOptionSerializer.cs b/src/VisualStudio/CSharp/Impl/Options/CSharpSettingsManagerOptionSerializer.cs index 72ed0fac23cbe..34c90be2ba566 100644 --- a/src/VisualStudio/CSharp/Impl/Options/CSharpSettingsManagerOptionSerializer.cs +++ b/src/VisualStudio/CSharp/Impl/Options/CSharpSettingsManagerOptionSerializer.cs @@ -82,6 +82,7 @@ protected override ImmutableDictionary CreateStorageKeyToOption { new KeyValuePair(GetStorageKeyForOption(CompletionOptions.IncludeKeywords), CompletionOptions.IncludeKeywords), new KeyValuePair(GetStorageKeyForOption(CompletionOptions.TriggerOnTypingLetters), CompletionOptions.TriggerOnTypingLetters), + new KeyValuePair(GetStorageKeyForOption(CompletionOptions.TriggerOnDeletion), CompletionOptions.TriggerOnDeletion), new KeyValuePair(GetStorageKeyForOption(CompletionOptions.ShowCompletionItemFilters), CompletionOptions.ShowCompletionItemFilters), new KeyValuePair(GetStorageKeyForOption(CompletionOptions.HighlightMatchingPortionsOfCompletionListItems), CompletionOptions.HighlightMatchingPortionsOfCompletionListItems), }); @@ -146,6 +147,7 @@ protected override bool SupportsOption(IOption option, string languageName) { if (option == CompletionOptions.IncludeKeywords || option == CompletionOptions.TriggerOnTypingLetters || + option == CompletionOptions.TriggerOnDeletion || option == CompletionOptions.ShowCompletionItemFilters || option == CompletionOptions.HighlightMatchingPortionsOfCompletionListItems || option == CompletionOptions.EnterKeyBehavior || @@ -272,9 +274,30 @@ public override bool TryFetch(OptionKey optionKey, out object value) return FetchEnterKeyBehavior(optionKey, out value); } + if (optionKey.Option == CompletionOptions.TriggerOnDeletion) + { + return FetchTriggerOnDeletion(optionKey, out value); + } + return base.TryFetch(optionKey, out value); } + private bool FetchTriggerOnDeletion(OptionKey optionKey, out object value) + { + if (!base.TryFetch(optionKey, out value)) + { + return false; + } + + if (value == null) + { + // The default behavior for c# is to not trigger completion on deletion. + value = (bool?)false; + } + + return true; + } + private bool FetchStyleBool(string settingName, out object value) { var typeStyleValue = Manager.GetValueOrDefault(settingName); diff --git a/src/VisualStudio/CSharp/Impl/Options/IntelliSenseOptionPageControl.xaml b/src/VisualStudio/CSharp/Impl/Options/IntelliSenseOptionPageControl.xaml index fef4bfc5e01c6..725ed7a7d5e46 100644 --- a/src/VisualStudio/CSharp/Impl/Options/IntelliSenseOptionPageControl.xaml +++ b/src/VisualStudio/CSharp/Impl/Options/IntelliSenseOptionPageControl.xaml @@ -15,9 +15,9 @@ - @@ -27,6 +27,11 @@ + + CSharpVSResources.Option_Show_completion_list_after_a_character_is_typed; + + public static string Option_Show_completion_list_after_a_character_is_deleted => + CSharpVSResources.Option_Show_completion_list_after_a_character_is_deleted; public static string Option_CompletionLists { diff --git a/src/VisualStudio/VisualBasic/Impl/BasicVSResources.Designer.vb b/src/VisualStudio/VisualBasic/Impl/BasicVSResources.Designer.vb index 745daece03034..eabfac302c769 100644 --- a/src/VisualStudio/VisualBasic/Impl/BasicVSResources.Designer.vb +++ b/src/VisualStudio/VisualBasic/Impl/BasicVSResources.Designer.vb @@ -388,6 +388,24 @@ Namespace Microsoft.VisualStudio.LanguageServices.VisualBasic End Get End Property + ''' + ''' Looks up a localized string similar to Show completion list after a character is _deleted. + ''' + Friend Shared ReadOnly Property Option_Show_completion_list_after_a_character_is_deleted() As String + Get + Return ResourceManager.GetString("Option_Show_completion_list_after_a_character_is_deleted", resourceCulture) + End Get + End Property + + ''' + ''' Looks up a localized string similar to _Show completion list after a character is typed. + ''' + Friend Shared ReadOnly Property Option_Show_completion_list_after_a_character_is_typed() As String + Get + Return ResourceManager.GetString("Option_Show_completion_list_after_a_character_is_typed", resourceCulture) + End Get + End Property + ''' ''' Looks up a localized string similar to Suggest imports for types in _NuGet packages. ''' diff --git a/src/VisualStudio/VisualBasic/Impl/BasicVSResources.resx b/src/VisualStudio/VisualBasic/Impl/BasicVSResources.resx index cdd50c3b7f550..2c528241bb342 100644 --- a/src/VisualStudio/VisualBasic/Impl/BasicVSResources.resx +++ b/src/VisualStudio/VisualBasic/Impl/BasicVSResources.resx @@ -258,4 +258,10 @@ _Never add new line on enter + + Show completion list after a character is _deleted + + + _Show completion list after a character is typed + \ No newline at end of file diff --git a/src/VisualStudio/VisualBasic/Impl/Options/IntelliSenseOptionPageControl.xaml b/src/VisualStudio/VisualBasic/Impl/Options/IntelliSenseOptionPageControl.xaml index 6871862ce7eb6..b842572ded3e3 100644 --- a/src/VisualStudio/VisualBasic/Impl/Options/IntelliSenseOptionPageControl.xaml +++ b/src/VisualStudio/VisualBasic/Impl/Options/IntelliSenseOptionPageControl.xaml @@ -15,6 +15,17 @@ + + + + + diff --git a/src/VisualStudio/VisualBasic/Impl/Options/IntelliSenseOptionPageControl.xaml.vb b/src/VisualStudio/VisualBasic/Impl/Options/IntelliSenseOptionPageControl.xaml.vb index 18dc0611a44d6..9f108056369d1 100644 --- a/src/VisualStudio/VisualBasic/Impl/Options/IntelliSenseOptionPageControl.xaml.vb +++ b/src/VisualStudio/VisualBasic/Impl/Options/IntelliSenseOptionPageControl.xaml.vb @@ -1,5 +1,6 @@ ' Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +Imports System.Windows Imports Microsoft.CodeAnalysis Imports Microsoft.CodeAnalysis.Completion Imports Microsoft.VisualStudio.LanguageServices.Implementation.Options @@ -12,6 +13,10 @@ Namespace Microsoft.VisualStudio.LanguageServices.VisualBasic.Options MyBase.New(serviceProvider) InitializeComponent() + BindToOption(Show_completion_list_after_a_character_is_typed, CompletionOptions.TriggerOnTypingLetters, LanguageNames.VisualBasic) + Show_completion_list_after_a_character_is_deleted.IsChecked = Me.OptionService.GetOption( + CompletionOptions.TriggerOnDeletion, LanguageNames.VisualBasic) <> False + BindToOption(Show_completion_item_filters, CompletionOptions.ShowCompletionItemFilters, LanguageNames.VisualBasic) BindToOption(Highlight_matching_portions_of_completion_list_items, CompletionOptions.HighlightMatchingPortionsOfCompletionListItems, LanguageNames.VisualBasic) @@ -19,5 +24,17 @@ Namespace Microsoft.VisualStudio.LanguageServices.VisualBasic.Options BindToOption(Only_add_new_line_on_enter_with_whole_word, CompletionOptions.EnterKeyBehavior, EnterKeyRule.AfterFullyTypedWord, LanguageNames.VisualBasic) BindToOption(Always_add_new_line_on_enter, CompletionOptions.EnterKeyBehavior, EnterKeyRule.Always, LanguageNames.VisualBasic) End Sub + + Private Sub Show_completion_list_after_a_character_is_deleted_Checked(sender As Object, e As RoutedEventArgs) + Me.OptionService.SetOptions( + Me.OptionService.GetOptions().WithChangedOption( + CompletionOptions.TriggerOnDeletion, LanguageNames.VisualBasic, value:=True)) + End Sub + + Private Sub Show_completion_list_after_a_character_is_deleted_Unchecked(sender As Object, e As RoutedEventArgs) + Me.OptionService.SetOptions( + Me.OptionService.GetOptions().WithChangedOption( + CompletionOptions.TriggerOnDeletion, LanguageNames.VisualBasic, value:=False)) + End Sub End Class End Namespace \ No newline at end of file diff --git a/src/VisualStudio/VisualBasic/Impl/Options/IntelliSenseOptionPageStrings.vb b/src/VisualStudio/VisualBasic/Impl/Options/IntelliSenseOptionPageStrings.vb index cb5613263810c..092a0849adbc6 100644 --- a/src/VisualStudio/VisualBasic/Impl/Options/IntelliSenseOptionPageStrings.vb +++ b/src/VisualStudio/VisualBasic/Impl/Options/IntelliSenseOptionPageStrings.vb @@ -5,6 +5,12 @@ Namespace Microsoft.VisualStudio.LanguageServices.VisualBasic.Options Public ReadOnly Property Option_CompletionLists As String = BasicVSResources.Option_CompletionLists + Public ReadOnly Property Option_Show_completion_list_after_a_character_is_typed As String = + BasicVSResources.Option_Show_completion_list_after_a_character_is_typed + + Public ReadOnly Property Option_Show_completion_list_after_a_character_is_deleted As String = + BasicVSResources.Option_Show_completion_list_after_a_character_is_deleted + Public ReadOnly Property Option_Highlight_matching_portions_of_completion_list_items As String = BasicVSResources.Option_Highlight_matching_portions_of_completion_list_items diff --git a/src/VisualStudio/VisualBasic/Impl/Options/VisualBasicSettingsManagerOptionSerializer.vb b/src/VisualStudio/VisualBasic/Impl/Options/VisualBasicSettingsManagerOptionSerializer.vb index 5114dfaf33f13..9fe3ed81ecbe1 100644 --- a/src/VisualStudio/VisualBasic/Impl/Options/VisualBasicSettingsManagerOptionSerializer.vb +++ b/src/VisualStudio/VisualBasic/Impl/Options/VisualBasicSettingsManagerOptionSerializer.vb @@ -86,7 +86,9 @@ Namespace Microsoft.VisualStudio.LanguageServices.VisualBasic.Options End Property Protected Overrides Function SupportsOption([option] As IOption, languageName As String) As Boolean - If [option].Name = CompletionOptions.EnterKeyBehavior.Name Then + If [option].Name = CompletionOptions.EnterKeyBehavior.Name OrElse + [option].Name = CompletionOptions.TriggerOnTypingLetters.Name OrElse + [option].Name = CompletionOptions.TriggerOnDeletion.Name Then Return True ElseIf languageName = LanguageNames.VisualBasic Then @@ -165,9 +167,26 @@ Namespace Microsoft.VisualStudio.LanguageServices.VisualBasic.Options Return FetchEnterKeyBehavior(optionKey, value) End If + If optionKey.Option Is CompletionOptions.TriggerOnDeletion Then + Return FetchTriggerOnDeletion(optionKey, value) + End If + Return MyBase.TryFetch(optionKey, value) End Function + Private Function FetchTriggerOnDeletion(optionKey As OptionKey, ByRef value As Object) As Boolean + If MyBase.TryFetch(optionKey, value) Then + If value Is Nothing Then + ' The default behavior for VB is to trigger completion on deletion. + value = CType(True, Boolean?) + End If + + Return True + End If + + Return False + End Function + Private Function FetchEnterKeyBehavior(optionKey As OptionKey, ByRef value As Object) As Boolean If MyBase.TryFetch(optionKey, value) Then If value.Equals(EnterKeyRule.Default) Then From da05dbe10e1935ae2c9c230663181627c58a731b Mon Sep 17 00:00:00 2001 From: CyrusNajmabadi Date: Fri, 17 Jun 2016 17:40:31 -0700 Subject: [PATCH 31/36] Rename parameter. --- .../Completion/Controller.Session_FilterModel.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/EditorFeatures/Core/Implementation/IntelliSense/Completion/Controller.Session_FilterModel.cs b/src/EditorFeatures/Core/Implementation/IntelliSense/Completion/Controller.Session_FilterModel.cs index 8d6ad949cddcb..7bbea0247011c 100644 --- a/src/EditorFeatures/Core/Implementation/IntelliSense/Completion/Controller.Session_FilterModel.cs +++ b/src/EditorFeatures/Core/Implementation/IntelliSense/Completion/Controller.Session_FilterModel.cs @@ -289,7 +289,7 @@ private bool IsHardSelection( Model model, PresentationItem bestFilterMatch, ITextSnapshot textSnapshot, - CompletionHelper completionRules, + CompletionHelper completionHelper, CompletionTrigger trigger, CompletionFilterReason reason) { @@ -317,7 +317,7 @@ private bool IsHardSelection( var viewSpan = model.GetViewBufferSpan(bestFilterMatch.Item.Span); var fullFilterText = model.GetCurrentTextInSnapshot(viewSpan, textSnapshot, endPoint: null); - var shouldSoftSelect = completionRules.ShouldSoftSelectItem(bestFilterMatch.Item, fullFilterText, trigger); + var shouldSoftSelect = completionHelper.ShouldSoftSelectItem(bestFilterMatch.Item, fullFilterText, trigger); if (shouldSoftSelect) { return false; @@ -325,7 +325,7 @@ private bool IsHardSelection( // If the user moved the caret left after they started typing, the 'best' match may not match at all // against the full text span that this item would be replacing. - if (!completionRules.MatchesFilterText(bestFilterMatch.Item, fullFilterText, trigger, reason, this.Controller.GetRecentItems())) + if (!completionHelper.MatchesFilterText(bestFilterMatch.Item, fullFilterText, trigger, reason, this.Controller.GetRecentItems())) { return false; } From 15c647c714eb175de4b4d9c18d673f620aad890c Mon Sep 17 00:00:00 2001 From: CyrusNajmabadi Date: Fri, 17 Jun 2016 18:20:49 -0700 Subject: [PATCH 32/36] Make the VB deletion behavior the default for all languages. --- .../InternalUtilities/StringExtensions.cs | 14 ++++++++++- .../Completion/CompletionHelper.cs | 11 --------- .../Controller.Session_FilterModel.cs | 23 +++++++++++++++++-- .../Completion/VisualBasicCompletionHelper.vb | 16 ++----------- 4 files changed, 36 insertions(+), 28 deletions(-) diff --git a/src/Compilers/Core/Portable/InternalUtilities/StringExtensions.cs b/src/Compilers/Core/Portable/InternalUtilities/StringExtensions.cs index b0c1469a12176..9574c95884db3 100644 --- a/src/Compilers/Core/Portable/InternalUtilities/StringExtensions.cs +++ b/src/Compilers/Core/Portable/InternalUtilities/StringExtensions.cs @@ -266,5 +266,17 @@ internal static bool All(this string arg, Predicate predicate) return true; } + + public static int GetCaseInsensitivePrefixLength(this string string1, string string2) + { + int x = 0; + while (x < string1.Length && x < string2.Length && + char.ToUpper(string1[x]) == char.ToUpper(string2[x])) + { + x++; + } + + return x; + } } -} +} \ No newline at end of file diff --git a/src/EditorFeatures/Core/Extensibility/Completion/CompletionHelper.cs b/src/EditorFeatures/Core/Extensibility/Completion/CompletionHelper.cs index c5a183ec94163..f3aa84ee50a48 100644 --- a/src/EditorFeatures/Core/Extensibility/Completion/CompletionHelper.cs +++ b/src/EditorFeatures/Core/Extensibility/Completion/CompletionHelper.cs @@ -235,17 +235,6 @@ protected static bool IsEnumMemberItem(CompletionItem item) return item.Tags.Contains(CompletionTags.EnumMember); } - protected int GetPrefixLength(string text, string pattern) - { - int x = 0; - while (x < text.Length && x < pattern.Length && char.ToUpper(text[x]) == char.ToUpper(pattern[x])) - { - x++; - } - - return x; - } - protected int CompareMatches(PatternMatch match1, PatternMatch match2, CompletionItem item1, CompletionItem item2) { // First see how the two items compare in a case insensitive fashion. Matches that diff --git a/src/EditorFeatures/Core/Implementation/IntelliSense/Completion/Controller.Session_FilterModel.cs b/src/EditorFeatures/Core/Implementation/IntelliSense/Completion/Controller.Session_FilterModel.cs index 7bbea0247011c..6588ba88b12df 100644 --- a/src/EditorFeatures/Core/Implementation/IntelliSense/Completion/Controller.Session_FilterModel.cs +++ b/src/EditorFeatures/Core/Implementation/IntelliSense/Completion/Controller.Session_FilterModel.cs @@ -152,7 +152,7 @@ private Model FilterModelInBackgroundWorker( } var filterText = model.GetCurrentTextInSnapshot(currentItem.Item.Span, textSnapshot, textSpanToText); - var matchesFilterText = helper.MatchesFilterText(currentItem.Item, filterText, model.Trigger, filterReason, recentItems); + var matchesFilterText = MatchesFilterText(helper, currentItem.Item, filterText, model.Trigger, filterReason, recentItems); itemToFilterText[currentItem.Item] = filterText; if (matchesFilterText) @@ -256,6 +256,25 @@ private Model FilterModelInBackgroundWorker( return result; } + private static bool MatchesFilterText( + CompletionHelper helper, CompletionItem item, string filterText, CompletionTrigger trigger, CompletionFilterReason filterReason, ImmutableArray recentItems) + { + // For the deletion we bake in the core logic for how matching should work. + // This way deletion feels the same across all languages that opt into deletion + // as a completion trigger. + + // Specifically, to avoid being too aggressive when matching an item during + // completion, we require that the current filter text be a prefix of the + // item in the list. + if (filterReason == CompletionFilterReason.BackspaceOrDelete && + trigger.Kind == CompletionTriggerKind.Deletion) + { + return item.FilterText.GetCaseInsensitivePrefixLength(filterText) > 0; + } + + return helper.MatchesFilterText(item, filterText, trigger, filterReason, recentItems); + } + private bool ItemIsFilteredOut( CompletionItem item, ImmutableDictionary filterState) @@ -325,7 +344,7 @@ private bool IsHardSelection( // If the user moved the caret left after they started typing, the 'best' match may not match at all // against the full text span that this item would be replacing. - if (!completionHelper.MatchesFilterText(bestFilterMatch.Item, fullFilterText, trigger, reason, this.Controller.GetRecentItems())) + if (!MatchesFilterText(completionHelper, bestFilterMatch.Item, fullFilterText, trigger, reason, this.Controller.GetRecentItems())) { return false; } diff --git a/src/EditorFeatures/VisualBasic/Completion/VisualBasicCompletionHelper.vb b/src/EditorFeatures/VisualBasic/Completion/VisualBasicCompletionHelper.vb index b17ae351520aa..0fb933438427c 100644 --- a/src/EditorFeatures/VisualBasic/Completion/VisualBasicCompletionHelper.vb +++ b/src/EditorFeatures/VisualBasic/Completion/VisualBasicCompletionHelper.vb @@ -1,6 +1,5 @@ Imports System.Collections.Immutable Imports System.Composition -Imports System.Globalization Imports Microsoft.CodeAnalysis.Completion Imports Microsoft.CodeAnalysis.Host Imports Microsoft.CodeAnalysis.Host.Mef @@ -41,22 +40,11 @@ Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.Completion End Get End Property - Public Overrides Function MatchesFilterText(item As CompletionItem, filterText As String, trigger As CompletionTrigger, filterReason As CompletionFilterReason, Optional recentItems As ImmutableArray(Of String) = Nothing) As Boolean - ' If this Is a session started on backspace, we use a much looser prefix match check - ' to see if an item matches - - If filterReason = CompletionFilterReason.BackspaceOrDelete AndAlso trigger.Kind = CompletionTriggerKind.Deletion Then - Return GetPrefixLength(item.FilterText, filterText) > 0 - End If - - Return MyBase.MatchesFilterText(item, filterText, trigger, filterReason, recentItems) - End Function - Public Overrides Function IsBetterFilterMatch(item1 As CompletionItem, item2 As CompletionItem, filterText As String, trigger As CompletionTrigger, filterReason As CompletionFilterReason, Optional recentItems As ImmutableArray(Of String) = Nothing) As Boolean If filterReason = CompletionFilterReason.BackspaceOrDelete Then - Dim prefixLength1 = GetPrefixLength(item1.FilterText, filterText) - Dim prefixLength2 = GetPrefixLength(item2.FilterText, filterText) + Dim prefixLength1 = item1.FilterText.GetCaseInsensitivePrefixLength(filterText) + Dim prefixLength2 = item2.FilterText.GetCaseInsensitivePrefixLength(filterText) Return prefixLength1 > prefixLength2 OrElse ((item1.Rules.MatchPriority > MatchPriority.Default AndAlso Not item2.Rules.MatchPriority > MatchPriority.Default) AndAlso Not IsEnumMemberItem(item1)) End If From 13f87f19c3849a1ebec4d2a0770f2ac48b8dd3af Mon Sep 17 00:00:00 2001 From: Tanner Gooding Date: Fri, 17 Jun 2016 22:12:53 -0700 Subject: [PATCH 33/36] Fixing the VsixVersion to be 42.42.42.42 for unofficial builds. --- build/Targets/VSL.Versions.targets | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/build/Targets/VSL.Versions.targets b/build/Targets/VSL.Versions.targets index ab66a0006c758..3a40e4aecc07c 100644 --- a/build/Targets/VSL.Versions.targets +++ b/build/Targets/VSL.Versions.targets @@ -57,6 +57,7 @@ day-to-day upgrades don't break assembly references to other installed apps. --> $(RoslynSemanticVersion).0 + $(RoslynSemanticVersion).$(BuildNumberPart1.Trim())$(BuildNumberPart2.Trim()) @@ -64,6 +65,7 @@ $(RoslynSemanticVersion).0 $(RoslynSemanticVersion).0 + $(RoslynSemanticVersion).$(BuildNumberPart1.Trim())$(BuildNumberPart2.Trim()) @@ -75,12 +77,13 @@ 42.42.42.42 42.42.42.42 + 42.42.42.42 - + From 0e4d6a3bd2ada2568c049ff18a412aeca29b53be Mon Sep 17 00:00:00 2001 From: CyrusNajmabadi Date: Sun, 19 Jun 2016 00:46:41 -0700 Subject: [PATCH 34/36] Make more deletion code non-VB specific. --- .../Controller.Session_FilterModel.cs | 37 ++++++++++++++++++- .../Completion/VisualBasicCompletionHelper.vb | 7 ---- 2 files changed, 35 insertions(+), 9 deletions(-) diff --git a/src/EditorFeatures/Core/Implementation/IntelliSense/Completion/Controller.Session_FilterModel.cs b/src/EditorFeatures/Core/Implementation/IntelliSense/Completion/Controller.Session_FilterModel.cs index d6a1c08ca58a2..141cf208e7085 100644 --- a/src/EditorFeatures/Core/Implementation/IntelliSense/Completion/Controller.Session_FilterModel.cs +++ b/src/EditorFeatures/Core/Implementation/IntelliSense/Completion/Controller.Session_FilterModel.cs @@ -163,7 +163,7 @@ private Model FilterModelInBackgroundWorker( // If we have no best match, or this match is better than the last match, // then the current item is the best filter match. if (bestFilterMatch == null || - helper.IsBetterFilterMatch(currentItem.Item, bestFilterMatch.Item, filterText, model.Trigger, filterReason, recentItems)) + IsBetterFilterMatch(helper, currentItem.Item, bestFilterMatch.Item, filterText, model.Trigger, filterReason, recentItems)) { bestFilterMatch = currentItem; } @@ -257,8 +257,41 @@ private Model FilterModelInBackgroundWorker( return result; } + private static bool IsBetterFilterMatch( + CompletionHelper helper, CompletionItem item1, CompletionItem item2, + string filterText, CompletionTrigger trigger, + CompletionFilterReason filterReason, ImmutableArray recentItems) + { + // For the deletion we bake in the core logic for how betterness should work. + // This way deletion feels the same across all languages that opt into deletion + // as a completion trigger. + if (filterReason == CompletionFilterReason.BackspaceOrDelete) + { + var prefixLength1 = item1.FilterText.GetCaseInsensitivePrefixLength(filterText); + var prefixLength2 = item2.FilterText.GetCaseInsensitivePrefixLength(filterText); + + // Prefer the item that matches a longer prefix of the filter text. + if (prefixLength1 > prefixLength2) + { + return true; + } + + // If the lengths are the same, prefer the one with the higher match priority. + if (item1.Rules.MatchPriority > item2.Rules.MatchPriority) + { + return true; + } + + return false; + } + + return helper.IsBetterFilterMatch(item1, item2, filterText, trigger, filterReason, recentItems); + } + private static bool MatchesFilterText( - CompletionHelper helper, CompletionItem item, string filterText, CompletionTrigger trigger, CompletionFilterReason filterReason, ImmutableArray recentItems) + CompletionHelper helper, CompletionItem item, + string filterText, CompletionTrigger trigger, + CompletionFilterReason filterReason, ImmutableArray recentItems) { // For the deletion we bake in the core logic for how matching should work. // This way deletion feels the same across all languages that opt into deletion diff --git a/src/EditorFeatures/VisualBasic/Completion/VisualBasicCompletionHelper.vb b/src/EditorFeatures/VisualBasic/Completion/VisualBasicCompletionHelper.vb index 61db270a59981..cdf848542bcf4 100644 --- a/src/EditorFeatures/VisualBasic/Completion/VisualBasicCompletionHelper.vb +++ b/src/EditorFeatures/VisualBasic/Completion/VisualBasicCompletionHelper.vb @@ -41,13 +41,6 @@ Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.Completion End Property Public Overrides Function IsBetterFilterMatch(item1 As CompletionItem, item2 As CompletionItem, filterText As String, trigger As CompletionTrigger, filterReason As CompletionFilterReason, Optional recentItems As ImmutableArray(Of String) = Nothing) As Boolean - - If filterReason = CompletionFilterReason.BackspaceOrDelete Then - Dim prefixLength1 = item1.FilterText.GetCaseInsensitivePrefixLength(filterText) - Dim prefixLength2 = item2.FilterText.GetCaseInsensitivePrefixLength(filterText) - Return prefixLength1 > prefixLength2 OrElse ((item1.Rules.MatchPriority > MatchPriority.Default AndAlso Not item2.Rules.MatchPriority > MatchPriority.Default) AndAlso Not IsEnumMemberItem(item1)) - End If - If IsEnumMemberItem(item2) Then Dim match1 = GetMatch(item1, filterText) Dim match2 = GetMatch(item2, filterText) From 403afb884e33611738e2a2118bc24b2ceeccee92 Mon Sep 17 00:00:00 2001 From: CyrusNajmabadi Date: Sun, 19 Jun 2016 00:59:22 -0700 Subject: [PATCH 35/36] Remove unnecessary parameter. --- .../Core/Extensibility/Completion/CompletionHelper.cs | 6 ++++-- .../Completion/Controller.Session_FilterModel.cs | 4 ++-- .../Test2/IntelliSense/CompletionRulesTests.vb | 4 ++-- .../VisualBasic/Completion/VisualBasicCompletionHelper.vb | 7 +++++-- 4 files changed, 13 insertions(+), 8 deletions(-) diff --git a/src/EditorFeatures/Core/Extensibility/Completion/CompletionHelper.cs b/src/EditorFeatures/Core/Extensibility/Completion/CompletionHelper.cs index e5d8edd55d391..54c461815d541 100644 --- a/src/EditorFeatures/Core/Extensibility/Completion/CompletionHelper.cs +++ b/src/EditorFeatures/Core/Extensibility/Completion/CompletionHelper.cs @@ -64,7 +64,9 @@ public IReadOnlyList GetHighlightedSpans(CompletionItem completionItem /// iff the completion item matches and should be included in the filtered completion /// results, or false if it should not be. /// - public virtual bool MatchesFilterText(CompletionItem item, string filterText, CompletionTrigger trigger, CompletionFilterReason filterReason, ImmutableArray recentItems = default(ImmutableArray)) + public virtual bool MatchesFilterText( + CompletionItem item, string filterText, + CompletionTrigger trigger, ImmutableArray recentItems) { // If the user hasn't typed anything, and this item was preselected, or was in the // MRU list, then we definitely want to include it. @@ -166,7 +168,7 @@ private PatternMatcher GetEnUSPatternMatcher(string value) /// Returns true if item1 is a better completion item than item2 given the provided filter /// text, or false if it is not better. /// - public virtual bool IsBetterFilterMatch(CompletionItem item1, CompletionItem item2, string filterText, CompletionTrigger trigger, CompletionFilterReason filterReason, ImmutableArray recentItems = default(ImmutableArray)) + public virtual bool IsBetterFilterMatch(CompletionItem item1, CompletionItem item2, string filterText, CompletionTrigger trigger, ImmutableArray recentItems) { var match1 = GetMatch(item1, filterText); var match2 = GetMatch(item2, filterText); diff --git a/src/EditorFeatures/Core/Implementation/IntelliSense/Completion/Controller.Session_FilterModel.cs b/src/EditorFeatures/Core/Implementation/IntelliSense/Completion/Controller.Session_FilterModel.cs index 141cf208e7085..470f042db6001 100644 --- a/src/EditorFeatures/Core/Implementation/IntelliSense/Completion/Controller.Session_FilterModel.cs +++ b/src/EditorFeatures/Core/Implementation/IntelliSense/Completion/Controller.Session_FilterModel.cs @@ -285,7 +285,7 @@ private static bool IsBetterFilterMatch( return false; } - return helper.IsBetterFilterMatch(item1, item2, filterText, trigger, filterReason, recentItems); + return helper.IsBetterFilterMatch(item1, item2, filterText, trigger, recentItems); } private static bool MatchesFilterText( @@ -306,7 +306,7 @@ private static bool MatchesFilterText( return item.FilterText.GetCaseInsensitivePrefixLength(filterText) > 0; } - return helper.MatchesFilterText(item, filterText, trigger, filterReason, recentItems); + return helper.MatchesFilterText(item, filterText, trigger, recentItems); } private bool ItemIsFilteredOut( diff --git a/src/EditorFeatures/Test2/IntelliSense/CompletionRulesTests.vb b/src/EditorFeatures/Test2/IntelliSense/CompletionRulesTests.vb index 63a174dea0432..ca87b99404f97 100644 --- a/src/EditorFeatures/Test2/IntelliSense/CompletionRulesTests.vb +++ b/src/EditorFeatures/Test2/IntelliSense/CompletionRulesTests.vb @@ -51,7 +51,7 @@ Namespace Microsoft.CodeAnalysis.Editor.UnitTests.IntelliSense Dim helper = CompletionHelper.GetHelper(workspace, LanguageNames.CSharp) For Each word In wordsToMatch Dim item = CompletionItem.Create(word) - Assert.True(helper.MatchesFilterText(item, v, CompletionTrigger.Default, CompletionFilterReason.TypeChar), $"Expected item {word} does not match {v}") + Assert.True(helper.MatchesFilterText(item, v, CompletionTrigger.Default, Nothing), $"Expected item {word} does not match {v}") Next End Using End Sub @@ -62,7 +62,7 @@ Namespace Microsoft.CodeAnalysis.Editor.UnitTests.IntelliSense Dim helper = CompletionHelper.GetHelper(workspace, LanguageNames.CSharp) For Each word In wordsToNotMatch Dim item = CompletionItem.Create(word) - Assert.False(helper.MatchesFilterText(item, v, CompletionTrigger.Default, CompletionFilterReason.TypeChar), $"Unexpected item {word} matches {v}") + Assert.False(helper.MatchesFilterText(item, v, CompletionTrigger.Default, Nothing), $"Unexpected item {word} matches {v}") Next End Using diff --git a/src/EditorFeatures/VisualBasic/Completion/VisualBasicCompletionHelper.vb b/src/EditorFeatures/VisualBasic/Completion/VisualBasicCompletionHelper.vb index cdf848542bcf4..6ed5c34fc3a8b 100644 --- a/src/EditorFeatures/VisualBasic/Completion/VisualBasicCompletionHelper.vb +++ b/src/EditorFeatures/VisualBasic/Completion/VisualBasicCompletionHelper.vb @@ -40,7 +40,10 @@ Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.Completion End Get End Property - Public Overrides Function IsBetterFilterMatch(item1 As CompletionItem, item2 As CompletionItem, filterText As String, trigger As CompletionTrigger, filterReason As CompletionFilterReason, Optional recentItems As ImmutableArray(Of String) = Nothing) As Boolean + Public Overrides Function IsBetterFilterMatch( + item1 As CompletionItem, item2 As CompletionItem, + filterText As String, trigger As CompletionTrigger, + recentItems As ImmutableArray(Of String)) As Boolean If IsEnumMemberItem(item2) Then Dim match1 = GetMatch(item1, filterText) Dim match2 = GetMatch(item2, filterText) @@ -59,7 +62,7 @@ Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.Completion End If End If - Return MyBase.IsBetterFilterMatch(item1, item2, filterText, trigger, filterReason, recentItems) + Return MyBase.IsBetterFilterMatch(item1, item2, filterText, trigger, recentItems) End Function End Class End Namespace \ No newline at end of file From ebc4ef3adf82800a028450cc09fbd2ae8de970ac Mon Sep 17 00:00:00 2001 From: CyrusNajmabadi Date: Sun, 19 Jun 2016 01:38:58 -0700 Subject: [PATCH 36/36] Don't preselect softly selected items. --- .../Completion/Controller.Session_FilterModel.cs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/EditorFeatures/Core/Implementation/IntelliSense/Completion/Controller.Session_FilterModel.cs b/src/EditorFeatures/Core/Implementation/IntelliSense/Completion/Controller.Session_FilterModel.cs index 470f042db6001..7a8212ed454eb 100644 --- a/src/EditorFeatures/Core/Implementation/IntelliSense/Completion/Controller.Session_FilterModel.cs +++ b/src/EditorFeatures/Core/Implementation/IntelliSense/Completion/Controller.Session_FilterModel.cs @@ -277,7 +277,14 @@ private static bool IsBetterFilterMatch( } // If the lengths are the same, prefer the one with the higher match priority. - if (item1.Rules.MatchPriority > item2.Rules.MatchPriority) + // But only if it's an item that would have been hard selected. We don't want + // to aggressively select an item that was only going to be softly offered. + var item1Priority = item1.Rules.SelectionBehavior == CompletionItemSelectionBehavior.HardSelection + ? item1.Rules.MatchPriority : MatchPriority.Default; + var item2Priority = item2.Rules.SelectionBehavior == CompletionItemSelectionBehavior.HardSelection + ? item2.Rules.MatchPriority : MatchPriority.Default; + + if (item1Priority > item2Priority) { return true; }