diff --git a/build/Targets/Imports.targets b/build/Targets/Imports.targets index 48f8965adce1f06f2e7fcc36da3fcf19f597ebb9..a41f3d1d7df7e9d2e40d22dc0f8b2e88006ffa5f 100644 --- a/build/Targets/Imports.targets +++ b/build/Targets/Imports.targets @@ -11,14 +11,14 @@ <_CopyReferences>false <_CopyProjectReferences>false false - $(OutDir)Exes\$(MSBuildProjectName)\ + $(OutputPath)Exes\$(MSBuildProjectName)\ <_IsAnyUnitTest>true true - $(OutDir)UnitTests\$(MSBuildProjectName)\ + $(OutputPath)UnitTests\$(MSBuildProjectName)\ @@ -39,21 +39,21 @@ <_IsAnyUnitTest>true <_IsAnyPortableUnitTest>true false - $(OutDir)Dlls\$(MSBuildProjectName)\ + $(OutputPath)Dlls\$(MSBuildProjectName)\ <_CopyReferences>false false - $(OutDir)Exes\ + $(OutputPath)Exes\ <_CopyReferences>false true - $(OutDir)Vsix\$(MSBuildProjectName)\ + $(OutputPath)Vsix\$(MSBuildProjectName)\ @@ -62,8 +62,8 @@ $(AllowedReferenceRelatedFileExtensions);.pdb true - $(OutDir)CoreClrTest - $(OutDir)UnitTests\Portable\ + $(OutputPath)CoreClrTest + $(OutputPath)UnitTests\Portable\ @@ -74,19 +74,19 @@ <_CopyReferences>false <_CopyProjectReferences>false false - $(OutDir)Dlls\$(MSBuildProjectName)\ + $(OutputPath)Dlls\$(MSBuildProjectName)\ true - $(OutDir)Exes\$(MSBuildProjectName)\ + $(OutputPath)Exes\$(MSBuildProjectName)\ true - $(OutDir)Exes\$(MSBuildProjectName)\ + $(OutputPath)Exes\$(MSBuildProjectName)\ @@ -114,7 +114,6 @@ - $(OutDir) 0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9 002400000480000094000000060200000024000052534131000400000100010055e0217eb635f69281051f9a823e0c7edd90f28063eb6c7a742a19b4f6139778ee0af438f47aed3b6e9f99838aa8dba689c7a71ddb860c96d923830b57bbd5cd6119406ddb9b002cf1c723bf272d6acbb7129e9d6dd5a5309c94e0ff4b2c884d45a55f475cd7dba59198086f61f5a8c8b5e601c0edbf269733f6f578fc8579c2 diff --git a/build/Targets/Settings.props b/build/Targets/Settings.props index a702d4dd7508d0b750deae80ce422e0dfa77ee06..db50ab1d7abc9b38598dac62c3e39c9df0089edd 100644 --- a/build/Targets/Settings.props +++ b/build/Targets/Settings.props @@ -27,7 +27,7 @@ true true $(RepoRoot)Binaries\ - $(BaseOutputPath)$(Configuration)\ + $(BaseOutputPath)$(Configuration)\ $(RepoRoot)Binaries\Obj\ $(BaseIntermediateOutputPath)$(Configuration)\$(MSBuildProjectName)\ @@ -47,7 +47,7 @@ $(VisualStudioVersion) false diff --git a/build/scripts/build.ps1 b/build/scripts/build.ps1 index 4df62deb017bf5366a39bd0ee65ea0d06079300e..0c53de2ab25be81e0af290c3ddbbefc22b52df7b 100644 --- a/build/scripts/build.ps1 +++ b/build/scripts/build.ps1 @@ -4,6 +4,7 @@ param ( [switch]$build = $false, [switch]$restore = $false, [switch]$test = $false, + [switch]$test64 = $false, [switch]$clean = $false, [switch]$clearPackageCache = $false, [string]$project = "", @@ -17,7 +18,8 @@ function Print-Usage() { Write-Host "Build.ps1" Write-Host "`t-build Run a build operation (default false)" Write-Host "`t-restore Run a restore operation (default false)" - Write-Host "`t-test Run tests (default false)" + Write-Host "`t-test Run unit tests (default false)" + Write-Host "`t-test64 Run unit tests in 64 bit mode" Write-Host "`t-clean Do a clean build / restore (default false)" Write-Host "`t-clearPackageCache Clear package cache before restoring" Write-Host "`t-project Project the build or restore should target" @@ -38,7 +40,11 @@ function Run-Build() { function Run-Test() { $proj = Join-Path $repoDir "BuildAndTest.proj" - Exec-Command $msbuild "/v:m /p:SkipCoreClr=true /t:Test $proj" | Out-Host + $args = "/v:m /p:SkipCoreClr=true /p:ManualTest=true /t:Test $proj" + if ($test64) { + $args += " /p:Test64=true" + } + Exec-Command $msbuild $args | Out-Host } try { @@ -68,7 +74,7 @@ try { Run-Build } - if ($test) { + if ($test -or $test64) { Run-Test } } diff --git a/src/Compilers/CSharp/Test/Symbol/Symbols/SymbolErrorTests.cs b/src/Compilers/CSharp/Test/Symbol/Symbols/SymbolErrorTests.cs index 074bcaa3900f01b6c30307c6e82323f0d4268a2b..4b704f208a0b8a4e58f1a246ff0b4387916a1d4f 100644 --- a/src/Compilers/CSharp/Test/Symbol/Symbols/SymbolErrorTests.cs +++ b/src/Compilers/CSharp/Test/Symbol/Symbols/SymbolErrorTests.cs @@ -4066,7 +4066,7 @@ public class Test // TODO... } - [Fact(Skip = "https://github.com/dotnet/roslyn/issues/18800")] + [Fact] public void CS0306ERR_BadTypeArgument01() { var source = diff --git a/src/EditorFeatures/CSharpTest/Diagnostics/SimplifyTypeNames/SimplifyTypeNamesTests.cs b/src/EditorFeatures/CSharpTest/Diagnostics/SimplifyTypeNames/SimplifyTypeNamesTests.cs index 58641db39f307bed91384d40c32d0dbaa891e385..5125d16bbb921fade20fadea53a2ad6f0a5d06ad 100644 --- a/src/EditorFeatures/CSharpTest/Diagnostics/SimplifyTypeNames/SimplifyTypeNamesTests.cs +++ b/src/EditorFeatures/CSharpTest/Diagnostics/SimplifyTypeNames/SimplifyTypeNamesTests.cs @@ -3820,6 +3820,81 @@ static void G(out object o) parseOptions: CSharpParseOptions.Default.WithLanguageVersion(LanguageVersion.CSharp6)); } + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsSimplifyTypeNames)] + [WorkItem(15996, "https://github.com/dotnet/roslyn/issues/15996")] + public async Task TestMemberOfBuiltInType1() + { + await TestAsync( +@"using System; +class C +{ + void Main() + { + [|UInt32|] value = UInt32.MaxValue; + } +}", +@"using System; +class C +{ + void Main() + { + uint value = UInt32.MaxValue; + } +}", + parseOptions: CSharpParseOptions.Default, + options: PreferIntrinsicTypeInDeclaration); + } + + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsSimplifyTypeNames)] + [WorkItem(15996, "https://github.com/dotnet/roslyn/issues/15996")] + public async Task TestMemberOfBuiltInType2() + { + await TestAsync( +@"using System; +class C +{ + void Main() + { + UInt32 value = [|UInt32|].MaxValue; + } +}", +@"using System; +class C +{ + void Main() + { + UInt32 value = uint.MaxValue; + } +}", + parseOptions: CSharpParseOptions.Default, + options: PreferIntrinsicTypeInMemberAccess); + } + + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsSimplifyTypeNames)] + [WorkItem(15996, "https://github.com/dotnet/roslyn/issues/15996")] + public async Task TestMemberOfBuiltInType3() + { + await TestAsync( +@"using System; +class C +{ + void Main() + { + [|UInt32|].Parse(""foo""); + } +}", +@"using System; +class C +{ + void Main() + { + uint.Parse(""foo""); + } +}", + parseOptions: CSharpParseOptions.Default, + options: PreferIntrinsicTypeInMemberAccess); + } + private async Task TestWithPredefinedTypeOptionsAsync(string code, string expected, int index = 0) { await TestInRegularAndScriptAsync(code, expected, index: index, options: PreferIntrinsicTypeEverywhere); diff --git a/src/EditorFeatures/CSharpTest/ImplementAbstractClass/ImplementAbstractClassTests.cs b/src/EditorFeatures/CSharpTest/ImplementAbstractClass/ImplementAbstractClassTests.cs index 9354b3d36a16bd20c4ce1b7bb15cd1ec4be1f9d3..594148a084bee36ffc524e4d4e07d802c7b463a1 100644 --- a/src/EditorFeatures/CSharpTest/ImplementAbstractClass/ImplementAbstractClassTests.cs +++ b/src/EditorFeatures/CSharpTest/ImplementAbstractClass/ImplementAbstractClassTests.cs @@ -1552,5 +1552,38 @@ public override void M2(T? i = null) } }"); } + + [WorkItem(13932, "https://github.com/dotnet/roslyn/issues/13932")] + [WorkItem(5898, "https://github.com/dotnet/roslyn/issues/5898")] + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsImplementInterface)] + public async Task TestAutoProperties() + { + await TestInRegularAndScript1Async( +@"abstract class AbstractClass +{ + public abstract int ReadOnlyProp { get; } + public abstract int ReadWriteProp { get; set; } + public abstract int WriteOnlyProp { set; } +} + +class [|C|] : AbstractClass +{ +}", +@"abstract class AbstractClass +{ + public abstract int ReadOnlyProp { get; } + public abstract int ReadWriteProp { get; set; } + public abstract int WriteOnlyProp { set; } +} + +class C : AbstractClass +{ + public override int ReadOnlyProp { get; } + public override int ReadWriteProp { get; set; } + public override int WriteOnlyProp { set => throw new System.NotImplementedException(); } +}", parameters: new TestParameters(options: Option( + ImplementTypeOptions.PropertyGenerationBehavior, + ImplementTypePropertyGenerationBehavior.PreferAutoProperties))); + } } } \ No newline at end of file diff --git a/src/EditorFeatures/CSharpTest/ImplementInterface/ImplementInterfaceTests.cs b/src/EditorFeatures/CSharpTest/ImplementInterface/ImplementInterfaceTests.cs index 8c65728e7fc13a1b5cd3353e371e72e5e89e27d4..bf80853e2d6174b741c1dba1d02a62467aa5a5c6 100644 --- a/src/EditorFeatures/CSharpTest/ImplementInterface/ImplementInterfaceTests.cs +++ b/src/EditorFeatures/CSharpTest/ImplementInterface/ImplementInterfaceTests.cs @@ -3,7 +3,6 @@ using System.Collections.Generic; using System.Threading.Tasks; using Microsoft.CodeAnalysis.CodeFixes; -using Microsoft.CodeAnalysis.CodeStyle; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.CodeStyle; using Microsoft.CodeAnalysis.CSharp.ImplementInterface; @@ -6705,5 +6704,40 @@ class C : I } }"); } + + [WorkItem(13932, "https://github.com/dotnet/roslyn/issues/13932")] + [WorkItem(5898, "https://github.com/dotnet/roslyn/issues/5898")] + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsImplementInterface)] + public async Task TestAutoProperties() + { + await TestInRegularAndScript1Async( +@"interface IInterface +{ + int ReadOnlyProp { get; } + int ReadWriteProp { get; set; } + int WriteOnlyProp { set; } +} + +class Class : [|IInterface|] +{ +}", +@"interface IInterface +{ + int ReadOnlyProp { get; } + int ReadWriteProp { get; set; } + int WriteOnlyProp { set; } +} + +class Class : IInterface +{ + public int ReadOnlyProp { get; } + + public int ReadWriteProp { get; set; } + + public int WriteOnlyProp { set => throw new System.NotImplementedException(); } +}", parameters: new TestParameters(options: Option( + ImplementTypeOptions.PropertyGenerationBehavior, + ImplementTypePropertyGenerationBehavior.PreferAutoProperties))); + } } } \ No newline at end of file diff --git a/src/EditorFeatures/Core/Implementation/Suggestions/SuggestedActionsSource.cs b/src/EditorFeatures/Core/Implementation/Suggestions/SuggestedActionsSource.cs index 2c65683e1aea07bf5e885e6a048592e0018d1847..460cec8a319a4e01a8b37eeb108285c15ef32ba6 100644 --- a/src/EditorFeatures/Core/Implementation/Suggestions/SuggestedActionsSource.cs +++ b/src/EditorFeatures/Core/Implementation/Suggestions/SuggestedActionsSource.cs @@ -42,6 +42,8 @@ private class SuggestedActionsSource : ForegroundThreadAffinitizedObject, ISugge private Workspace _workspace; private int _lastSolutionVersionReported; + public event EventHandler SuggestedActionsChanged; + public SuggestedActionsSource(SuggestedActionsSourceProvider owner, ITextView textView, ITextBuffer textBuffer) { _owner = owner; @@ -64,7 +66,37 @@ public SuggestedActionsSource(SuggestedActionsSourceProvider owner, ITextView te _registration.WorkspaceChanged += OnWorkspaceChanged; } - public event EventHandler SuggestedActionsChanged; + public void Dispose() + { + if (_owner != null) + { + var updateSource = (IDiagnosticUpdateSource)_owner._diagnosticService; + updateSource.DiagnosticsUpdated -= OnDiagnosticsUpdated; + } + + if (_workspace != null) + { + _workspace.DocumentActiveContextChanged -= OnActiveContextChanged; + } + + if (_registration != null) + { + _registration.WorkspaceChanged -= OnWorkspaceChanged; + } + + if (_textView != null) + { + _textView.Closed -= OnTextViewClosed; + } + + _owner = null; + _workspace = null; + _registration = null; + _textView = null; + _subjectBuffer = null; + } + + private bool IsDisposed => _subjectBuffer == null; public bool TryGetTelemetryId(out Guid telemetryId) { @@ -111,6 +143,11 @@ public bool TryGetTelemetryId(out Guid telemetryId) { AssertIsForeground(); + if (IsDisposed) + { + return null; + } + using (Logger.LogBlock(FunctionId.SuggestedActions_GetSuggestedActions, cancellationToken)) { var document = range.Snapshot.GetOpenDocumentInCurrentContextWithChanges(); @@ -529,7 +566,7 @@ private static SuggestedActionSetPriority GetSuggestedActionSetPriority(CodeActi requestedActionCategories.Contains(PredefinedSuggestedActionCategoryNames.Refactoring)) { // Get the selection while on the UI thread. - var selection = TryGetCodeRefactoringSelection(_subjectBuffer, _textView, range); + var selection = TryGetCodeRefactoringSelection(range); if (!selection.HasValue) { // this is here to fail test and see why it is failed. @@ -593,20 +630,60 @@ private static SuggestedActionSetPriority GetSuggestedActionSetPriority(CodeActi SnapshotSpan range, CancellationToken cancellationToken) { - // Explicitly hold onto below fields in locals and use these locals throughout this code path to avoid crashes - // if these fields happen to be cleared by Dispose() below. This is required since this code path involves - // code that can run asynchronously from background thread. - var view = _textView; - var buffer = _subjectBuffer; var provider = _owner; - if (view == null || buffer == null || provider == null) + if (IsDisposed) { + // We've already been disposed. No point in continuing. return false; } - using (var asyncToken = provider.OperationListener.BeginAsyncOperation("HasSuggestedActionsAsync")) - { + using (var asyncToken = _owner.OperationListener.BeginAsyncOperation("HasSuggestedActionsAsync")) + { + // Acquire the user's selection up front, before we do any async work, so that we can + // directly grab it when we're on the UI thread. That way we don't have any reentrancy + // blocking concerns if VS wants to block on this call (for example, if the user + // explicitly invokes the 'show smart tag' command). + // + // This work must happen on the UI thread as it needs to access the _textView's mutable + // state. + // + // Note: we may be called in one of two VS scenarios: + // 1) User has moved caret to a new line. In this case VS will call into us in the + // bg to see if we have any suggested actions for this line. In order to figure + // this out, we need to see what selectoin the user has (for refactorings), which + // necessitates going back to the fg. + // + // 2) User moves to a line and immediately hits ctrl-dot. In this case, on the UI + // thread VS will kick us off and then immediately block to get the results so + // that they can expand the lightbulb. In this case we cannot do BG work first, + // then call back into the UI thread to try to get the user selection. This will + // deadlock as the UI thread is blocked on us. + // + // There are two solution to '2'. Either introduce reentrancy (which we really don't + // like to do), or just ensure that we acquire and get the users selection up front. + // This means that when we're called from the UI therad, we never try to go back to the + // UI thread. + TextSpan? selection = null; + if (IsForeground()) + { + selection = TryGetCodeRefactoringSelection(range); + } + else + { + await InvokeBelowInputPriority(() => + { + // Make sure we were not disposed between kicking off this work and getting + // to this point. + if (IsDisposed) + { + return; + } + + selection = TryGetCodeRefactoringSelection(range); + }).ConfigureAwait(false); + } + var document = range.Snapshot.GetOpenDocumentInCurrentContextWithChanges(); if (document == null) { @@ -617,7 +694,7 @@ private static SuggestedActionSetPriority GetSuggestedActionSetPriority(CodeActi return await HasFixesAsync(provider, document, range, cancellationToken).ConfigureAwait(false) || - await HasRefactoringsAsync(provider, document, buffer, view, range, cancellationToken).ConfigureAwait(false); + await HasRefactoringsAsync(provider, document, selection, cancellationToken).ConfigureAwait(false); } } @@ -658,11 +735,16 @@ private static SuggestedActionSetPriority GetSuggestedActionSetPriority(CodeActi private async Task HasRefactoringsAsync( SuggestedActionsSourceProvider provider, Document document, - ITextBuffer buffer, - ITextView view, - SnapshotSpan range, + TextSpan? selection, CancellationToken cancellationToken) { + if (!selection.HasValue) + { + // this is here to fail test and see why it is failed. + Trace.WriteLine("given range is not current"); + return false; + } + var workspace = document.Project.Solution.Workspace; var supportsFeatureService = workspace.Services.GetService(); @@ -670,28 +752,6 @@ private static SuggestedActionSetPriority GetSuggestedActionSetPriority(CodeActi provider._codeRefactoringService != null && supportsFeatureService.SupportsRefactorings(document)) { - TextSpan? selection = null; - if (IsForeground()) - { - // This operation needs to happen on UI thread because it needs to access textView.Selection. - selection = TryGetCodeRefactoringSelection(buffer, view, range); - } - else - { - await InvokeBelowInputPriority(() => - { - // This operation needs to happen on UI thread because it needs to access textView.Selection. - selection = TryGetCodeRefactoringSelection(buffer, view, range); - }).ConfigureAwait(false); - } - - if (!selection.HasValue) - { - // this is here to fail test and see why it is failed. - Trace.WriteLine("given range is not current"); - return false; - } - return await Task.Run( () => provider._codeRefactoringService.HasRefactoringsAsync( document, selection.Value, cancellationToken), @@ -701,11 +761,14 @@ private static SuggestedActionSetPriority GetSuggestedActionSetPriority(CodeActi return false; } - private static TextSpan? TryGetCodeRefactoringSelection(ITextBuffer buffer, ITextView view, SnapshotSpan range) + private TextSpan? TryGetCodeRefactoringSelection(SnapshotSpan range) { - var selectedSpans = view.Selection.SelectedSpans - .SelectMany(ss => view.BufferGraph.MapDownToBuffer(ss, SpanTrackingMode.EdgeExclusive, buffer)) - .Where(ss => !view.IsReadOnlyOnSurfaceBuffer(ss)) + this.AssertIsForeground(); + Debug.Assert(!this.IsDisposed); + + var selectedSpans = _textView.Selection.SelectedSpans + .SelectMany(ss => _textView.BufferGraph.MapDownToBuffer(ss, SpanTrackingMode.EdgeExclusive, _subjectBuffer)) + .Where(ss => !_textView.IsReadOnlyOnSurfaceBuffer(ss)) .ToList(); // We only support refactorings when there is a single selection in the document. @@ -796,39 +859,6 @@ private void OnSuggestedActionsChanged(Workspace currentWorkspace, DocumentId cu Volatile.Write(ref _lastSolutionVersionReported, solutionVersion); } - - public void Dispose() - { - if (_owner != null) - { - var updateSource = (IDiagnosticUpdateSource)_owner._diagnosticService; - updateSource.DiagnosticsUpdated -= OnDiagnosticsUpdated; - _owner = null; - } - - if (_workspace != null) - { - _workspace.DocumentActiveContextChanged -= OnActiveContextChanged; - _workspace = null; - } - - if (_registration != null) - { - _registration.WorkspaceChanged -= OnWorkspaceChanged; - _registration = null; - } - - if (_textView != null) - { - _textView.Closed -= OnTextViewClosed; - _textView = null; - } - - if (_subjectBuffer != null) - { - _subjectBuffer = null; - } - } } } } \ No newline at end of file diff --git a/src/EditorFeatures/Test/Utilities/PatternMatcherTests.cs b/src/EditorFeatures/Test/Utilities/PatternMatcherTests.cs index 7a481d5702a8d0b18176f35eb5d2ab0eafc1d6f1..0b4005470aafe123a019adc4b65c106260a74c7a 100644 --- a/src/EditorFeatures/Test/Utilities/PatternMatcherTests.cs +++ b/src/EditorFeatures/Test/Utilities/PatternMatcherTests.cs @@ -182,6 +182,8 @@ private void VerifyBreakIntoCharacterParts(string original, params string[] part [InlineData("[|Fog|]Bar", "fog", PatternMatchKind.Prefix, CaseInsensitive)] [InlineData("[|fog|]BarFoo", "Fog", PatternMatchKind.Prefix, CaseInsensitive)] + [InlineData("[|system.ref|]lection", "system.ref", PatternMatchKind.Prefix, CaseSensitive)] + [InlineData("Fog[|B|]ar", "b", PatternMatchKind.Substring, CaseInsensitive)] [InlineData("_[|my|]Button", "my", PatternMatchKind.Substring, CaseSensitive)] @@ -234,10 +236,10 @@ private void VerifyBreakIntoCharacterParts(string original, params string[] part [InlineData("my[|_b|]utton", "_B", PatternMatchKind.CamelCase, CaseInsensitive, PatternMatcher.CamelCaseContiguousBonus)] [InlineData("[|_|]my_[|b|]utton", "_B", PatternMatchKind.CamelCase, CaseInsensitive, PatternMatcher.CamelCaseMatchesFromStartBonus)] - public void TryMatchSingleWordPattern( + public void TestNonFuzzyMatch( string candidate, string pattern, int matchKindInt, bool isCaseSensitive, int? camelCaseWeight = null) { - var match = TryMatchSingleWordPattern(candidate, pattern); + var match = TestNonFuzzyMatch(candidate, pattern); Assert.NotNull(match); var matchKind = (PatternMatchKind)matchKindInt; @@ -269,9 +271,10 @@ private void VerifyBreakIntoCharacterParts(string original, params string[] part [InlineData("FogBarBaz", "FZ")] [InlineData("_mybutton", "myB")] [InlineData("FogBarChangedEventArgs", "changedeventarrrgh")] - public void TryMatchSingleWordPattern_NoMatch(string candidate, string pattern) + [InlineData("runtime.native.system", "system.reflection")] + public void TestNonFuzzyMatch_NoMatch(string candidate, string pattern) { - var match = TryMatchSingleWordPattern(candidate, pattern); + var match = TestNonFuzzyMatch(candidate, pattern); Assert.Null(match); } @@ -470,7 +473,7 @@ public void TryMatchSingleWordPattern_CultureAwareSingleWordPreferCaseSensitiveE try { - var match = TryMatchSingleWordPattern("[|ioo|]", "\u0130oo"); // u0130 = Capital I with dot + var match = TestNonFuzzyMatch("[|ioo|]", "\u0130oo"); // u0130 = Capital I with dot Assert.Equal(PatternMatchKind.Exact, match.Value.Kind); Assert.False(match.Value.IsCaseSensitive); @@ -499,11 +502,15 @@ private static IList BreakIntoCharacterParts(string identifier) private static IList BreakIntoWordParts(string identifier) => PartListToSubstrings(identifier, StringBreaker.BreakIntoWordParts(identifier)); - private static PatternMatch? TryMatchSingleWordPattern(string candidate, string pattern) + private static PatternMatch? TestNonFuzzyMatch(string candidate, string pattern) { MarkupTestFile.GetSpans(candidate, out candidate, out ImmutableArray spans); - var match = new PatternMatcher(pattern).MatchSingleWordPattern_ForTestingOnly(candidate); + var match = new PatternMatcher(pattern).GetFirstMatch(candidate, includeMatchSpans: true); + if (match?.Kind == PatternMatchKind.Fuzzy) + { + match = null; + } if (match == null) { @@ -536,4 +543,4 @@ private static IEnumerable TryMatchMultiWordPattern(string candida } } } -} +} \ No newline at end of file diff --git a/src/EditorFeatures/Test2/IntelliSense/CompletionRulesTests.vb b/src/EditorFeatures/Test2/IntelliSense/CompletionRulesTests.vb index a270b6cf50ba6e5069ec95ba24c86c5029fca646..fb1571ec4853bbc5e0059b5fff1f9e0017fe9a48 100644 --- a/src/EditorFeatures/Test2/IntelliSense/CompletionRulesTests.vb +++ b/src/EditorFeatures/Test2/IntelliSense/CompletionRulesTests.vb @@ -3,6 +3,7 @@ Imports System.Globalization Imports Microsoft.CodeAnalysis.Completion Imports Microsoft.CodeAnalysis.Editor.UnitTests.Workspaces +Imports Microsoft.CodeAnalysis.Text Namespace Microsoft.CodeAnalysis.Editor.UnitTests.IntelliSense ' These tests adapted from David Kean's table at @@ -10,7 +11,7 @@ Namespace Microsoft.CodeAnalysis.Editor.UnitTests.IntelliSense Public Class CompletionRulesTests Public Sub TestMatchLowerCaseEnglishI() - Dim wordsToMatch = {"index", "Index", "işte", "İşte"} + Dim wordsToMatch = {"[|i|]ndex", "[|I|]ndex", "[|i|]şte", "[|İ|]şte"} Dim wordsToNotMatch = {"ırak"} TestMatches("i", wordsToMatch) @@ -19,7 +20,7 @@ Namespace Microsoft.CodeAnalysis.Editor.UnitTests.IntelliSense Public Sub TestMatchDottedUpperTurkishI() - Dim wordsToMatch = {"index", "işte", "İşte"} + Dim wordsToMatch = {"[|i|]ndex", "[|i|]şte", "[|İ|]şte"} Dim wordsToNotMatch = {"ırak", "Irak", "Index"} TestMatches("İ", wordsToMatch) @@ -28,7 +29,7 @@ Namespace Microsoft.CodeAnalysis.Editor.UnitTests.IntelliSense Public Sub TestMatchNonDottedLowerTurkishI() - Dim wordsToMatch = {"ırak", "Irak"} + Dim wordsToMatch = {"[|ı|]rak", "[|I|]rak"} Dim wordsToNotMatch = {"index", "işte", "İşte"} TestMatches("ı", wordsToMatch) @@ -37,31 +38,48 @@ Namespace Microsoft.CodeAnalysis.Editor.UnitTests.IntelliSense Public Sub TestMatchEnglishUpperI() - Dim wordsToMatch = {"Index", "index", "ırak", "Irak"} + ' In turkish-culture "I" will not match "index". However, we want to verify that + ' the underlying completion helper will fallback to doing an en-us check if the + ' tr-tr check fails, and that it properly also returns the matched spans in this case. + + Dim wordsToMatch = {"[|I|]ndex", "[|i|]ndex", "[|ı|]rak", "[|I|]rak"} Dim wordsToNotMatch = {"İşte"} TestMatches("I", wordsToMatch) TestNotMatches("I", wordsToNotMatch) End Sub - Private Sub TestMatches(v As String, wordsToMatch() As String) + Private Sub TestMatches(pattern As String, wordsToMatch() As String) Dim culture = New CultureInfo("tr-TR", useUserOverride:=False) Dim workspace = New TestWorkspace Dim helper = CompletionHelper.GetHelper(workspace, LanguageNames.CSharp) - For Each word In wordsToMatch + + For Each wordMarkup In wordsToMatch + Dim word As String = Nothing + Dim wordMatchSpan As TextSpan = Nothing + MarkupTestFile.GetSpan(wordMarkup, word, wordMatchSpan) + Dim item = CompletionItem.Create(word) - Assert.True(helper.MatchesPattern(item.FilterText, v, culture), $"Expected item {word} does not match {v}") + Assert.True(helper.MatchesPattern(item.FilterText, pattern, culture), $"Expected item {word} does not match {pattern}") + + Dim highlightedSpans = helper.GetHighlightedSpans(item.FilterText, pattern, culture) + Assert.NotEmpty(highlightedSpans) + Assert.Equal(1, highlightedSpans.Length) + Assert.Equal(wordMatchSpan, highlightedSpans(0)) Next End Sub - Private Sub TestNotMatches(v As String, wordsToNotMatch() As String) + Private Sub TestNotMatches(pattern As String, wordsToNotMatch() As String) Dim culture = New CultureInfo("tr-TR", useUserOverride:=False) Dim workspace = New TestWorkspace Dim helper = CompletionHelper.GetHelper(workspace, LanguageNames.CSharp) For Each word In wordsToNotMatch Dim item = CompletionItem.Create(word) - Assert.False(helper.MatchesPattern(item.FilterText, v, culture), $"Unexpected item {word} matches {v}") + Assert.False(helper.MatchesPattern(item.FilterText, pattern, culture), $"Unexpected item {word} matches {pattern}") + + Dim highlightedSpans = helper.GetHighlightedSpans(item.FilterText, pattern, culture) + Assert.Empty(highlightedSpans) Next End Sub End Class diff --git a/src/EditorFeatures/VisualBasicTest/Diagnostics/SimplifyTypeNames/SimplifyTypeNamesTests.vb b/src/EditorFeatures/VisualBasicTest/Diagnostics/SimplifyTypeNames/SimplifyTypeNamesTests.vb index 5ee61c03636f79171ade3ad932d0914665ac3dd2..9e9732a845bf8bfaeae3027073e2c30c69eded6d 100644 --- a/src/EditorFeatures/VisualBasicTest/Diagnostics/SimplifyTypeNames/SimplifyTypeNamesTests.vb +++ b/src/EditorFeatures/VisualBasicTest/Diagnostics/SimplifyTypeNames/SimplifyTypeNamesTests.vb @@ -2430,5 +2430,62 @@ End Class") diagnosticId:=IDEDiagnosticIds.RemoveQualificationDiagnosticId, diagnosticSeverity:=DiagnosticSeverity.Error) End Function + + + + Public Async Function TestMemberOfBuiltInType1() As Task + Await TestInRegularAndScriptAsync( +"Imports System +Module Module1 + Sub Main() + Dim var As [|UInt32|] = UInt32.MinValue + End Sub +End Module", +"Imports System +Module Module1 + Sub Main() + Dim var As UInteger = UInt32.MinValue + End Sub +End Module", + options:=PreferIntrinsicPredefinedTypeInDeclaration()) + End Function + + + + Public Async Function TestMemberOfBuiltInType2() As Task + Await TestInRegularAndScriptAsync( +"Imports System +Module Module1 + Sub Main() + Dim var As UInt32 = [|UInt32|].MinValue + End Sub +End Module", +"Imports System +Module Module1 + Sub Main() + Dim var As UInt32 = UInteger.MinValue + End Sub +End Module", + options:=PreferIntrinsicTypeInMemberAccess()) + End Function + + + + Public Async Function TestMemberOfBuiltInType3() As Task + Await TestInRegularAndScriptAsync( +"Imports System +Module Module1 + Sub Main() + [|UInt32|].Parse(""Foo"") + End Sub +End Module", +"Imports System +Module Module1 + Sub Main() + UInteger.Parse(""Foo"") + End Sub +End Module", + options:=PreferIntrinsicTypeInMemberAccess()) + End Function End Class End Namespace \ No newline at end of file diff --git a/src/EditorFeatures/VisualBasicTest/ImplementAbstractClass/ImplementAbstractClassTests.vb b/src/EditorFeatures/VisualBasicTest/ImplementAbstractClass/ImplementAbstractClassTests.vb index 68036de39b15f4ff7064c5337a0bb0a0dbe97396..0060c89b6c5ec788235926865dc709822d1a1bef 100644 --- a/src/EditorFeatures/VisualBasicTest/ImplementAbstractClass/ImplementAbstractClassTests.vb +++ b/src/EditorFeatures/VisualBasicTest/ImplementAbstractClass/ImplementAbstractClassTests.vb @@ -3,6 +3,7 @@ Imports Microsoft.CodeAnalysis.CodeFixes Imports Microsoft.CodeAnalysis.Diagnostics Imports Microsoft.CodeAnalysis.Editor.VisualBasic.UnitTests.Diagnostics +Imports Microsoft.CodeAnalysis.ImplementType Imports Microsoft.CodeAnalysis.VisualBasic.ImplementAbstractClass Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.UnitTests.ImplementAbstractClass @@ -574,5 +575,42 @@ Class x End Function End Class") End Function + + + + + Public Async Function TestAutoProperties() As Task + Await TestInRegularAndScript1Async( +"MustInherit Class AbstractClass + MustOverride ReadOnly Property ReadOnlyProp As Integer + MustOverride Property ReadWriteProp As Integer + MustOverride WriteOnly Property WriteOnlyProp As Integer +End Class + +Class [|C|] + Inherits AbstractClass + +End Class", +"MustInherit Class AbstractClass + MustOverride ReadOnly Property ReadOnlyProp As Integer + MustOverride Property ReadWriteProp As Integer + MustOverride WriteOnly Property WriteOnlyProp As Integer +End Class + +Class C + Inherits AbstractClass + + Public Overrides ReadOnly Property ReadOnlyProp As Integer + Public Overrides Property ReadWriteProp As Integer + + Public Overrides WriteOnly Property WriteOnlyProp As Integer + Set(value As Integer) + Throw New System.NotImplementedException() + End Set + End Property +End Class", parameters:=New TestParameters(options:=[Option]( + ImplementTypeOptions.PropertyGenerationBehavior, + ImplementTypePropertyGenerationBehavior.PreferAutoProperties))) + End Function End Class End Namespace \ No newline at end of file diff --git a/src/EditorFeatures/VisualBasicTest/ImplementInterface/ImplementInterfaceTests.vb b/src/EditorFeatures/VisualBasicTest/ImplementInterface/ImplementInterfaceTests.vb index 4d211574599c0e8e3421f35ac84b8a2ec88f6c40..66e9ce5fc2865378052983d6b0144aecc7213155 100644 --- a/src/EditorFeatures/VisualBasicTest/ImplementInterface/ImplementInterfaceTests.vb +++ b/src/EditorFeatures/VisualBasicTest/ImplementInterface/ImplementInterfaceTests.vb @@ -3,6 +3,7 @@ Imports Microsoft.CodeAnalysis.CodeFixes Imports Microsoft.CodeAnalysis.Diagnostics Imports Microsoft.CodeAnalysis.Editor.VisualBasic.UnitTests.Diagnostics +Imports Microsoft.CodeAnalysis.ImplementType Imports Microsoft.CodeAnalysis.VisualBasic.ImplementInterface Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.UnitTests.ImplementInterface @@ -4518,5 +4519,41 @@ Namespace System End Structure End Namespace") End Function + + + + + Public Async Function TestAutoProperties() As Task + Await TestInRegularAndScript1Async( +"interface IInterface + readonly property ReadOnlyProp as integer + property ReadWriteProp as integer + writeonly property WriteOnlyProp as integer +end interface + +class Class + implements [|IInterface|] +end class", +"interface IInterface + readonly property ReadOnlyProp as integer + property ReadWriteProp as integer + writeonly property WriteOnlyProp as integer +end interface + +class Class + implements IInterface + + Public ReadOnly Property ReadOnlyProp As Integer Implements IInterface.ReadOnlyProp + Public Property ReadWriteProp As Integer Implements IInterface.ReadWriteProp + + Public WriteOnly Property WriteOnlyProp As Integer Implements IInterface.WriteOnlyProp + Set(value As Integer) + Throw New System.NotImplementedException() + End Set + End Property +end class", parameters:=New TestParameters(options:=[Option]( + ImplementTypeOptions.PropertyGenerationBehavior, + ImplementTypePropertyGenerationBehavior.PreferAutoProperties))) + End Function End Class End Namespace \ No newline at end of file diff --git a/src/Features/Core/Portable/Completion/CompletionHelper.cs b/src/Features/Core/Portable/Completion/CompletionHelper.cs index 2ad6c674d19326786a8b55f49fa84fe166a19fa1..f98f9b1d3b6e784c0c427aa6c2e10b30256898f9 100644 --- a/src/Features/Core/Portable/Completion/CompletionHelper.cs +++ b/src/Features/Core/Portable/Completion/CompletionHelper.cs @@ -112,7 +112,7 @@ public bool MatchesPattern(string text, string pattern, CultureInfo culture) if (!culture.Equals(EnUSCultureInfo)) { patternMatcher = this.GetPatternMatcher(pattern, EnUSCultureInfo); - match = patternMatcher.GetFirstMatch(completionItemText); + match = patternMatcher.GetFirstMatch(completionItemText, includeMatchSpans); if (match != null) { return match; diff --git a/src/Features/Core/Portable/ImplementAbstractClass/AbstractImplementAbstractClassService.Editor.cs b/src/Features/Core/Portable/ImplementAbstractClass/AbstractImplementAbstractClassService.Editor.cs index d3a42a80ee5b51285af2953936c2b0a70061a914..49e68b7e242663d3a950d103d729771e9c046aea 100644 --- a/src/Features/Core/Portable/ImplementAbstractClass/AbstractImplementAbstractClassService.Editor.cs +++ b/src/Features/Core/Portable/ImplementAbstractClass/AbstractImplementAbstractClassService.Editor.cs @@ -1,7 +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.Generic; using System.Collections.Immutable; using System.Linq; using System.Threading; @@ -37,11 +35,12 @@ public async Task GetEditAsync(CancellationToken cancellationToken) { var unimplementedMembers = _state.UnimplementedMembers; + var options = await _document.GetOptionsAsync(cancellationToken).ConfigureAwait(false); + var propertyGenerationBehavior = options.GetOption(ImplementTypeOptions.PropertyGenerationBehavior); + var memberDefinitions = GenerateMembers( - unimplementedMembers, - cancellationToken); + unimplementedMembers, propertyGenerationBehavior, cancellationToken); - var options = await _document.GetOptionsAsync(cancellationToken).ConfigureAwait(false); var insertionBehavior = options.GetOption(ImplementTypeOptions.InsertionBehavior); var groupMembers = insertionBehavior == ImplementTypeInsertionBehavior.WithOtherMembersOfTheSameKind; @@ -51,23 +50,25 @@ public async Task GetEditAsync(CancellationToken cancellationToken) memberDefinitions, new CodeGenerationOptions( _state.Location.GetLocation(), - autoInsertionLocation: groupMembers, + autoInsertionLocation: groupMembers, sortMembers: groupMembers), cancellationToken).ConfigureAwait(false); } private ImmutableArray GenerateMembers( ImmutableArray<(INamedTypeSymbol type, ImmutableArray members)> unimplementedMembers, + ImplementTypePropertyGenerationBehavior propertyGenerationBehavior, CancellationToken cancellationToken) { return unimplementedMembers.SelectMany(t => t.members) - .Select(m => GenerateMember(m, cancellationToken)) + .Select(m => GenerateMember(m, propertyGenerationBehavior, cancellationToken)) .WhereNotNull() .ToImmutableArray(); } private ISymbol GenerateMember( ISymbol member, + ImplementTypePropertyGenerationBehavior propertyGenerationBehavior, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); @@ -76,32 +77,29 @@ public async Task GetEditAsync(CancellationToken cancellationToken) var syntaxFacts = _document.Project.LanguageServices.GetService(); var addUnsafe = member.IsUnsafe() && !syntaxFacts.IsUnsafeContext(_state.Location); - return GenerateMember(member, addUnsafe, cancellationToken); + return GenerateMember(member, addUnsafe, propertyGenerationBehavior, cancellationToken); } private ISymbol GenerateMember( ISymbol member, bool addUnsafe, + ImplementTypePropertyGenerationBehavior propertyGenerationBehavior, CancellationToken cancellationToken) { var modifiers = new DeclarationModifiers(isOverride: true, isUnsafe: addUnsafe); var accessibility = member.ComputeResultantAccessibility(_state.ClassType); - if (member.Kind == SymbolKind.Method) - { - return GenerateMethod((IMethodSymbol)member, modifiers, accessibility, cancellationToken); - } - else if (member.Kind == SymbolKind.Property) - { - return GenerateProperty((IPropertySymbol)member, modifiers, accessibility, cancellationToken); - } - else if (member.Kind == SymbolKind.Event) + switch (member) { - var @event = (IEventSymbol)member; - return CodeGenerationSymbolFactory.CreateEventSymbol( - @event, - accessibility: accessibility, - modifiers: modifiers); + case IMethodSymbol method: + return GenerateMethod(method, modifiers, accessibility, cancellationToken); + + case IPropertySymbol property: + return GenerateProperty(property, modifiers, accessibility, propertyGenerationBehavior, cancellationToken); + + case IEventSymbol @event: + return CodeGenerationSymbolFactory.CreateEventSymbol( + @event, accessibility: accessibility, modifiers: modifiers); } return null; @@ -128,18 +126,27 @@ public async Task GetEditAsync(CancellationToken cancellationToken) IPropertySymbol property, DeclarationModifiers modifiers, Accessibility accessibility, + ImplementTypePropertyGenerationBehavior propertyGenerationBehavior, CancellationToken cancellationToken) { + if (property.GetMethod == null) + { + // Can't generate an auto-prop for a setter-only property. + propertyGenerationBehavior = ImplementTypePropertyGenerationBehavior.PreferThrowingProperties; + } + var syntaxFactory = _document.Project.LanguageServices.GetService(); - var throwingBody = syntaxFactory.CreateThrowNotImplementedStatementBlock( - _model.Compilation); + + var accessorBody = propertyGenerationBehavior == ImplementTypePropertyGenerationBehavior.PreferAutoProperties + ? default(ImmutableArray) + : syntaxFactory.CreateThrowNotImplementedStatementBlock(_model.Compilation); var getMethod = ShouldGenerateAccessor(property.GetMethod) ? CodeGenerationSymbolFactory.CreateAccessorSymbol( property.GetMethod, attributes: default(ImmutableArray), accessibility: property.GetMethod.ComputeResultantAccessibility(_state.ClassType), - statements: throwingBody) + statements: accessorBody) : null; var setMethod = ShouldGenerateAccessor(property.SetMethod) @@ -147,7 +154,7 @@ public async Task GetEditAsync(CancellationToken cancellationToken) property.SetMethod, attributes: default(ImmutableArray), accessibility: property.SetMethod.ComputeResultantAccessibility(_state.ClassType), - statements: throwingBody) + statements: accessorBody) : null; return CodeGenerationSymbolFactory.CreatePropertySymbol( @@ -158,7 +165,8 @@ public async Task GetEditAsync(CancellationToken cancellationToken) setMethod: setMethod); } - private bool ShouldGenerateAccessor(IMethodSymbol method) => method != null && _state.ClassType.FindImplementationForAbstractMember(method) == null; + private bool ShouldGenerateAccessor(IMethodSymbol method) + => method != null && _state.ClassType.FindImplementationForAbstractMember(method) == null; } } -} +} \ No newline at end of file diff --git a/src/Features/Core/Portable/ImplementInterface/AbstractImplementInterfaceService.CodeAction.cs b/src/Features/Core/Portable/ImplementInterface/AbstractImplementInterfaceService.CodeAction.cs index 7c3bcba99af707394843a09c789899063a0818e8..4071f72f9680971d1fe5edee3c812ceb1181eca2 100644 --- a/src/Features/Core/Portable/ImplementInterface/AbstractImplementInterfaceService.CodeAction.cs +++ b/src/Features/Core/Portable/ImplementInterface/AbstractImplementInterfaceService.CodeAction.cs @@ -176,15 +176,15 @@ public Task GetUpdatedDocumentAsync(CancellationToken cancellationToke var compilation = await result.Project.GetCompilationAsync(cancellationToken).ConfigureAwait(false); var isComImport = unimplementedMembers.Any(t => t.Item1.IsComImport); + var options = await document.GetOptionsAsync(cancellationToken).ConfigureAwait(false); + var propertyGenerationBehavior = options.GetOption(ImplementTypeOptions.PropertyGenerationBehavior); + var memberDefinitions = GenerateMembers( - compilation, - unimplementedMembers, - cancellationToken); + compilation, unimplementedMembers, propertyGenerationBehavior, cancellationToken); // Only group the members in the destination if the user wants that *and* // it's not a ComImport interface. Member ordering in ComImport interfaces // matters, so we don't want to much with them. - var options = await document.GetOptionsAsync(cancellationToken).ConfigureAwait(false); var insertionBehavior = options.GetOption(ImplementTypeOptions.InsertionBehavior); var groupMembers = !isComImport && insertionBehavior == ImplementTypeInsertionBehavior.WithOtherMembersOfTheSameKind; @@ -203,6 +203,7 @@ public Task GetUpdatedDocumentAsync(CancellationToken cancellationToke private ImmutableArray GenerateMembers( Compilation compilation, ImmutableArray<(INamedTypeSymbol type, ImmutableArray members)> unimplementedMembers, + ImplementTypePropertyGenerationBehavior propertyGenerationBehavior, CancellationToken cancellationToken) { // As we go along generating members we may end up with conflicts. For example, say @@ -229,7 +230,9 @@ public Task GetUpdatedDocumentAsync(CancellationToken cancellationToke foreach (var unimplementedInterfaceMember in unimplementedInterfaceMembers) { - var member = GenerateMember(compilation, unimplementedInterfaceMember, implementedVisibleMembers, cancellationToken); + var member = GenerateMember( + compilation, unimplementedInterfaceMember, implementedVisibleMembers, + propertyGenerationBehavior, cancellationToken); if (member != null) { implementedMembers.Add(member); @@ -272,6 +275,7 @@ private string DetermineMemberName(ISymbol member, List implementedVisi Compilation compilation, ISymbol member, List implementedVisibleMembers, + ImplementTypePropertyGenerationBehavior propertyGenerationBehavior, CancellationToken cancellationToken) { // First check if we already generate a member that matches the member we want to @@ -308,7 +312,9 @@ private string DetermineMemberName(ISymbol member, List implementedVisi var syntaxFacts = Document.GetLanguageService(); var addUnsafe = member.IsUnsafe() && !syntaxFacts.IsUnsafeContext(State.Location); - return GenerateMember(compilation, member, memberName, generateInvisibleMember, generateAbstractly, addNew, addUnsafe, cancellationToken); + return GenerateMember( + compilation, member, memberName, generateInvisibleMember, generateAbstractly, + addNew, addUnsafe, propertyGenerationBehavior, cancellationToken); } private bool GenerateInvisibleMember(ISymbol member, string memberName) @@ -376,6 +382,7 @@ private static bool IsUnexpressibleTypeParameter(ITypeParameterSymbol typeParame bool generateAbstractly, bool addNew, bool addUnsafe, + ImplementTypePropertyGenerationBehavior propertyGenerationBehavior, CancellationToken cancellationToken) { var factory = this.Document.GetLanguageService(); @@ -386,35 +393,28 @@ private static bool IsUnexpressibleTypeParameter(ITypeParameterSymbol typeParame ? Accessibility.Public : Accessibility.Private; - if (member.Kind == SymbolKind.Method) - { - var method = (IMethodSymbol)member; - - return GenerateMethod(compilation, method, accessibility, modifiers, generateAbstractly, useExplicitInterfaceSymbol, memberName, cancellationToken); - } - else if (member.Kind == SymbolKind.Property) - { - var property = (IPropertySymbol)member; - - return GenerateProperty(compilation, property, accessibility, modifiers, generateAbstractly, useExplicitInterfaceSymbol, memberName, cancellationToken); - } - else if (member.Kind == SymbolKind.Event) + switch (member) { - var @event = (IEventSymbol)member; - - var accessor = CodeGenerationSymbolFactory.CreateAccessorSymbol( - attributes: default(ImmutableArray), - accessibility: Accessibility.NotApplicable, - statements: factory.CreateThrowNotImplementedStatementBlock(compilation)); - - return CodeGenerationSymbolFactory.CreateEventSymbol( - @event, - accessibility: accessibility, - modifiers: modifiers, - explicitInterfaceSymbol: useExplicitInterfaceSymbol ? @event : null, - name: memberName, - addMethod: GetAddOrRemoveMethod(generateInvisibly, accessor, memberName, factory.AddEventHandler), - removeMethod: GetAddOrRemoveMethod(generateInvisibly, accessor, memberName, factory.RemoveEventHandler)); + case IMethodSymbol method: + return GenerateMethod(compilation, method, accessibility, modifiers, generateAbstractly, useExplicitInterfaceSymbol, memberName, cancellationToken); + + case IPropertySymbol property: + return GenerateProperty(compilation, property, accessibility, modifiers, generateAbstractly, useExplicitInterfaceSymbol, memberName, propertyGenerationBehavior, cancellationToken); + + case IEventSymbol @event: + var accessor = CodeGenerationSymbolFactory.CreateAccessorSymbol( + attributes: default(ImmutableArray), + accessibility: Accessibility.NotApplicable, + statements: factory.CreateThrowNotImplementedStatementBlock(compilation)); + + return CodeGenerationSymbolFactory.CreateEventSymbol( + @event, + accessibility: accessibility, + modifiers: modifiers, + explicitInterfaceSymbol: useExplicitInterfaceSymbol ? @event : null, + name: memberName, + addMethod: GetAddOrRemoveMethod(generateInvisibly, accessor, memberName, factory.AddEventHandler), + removeMethod: GetAddOrRemoveMethod(generateInvisibly, accessor, memberName, factory.RemoveEventHandler)); } return null; diff --git a/src/Features/Core/Portable/ImplementInterface/AbstractImplementInterfaceService.CodeAction_Property.cs b/src/Features/Core/Portable/ImplementInterface/AbstractImplementInterfaceService.CodeAction_Property.cs index d0166081792cb0b0c62ec06b267f2f8ec687db6d..c01ac73c953bffb1bbe90d3b47d203fa3c89ab21 100644 --- a/src/Features/Core/Portable/ImplementInterface/AbstractImplementInterfaceService.CodeAction_Property.cs +++ b/src/Features/Core/Portable/ImplementInterface/AbstractImplementInterfaceService.CodeAction_Property.cs @@ -7,6 +7,7 @@ using System.Threading; using Microsoft.CodeAnalysis.CodeGeneration; using Microsoft.CodeAnalysis.Editing; +using Microsoft.CodeAnalysis.ImplementType; using Microsoft.CodeAnalysis.LanguageServices; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Shared.Utilities; @@ -25,16 +26,19 @@ internal partial class ImplementInterfaceCodeAction bool generateAbstractly, bool useExplicitInterfaceSymbol, string memberName, + ImplementTypePropertyGenerationBehavior propertyGenerationBehavior, CancellationToken cancellationToken) { var factory = this.Document.GetLanguageService(); var attributesToRemove = AttributesToRemove(compilation); - var getAccessor = GenerateGetAccessor(compilation, property, accessibility, generateAbstractly, - useExplicitInterfaceSymbol, attributesToRemove, cancellationToken); + var getAccessor = GenerateGetAccessor( + compilation, property, accessibility, generateAbstractly, useExplicitInterfaceSymbol, + propertyGenerationBehavior, attributesToRemove, cancellationToken); - var setAccessor = GenerateSetAccessor(compilation, property, accessibility, - generateAbstractly, useExplicitInterfaceSymbol, attributesToRemove, cancellationToken); + var setAccessor = GenerateSetAccessor( + compilation, property, accessibility, generateAbstractly, useExplicitInterfaceSymbol, + propertyGenerationBehavior, attributesToRemove, cancellationToken); var syntaxFacts = Document.GetLanguageService(); var parameterNames = NameGenerator.EnsureUniqueness( @@ -73,6 +77,7 @@ private INamedTypeSymbol[] AttributesToRemove(Compilation compilation) Accessibility accessibility, bool generateAbstractly, bool useExplicitInterfaceSymbol, + ImplementTypePropertyGenerationBehavior propertyGenerationBehavior, INamedTypeSymbol[] attributesToRemove, CancellationToken cancellationToken) { @@ -81,6 +86,12 @@ private INamedTypeSymbol[] AttributesToRemove(Compilation compilation) return null; } + if (property.GetMethod == null) + { + // Can't have an auto-prop with just a setter. + propertyGenerationBehavior = ImplementTypePropertyGenerationBehavior.PreferThrowingProperties; + } + var setMethod = property.SetMethod.RemoveInaccessibleAttributesAndAttributesOfTypes( this.State.ClassOrStructType, attributesToRemove); @@ -90,7 +101,8 @@ private INamedTypeSymbol[] AttributesToRemove(Compilation compilation) attributes: default(ImmutableArray), accessibility: accessibility, explicitInterfaceSymbol: useExplicitInterfaceSymbol ? property.SetMethod : null, - statements: GetSetAccessorStatements(compilation, property, generateAbstractly, cancellationToken)); + statements: GetSetAccessorStatements( + compilation, property, generateAbstractly, propertyGenerationBehavior, cancellationToken)); } private IMethodSymbol GenerateGetAccessor( @@ -99,6 +111,7 @@ private INamedTypeSymbol[] AttributesToRemove(Compilation compilation) Accessibility accessibility, bool generateAbstractly, bool useExplicitInterfaceSymbol, + ImplementTypePropertyGenerationBehavior propertyGenerationBehavior, INamedTypeSymbol[] attributesToRemove, CancellationToken cancellationToken) { @@ -116,13 +129,15 @@ private INamedTypeSymbol[] AttributesToRemove(Compilation compilation) attributes: default(ImmutableArray), accessibility: accessibility, explicitInterfaceSymbol: useExplicitInterfaceSymbol ? property.GetMethod : null, - statements: GetGetAccessorStatements(compilation, property, generateAbstractly, cancellationToken)); + statements: GetGetAccessorStatements( + compilation, property, generateAbstractly, propertyGenerationBehavior, cancellationToken)); } private ImmutableArray GetSetAccessorStatements( Compilation compilation, IPropertySymbol property, bool generateAbstractly, + ImplementTypePropertyGenerationBehavior propertyGenerationBehavior, CancellationToken cancellationToken) { if (generateAbstractly) @@ -157,13 +172,16 @@ private INamedTypeSymbol[] AttributesToRemove(Compilation compilation) return ImmutableArray.Create(factory.ExpressionStatement(expression)); } - return factory.CreateThrowNotImplementedStatementBlock(compilation); + return propertyGenerationBehavior == ImplementTypePropertyGenerationBehavior.PreferAutoProperties + ? default(ImmutableArray) + : factory.CreateThrowNotImplementedStatementBlock(compilation); } private ImmutableArray GetGetAccessorStatements( Compilation compilation, IPropertySymbol property, bool generateAbstractly, + ImplementTypePropertyGenerationBehavior propertyGenerationBehavior, CancellationToken cancellationToken) { if (generateAbstractly) @@ -196,8 +214,10 @@ private INamedTypeSymbol[] AttributesToRemove(Compilation compilation) return ImmutableArray.Create(factory.ReturnStatement(expression)); } - return factory.CreateThrowNotImplementedStatementBlock(compilation); + return propertyGenerationBehavior == ImplementTypePropertyGenerationBehavior.PreferAutoProperties + ? default(ImmutableArray) + : factory.CreateThrowNotImplementedStatementBlock(compilation); } } } -} +} \ No newline at end of file diff --git a/src/Features/Core/Portable/ImplementInterface/AbstractImplementInterfaceService.cs b/src/Features/Core/Portable/ImplementInterface/AbstractImplementInterfaceService.cs index 660fb901fe65e9bea6b0e41dfe3ca9140a0f3e9f..7af03d954dd68d50cb01a2226415604a121e6a9c 100644 --- a/src/Features/Core/Portable/ImplementInterface/AbstractImplementInterfaceService.cs +++ b/src/Features/Core/Portable/ImplementInterface/AbstractImplementInterfaceService.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.Linq; using System.Threading; diff --git a/src/Features/Core/Portable/ImplementType/ImplementTypeOptions.cs b/src/Features/Core/Portable/ImplementType/ImplementTypeOptions.cs index 853ba9de2a7018ef55ac25316b4fddf8a60d56fa..3f008ab326e0c4cc6084687bfaebb28257993efe 100644 --- a/src/Features/Core/Portable/ImplementType/ImplementTypeOptions.cs +++ b/src/Features/Core/Portable/ImplementType/ImplementTypeOptions.cs @@ -10,6 +10,12 @@ internal enum ImplementTypeInsertionBehavior AtTheEnd = 1, } + internal enum ImplementTypePropertyGenerationBehavior + { + PreferThrowingProperties = 0, + PreferAutoProperties = 1, + } + internal static class ImplementTypeOptions { public static readonly PerLanguageOption InsertionBehavior = @@ -19,5 +25,14 @@ internal static class ImplementTypeOptions defaultValue: ImplementTypeInsertionBehavior.WithOtherMembersOfTheSameKind, storageLocations: new RoamingProfileStorageLocation( $"TextEditor.%LANGUAGE%.{nameof(ImplementTypeOptions)}.{nameof(InsertionBehavior)}")); + + public static readonly PerLanguageOption PropertyGenerationBehavior = + new PerLanguageOption( + nameof(ImplementTypeOptions), + nameof(PropertyGenerationBehavior), + defaultValue: ImplementTypePropertyGenerationBehavior.PreferThrowingProperties, + storageLocations: new RoamingProfileStorageLocation( + $"TextEditor.%LANGUAGE%.{nameof(ImplementTypeOptions)}.{nameof(PropertyGenerationBehavior)}")); + } } \ No newline at end of file diff --git a/src/NuGet/NuGet.proj b/src/NuGet/NuGet.proj index 7c043f413f0e77a5c8b493d9f5e59f91b2b85733..2cf66880af289e0e57d3b14b2702752dc1b4b307 100644 --- a/src/NuGet/NuGet.proj +++ b/src/NuGet/NuGet.proj @@ -5,19 +5,19 @@ - + - + - + - + - \ No newline at end of file + diff --git a/src/Setup/DevDivPackages/Roslyn.proj b/src/Setup/DevDivPackages/Roslyn.proj index 7fd9635c5c66ee23ca34362495d72d17ecfbd34a..f7349818e07da336612bf112cea0ae164ae447af 100644 --- a/src/Setup/DevDivPackages/Roslyn.proj +++ b/src/Setup/DevDivPackages/Roslyn.proj @@ -3,16 +3,16 @@ - $(OutDir)DevDivInsertionFiles + $(OutputPath)\DevDivInsertionFiles $(InsertionFilesDir)\VS.Tools.Roslyn - $(OutDir)DevDivPackages\Roslyn + $(OutputPath)\DevDivPackages\Roslyn $(NuGetPerBuildPreReleaseVersion) $(NuGetReleaseVersion)-cibuild - $(OutDir) + $(OutputPath) $(NuGetPerBuildPreReleaseVersion) @@ -30,4 +30,4 @@ - \ No newline at end of file + diff --git a/src/Setup/DevDivVsix/CompilersPackage/Microsoft.CodeAnalysis.Compilers.swixproj b/src/Setup/DevDivVsix/CompilersPackage/Microsoft.CodeAnalysis.Compilers.swixproj index 03f9fc4b50c60a3a6c24dac2f10da3a10189c6ae..1be98515de706f33cd1dc7398c5e67fd383932f2 100644 --- a/src/Setup/DevDivVsix/CompilersPackage/Microsoft.CodeAnalysis.Compilers.swixproj +++ b/src/Setup/DevDivVsix/CompilersPackage/Microsoft.CodeAnalysis.Compilers.swixproj @@ -6,7 +6,8 @@ neutral false - $(OutDir)Insertion + $(OutputPath) + $(OutputPath)\Insertion true vsix @@ -14,7 +15,7 @@ - $(PackagePreprocessorDefinitions);Version=$(VsixVersion);OutputPath=$(OutDir) + $(PackagePreprocessorDefinitions);Version=$(VsixVersion);OutputPath=$(RoslynOutputPath) $(BaseIntermediateOutputPath)$(Configuration)\ @@ -23,4 +24,4 @@ - \ No newline at end of file + diff --git a/src/Setup/DevDivVsix/CompilersPackage/Microsoft.CodeAnalysis.Compilers.vsmanproj b/src/Setup/DevDivVsix/CompilersPackage/Microsoft.CodeAnalysis.Compilers.vsmanproj index d17ac1dd62634d8e2764531c837fcb1844737cad..2436b6d3b4ba01ccc08443b62ea9759760360cfc 100644 --- a/src/Setup/DevDivVsix/CompilersPackage/Microsoft.CodeAnalysis.Compilers.vsmanproj +++ b/src/Setup/DevDivVsix/CompilersPackage/Microsoft.CodeAnalysis.Compilers.vsmanproj @@ -6,7 +6,7 @@ true true - $(OutDir)Insertion\ + $(OutputPath)Insertion\ true false false @@ -29,4 +29,4 @@ - \ No newline at end of file + diff --git a/src/Setup/DevDivVsix/MicrosoftCodeAnalysisLanguageServices/Microsoft.CodeAnalysis.LanguageServices.vsmanproj b/src/Setup/DevDivVsix/MicrosoftCodeAnalysisLanguageServices/Microsoft.CodeAnalysis.LanguageServices.vsmanproj index f1e02c279325502c9acef1c6d62d88de1bf2fc69..bd502c3f9e81c606d5b67fb364237f728612bed3 100644 --- a/src/Setup/DevDivVsix/MicrosoftCodeAnalysisLanguageServices/Microsoft.CodeAnalysis.LanguageServices.vsmanproj +++ b/src/Setup/DevDivVsix/MicrosoftCodeAnalysisLanguageServices/Microsoft.CodeAnalysis.LanguageServices.vsmanproj @@ -6,7 +6,7 @@ true true - $(OutDir)Insertion + $(OutputPath)\Insertion true false false @@ -38,4 +38,4 @@ - \ No newline at end of file + diff --git a/src/Setup/DevDivVsix/PortableFacades/PortableFacades.swixproj b/src/Setup/DevDivVsix/PortableFacades/PortableFacades.swixproj index f931f13551c3ff4ab3a078ad5085d678ef69dfc9..9163ec5a36dbcfbacd96b3701e8ceaf564ab84c3 100644 --- a/src/Setup/DevDivVsix/PortableFacades/PortableFacades.swixproj +++ b/src/Setup/DevDivVsix/PortableFacades/PortableFacades.swixproj @@ -6,7 +6,7 @@ neutral false - $(OutDir)Insertion + $(OutputPath)\Insertion true vsix @@ -23,4 +23,4 @@ - \ No newline at end of file + diff --git a/src/Setup/DevDivVsix/PortableFacades/PortableFacades.vsmanproj b/src/Setup/DevDivVsix/PortableFacades/PortableFacades.vsmanproj index a6fecdc827d36e390e91965dd3d8386211df2a61..89dc8b22de47e1bf72a01d8d741ad4eeb8975799 100644 --- a/src/Setup/DevDivVsix/PortableFacades/PortableFacades.vsmanproj +++ b/src/Setup/DevDivVsix/PortableFacades/PortableFacades.vsmanproj @@ -6,7 +6,7 @@ true true - $(OutDir)Insertion\ + $(OutputPath)\Insertion\ true false false @@ -24,4 +24,4 @@ - \ No newline at end of file + diff --git a/src/Setup/SetupStep1.proj b/src/Setup/SetupStep1.proj index 04f418a8306cfe5e2e6ba921188be63a69d81de6..2a5d8d0393e03190f4fa23a1dac1859393811b18 100644 --- a/src/Setup/SetupStep1.proj +++ b/src/Setup/SetupStep1.proj @@ -25,7 +25,7 @@ where building multiple projects that produce VSIXes larger than 10MB will race against each other --> - + @@ -40,4 +40,4 @@ - \ No newline at end of file + diff --git a/src/Setup/SetupStep2.proj b/src/Setup/SetupStep2.proj index 4f6b35d31de34f538fc3807b6aef21a700c7b1e5..aa04243ae80dd1b60db810f661da63fdab92cf87 100644 --- a/src/Setup/SetupStep2.proj +++ b/src/Setup/SetupStep2.proj @@ -8,7 +8,7 @@ - + @@ -23,4 +23,4 @@ - \ No newline at end of file + diff --git a/src/Setup/Vsix/Vsix.proj b/src/Setup/Vsix/Vsix.proj index 1b53f5670354939bbd17732bd38f9b714375312c..21b7f52dfded6ca6035bfd283e746d60a7aa34c8 100644 --- a/src/Setup/Vsix/Vsix.proj +++ b/src/Setup/Vsix/Vsix.proj @@ -6,7 +6,7 @@ - + - \ No newline at end of file + diff --git a/src/VisualStudio/CSharp/Impl/Options/AdvancedOptionPageControl.xaml b/src/VisualStudio/CSharp/Impl/Options/AdvancedOptionPageControl.xaml index bb02d9d38ec13917d81a2cccc549eafbb86477a2..a365fc75093acacec5c213bb2aee71f04a8884c4 100644 --- a/src/VisualStudio/CSharp/Impl/Options/AdvancedOptionPageControl.xaml +++ b/src/VisualStudio/CSharp/Impl/Options/AdvancedOptionPageControl.xaml @@ -100,6 +100,16 @@ x:Name="at_the_end" Content="{x:Static local:AdvancedOptionPageStrings.Option_at_the_end}"/> + +